hookspec.py
Overview
The `hookspec.py` file defines **hook specifications** for the pytest testing framework. These specifications declare the formal extension points (hooks) that plugins and conftest files can implement to customize and extend pytest's behavior throughout its lifecycle.
This file leverages the **pluggy** plugin system to declare hooks that cover all phases of pytest operation, including:
Plugin initialization and configuration
Command line option parsing
Test collection and item generation
Test execution and reporting
Fixture setup and teardown
Error handling and debugging
Assertion customization
Terminal reporting and summary output
Each hook is decorated with [@hookspec](/projects/286/67223) from pluggy and includes detailed docstrings that explain parameters, expected return values, usage notes, and plugin author guidance.
`hookspec.py` is a fundamental part of pytest’s extensibility infrastructure, defining the "API" that plugins implement to influence pytest’s behavior.
Detailed Hook Specifications
Below is an explanation of the main hooks defined in this file, categorized by their functional areas.
1. Initialization Hooks
pytest_addhooks(pluginmanager: PytestPluginManager) -> NoneCalled at plugin registration to allow adding new hooks.
Plugins can add new hook specifications dynamically via
pluginmanager.add_hookspecs.Called immediately when a conftest plugin is registered.
Note: Incompatible with hook wrappers.
pytest_plugin_registered(plugin: _PluggyPlugin, plugin_name: str, manager: PytestPluginManager) -> NoneCalled when a new pytest plugin is registered.
Allows plugins to react to other plugins being registered.
Called immediately for existing plugins when a conftest implementing this hook is registered.
Note: Incompatible with hook wrappers.
pytest_addoption(parser: Parser, pluginmanager: PytestPluginManager) -> NoneRegister command line options and ini-style configuration.
Plugins add options via parser.addoption() or ini values via
parser.addini().The config object later exposes these options.
Called once at the start of a test run.
Called immediately when a conftest plugin is registered.
Note: Incompatible with hook wrappers.
pytest_configure(config: Config) -> NonePerform initial configuration after parsing CLI options.
Called for every initial conftest and other conftests as they register.
Note: Incompatible with hook wrappers.
2. Bootstrapping Hooks
pytest_cmdline_parse(pluginmanager: PytestPluginManager, args: list[str]) -> Config | NoneParse command line arguments and return a Config object.
Stops at first non-None result (firstresult hook).
Only called for plugin classes passed to
pytest.main().Not called for conftest files.
pytest_load_initial_conftests(early_config: Config, parser: Parser, args: list[str]) -> NoneLoad initial conftest files before CLI parsing.
Not called for conftest files.
pytest_cmdline_main(config: Config) -> ExitCode | int | NonePerform the main command line action (e.g., runtest loop).
Stops at first non-None result.
Only called for initial conftests.
3. Test Collection Hooks
pytest_collection(session: Session) -> object | NonePerform collection phase for the test session.
Stops at first non-None result.
The default collection process involves multiple hooks (see docstring).
Only called for initial conftests.
pytest_collection_modifyitems(session: Session, config: Config, items: list[Item]) -> NoneModify, filter, or reorder collected test items in-place.
When deselecting items, must call
pytest_deselectedexplicitly.Implementable by any conftest plugin.
pytest_collection_finish(session: Session) -> NoneCalled after collection and modification of items.
Implementable by any conftest plugin.
pytest_ignore_collect(collection_path: Path, path: LEGACY_PATH, config: Config) -> bool | NoneReturn True to ignore a path during collection.
Stops at first non-None result.
Both
collection_path(pathlib.Path) and deprecatedpathprovided.Any conftest file in parent directories of the path may implement.
pytest_collect_directory(path: Path, parent: Collector) -> Collector | NoneCreate a directory collector node or None.
Should subclass pytest.Directory if possible.
Stops at first non-None result.
Only conftest files in parent directories consulted.
pytest_collect_file(file_path: Path, path: LEGACY_PATH, parent: Collector) -> Collector | NoneCreate a file collector node or None.
Should subclass pytest.File if possible.
file_pathis pathlib.Path,pathis deprecated.Any conftest in parent directories consulted.
Logging hooks for collection:
pytest_collectstart(collector: Collector) -> Nonepytest_itemcollected(item: Item) -> Nonepytest_collectreport(report: CollectReport) -> Nonepytest_deselected(items: Sequence[Item]) -> Nonepytest_make_collect_report(collector: Collector) -> CollectReport | None (firstresult)
4. Python Test Function Related Hooks
pytest_pycollect_makemodule(module_path: Path, path: LEGACY_PATH, parent) -> Module | NoneReturn a Module collector or None for a given module path.
Stops at first non-None result.
module_pathis pathlib.Path;pathis deprecated.
pytest_pycollect_makeitem(collector: Module | Class, name: str, obj: object) -> None | Item | Collector | list[Item | Collector]Return a custom item or collector for a Python object.
Stops at first non-None result.
pytest_pyfunc_call(pyfuncitem: Function) -> object | NoneCall the underlying test function.
Stops at first non-None result.
pytest_generate_tests(metafunc: Metafunc) -> NoneParametrize test calls for a test function.
pytest_make_parametrize_id(config: Config, val: object, argname: str) -> str | NoneReturn a user-friendly string for parameterized values.
Stops at first non-None result.
5. Test Execution (Runtest) Hooks
pytest_runtestloop(session: Session) -> object | NoneMain runtest loop after collection.
Stops at first non-None result.
pytest_runtest_protocol(item: Item, nextitem: Item | None) -> object | NonePerform the runtest protocol for a single item.
Includes setup, call, teardown phases with corresponding hooks.
Stops at first non-None result.
pytest_runtest_logstart(nodeid: str, location: tuple[str, int | None, str]) -> NoneCalled at the start of running runtest protocol for an item.
pytest_runtest_logfinish(nodeid: str, location: tuple[str, int | None, str]) -> NoneCalled at the end of running runtest protocol for an item.
pytest_runtest_setup(item: Item) -> NonePerform setup phase for a test item.
pytest_runtest_call(item: Item) -> NoneRun the test call phase.
pytest_runtest_teardown(item: Item, nextitem: Item | None) -> NonePerform teardown phase for a test item.
pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport | NoneCreate a test report for each runtest phase.
Stops at first non-None result.
pytest_runtest_logreport(report: TestReport) -> NoneProcess the test report for each runtest phase.
Report serialization hooks:
6. Fixture Related Hooks
pytest_fixture_setup(fixturedef: FixtureDef[Any], request: SubRequest) -> object | NonePerform fixture setup.
Stops at first non-None result.
If fixture function returns None, other implementations are called.
pytest_fixture_post_finalizer(fixturedef: FixtureDef[Any], request: SubRequest) -> NoneCalled after fixture teardown but before cache clearing.
7. Test Session Related Hooks
pytest_sessionstart(session: Session) -> NoneCalled after session creation, before collection and runtest loop.
pytest_sessionfinish(session: Session, exitstatus: int | ExitCode) -> NoneCalled after whole test run finished, before process exit.
pytest_unconfigure(config: Config) -> NoneCalled just before test process exit.
8. Assertion Customization Hooks
pytest_assertrepr_compare(config: Config, op: str, left: object, right: object) -> list[str] | NoneReturn explanation strings for failing assert comparisons.
pytest_assertion_pass(item: Item, lineno: int, orig: str, expl: str) -> NoneCalled when an assertion passes (opt-in via ini option).
9. Reporting and Terminal Hooks
pytest_report_header(config: Config, start_path: Path, startdir: LEGACY_PATH) -> str | list[str]Return header strings for terminal report.
pytest_report_collectionfinish(config: Config, start_path: Path, startdir: LEGACY_PATH, items: Sequence[Item]) -> str | list[str]Return strings after collection finishes.
pytest_report_teststatus(report: CollectReport | TestReport, config: Config) -> TestShortLogReport | tuple[str, str, str | tuple[str, Mapping[str, bool]]]Return test status for result-category, short letter, and verbose word.
Stops at first non-None result.
pytest_terminal_summary(terminalreporter: TerminalReporter, exitstatus: ExitCode, config: Config) -> NoneAdd sections to terminal summary reporting.
pytest_warning_recorded(warning_message: warnings.WarningMessage, when: Literal["config", "collect", "runtest"], nodeid: str, location: tuple[str, int, str] | None) -> NoneProcess captured warnings.
10. Skipping and Marker Hooks
pytest_markeval_namespace(config: Config) -> dict[str, Any]Provide global namespace for evaluating marker conditions.
11. Error Handling and Debugging Hooks
pytest_internalerror(excrepr: ExceptionRepr, excinfo: ExceptionInfo[BaseException]) -> bool | NoneCalled for internal errors.
Return True to suppress default internal error message.
pytest_keyboard_interrupt(excinfo: ExceptionInfo[KeyboardInterrupt | Exit]) -> NoneCalled on keyboard interrupt.
pytest_exception_interact(node: Item | Collector, call: CallInfo[Any], report: CollectReport | TestReport) -> NoneCalled when an exception can be interactively handled.
pytest_enter_pdb(config: Config, pdb: pdb.Pdb) -> NoneCalled before entering pdb interactive mode.
pytest_leave_pdb(config: Config, pdb: pdb.Pdb) -> NoneCalled after leaving pdb interactive mode.
Important Implementation Details
Use of pluggy’s
HookspecMarker:The global
hookspecmarker is instantiated as HookspecMarker("pytest") and used to decorate all hook specification functions. This clearly declares them as pytest hooks.Hook Behavior Flags:
firstresult=True on hooks causes pytest to stop calling further implementations once a non-None result is returned.
historic=True causes hooks to replay past calls for late-registered plugins.
warn_on_impl_args is used to warn about deprecated arguments (e.g., transition from legacy string paths to pathlib.Path).
Deprecation and Compatibility:
Many hooks maintain deprecated parameters (like
pathas a string) alongside newer pathlib.Path parameters for backward compatibility, emitting warnings when deprecated arguments are used.Usage in Conftest Files:
Most hooks include usage notes indicating whether they are called for conftest plugins, and under what conditions.
No Class Definitions:
This file exclusively defines functions as hook specifications, not classes.
Interaction with Other Parts of the System
Plugin Manager (
pytest.config.PytestPluginManager):The hooks defined here are registered with the plugin manager, which manages plugin registration and dispatch of these hooks during test runtime.
Conftest Files:
Conftests implement these hooks to extend or customize pytest behavior for specific test directories.
Test Collection and Execution Modules:
Other pytest components implement hook implementations (hookimpls) for these hooks to perform collection, test running, reporting, fixture management, etc.
Assertion Rewriting:
The assertion customization hooks integrate with pytest’s assertion rewriting subsystem to provide detailed assertion introspection and explanation.
Terminal and Reporting:
Hooks here influence terminal output and reporting plugins, such as the default terminal reporter.
Usage Examples
Example: Implementing a plugin that adds a custom command-line option:
import pytest
def pytest_addoption(parser):
parser.addoption("--myopt", action="store_true", help="Enable my option")
def pytest_configure(config):
if config.getoption("--myopt"):
print("My custom option enabled")
Example: Skipping collection of certain files:
def pytest_ignore_collect(collection_path, path, config):
if collection_path.name == "skipme.py":
return True
return None
Example: Customizing test collection:
def pytest_collection_modifyitems(session, config, items):
# reorder items or deselect some
items[:] = [item for item in items if "slow" not in item.keywords]
Mermaid Diagram: Structure of hookspec.py File (Hook Specifications)
classDiagram
class HookSpecs {
<<interface>>
+pytest_addhooks(pluginmanager)
+pytest_plugin_registered(plugin, plugin_name, manager)
+pytest_addoption(parser, pluginmanager)
+pytest_configure(config)
+pytest_cmdline_parse(pluginmanager, args)
+pytest_load_initial_conftests(early_config, parser, args)
+pytest_cmdline_main(config)
+pytest_collection(session)
+pytest_collection_modifyitems(session, config, items)
+pytest_collection_finish(session)
+pytest_ignore_collect(collection_path, path, config)
+pytest_collect_directory(path, parent)
+pytest_collect_file(file_path, path, parent)
+pytest_collectstart(collector)
+pytest_itemcollected(item)
+pytest_collectreport(report)
+pytest_deselected(items)
+pytest_make_collect_report(collector)
+pytest_pycollect_makemodule(module_path, path, parent)
+pytest_pycollect_makeitem(collector, name, obj)
+pytest_pyfunc_call(pyfuncitem)
+pytest_generate_tests(metafunc)
+pytest_make_parametrize_id(config, val, argname)
+pytest_runtestloop(session)
+pytest_runtest_protocol(item, nextitem)
+pytest_runtest_logstart(nodeid, location)
+pytest_runtest_logfinish(nodeid, location)
+pytest_runtest_setup(item)
+pytest_runtest_call(item)
+pytest_runtest_teardown(item, nextitem)
+pytest_runtest_makereport(item, call)
+pytest_runtest_logreport(report)
+pytest_report_to_serializable(config, report)
+pytest_report_from_serializable(config, data)
+pytest_fixture_setup(fixturedef, request)
+pytest_fixture_post_finalizer(fixturedef, request)
+pytest_sessionstart(session)
+pytest_sessionfinish(session, exitstatus)
+pytest_unconfigure(config)
+pytest_assertrepr_compare(config, op, left, right)
+pytest_assertion_pass(item, lineno, orig, expl)
+pytest_report_header(config, start_path, startdir)
+pytest_report_collectionfinish(config, start_path, startdir, items)
+pytest_report_teststatus(report, config)
+pytest_terminal_summary(terminalreporter, exitstatus, config)
+pytest_warning_recorded(warning_message, when, nodeid, location)
+pytest_markeval_namespace(config)
+pytest_internalerror(excrepr, excinfo)
+pytest_keyboard_interrupt(excinfo)
+pytest_exception_interact(node, call, report)
+pytest_enter_pdb(config, pdb)
+pytest_leave_pdb(config, pdb)
}
Summary
hookspec.pydeclares all official pytest hook specifications that plugins and conftest files implement.It uses pluggy to define these hooks clearly with decorators and documentation.
Hooks cover the full pytest lifecycle: initialization, configuration, collection, test execution, fixtures, reporting, error handling, and more.
The file plays a critical role in pytest’s plugin system by defining extension points that enable a rich ecosystem of plugins.
It maintains backward compatibility and smooth transitions by supporting legacy parameters and issuing warnings.
Plugin authors refer to this file’s hookspecs to understand how to extend pytest.
The hooks defined here are invoked by the pytest core via the plugin manager during test runs.