Plugin Manager and Configuration
Purpose
This subtopic addresses the management and orchestration of pytest plugins and the configuration system that governs test runs. It solves the problem of dynamically loading, registering, enabling, disabling, and configuring plugins while parsing command-line options and ini-files. This ensures pytest is extensible, configurable, and adaptable to diverse testing needs without compromising core stability.
Specifically, it handles:
Plugin registration and lifecycle, including builtin, third-party, and conftest.py plugins.
Loading plugins from various sources: command-line flags, environment variables, entry points, and conftest files.
Managing plugin enablement and blocking (disabling) via command-line options.
Parsing and applying configuration options from ini files, environment variables, and command line.
Integrating with the plugin hook system to enable plugins to extend pytest behavior.
Providing a centralized
Configobject that exposes option values and plugin manager to the rest of pytest.
This capability is essential because pytest relies heavily on plugins to extend and customize its behavior, and managing these plugins in a consistent, flexible, and user-friendly manner is a core need.
Functionality
Plugin Manager (PytestPluginManager)
Extends pluggy.PluginManager with pytest-specific logic.
Maintains sets of loaded conftest plugins and tracks plugin blocking.
Loads conftest.py files as plugins, caching modules by directory for efficient reuse.
Supports disabling plugins via -p no: command-line options.
Imports plugins from environment variables (
PYTEST_PLUGINS) and entry points (pytest11).Registers plugins and emits
pytest_plugin_registeredhook for further processing.Handles legacy hook implementation markers and options parsing for plugins.
Tracks skipped plugins and issues warnings if necessary.
Example snippet illustrating plugin registration and conftest loading:
def register(self, plugin: _PluggyPlugin, name: str | None = None) -> str | None:
plugin_name = super().register(plugin, name)
if plugin_name is not None:
self.hook.pytest_plugin_registered.call_historic(
kwargs=dict(plugin=plugin, plugin_name=plugin_name, manager=self)
)
if isinstance(plugin, types.ModuleType):
self.consider_module(plugin)
return plugin_name
def _importconftest(self, conftestpath, importmode, rootpath, *, consider_namespace_packages):
# Import conftest.py as plugin and register it
mod = import_path(conftestpath, mode=importmode, root=rootpath,
consider_namespace_packages=consider_namespace_packages)
self._conftest_plugins.add(mod)
self.consider_conftest(mod, registration_name=str(conftestpath))
return mod
Configuration (Config)
Encapsulates all configuration state, including command-line options, ini-file values, and plugin manager.
Provides methods to parse command-line arguments, ini files, and environment variables.
Exposes
getoption()andgetini()to retrieve option and ini values respectively.Manages the lifecycle of configuration, with setup and cleanup hooks.
Integrates with the plugin manager to load and manage plugins as part of configuration.
Parses and applies plugin blocking and enabling directives from command-line and environment.
Supports warning filtering and reporting during configuration.
Decides test collection paths based on command-line args, ini
testpaths, and invocation directory.Supports adding custom ini values and verbosity levels.
Example snippet of option retrieval and plugin manager exposure:
class Config:
def __init__(self, pluginmanager: PytestPluginManager, *, invocation_params=None):
self.option = argparse.Namespace()
self.pluginmanager = pluginmanager
...
def getoption(self, name: str, default: Any = notset, skip: bool = False):
name = self._opt2dest.get(name, name)
val = getattr(self.option, name)
...
return val
Plugin Loading Workflow
Pre-parsing Command Line: Detects
-poptions to block or enable plugins early.Loading Default Plugins: Imports and registers built-in plugins.
Loading Entry Point Plugins: Loads plugins declared via setuptools entry points.
Loading Plugins from Environment: Loads plugins listed in
PYTEST_PLUGINS.Loading Conftest Plugins: Recursively loads conftest.py files as plugins across directories.
Registering Plugins: Registers plugins and emits hooks for further initialization.
This ordering ensures that disabled plugins are not imported, and that plugin hooks are available before test collection and execution.
Relationship
This subtopic is a critical part of the broader **Plugin System and Hooks** topic. While **Hook Specification Definitions** define the interfaces plugins can implement, this subtopic manages the actual plugins' lifecycle and configuration.
It complements hook specifications by finding and registering plugins that implement those hooks.
It integrates with configuration parsing to control plugin enablement and behavior.
It works closely with test collection to load conftest plugins that can affect test discovery.
It enables extensibility by supporting plugin registration from multiple sources, including third-party packages and user conftest files.
Without this configuration and plugin manager layer, pytest would lack the extensible architecture that allows its rich ecosystem of plugins and user customizations.
Diagram
The following flowchart illustrates the plugin loading and configuration process managed by the `PytestPluginManager` and `Config` classes:
flowchart TD
A[Start: pytest invocation] --> B[Parse command-line args]
B --> C[Consider plugin block/enable flags]
C --> D[Load default builtin plugins]
D --> E[Load plugins from setuptools entry points]
E --> F[Load plugins from PYTEST_PLUGINS env var]
F --> G[Load conftest.py plugins recursively]
G --> H[Register all plugins with PluginManager]
H --> I[Parse ini files and apply configurations]
I --> J[Finalize Config and PluginManager state]
J --> K[pytest ready to collect and run tests]
This flow captures the key steps and ordering in the plugin and configuration setup phase that precedes test collection and execution.
By managing plugin lifecycle and configuration parsing, this subtopic enables pytest's hallmark flexibility and extensibility, ensuring plugins are loaded correctly, configured properly, and integrated seamlessly into the test run.