test_assertrewrite.py
Overview
The [test_assertrewrite.py](/projects/286/67351) file is a comprehensive test suite dedicated to verifying the behavior, correctness, and robustness of **pytest's assertion rewriting mechanism**. This mechanism enhances Python's built-in `assert` statements by rewriting them at import time to provide detailed introspection on assertion failures, including expression values and context. This file:
Tests the rewriting of Python source code assertions into enhanced ASTs.
Validates the preservation of source code formatting, locations, and comments.
Checks integration with pytest's import hooks and plugin system.
Covers edge cases, such as multi-line assertions, walrus operators, and complex boolean expressions.
Verifies compatibility with packaging, bytecode caching, and PEPs.
Ensures proper behavior of assertion pass hooks and error message formatting.
This file is vital for maintaining the correctness and user-friendliness of pytest's powerful assertion introspection feature.
Detailed Explanation of Key Components
Functions
rewrite(src: str) -> ast.Module
Parses and rewrites all `assert` statements in the provided source code string.
Parameters:
src (
str): Python source code as a string.
Returns:
ast.Module: An AST module node with assertions rewritten for enhanced error reporting.
Usage Example:
source = "def test(): assert 1 == 2" mod = rewrite(source) compiled = compile(mod, "<test>", "exec") exec(compiled)Implementation Details:
Usesast.parseto create an AST, then callsrewrite_asserts(imported frompytestinternals) to transformassertnodes into expanded forms that capture detailed assertion info at runtime.
getmsg(f, extra_ns: Mapping[str, object] | None = None, *, must_pass: bool = False) -> str | None
Rewrites assertions in function `f`, executes it, and captures the assertion failure message, if any.
Parameters:
f(callable): A function containingassertstatements.extra_ns(Mapping[str, object] | None, optional): Additional namespace variables for executingf.must_pass(bool, optional, keyword-only): IfTrue, test expects no assertion failure; raises if assertion fails.
Returns:
str | None: The string message from the assertion failure, orNoneif no failure andmust_passisTrue.
Usage Example:
def test_func(): assert 1 == 2 msg = getmsg(test_func) print(msg) # outputs assertion failure messageImplementation Details:
Extracts source code off, rewrites assertions, compiles, executes in a custom namespace, then captures and formats anyAssertionErrormessage.
Class: TestAssertionRewrite
This class contains numerous test methods verifying the correctness and behavior of pytest's assertion rewriting on source code and functions.
Key Tests:
test_place_initial_imports: Checks correct placement of import statements after rewriting.test_location_is_set: Ensures AST nodes have correct line and column location metadata after rewriting.test_positions_are_preserved: Validates preservation of bytecode instruction positions when assertions are rewritten.test_dont_rewrite: Verifies that modules marked with'PYTEST_DONT_REWRITE'are not rewritten.test_name: Confirms assertion messages when variables are used or missing.test_boolop,test_unary_op,test_binary_op: Tests rewriting of boolean, unary, and binary operations in assertions.test_call,test_attribute,test_comparisons: Tests assertion rewriting for function calls, attribute access, and comparisons.test_assertion_message*: Tests handling of assertion messages, including multiline, tuple, expression, byte strings, and escaping.test_assert_fixture: Tests assertion rewriting when referring to pytest fixtures.Additional tests cover complex expressions, short-circuit evaluation, and edge cases involving exceptions raised inside special methods.
Usage:
These tests are run automatically by pytest to verify that assertion rewriting produces expected detailed error messages and does not break normal code semantics.
Class: TestRewriteOnImport
Tests that assertion rewriting works correctly during module import, including:
Handling of
__pycache__directories and files (even if read-only or files instead of directories).Importing from zip archives.
Loading resources via
importlib.resources.files.Behavior with readonly directories and environment variables that disable bytecode writing.
Handling orphaned
.pycfiles without source.Ensuring cached
.pycfiles include pytest version tags to avoid stale cache usage.Interactions with various package layouts, newline styles, and import warnings.
Confirming that repeated rewriting of a module does not produce warnings unnecessarily.
Tests for a variety of edge cases in import rewriting behavior.
Class: TestAssertionRewriteHookDetails
Focused on low-level internals of the assertion rewriting import hook:
Ensures that
sys.meta_pathmunging does not break rewriting.Tests writing and reading
.pycfiles generated after rewriting.Validates compatibility with
pkg_resourcesfor resource loading.Tests reading corrupted
.pycfiles gracefully.Confirms that reloading a modified module reloads the rewritten code.
Implements support for PEP-302 import API features like
get_data.
Class: TestEarlyRewriteBailout
Tests optimizations in the assertion rewriting hook to avoid unnecessary import work:
Confirms that modules not matching pytest's "python_files" patterns or known initial paths are not rewritten.
Tests behavior when patterns include subdirectories.
Verifies correct handling when current working directory changes.
Class: TestAssertionPass
Tests the experimental pytest hook `pytest_assertion_pass` that is called on successful assertions if enabled:
Validates default off state and enabling via configuration.
Checks that the hook is called with appropriate parameters.
Ensures hook is not called if no hook implementation is registered or option disabled.
Other Test Classes
TestPyCacheDir: Tests computation and usage of.pyccache directories, including integration withsys.pycache_prefix.TestReprSizeVerbosity: Validates control of assertion message length truncation based on pytest verbosity levels.TestSafereprUnbounded: Tests safe representation of bound and unbound methods to avoid overly verbose or problematicrepr()calls.TestIssue*: Regression tests for specific issues and corner cases related to assertion rewriting.
Important Implementation Details and Algorithms
Assertion Rewriting:
Uses Python's AST manipulation to transformassertstatements into expanded code that captures the expression and its sub-expressions' values. This enables detailed assertion failure messages with value introspection.Preservation of Source Locations:
Tests ensure that rewriting preserves original source code line numbers and column offsets to maintain accurate tracebacks and error reporting.Bytecode Cache Handling:
The import hook writes.pycfiles with a special pytest tag to avoid stale cache reuse, including support for system cache prefixes and bytecode writing options.Import Hook Optimization:
The rewriting hook avoids expensivefind_speccalls for modules that clearly do not need rewriting, improving import performance. It also handles edge cases like package imports and zip imports.Handling of Walrus Operator:
Tests confirm that the rewriting supports the assignment expression (:=) without variable leakage or conflicts across tests.Assertion Pass Hook:
An optional pytest hook can be triggered on successful assertions, allowing custom user actions on assertion success.
Interactions with Other Parts of the System
pytest Assertion Framework:
This file tests the core assertion rewriting feature of pytest, which integrates deeply with pytest's test collection, execution, and reporting subsystems.Import System:
Uses Python's import machinery hooks (sys.meta_path) to intercept module loading and apply assertion rewriting on-the-fly.pytest Plugins and Fixtures:
Verifies compatibility with pytest plugins and fixtures, including those that disable rewriting or use special import patterns.File System and Bytecode Cache:
Interacts with the file system to read/write.pycfiles, manage cache directories, and handle read-only or unusual directory states.Python Language Features:
Handles modern Python syntax features like the walrus operator, multi-line assertions, and complex expressions to maintain compatibility and correctness.
Visual Diagram
Below is a class diagram summarizing the key test classes and their main responsibilities in this file:
classDiagram
class TestAssertionRewrite {
+test_place_initial_imports()
+test_location_is_set()
+test_positions_are_preserved()
+test_dont_rewrite()
+test_boolop()
+test_call()
+test_comparisons()
+test_assertion_message()
}
class TestRewriteOnImport {
+test_pycache_is_a_file()
+test_zipfile()
+test_readonly()
+test_dont_write_bytecode()
+test_cached_pyc_includes_pytest_version()
}
class TestAssertionRewriteHookDetails {
+test_sys_meta_path_munged()
+test_write_pyc()
+test_read_pyc()
+test_reload_is_same_and_reloads()
+test_get_data_support()
}
class TestEarlyRewriteBailout {
+test_basic()
+test_pattern_contains_subdirectories()
+test_cwd_changed()
}
class TestAssertionPass {
+test_hook_call()
+test_hook_call_with_parens()
+test_hook_not_called_without_hookimpl()
}
class TestPyCacheDir {
+test_get_cache_dir()
+test_sys_pycache_prefix_integration()
}
class TestReprSizeVerbosity {
+test_get_maxsize_for_saferepr()
+test_default_verbosity()
+test_increased_verbosity()
}
TestAssertionRewrite <|-- TestRewriteOnImport
TestRewriteOnImport <|-- TestAssertionRewriteHookDetails
TestAssertionRewriteHookDetails <|-- TestEarlyRewriteBailout
TestEarlyRewriteBailout <|-- TestAssertionPass
TestPyCacheDir <|-- TestReprSizeVerbosity
Summary
[test_assertrewrite.py](/projects/286/67351) rigorously validates pytest's assertion rewriting mechanism, ensuring that Python `assert` statements provide rich, informative failure messages that aid debugging. It covers rewriting correctness, import-time rewriting behavior, caching, compatibility with language features like the walrus operator, and integration with pytest's plugin and fixture systems. The tests in this file safeguard one of pytest's most celebrated features, allowing users to write succinct tests with powerful failure introspection.