Supprimer Rendre public Rendre privé Add tags Delete tags
  Ajouter un tag   Annuler
  Supprimer le tag   Annuler
  • • DevOps notes •
  •  
  • AI
  • Tags
  • Connexion

K9s - Manage Your Kubernetes Clusters In Stylehttps://k9scli.io/

  • kubernetes
  • kubernetes

Kubernetes CLI To Manage Your Clusters In Style!

3 hours ago Permalien
cluster icon
  • Kubernetes Spec : Reference Guide and Documentation : Find the documentation for all Kubernetes resources, properties, types, and examples.
  • Argo CD - Declarative GitOps CD for Kubernetes : Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes.
  • Building Kubernetes Operators : A comprehensive, hands-on and free course for building production-ready Kubernetes operators using Kubebuilder.

Building Kubernetes Operatorshttps://piyushjajoo.github.io/k8s-operators-course/

  • kubernetes
  • kubernetes

A comprehensive, hands-on and free course for building production-ready Kubernetes operators using Kubebuilder.

3 hours ago Permalien
cluster icon
  • Kubernetes Spec : Reference Guide and Documentation : Find the documentation for all Kubernetes resources, properties, types, and examples.
  • K9s - Manage Your Kubernetes Clusters In Style : Kubernetes CLI To Manage Your Clusters In Style!
  • Argo CD - Declarative GitOps CD for Kubernetes : Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes.

Argo CD - Declarative GitOps CD for Kuberneteshttps://argo-cd.readthedocs.io/en/stable/

  • kubernetes
  • kubernetes

Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes.

3 hours ago Permalien
cluster icon
  • Building Kubernetes Operators : A comprehensive, hands-on and free course for building production-ready Kubernetes operators using Kubebuilder.
  • K9s - Manage Your Kubernetes Clusters In Style : Kubernetes CLI To Manage Your Clusters In Style!
  • Kubernetes Spec : Reference Guide and Documentation : Find the documentation for all Kubernetes resources, properties, types, and examples.

Kubernetes Spec : Reference Guide and Documentationhttps://kubespec.dev/

  • kubernetes
  • kubernetes

Find the documentation for all Kubernetes resources, properties, types, and examples.

3 hours ago Permalien
cluster icon
  • Argo CD - Declarative GitOps CD for Kubernetes : Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes.
  • K9s - Manage Your Kubernetes Clusters In Style : Kubernetes CLI To Manage Your Clusters In Style!
  • Building Kubernetes Operators : A comprehensive, hands-on and free course for building production-ready Kubernetes operators using Kubebuilder.

AWS (SAA-C03) Mind maphttps://www.mindmeister.com/app/map/3471885158?t=lE6MXlXHYC

  • aws
  • aws

3 hours ago Permalien
cluster icon
  • Floci: AWS Local Emulator alternative · GitHub : Light, fluffy, and always free - The AWS Local Emulator alternative

Is It Worth Automating?https://agileadam.com/worth-automating/

  • tools
  • tools

Is It Worth Automating?

Sometimes it costs you more time to automate something than it does to just do it.

3 hours ago Permalien
cluster icon
  • IP Subnet Calculator :
  • Chmod Calculator :

IP Subnet Calculatorhttps://www.calculator.net/ip-subnet-calculator.html

  • linux
  • tools
  • linux
  • tools

3 hours ago Permalien
cluster icon
  • Chmod Calculator :
  • System Run Level and Boot Process : System Run Level init 0 → shutdown init 1 → single user mode → troubleshoot init 6 → reboot the system init 2 → multiuser without networking init 3 →...
  • Networking : Interface configuration files: /etc/nsswitch.conf → where resolve hostname to IP address /etc/hosts → add new IP to resolve /etc/resolv.conf → r...
  • Stratis : Stratis → advanced storage management Extend filesystem automatically when needed dnf install stratis-cli stratisd dnf makecache --refresh Upda...
  • Kickstart – Automate Linux Install : Kickstart server Make Kickstart available on the network Make installation source available Make boot media available Start Kickstart instal...

Chmod Calculatorhttps://nettools.club/chmod_calc

  • linux
  • tools
  • linux
  • tools

3 hours ago Permalien
cluster icon
  • IP Subnet Calculator :
  • Linux Boot Process / Optimizing Boot Performance : Linux Boot Process → Older Version BIOS → Basic Input / Output System executes MBR MBR → Master Boot Record executes GRUB GRUB → Grand U...
  • Bash : First Line of Script #!/bin/bash → defines the shell interpreter Comments Use # for commenting Common Elements Commands: echo, cp, etc. Statement...
  • Alias and command history : alias l="ls -al" → define alias unalias l → remove alias Persistent Alias Config User-specific: ~/.bashrc Global: /etc/bashrc Add: alias l="ls -a...
  • Is It Worth Automating? : Is It Worth Automating? Sometimes it costs you more time to automate something than it does to just do it.

SadServers: Linux, DevOps & SRE Labs | Interview & Hiring Assessmenthttps://sadservers.com/

  • linux
  • adminsys
  • linux
  • adminsys

Hands-On Linux & DevOps
Real Challenges. Real Infra. Real Skills.

Master Linux & DevOps troubleshooting on live servers.

Fun, real-world challenges for engineers
and powerful assessments for hiring teams.

3 hours ago Permalien
cluster icon
  • Tuned : Tweaks with Tuned / Podman Tweaks with Tuned systemctl enable tuned tuned-adm active check which profile is active tuned-adm list list all prof...
  • Logical Volume Management (LVM) : LVM (Logical Volume Management) Combine disk together by software Add new HDD on the fly to extend disk space Set LVM in Linux install Desired...
  • Chmod Calculator :
  • Crontab / at : Crontab Basics crontab -e → edit crontab crontab -l → list crontab entries crontab -r → remove crontab entries systemctl status crond → check crond s...
  • Alias and command history : alias l="ls -al" → define alias unalias l → remove alias Persistent Alias Config User-specific: ~/.bashrc Global: /etc/bashrc Add: alias l="ls -a...

Enroll - Reverse-engineering servers into Ansiblehttps://enroll.sh/

  • ansible
  • adminsys
  • ansible
  • adminsys

