Assertion Rewriting

Overview

The **Assertion Rewriting** module provides a mechanism to automatically transform Python `assert` statements in test code into enhanced forms that produce detailed, user-friendly error messages when assertions fail. This rewriting occurs transparently during module import via a custom import hook that intercepts test module loading, modifies their abstract syntax trees (ASTs), and compiles the rewritten code for execution.

This feature addresses the common problem in testing where default assertion failures show only the expression that failed without detailed introspection into intermediate values or sub-expressions. By rewriting asserts, pytest can present rich failure explanations that help developers quickly understand why a test failed, improving debugging efficiency.


Core Concepts and Purpose


How Assertion Rewriting Works

1. Import Hook Interception

2. AST Parsing and Transformation

3. Enhanced Assertion Messages

4. Execution of Rewritten Code


Interaction with Other Parts of the System


Key Functional Components

AssertionRewritingHook (Import Hook)

AssertionRewriter (AST Transformer)

Helper Functions and Utilities


Example: How an assert Statement is Rewritten

Original code:

assert x + y == z

Rewritten code (conceptualized):

# Evaluate sub-expressions and assign to temporary variables
@py_assert0 = x
@py_assert1 = y
@py_assert2 = @py_assert0 + @py_assert1
@py_assert3 = z

# Build explanation string with intermediate values
explanation = "(@py_assert0 + @py_assert1) == @py_assert3\n({0} + {1} == {2})".format(
    repr(@py_assert0), repr(@py_assert1), repr(@py_assert3)
)

# Check condition and raise detailed AssertionError if false
if not (@py_assert2 == @py_assert3):
    raise AssertionError("assert " + explanation)

# Clear temporary variables
@py_assert0 = None
@py_assert1 = None
@py_assert2 = None
@py_assert3 = None

This expanded form provides explicit feedback on the values of `x`, `y`, `z`, and the result of `x + y`, which aids debugging.


Visual Diagram: Assertion Rewriting Workflow

sequenceDiagram
    participant Importer as Python Import System
    participant Hook as AssertionRewritingHook
    participant AST as AssertionRewriter (AST Transformer)
    participant Cache as Bytecode Cache (.pyc)
    participant TestModule as Test Module Execution

    Importer->>Hook: Request to import module
    Hook->>Hook: Decide if rewriting needed
    alt Rewrite required
        Hook->>Cache: Check for cached rewritten bytecode
        alt Cache hit
            Hook->>TestModule: Load and execute cached bytecode
        else Cache miss
            Hook->>Hook: Read source file
            Hook->>AST: Parse and rewrite asserts in AST
            AST->>Hook: Return rewritten AST
            Hook->>Hook: Compile rewritten AST to bytecode
            Hook->>Cache: Write rewritten bytecode to cache
            Hook->>TestModule: Execute rewritten bytecode
        end
    else No rewrite
        Importer->>TestModule: Load and execute original bytecode
    end

Summary of Assertion Rewriting Module