init.py
Overview
This `__init__.py` file is part of the **pytest** assertion rewriting plugin. Its primary purpose is to enable and manage enhanced assertion introspection and debugging during test execution by rewriting Python `assert` statements in test modules. This rewriting allows pytest to provide detailed, user-friendly explanations when assertions fail, significantly improving debugging efficiency.
Key functionalities include:
Registering modules for assertion rewriting.
Installing an import hook that rewrites
assertstatements on module import.Configuring pytest command-line options and ini settings related to assertion debugging.
Managing assertion state across test sessions.
Setting up hooks to customize assertion failure reporting and pass events.
Providing integration points for other pytest plugins or user code to extend assertion reporting.
This file acts as the central coordination point for assertion rewriting and presentation, interfacing closely with pytest’s import system, configuration, and test execution lifecycle.
Classes
RewriteHook (Protocol)
A protocol defining the interface for import hooks that support marking modules for assertion rewriting.
Methods
mark_rewrite(*names: str) -> None
Marks one or more module names for assertion rewriting on import.
DummyRewriteHook
A no-operation import hook implementation used when assertion rewriting is disabled or unavailable.
Methods
mark_rewrite(*names: str) -> None
Does nothing. Exists to provide a safe fallback.
AssertionState
Represents and tracks the current state of the assertion plugin within a pytest run.
Attributes
mode(str): The assertion mode, e.g.,"rewrite"or"plain".trace(Any): Tracing/logging facility scoped to assertion events.hook(rewrite.AssertionRewritingHook | None): The installed assertion rewriting import hook, if any.
Constructor
def __init__(self, config: Config, mode) -> None:
Parameters:
config(Config): The pytest configuration object.mode(str): The assertion mode to use.
Usage Example:
state = AssertionState(config, "rewrite")
print(state.mode) # Output: "rewrite"
Functions
pytest_addoption(parser: Parser) -> None
Registers pytest CLI options and ini configuration entries related to assertion debugging.
Parameters:
parser(Parser): The pytest argument parser.
Functionality:
Adds
--assertoption to control assertion rewriting mode (rewriteorplain).Adds ini options to enable assertion pass hooks and to configure truncation limits for assertion explanations.
Adds verbosity control specifically for assertion reporting.
Usage: Automatically called by pytest during plugin registration.
register_assert_rewrite(*names: str) -> None
Registers one or more module names to have their `assert` statements rewritten on import.
Parameters:
*names(str): One or more module or package names.
Raises:
TypeErrorif any argument is not a string.
Behavior:
Finds the installed assertion rewriting import hook in
sys.meta_path.If not found, uses a dummy no-op hook.
Marks the specified modules/packages to be rewritten.
Usage Example:
register_assert_rewrite("myplugin.assert_helpers", "myplugin.submodule")
Typically called in a plugin’s `__init__.py` to ensure rewriting before import.
install_importhook(config: Config) -> rewrite.AssertionRewritingHook
Installs the assertion rewriting import hook into Python’s import machinery.
Parameters:
config(Config): The pytest configuration object.
Returns:
The installed
AssertionRewritingHookinstance.
Behavior:
Creates and inserts a new
AssertionRewritingHookat the front ofsys.meta_path.Stores the hook and assertion state in
config.stash.Registers a cleanup function to remove the hook after the test session.
Raises
SystemErrorif installation fails.
Usage: Called during pytest initialization to enable assertion rewriting.
pytest_collection(session: Session) -> None
Pytest hook called during test collection.
Parameters:
session(Session): The current pytest session.
Behavior:
Retrieves the assertion state from
session.config.stash.If the assertion rewriting hook is installed, associates it with the session.
pytest_runtest_protocol(item: Item) -> Generator[None, object, object]
Pytest hook wrapping the test run protocol.
Parameters:
item(Item): The test item being executed.
Behavior:
Sets up custom hooks for assertion comparison and assertion pass events.
Overrides internal utility functions to use pytest hooks for assertion reporting.
Ensures restored state after test execution completes.
Yield:
Control back to pytest’s normal runtest execution.
Usage:
This is an internal pytest hook implementation to enhance assertion introspection during test runs.
pytest_sessionfinish(session: Session) -> None
Pytest hook called at the end of a test session.
Parameters:
session(Session): The pytest session.
Behavior:
Unsets the session reference from the assertion rewriting hook to clean up.
pytest_assertrepr_compare(config: Config, op: str, left: Any, right: Any) -> list[str] | None
Pytest hook to provide detailed assertion failure explanations for comparison expressions.
Parameters:
config(Config): The pytest configuration object.op(str): The comparison operator (e.g.,"==","!=").left(Any): Left-hand side operand.right(Any): Right-hand side operand.
Returns:
A list of formatted explanation strings on failure, or
Noneif no explanation.
Behavior:
Delegates to
util.assertrepr_comparewhich generates human-readable assertion failure messages.
Implementation Details & Algorithms
Assertion Rewriting:
Uses a custom import hook (AssertionRewritingHook) inserted into Python’ssys.meta_pathto intercept module imports. This hook rewrites allassertstatements in the module’s source code to enable detailed introspection on assertion failure.Hook Registration:
Theregister_assert_rewritefunction must be called before the target modules are imported to ensure rewriting occurs.Assertion Reporting:
Thepytest_runtest_protocolhook temporarily overrides utility functions with pytest hook calls, enabling plugins and user code to customize assertion failure messages.Truncation and Formatting:
Assertion failure explanations may be truncated based on user-configured line and character limits to avoid overly verbose output, with newline and percentage characters escaped for safe formatting.
Interaction with Other System Components
Imports and uses:
_pytest.assertion.rewrite: Implements the rewriting import hook._pytest.assertion.truncate: Handles truncation of assertion explanations._pytest.assertion.util: Provides base utilities for assertion representation._pytest.config: For configuration management and CLI option parsing._pytest.nodes.Item: Represents individual test items used during runtest.Pytest’s hook system (
hookimpl) to integrate into various lifecycle points.
Provides hooks that other pytest plugins or user code can override to customize assertion comparison and pass reporting (e.g.,
pytest_assertrepr_compare,pytest_assertion_pass).Maintains assertion state within
config.stashfor cross-plugin and cross-session coordination.
Usage Summary
A typical plugin or package wanting to use assertion rewriting would:
Call
register_assert_rewrite("your_module")in its__init__.py.Rely on pytest to install the import hook automatically.
Optionally customize assertion reporting using pytest hooks.
Visual Diagram
This diagram illustrates the major classes and functions and their relationships within this file, focusing on assertion rewriting and hook integration.
classDiagram
class AssertionState {
-mode: str
-trace: Any
-hook: AssertionRewritingHook | None
+__init__(config: Config, mode)
}
class DummyRewriteHook {
+mark_rewrite(*names: str)
}
class RewriteHook {
<<Protocol>>
+mark_rewrite(*names: str)
}
class AssertionRewritingHook {
<<from _pytest.assertion.rewrite>>
}
class Config {
<<from _pytest.config>>
}
class Parser {
<<from _pytest.config.argparsing>>
}
class Item {
<<from _pytest.nodes>>
}
class Session {
<<from _pytest.main>>
}
%% Functions
class pytest_addoption {
+pytest_addoption(parser: Parser)
}
class register_assert_rewrite {
+register_assert_rewrite(*names: str)
}
class install_importhook {
+install_importhook(config: Config)
}
class pytest_collection {
+pytest_collection(session: Session)
}
class pytest_runtest_protocol {
+pytest_runtest_protocol(item: Item)
}
class pytest_sessionfinish {
+pytest_sessionfinish(session: Session)
}
class pytest_assertrepr_compare {
+pytest_assertrepr_compare(config: Config, op: str, left: Any, right: Any)
}
%% Relationships
AssertionState --> Config
AssertionState --> AssertionRewritingHook
register_assert_rewrite ..|> RewriteHook
install_importhook --> AssertionState
install_importhook --> AssertionRewritingHook
pytest_collection --> AssertionState
pytest_runtest_protocol --> Item
pytest_sessionfinish --> AssertionState
pytest_assertrepr_compare --> Config
Summary
This `__init__.py` file is central to pytest's assertion rewriting mechanism, enabling detailed assertion failure reporting by rewriting the source code of test modules during import. It manages command-line options, installs import hooks, maintains plugin state, and integrates deeply with pytest’s lifecycle hooks to provide rich assertion debugging capabilities. Understanding this file is critical for extending or customizing pytest’s assertion introspection behavior.