Get an existing Linux host into Ansible in seconds.

Enroll inspects a Debian-like or RedHat-like system, harvests the state that matters, and generates Ansible roles/playbooks so you can bring snowflakes under management fast.

3 hours ago Permalien
cluster icon
  • SadServers: Linux, DevOps & SRE Labs | Interview & Hiring Assessment : Hands-On Linux & DevOps Real Challenges. Real Infra. Real Skills. Master Linux & DevOps troubleshooting on live servers. Fun, real-world challenges fo...
  • Ansible : Control node → server which runs Ansible Modules → command executed on client side (found pre-made modules on Ansible website) Task → multiple proced...

Floci: AWS Local Emulator alternative · GitHubhttps://github.com/floci-io/floci

  • aws
  • aws

Light, fluffy, and always free - The AWS Local Emulator alternative

3 hours ago Permalien
cluster icon
  • AWS (SAA-C03) Mind map :

Adding Tests to a Multi-File Project/shaare/PAwfIg

  • python
  • python

Adding Tests to a Multi-File Project

Standard Project Layout with Tests

To maintain a clean and organized codebase, it is standard practice to separate your application code from your test code. This is typically achieved by creating a dedicated tests directory at the project's root level.

  • The project root contains the main application package (e.g., devops_utils) and the tests directory as siblings.
  • The tests directory houses all the test files. It is common for the structure inside tests to mirror the structure of the application package to keep tests organized as the project grows.
  • It is also good practice to place an empty __init__.py file inside the tests directory to ensure it can be treated as a package if needed, although pytest's discovery mechanism is powerful enough to work without it in most cases.

Importing Application Code into Tests

To test your application code, your test files must import the functions, classes, and variables that need to be verified. The standard and most robust way to do this is by using absolute imports that start from the project root.

  • A test file, such as tests/test_file_ops.py, will use an import statement like from devops_utils.file_ops import check_file_extension.
  • This import syntax assumes that the project's root directory is on Python's module search path (sys.path).
  • Attempting to run a test file directly as a Python script will fail with a ModuleNotFoundError, because the project root is not automatically added to the path in that context. This is why a test runner like pytest is necessary.

Running pytest for Project Discovery

The pytest framework is designed to handle the complexities of testing structured projects. When you run pytest from your project's root directory, it intelligently prepares the environment for test execution.

  • Pytest begins by scanning upwards from the current directory to find a configuration file (like pyproject.toml or pytest.ini), which it uses to identify the project's root.
  • Once the root is identified, pytest automatically adds this directory to sys.path.
  • With the path correctly configured, the absolute imports within your test files will resolve successfully, allowing your tests to find and execute the application code.

Robust Test Execution with python -m pytest

While running pytest directly is often sufficient, an even more reliable method is to invoke it as a module using the python -m flag. This approach is highly recommended, especially in automated environments like CI/CD pipelines.

  • The command python -m pytest guarantees that the current working directory is added to sys.path before pytest begins its execution.
  • This method eliminates many path-related ambiguities that can arise in complex project structures or different operating environments.
  • It is considered the most robust and explicit way to run your test suite, ensuring consistent behavior across different machines and setups.

Common Pitfalls & How to Avoid Them

When setting up tests for a multi-file project, there are several common issues that can be easily avoided.

  • ModuleNotFoundError during test runs: This is the most frequent problem and is almost always caused by running pytest from the wrong directory. To avoid this, always run pytest from the project root, or preferably use python -m pytest. An editable install (pip install -e .) provides the most robust solution.
  • Tests importing other tests: This is considered an anti-pattern as it creates implicit dependencies between tests. If you need to share test logic or data, use conftest.py to define shared fixtures.
  • Using relative imports in tests: Imports like from ..devops_utils import ... are fragile and should be avoided in test files. Always use absolute imports from the project root (e.g., from devops_utils.file_utils import ...).
1 month ago Permalien
cluster icon
  • Fixtures in Pytest : Fixtures in Pytest As tests grow more complex, repeating setup and cleanup steps makes tests harder to read and maintain. Pytest fixtures allow centr...
  • Temporary Files and Directories : Temporary Files and Directories Automation scripts often need scratch space for intermediate data without cluttering the filesystem or risking name c...
  • Automated Testing with Pytest : Assertions in Pytest Pytest uses Python’s built-in assert statement to declare expected conditions in tests, making test code concise and readable. W...
  • Logging Anatomy : Python Logging Anatomy Python’s logging module has five core components: Loggers, Log Records, Handlers, Formatters and Filters. Loggers are hierar...
  • Generators : Generators Writing a class-based iterator requires __iter__() and __next__(), plus manual state management and StopIteration handling. Generator fu...

Editable Installs with pyproject.toml/shaare/oYfOwA

  • python
  • python

Editable Installs with pyproject.toml

The Python interpreter doesn't automatically know about our project's structure. The modern and most robust solution is to formally define our project as an installable package. By creating a standard pyproject.toml metadata file, we can perform an "editable install" using the command pip install -e .. This seamlessly links our source code into the virtual environment, making our packages importable from anywhere without manual path hacks or special commands. This is the standard, professional workflow for developing Python applications.

The pyproject.toml File

The pyproject.toml file is the modern, unified standard for configuring Python projects, replacing older files like setup.py.

  • It is written in the simple and readable TOML (Tom's Obvious, Minimal Language) format.
  • It defines the project's build system, such as setuptools, in the [build-system] table.
  • It specifies essential project metadata, like the package name, version, and dependencies, in the [project] table.
  • It can also serve as a central location for configuring development tools like formatters and linters.

Editable Installs: pip install -e .

An "editable" or "development" install is a special mode for installing packages with pip.

  • The command pip install -e . installs the project from the current directory in "editable" mode.
  • Instead of copying files, it creates a special link from the virtual environment's site-packages directory back to the original source code.
  • The main benefit is that any changes made to the source .py files are immediately reflected in the installed package without needing to run pip install again.

Solving the Import Problem Permanently

Performing an editable install provides a definitive solution to the path and import issues encountered during development.

  • After an editable install, the project is effectively on sys.path for the entire activated virtual environment.
  • There is no longer any need to manually set the PYTHONPATH environment variable.
  • You can now run scripts that are inside packages directly, and they will be able to use absolute imports from the project root.
  • This approach creates a self-contained and reproducible environment, which is the standard for professional Python development.

Console Scripts vs. python -m

While a script with an if __name__ == "__main__" block can be run with python -m, defining a console script in pyproject.toml is the preferred professional approach.

  • User Experience: A console script (ping-check) is short and intuitive. A python -m command is long, exposes the internal module path, and is easy to mistype.
  • Abstraction: A console script hides your project's internal structure. You can refactor your code internally, and as long as you update the pyproject.toml file, the command remains the same for the user. The python -m command breaks if you rename or move the target file.
  • Clarity of Intent: Declaring a script in pyproject.toml clearly marks it as a public, supported command-line interface for your package.
[project]
name = "devops_utils"
version = "0.1.0"

[project.scripts]
check_host = "devops_utils.network_utils.check_host:main"
1 month ago Permalien
cluster icon
  • Working with YAML files : Working with YAML files YAML (“YAML Ain’t Markup Language”) focuses on human readability. Indentation replaces braces and brackets, comments are allo...
  • Handling Errors and Status Codes : Handling Errors and Status Codes HTTP status codes communicate the outcome of an API request, and handling them correctly is key to robust automation...
  • Working with JSON files : Working with JSON files JSON is the standard format for data exchange in web services and cloud APIs. Python’s built-in json module provides function...
  • Python Functions Are First‑Class Citizens : Python Functions Are First‑Class Citizens In Python, functions behave like any other object (strings, ints, lists). Because they are "first‑clas...
  • Typing : Introduction Python is a dynamically typed language, meaning you can assign values to variables without declaring their types, and type checking happ...

Running Python modules/shaare/k00RHQ

  • python
  • python

Running Scripts: python -m vs. python file.py

The Core Difference: What is "Entry Point Zero"?

The key to understanding the difference lies in the first entry of sys.path. When Python initializes, it needs to know where to start looking for modules. The way you call it determines this "entry point zero".

  • When you run a script directly using python path/to/script.py, the interpreter's main task is to execute that specific file. It sets the first entry of sys.path to be the directory that contains the script.

  • When you run a script as a module using python -m package.module, the interpreter's goal is to locate and run a module within an importable package. It sets the first entry of sys.path to be the current working directory from which the command was executed. This allows absolute imports from the project root to succeed.

Best Practice: Separating Library Code from Scripts

While you can run any file with python -m, it can lead to a RuntimeWarning if the file is both a library (meant to be imported) and a script (meant to be run). The best practice is to separate these roles.

  • Library Modules: These files (like our file_ops.py and network_ops.py) should contain only reusable functions and classes. They should not contain an if __name__ == "__main__": block for complex script logic.
  • Runner Scripts: For any action you want to make runnable from the command line, create a new, separate script.

Common Pitfalls & How to Avoid Them

  • Running scripts inside packages directly with python file.py will often cause ModuleNotFoundError for absolute imports. Avoid this by always running packaged scripts from the project root using python -m.
  • Making a single file both a complex library and a runnable script can lead to RuntimeWarning. Avoid this by separating concerns: create dedicated runner scripts that import from your library modules.
  • Forgetting the module path when using -m. The command must be the full dotted path to the script from the project root (e.g., python -m package.subpackage.script).
1 month ago Permalien
cluster icon
  • *args and **kwargs : Flexible Functions: *args and **kwargs We can use the syntax *args and **kwargs to accept a variable number of both positional and keyword arguments....
  • If / Elif / Else Logic : If / Elif / Else Logic Control the flow of scripts based on conditions using if, elif, and else. The if Statement An if statement executes a block of ...
  • Filesystem Paths : Working with Filesystem Paths in Python Manipulating paths as plain strings is error-prone and OS-specific. pathlib provides an object-oriented, cr...
  • Fixtures in Pytest : Fixtures in Pytest As tests grow more complex, repeating setup and cleanup steps makes tests harder to read and maintain. Pytest fixtures allow centr...
  • Functions: return vs yield : Functions: return vs yield Regular functions execute immediately, run to completion, and return a single value (or None). Generator functions retur...

Python package and subpackage/shaare/9j4UUQ

  • python
  • python

Introduction to Packages (__init__.py)

What is a Package?

A Python package provides a way to structure a project's module namespace by using directories. It enables a hierarchical organization of modules, mirroring the file system's structure.

  • Any directory that contains a file named __init__.py is recognized by the Python interpreter as a package.
  • A package directory can contain not only module files (ending in .py) but also other subdirectories that are themselves packages.

The Role of __init__.py

  • Its primary role is to serve as a marker, signaling to Python that the directory it resides in should be treated as a package. In older versions of Python, a directory without this file would not be recognized as a package. While newer versions have more lenient rules, explicitly including __init__.py is the standard and most compatible method.
  • The second purpose is for package initialization. Any code written inside the __init__.py file is executed once when the package or any of its modules are first imported, which can be useful for setting up package-level resources. For many packages, this file can simply be left empty.

Importing from a Package

  • You can import the entire module using the full path, such as import devops_utils.file_ops. Accessing its contents would then require the full prefix, like devops_utils.file_ops.check_file_extension().
  • Alternatively, you can use the from keyword to import the module more directly, as in from devops_utils import file_ops, which then allows access via file_ops.check_file_extension().
  • For even more direct access, you can import specific functions or variables, for example, from devops_utils.file_ops import check_file_extension. This makes the function available to be called directly as check_file_extension().

Using __init__.py to Control Imports

  • By importing members from the package's modules into the __init__.py file itself, you can make them appear as if they belong to the top-level package namespace.
  • This technique can create a simpler, more user-friendly API for your package, but it can also obscure the underlying module structure.

Importing from Subpackages

Project Structure with Subpackages

To improve organization, we can group related modules into their own dedicated subpackages. A subpackage is a directory inside a parent package that contains its own __init__.py file.

  • A top-level package, like devops_utils, can contain multiple subpackages.
  • For example, we can create a file_utils subpackage for file-related modules and a network_utils subpackage for networking modules.
  • Each of these subdirectories must contain an __init__.py file (which can be empty) to be recognized by Python as a package.

Absolute Imports

An absolute import provides the complete, explicit path to a module starting from a top-level directory that is on Python's search path (sys.path).

  • The syntax follows the project's directory structure, such as from package.subpackage.module import function.
  • This is the most recommended and readable way to import modules, particularly in top-level scripts that execute the application's logic.
  • Absolute imports are unambiguous and clearly state the origin of the imported code, which greatly improves code maintainability. For example, a script outside the devops_utils package would use from devops_utils.file_utils.file_ops import check_file_extension to access a function.

Relative Imports

A relative import specifies the path to a module based on the location of the file performing the import. This is done using dot notation.

  • The syntax uses dots to navigate the package hierarchy: . refers to the current package, while .. refers to the parent package.
  • An import like from .sibling_module import name brings in a name from a module in the same directory. An import like from ..parent_sibling.module import name navigates one level up and then down into a sibling package.
  • Relative imports are primarily used for communication within a package. Their main advantage is that they make the package self-contained; if you rename the top-level package, the internal relative imports will not break.

The ImportError Trap with Relative Imports

A significant pitfall arises when you attempt to directly execute a Python file that contains relative imports. This action will almost always result in an error.

  • Running a script like python devops_utils/network_utils/network_ops.py will raise an ImportError: attempted relative import with no known parent package.
  • This happens because when a file is run directly, Python sets its name (__name__) to "__main__" and does not recognize it as being part of a package. Consequently, it cannot resolve relative paths like . or ...
  • The rule of thumb is that relative imports should only be used for intra-package imports, and the application should be started from a top-level script that uses absolute imports to access the package's functionality.
1 month ago Permalien
cluster icon
  • Generators : Generators Writing a class-based iterator requires __iter__() and __next__(), plus manual state management and StopIteration handling. Generator fu...
  • Classes and Objects : Classes and Objects Beyond Built-ins: Python lets you define your own data types using class. Class: A blueprint or template for creating objects. De...
  • Generators and Lazy Pipelines : Generators and Lazy Pipelines You can chain generator functions to form multi-stage data pipelines that process items one at a time. No intermediat...
  • Range, zip : Efficient Looping: range Creating large lists for loops is memory-intensive (e.g., list(range(1_000_000))). range() stores only start, stop, and step...
  • Handling Errors and Status Codes : Handling Errors and Status Codes HTTP status codes communicate the outcome of an API request, and handling them correctly is key to robust automation...

Python Modules and the import System/shaare/XckVlw

  • python
  • python

Python Modules and the import System

What is a Module?

  • A module in Python corresponds directly to a single file containing Python code.
  • The module's name is derived from its filename.
  • Any file with a .py extension can be treated as a module.
  • The name used to import the module is the filename without the .py suffix. For example, a file named file_ops.py is imported as the file_ops module.

The import Statement

  • The most straightforward way to import a module is with import module_name
  • To use functions, variables, or classes from the imported module, you must prefix them with the module name and a dot, such as module_name.function_name.
  • This method creates a dedicated namespace for the imported module, which is highly effective at preventing name collisions. For instance, a variable named CONFIG in your script will not conflict with module_name.CONFIG.
  • It enhances code clarity by making it obvious where each function or variable originates, which is especially helpful in larger projects.

The from...import Statement

  • It's possible to bring only specific objects from a module with from ... import ....
  • You can use the as keyword to rename an imported object, for example, from file_ops import parse_yaml_file as parse_yaml.
  • This approach can make code more concise because it requires less typing (parse_yaml() instead of file_ops.parse_yaml_file()).
  • The primary drawback is the increased risk of name collisions. If you import a function named my_function and later define your own function with the same name, the original import will be overwritten.
  • Using from module import * is strongly discouraged because it imports all public names from the module, which can pollute the local namespace and make the code difficult to read and debug.

How Python Finds Modules: sys.path

When you execute an import statement, Python needs to locate the corresponding module file. It does this by searching through a specific list of directories.

  • The search path is stored in a list of strings called sys.path, which is part of the standard sys module.
  • The sys.path list is automatically populated and typically includes the directory of the script that is currently running, directories specified in the PYTHONPATH environment variable, and the default locations where Python and third-party packages are installed.
  • The fact that the script's own directory is the first entry on this path is why a script like main.py can seamlessly import utils.py when both files are located in the same folder.

Example of main.py

print("Main script starting...")

from devops_utils import (
    check_file_extension,
    is_host_up,
    check_hosts_from_config,
)
import sys

print(sys.path)

filenames = ["config.yaml", "script.sh"]

for filename in filenames:
    print(f"Checking {filename}")
    print(f"Result: {check_file_extension(filename)}")

print(f"\nIs localhost up? {is_host_up("localhost")}")
print(
    f"Is nonexistenthost12345 up? {is_host_up("nonexistenthost12345")}"
)

print(
    f"\nAre all hosts from servers_config.yaml up? {check_hosts_from_config("servers_config.yaml")}"
)

Example of file_ops.py

print("Module file_ops is being imported")

from typing import Any

try:
    import yaml
except (ModuleNotFoundError, ImportError):
    print(
        "Warning: PyYAML not found, parse_yaml_file will not work."
    )
    yaml = None

SUPPORTED_EXTENSIONS: list[str] = [".json", ".yaml", ".txt"]

def check_file_extension(filename: str) -> bool:
    """Checks if a file has a supported extension"""
    print(
        f"  - file_ops.check_file_extension called for {filename}"
    )
    return any(
        filename.endswith(ext) for ext in SUPPORTED_EXTENSIONS
    )

def parse_yaml_file(path_str: str) -> dict[str, Any]:
    """Parses a YAML file and returns its contents."""
    print(f"  - file_ops.parse_yaml_file called for {path_str}")
    if yaml:
        with open(path_str, "r") as file:
            return yaml.safe_load(file)
    else:
        return {}
1 month ago Permalien
cluster icon
  • Working with JSON files : Working with JSON files JSON is the standard format for data exchange in web services and cloud APIs. Python’s built-in json module provides function...
  • Lambda Functions : Lambda Functions Python functions defined with def allow multiple statements, clear naming, and support for docstrings, making them ideal for complex...
  • Adding Type Hints to Decorators and Generators : Adding Type Hints to Decorators and Generators Decorators and generators are advanced constructs that require specialized type hints to make their tr...
  • Functions: return vs yield : Functions: return vs yield Regular functions execute immediately, run to completion, and return a single value (or None). Generator functions retur...
  • Editable Installs with pyproject.toml : Editable Installs with pyproject.toml The Python interpreter doesn't automatically know about our project's structure. The modern and most robust solu...

Mocking/shaare/G_SN4g

  • python
  • python

Mocking Fundamentals

Introduction

  • When unit testing DevOps scripts that interact with external systems, tests can become slow, unreliable, difficult to set up, or even destructive.
  • Mocking replaces these real dependencies with controlled, fake objects so that tests run quickly and deterministically.
  • Python’s built-in unittest.mock module provides tools to create and configure these mock objects and to track interactions.

What is Mocking?

  • Mocking involves creating objects that mimic the behavior of real functions or classes in a controlled environment.
  • When your code calls a mocked object, you can specify what it returns, simulate exceptions, or inspect how it was called.
  • This allows you to isolate the logic under test and avoid side effects from actual external calls.

Using unittest.mock.patch

  • The patch function replaces a target object with a mock in a specified scope, either for the duration of a function (decorator) or within a context block (with).
  • As a decorator, patch injects the mock into the test function’s parameters; as a context manager, it yields the mock within the with block.
  • It’s important to patch the object where it is looked up in the module under test, not necessarily where it is originally defined.

MagicMock and Configuring Mock Objects

  • When you patch an object, you typically receive a MagicMock instance that you can configure.
  • Use mock.return_value to define what the mock will return when called.
  • Use mock.side_effect to simulate an exception being raised by the mock when invoked, to pass different values to be returned by each execution, or to pass a calable to replace the implemented function.
  • Assertion methods like assert_called_with and assert_called_once let you verify interactions with the mock.

Common Mocking Scenarios in DevOps

  • Network API Calls: Mock requests.get or requests.post to simulate successful responses, HTTP errors, or timeouts.
  • Filesystem Operations: Mock functions like open() or os.path.exists() to simulate file presence or content.
  • Subprocess Execution: Mock subprocess.run to avoid running real system commands and control return codes.
  • Time-Dependent Code: Patch time.sleep or mock datetime.now() to remove delays and make time-based tests deterministic.
from unittest.mock import patch, Mock
from pytest_mock import MockerFixture
from dummy_functions import check_file_exists, get_user_data

# Section: Using unittest.mock.patch
def test_check_file_exists_manual_patch() -> None:
    filepath = "/path/to/some/file.txt"

    patcher = patch("dummy_functions.os.path.exists")
    mock_exists = patcher.start()

    mock_exists.return_value = True

    try:
        result = check_file_exists(filepath=filepath)
        mock_exists.assert_called_once_with(filepath)
        assert result is True
    finally:
        patcher.stop()

def test_check_file_exists_context_manager() -> None:
    filepath = "/path/to/some/file.txt"

    with patch("dummy_functions.os.path.exists") as mock_exists:
        mock_exists.return_value = True

        result = check_file_exists(filepath=filepath)
        mock_exists.assert_called_once_with(filepath)
        assert result is True

@patch("dummy_functions.os.path.exists")
def test_check_file_exists_decorator(mock_exists: Mock) -> None:
    filepath = "/path/to/some/file.txt"

    mock_exists.return_value = True

    result = check_file_exists(filepath=filepath)
    mock_exists.assert_called_once_with(filepath)
    assert result is True

def test_check_file_pytest_mocker(mocker: MockerFixture) -> None:
    filepath = "/path/to/some/file.txt"

    mock_exists = mocker.patch("dummy_functions.os.path.exists")
    mock_exists.return_value = True

    result = check_file_exists(filepath=filepath)
    mock_exists.assert_called_once_with(filepath)
    assert result is True

# Section: MagicMock and Configuring Mock Objects
def test_get_user_data_success(mocker: MockerFixture) -> None:
    mock_api_response: dict[str, str | int] = {
        "id": 1,
        "name": "test user",
    }

    mock_get = mocker.patch("dummy_functions.requests.get")
    mock_get.return_value.status_code = 200
    mock_get.return_value.json.return_value = mock_api_response

    data = get_user_data(user_id=1)

    mock_get.assert_called_once_with(
        "https://api.example.com/users/1"
    )
    assert data == mock_api_response

Example of dummy_function.py

import requests
import os
import subprocess
from typing import Optional, Any

def get_user_data(user_id: str | int) -> dict[str, str | int]:
    response = requests.get(
        f"https://api.example.com/users/{user_id}"
    )
    print(f"Status code: {response.status_code}")
    response.raise_for_status()
    return response.json()

def check_file_exists(filepath: str | os.PathLike[str]) -> bool:
    return os.path.exists(filepath)

def get_external_ip():
    """Fetches the current external IP from an external service."""
    try:
        response = requests.get(
            "https://api.ipify.org?format=json", timeout=5
        )
        response.raise_for_status()
        return response.json().get("ip")
    except requests.exceptions.RequestException:
        return None

def get_current_user() -> Optional[str]:
    try:
        result = subprocess.run(
            ["whoami"],
            capture_output=True,
            text=True,
            check=True,
            timeout=5,
        )
        return result.stdout.strip()
    except (
        subprocess.CalledProcessError,
        subprocess.TimeoutExpired,
        FileNotFoundError,
    ):
        return None

def fetch_both_endpoints() -> (
    tuple[dict[str, Any], dict[str, Any]]
):
    """
    Fetch data from two endpoints and return their JSON responses as a tuple.
    """
    response2 = requests.get("https://api.example.com/second")
    response2.raise_for_status()
    data2 = response2.json()

    response1 = requests.get("https://api.example.com/first")
    response1.raise_for_status()
    data1 = response1.json()

    return data1, data2

Advanced Mocking Concepts

Using side_effect

  • The side_effect attribute on a mock allows you to control its behavior beyond a single return value.
  • List of values: When side_effect is set to a list, each call to the mock returns the next item in that list, in order.
  • Callable: When side_effect is a function, it is called with the same arguments as the mock, and its return value is used as the mock’s return.
  • Exception: When side_effect is an exception, it will raise that exception when the original function is called.
  • Use a list when you know the sequence and order of calls; use a function when behavior should vary based on arguments.

Choosing between Mock and MagicMock

  • Mock: A simple replacement that only creates attributes when accessed, and raises errors for undefined methods or attributes.
  • MagicMock: Inherits from Mock and implements Python’s magic methods (__len__, __enter__, etc.) by default.
  • Use Mock by default for stubbing external dependencies to catch unintended interactions.
  • Use MagicMock only when mocking objects that require special behavior, such as context managers or iterables.
import subprocess
import pytest
from unittest.mock import MagicMock
from pytest_mock import MockerFixture
from dummy_functions import (
    get_current_user,
    check_file_exists,
    fetch_both_endpoints,
)

# Section: Using side_effect - Exceptions

def test_get_current_user_command_fails(mocker: MockerFixture):
    mock_run = mocker.patch("dummy_functions.subprocess.run")
    mock_run.side_effect = subprocess.CalledProcessError(
        returncode=1, cmd=["whoami"]
    )

    result = get_current_user()

    assert result is None

# Section: Using side_effect - List for Multiple Calls

def test_check_file_exists_side_effect_list(
    mocker: MockerFixture,
):
    mock_exists = mocker.patch(
        "dummy_functions.os.path.exists",
        side_effect=[True, False],
    )

    assert check_file_exists("some/path/one") is True
    assert check_file_exists("some/path/two") is False

    assert mock_exists.call_count == 2

    assert [
        call.args for call in mock_exists.call_args_list
    ] == [("some/path/one",), ("some/path/two",)]

# Section: Using side_effect - Callable for Multiple Calls

def test_fetch_both_endpoints_by_url(mocker: MockerFixture):
    fake_responses: dict[str, MagicMock] = {}

    for url, data in [
        ("https://api.example.com/first", {"first": "data"}),
        ("https://api.example.com/second", {"second": "data"}),
    ]:
        resp = mocker.MagicMock()
        resp.status_code = 200
        resp.json.return_value = data

        fake_responses[url] = resp

    def _fake_get(url: str) -> MagicMock:
        return fake_responses[url]

    mocker.patch(
        "dummy_functions.requests.get", side_effect=_fake_get
    )

    result = fetch_both_endpoints()

    assert result == ({"first": "data"}, {"second": "data"})

# Section: Choosing between Mock and MagicMock

@pytest.mark.xfail(
    reason="Context managers do not work with Mock", strict=True
)
def test_context_manager_with_mock(mocker: MockerFixture):
    fake_cm = mocker.Mock()
    fake_cm.__enter__.return_value = fake_cm
    fake_cm.read.return_value = "file contents"

    mock_open = mocker.patch("builtins.open")
    mock_open.return_value = fake_cm

    with open("somefile.txt") as f:
        contents = f.read()

    mock_open.assert_called_once_with("somefile.txt")
    assert contents == "file contents"

def test_context_manager_with_magicmock(mocker: MockerFixture):
    fake_cm = mocker.MagicMock()
    fake_cm.__enter__.return_value = fake_cm
    fake_cm.read.return_value = "file contents"

    mock_open = mocker.patch("builtins.open")
    mock_open.return_value = fake_cm

    with open("somefile.txt") as f:
        contents = f.read()

    mock_open.assert_called_once_with("somefile.txt")
    assert contents == "file contents"
1 month ago Permalien
cluster icon
  • Configuring Pytest : Configuring Pytest As you start using Pytest extensively, typing -v or -m on the command line every time becomes tedious. Centralize your defaults in...
  • Running Python modules : Running Scripts: python -m vs. python file.py The Core Difference: What is "Entry Point Zero"? The key to understanding the difference lies in the fir...
  • If / Elif / Else Logic : If / Elif / Else Logic Control the flow of scripts based on conditions using if, elif, and else. The if Statement An if statement executes a block of ...
  • Working with YAML files : Working with YAML files YAML (“YAML Ain’t Markup Language”) focuses on human readability. Indentation replaces braces and brackets, comments are allo...
  • Handling Errors and Status Codes : Handling Errors and Status Codes HTTP status codes communicate the outcome of an API request, and handling them correctly is key to robust automation...

Parametrized Tests/shaare/0k7HqQ

  • python
  • python

Parametrized Tests

Introduction

  • Often, we need to test the same logic with different inputs and outputs, such as validating various IP address or hostname formats.
  • Writing individual test functions for each case leads to repetitive code and a test suite that is harder to maintain.
  • Parametrized tests allow a single test function to run multiple times with different data, adhering to the DRY principle and simplifying test maintenance.

The Problem: Duplicated Test Logic

  • A function that checks valid hostname character codes must be tested across letters, digits, hyphens, and invalid symbols.
  • Without parametrization, each input case requires its own test function, duplicating the assertion logic.
  • This approach increases verbosity and makes the test suite more error-prone and tedious to update.

Solution: @pytest.mark.parametrize

  • The @pytest.mark.parametrize(argnames, argvalues) decorator takes argument names and a list of value tuples to generate multiple test invocations.
  • Argument names can be provided as a comma-separated string or as a list of strings.
  • Each tuple in the argvalues list corresponds to a separate run of the test function, with tuple elements mapped to argument names.
  • Running Pytest with -v shows each parametrized case as a distinct test, simplifying result interpretation.

The pytest.param Construct

  • The pytest.param() function wraps a set of parameter values and allows you to attach metadata to that invocation:
    • id: a custom label shown in the test report.
    • marks: one or more markers (e.g., pytest.mark.xfail, pytest.mark.skip) applied only to that case.
  • This is useful when you want to:
    • Give human-readable names to complex or ambiguous parameter sets.
    • Mark individual cases as expected failures or skip them selectively.

Customizing Test IDs

  • By default, Pytest creates IDs from parameter values, which may be non-descriptive for complex data.
  • You can use pytest.param(..., id="custom_id") to assign clear, human-readable names to individual cases.
  • Alternatively, an ids list passed to parametrize can specify identifiers in the order of argvalues.
  • Custom IDs make it easier to identify failing cases in test reports and improve overall readability.
import pytest

def is_valid_hostname_char(char: str) -> bool:
    if "a" <= char <= "z":
        return True
    if "0" <= char <= "9":
        return True
    if char == "-":
        return True
    return False

def check_url_status(url: str) -> tuple[int | str, str]:
    if url == "https://google.com":
        return (200, "OK")
    if url == "https://fakesite123.org/notfound":
        return (404, "HTTP_ERROR (404)")
    if url == "http://httpbin.org/status/503":
        return (503, "HTTP_ERROR (503)")
    if url == "http://localhost:1":
        return ("CONNECTION_ERROR", "CONNECTION_ERROR")
    return ("UNKNOWN", "UNKNOWN")

# Section: The Problem: Duplicated Test Logic

"""
a -> True
5 -> True
- -> True
A -> False
_ -> False
"""

def test_is_valid_lower_case_a():
    assert is_valid_hostname_char("a") is True

def test_is_valid_5():
    assert is_valid_hostname_char("5") is True

def test_is_valid_hyphen():
    assert is_valid_hostname_char("-") is True

def test_is_valid_upper_case_A():
    assert is_valid_hostname_char("A") is False

def test_is_valid_underscore():
    assert is_valid_hostname_char("_") is False

# Section: Solution: @pytest.mark.parametrize

@pytest.mark.parametrize(
    "input_char, expected_result",
    [
        ("a", True),
        ("5", True),
        ("-", True),
        ("A", False),
        ("_", False),
        ("!", False),
    ],
)
def test_is_valid_hostname_char(
    input_char: str, expected_result: bool
):
    assert is_valid_hostname_char(input_char) is expected_result

# Section: Customizing Test IDs with pytest.param construct

@pytest.mark.parametrize(
    "input_char, expected_result",
    [
        pytest.param("a", True, id="lowercase_letter_a"),
        pytest.param("z", True, id="lowercase_letter_z"),
        pytest.param("0", True, id="digit_0"),
        pytest.param("-", True, id="hyphen"),
        pytest.param("A", False, id="uppercase_A_invalid"),
        pytest.param("_", False, id="underscore_invalid"),
    ],
)
def test_is_valid_hostname_custom_params(
    input_char: str, expected_result: bool
):
    assert is_valid_hostname_char(input_char) is expected_result

@pytest.mark.parametrize(
    "url_to_check, expected_status_code, expected_status_text",
    [
        ("https://google.com", 200, "OK"),
        (
            "https://fakesite123.org/notfound",
            404,
            "HTTP_ERROR (404)",
        ),
        (
            "http://httpbin.org/status/503",
            503,
            "HTTP_ERROR (503)",
        ),
        (
            "http://localhost:1",
            "CONNECTION_ERROR",
            "CONNECTION_ERROR",
        ),
        pytest.param(
            "https://pending.retries.tests",
            503,
            "HTTP_ERROR (503)",
            marks=(
                pytest.mark.xfail(
                    reason="Retry logic for 503 is not yet implemented."
                ),
                pytest.mark.api,
            ),
        ),
    ],
    ids=[
        "google_ok",
        "site_not_found",
        "server_error_503",
        "connection_error",
        "xfail_retry_case",
    ],
)
def test_various_url_statuses(
    url_to_check: str,
    expected_status_code: int,
    expected_status_text: str,
):
    status_code, status_text = check_url_status(url_to_check)
    assert status_code == expected_status_code
    assert status_text == expected_status_text
1 month ago Permalien
cluster icon
  • Handling Authentication : Handling Authentication APIs often require authentication to control access, rate limits, and auditing. Without authentication, requests to protected...
  • Numbers, strings : Numbers (int and float) int: Whole numbers (e.g., 10, 1024). No overflow due to arbitrary precision. float: Numbers with decimals (e.g., 3.14159). Us...
  • Pytest Markers : Pytest Markers Markers are decorators (@pytest.mark.) applied to tests to attach metadata. Built-in markers like skip, skipif, xfail, and parametrize...
  • Structured Logging : Introduction to Structured Logging Plain-text logs are hard to parse and brittle to format changes. Structured logging records events as key-value da...
  • Working with Environment Variables : Working with Environment Variables Environment variables are dynamic, named values provided by the operating system to running processes, enabling co...

Fixtures in Pytest/shaare/5ERODQ

  • python
  • python

Fixtures in Pytest

  • As tests grow more complex, repeating setup and cleanup steps makes tests harder to read and maintain.
  • Pytest fixtures allow centralizing shared setup and teardown logic, promoting DRY code.
  • Fixtures prepare resources or data before tests and can optionally clean up after tests complete.

What is a Fixture?

  • A fixture in Pytest is a function decorated with @pytest.fixture that provides a baseline environment or data for tests.
  • Fixtures can supply test data, manage the test environment (e.g., temporary files, mock services), and provide reusable resources (e.g., client objects).
  • Tests request fixtures by declaring them as function arguments, and Pytest handles executing the fixture and injecting its result.

Defining a Simple Fixture with @pytest.fixture

  • Use @pytest.fixture above a function to mark it as a fixture that can return or yield resources.
  • A fixture that returns a value runs its setup code, returns the value, and skips teardown logic.
  • A fixture that yields a value transforms into a generator: code before yield is setup, the yielded value goes to the test, and code after yield is teardown.

Using Fixtures in Test Functions

  • Tests receive fixture results by naming them as parameters; Pytest locates and executes matching fixtures automatically.
  • This approach removes boilerplate setup code from tests and keeps test functions concise.

Fixture Scope and Lifecycle

  • Fixture scope controls how often setup and teardown run: function (default), class, module, or session.
  • A function-scoped fixture runs for each test function; a session-scoped fixture runs only once for the entire test session.
  • Choosing the right scope balances resource isolation (function scope) against efficiency (session scope).

Fixture Teardown (Cleanup)

  • To ensure cleanup logic executes after tests, define fixtures with yield rather than return.
  • Code after yield always runs, even if the test fails or raises an error.
  • This pattern mimics a try...finally block, ensuring resources like temporary files or connections are properly released.

conftest.py: Sharing Fixtures

  • Defining fixtures in a conftest.py file makes them available across multiple test modules without explicit imports.
  • Placing conftest.py in a test directory or its parent enables automatic discovery of shared fixtures.
  • This structure keeps fixture definitions centralized and tests cleaner.
import pytest
from typing import Iterable

ManagedResource = dict[str, str]

@pytest.fixture
def managed_resource() -> Iterable[ManagedResource]:
    print("  [SHARED FIXTURE]: acquiring resource lock")
    resource = {"status": "lock_acquired"}
    yield resource
    print("  [SHARED FIXTURE]: releasing resource lock")
    resource["status"] = "lock_released"
1 month ago Permalien
cluster icon
  • Running Python modules : Running Scripts: python -m vs. python file.py The Core Difference: What is "Entry Point Zero"? The key to understanding the difference lies in the fir...
  • Concise Iteration: List Comprehensions : Concise Iteration: List Comprehensions Simple for loops to create lists can be verbose. We can leverage list comprehensions to define the list content...
  • Classes and Objects : Classes and Objects Beyond Built-ins: Python lets you define your own data types using class. Class: A blueprint or template for creating objects. De...
  • Handling Authentication : Handling Authentication APIs often require authentication to control access, rate limits, and auditing. Without authentication, requests to protected...
  • Pytest Markers : Pytest Markers Markers are decorators (@pytest.mark.) applied to tests to attach metadata. Built-in markers like skip, skipif, xfail, and parametrize...

Pytest Markers/shaare/80i_nw

  • python
  • python

Pytest Markers

  • Markers are decorators (@pytest.mark.<markername>) applied to tests to attach metadata.
  • Built-in markers like skip, skipif, xfail, and parametrize provide predefined behaviors.
  • Custom markers (e.g., slow, integration, api) help categorize tests by project-specific criteria.
  • Applying markers allows Pytest (and plugins) to filter, select, or modify tests during collection and execution.

Skipping Tests Unconditionally: @pytest.mark.skip

  • The skip marker unconditionally prevents a test from running, useful when a feature is disabled or under refactor.
  • Skipped tests show an s in the summary along with the provided reason.
  • Use @pytest.mark.skip(reason="...") to disable tests without removing their code.

Skipping Tests Conditionally: @pytest.mark.skipif

  • The skipif marker skips tests only when a specified condition evaluates to true at collection time.
  • Conditions can be expressions like sys.platform != "linux" or checks for optional dependencies.
  • This enables platform-specific, version-specific, or resource-dependent test filtering.

Expected Failures: @pytest.mark.xfail

  • The xfail marker marks tests expected to fail due to known bugs or unimplemented features.
  • Expected failures are reported as XFAIL and don’t cause the suite to fail; unexpected passes are reported as XPASS.

Custom Markers and Registration

  • Custom markers (e.g., slow, api, integration) help organize and categorize tests by functionality or runtime.
  • Tests can have multiple markers for fine-grained control (e.g., @pytest.mark.api and @pytest.mark.integration).
  • To avoid warnings, register custom markers in pytest.ini or pyproject.toml under the markers option.

Running Tests by Marker (m option)

  • The -m <expression> CLI option selects tests matching a marker expression.
  • Expressions support logical operators: and, or, and negation with not.
  • Examples: pytest -m slow, pytest -m "not slow", pytest -m "api and integration".

Common Pitfalls & How to Avoid Them

  • Overusing skip hides failures; prefer xfail for known bugs to maintain visibility.
  • Complex skipif conditions reduce readability; delegate logic to helper functions when needed.
  • Typos in marker names create unregistered markers; register all custom markers to catch errors early.
  • Forgetting to register markers leads to warnings; always define marker descriptions in configuration files.
import pytest
import time

try:
    import some_optional_library  # type: ignore
except ModuleNotFoundError:
    some_optional_library = None
# Section: Skipping Tests Unconditionally: @pytest.mark.skip

@pytest.mark.skip(
    reason="Skipping experimental feature until completion."
)
def test_new_experimental_feature() -> None:
    assert False

# Section: Skipping Tests Conditionally: @pytest.mark.skipif

@pytest.mark.skipif(
    some_optional_library is None,
    reason="Requires 'some_optional_library' to be installed.",
)
def test_with_optional_dependency() -> None:
    print(
        f"Running tests that depends on an optional library..."
    )
    assert some_optional_library

# Section: Expected Failures: @pytest.mark.xfail

@pytest.mark.xfail(
    reason="Bug #123: Division by zero not handled properly."
)
def test_divide_by_zero() -> None:
    _division = 1 / 0
    assert False

@pytest.mark.xfail  # Add strict=True to make XPASS lead to a failure
def test_expected_to_fail() -> None:
    assert True

# Section: Custom Markers and Registration

@pytest.mark.slow
def test_very_long_computations() -> None:
    time.sleep(5)
    assert True

@pytest.mark.api
@pytest.mark.smoke
def test_user_creation() -> None:
    assert True

# Section: Running Tests by Marker (m option)
1 month ago Permalien
cluster icon
  • Log Levels in Practice : Log Levels in Practice Python defines five standard levels with increasing severity: DEBUG (10): Detailed diagnostic information. INFO (20): Confirm...
  • For & While Loops : For & While Loops Python provides two main ways to repeat actions: for loops (for iterating over known sequences) and while loops (for repeating as lo...
  • *args and **kwargs : Flexible Functions: *args and **kwargs We can use the syntax *args and **kwargs to accept a variable number of both positional and keyword arguments....
  • Implementing Retries and Timeouts : Implementing Retries and Timeouts External services can be slow or unreliable, causing scripts to hang or fail unexpectedly. Timeouts and retries hel...
  • Classes and Objects : Classes and Objects Beyond Built-ins: Python lets you define your own data types using class. Class: A blueprint or template for creating objects. De...


(110)
1 / 6
Liens par page
  • 20
  • 50
  • 100
Filtrer par liens sans tag
Replier Replier tout Déplier Déplier tout Êtes-vous sûr de vouloir supprimer ce lien ? Êtes-vous sûr de vouloir supprimer ce tag ? Le gestionnaire de marque-pages personnel, minimaliste, et sans base de données par la communauté Shaarli