util.py
Overview
[util.py](/projects/286/67331) is a utility module dedicated to enhancing assertion debugging within the pytest testing framework. Its primary purpose is to provide rich, detailed, and human-readable explanations for assertion failures, especially when comparing complex data types such as sequences, sets, dictionaries, dataclasses, namedtuples, and text strings.
When an assertion fails, pytest's assertion rewriting machinery invokes functions in this module to generate specialized explanations that clarify why two objects did not satisfy the asserted condition. This includes generating diffs for strings, identifying differing elements in collections, and recursively comparing attributes of structured data types.
Key features include:
Type-specific comparison strategies for
==,!=,>,<,>=,<=, andnot in.Intelligent diff generation for strings that skips identical prefixes/suffixes to keep output concise.
Comprehensive comparison of collections (lists, sets, dicts) highlighting differences and extra items.
Drill-down comparisons for dataclasses, attrs classes, and namedtuples to pinpoint differing attributes.
Formatting of explanations with indentation and optional syntax highlighting.
Adaptation of explanation verbosity based on pytest's configuration.
Utility predicates for detecting data types and structures.
Integration points with pytest’s configuration and terminal output systems.
This module is crucial for improving the developer experience by making assertion failure messages more informative and actionable.
Detailed Explanations
Module-Level Variables
Variable | Type | Description |
|---|---|---|
`_reprcompare` | [Callable[[str, object, object], str | None] |
`_assertion_pass` | [Callable[[int, str, str], None] | None](/projects/286/67223) |
[Config | None](/projects/286/67332) |
Classes and Protocols
_HighlightFunc (Protocol)
Protocol describing a highlighting function interface.
def __call__(self, source: str, lexer: Literal["diff", "python"] = "python") -> str:
"""Apply syntax highlighting to the given source string."""
Parameters:
source: Source code or text to highlight.lexer: The lexer type to use for highlighting; either"diff"or"python".
Returns: Highlighted string.
**Usage:** Used as a type hint for highlighter functions passed to various comparison functions.
Functions
dummy_highlighter(source: str, lexer: Literal["diff", "python"] = "python") -> str
A no-op highlighter that returns the input text unmodified.
Purpose: Used when no syntax highlighting is needed, especially for post-processing diffs in
_notin_text.Parameters:
source: Text to "highlight".lexer: Ignored; default"python".
Returns: The original
sourcestring unaltered.
format_explanation(explanation: str) -> str
Formats an assertion explanation string applying a minimal mini-formatting language to represent nested or multi-line explanations.
Description: The explanation string may contain special newline markers (
\n{,\n},\n~) that control indentation and formatting.Parameters:
explanation: Raw explanation string with embedded formatting markers.
Returns: Formatted multiline string ready for display.
**Example:**
raw_expl = "Mismatch\n{Details\n~More info\n}"
print(format_explanation(raw_expl))
issequence(x: Any) -> bool
Returns `True` if `x` is a non-string sequence.
istext(x: Any) -> bool
Returns `True` if `x` is a string.
isdict(x: Any) -> bool
Returns `True` if `x` is a dictionary.
isset(x: Any) -> bool
Returns `True` if `x` is a set or frozenset.
isnamedtuple(obj: Any) -> bool
Returns `True` if `obj` is a namedtuple instance.
isdatacls(obj: Any) -> bool
Returns `True` if `obj` is a dataclass instance.
isattrs(obj: Any) -> bool
Returns `True` if `obj` is an instance of a class decorated with `@attrs`.
isiterable(obj: Any) -> bool
Returns `True` if `obj` is iterable but not a string.
has_default_eq(obj: object) -> bool
Determines if the object `obj` has the default equality method (`__eq__`) generated by dataclasses or attrs, by inspecting the code object filename or known markers.
Returns:
Trueif the__eq__implementation appears to be the default one.
assertrepr_compare(config, op: str, left: Any, right: Any, use_ascii: bool = False) -> list[str] | None
Main entry point to generate a human-readable explanation for why a comparison assertion failed.
Parameters:
config: pytestConfigobject for verbosity and terminal output.op: Operator string such as"==","!=",">=", etc.left: Left operand of the comparison.right: Right operand.use_ascii: Whether to use ASCII-only repr (defaults toFalse).
Returns: List of strings with the detailed explanation for the assertion failure, or
Noneif no specialized explanation applies.
**Usage Example:**
explanation = assertrepr_compare(config, "==", [1, 2, 3], [1, 2, 4])
if explanation:
print("\n".join(explanation))
_compare_eq_any(left: Any, right: Any, highlighter: _HighlightFunc, verbose: int = 0) -> list[str]
Helper to compare two objects with `==`, dispatching to specialized comparators based on type.
Handles strings, sequences, sets, dicts, dataclasses, attrs, namedtuples, and iterables.
Uses
highlighterto optionally add syntax highlight to output.Returns a list of explanation lines.
_diff_text(left: str, right: str, highlighter: _HighlightFunc, verbose: int = 0) -> list[str]
Generates a diff-style explanation between two strings.
Skips long identical prefixes and suffixes unless verbose.
Uses Python’s
difflib.ndiffand optionally highlights diff output.Returns explanation lines showing differences.
_compare_eq_iterable(left: Iterable[Any], right: Iterable[Any], highlighter: _HighlightFunc, verbose: int = 0) -> list[str]
Generates a detailed diff for two iterables.
Uses
difflibto create a line-by-line diff of pretty-printed representations.Returns a list of explanation lines.
Returns minimal output if verbosity is low and not running on CI.
_compare_eq_sequence(left: Sequence[Any], right: Sequence[Any], highlighter: _HighlightFunc, verbose: int = 0) -> list[str]
Compares two sequences element-wise, reporting the first differing index and any length differences.
_compare_eq_set(left: AbstractSet[Any], right: AbstractSet[Any], highlighter: _HighlightFunc, verbose: int = 0) -> list[str]
Compares two sets, reporting extra items in each set.
_compare_gt_set, _compare_lt_set, _compare_gte_set, _compare_lte_set
Specialized set comparisons for `>`, `<`, `>=`, `<=` operators that report differences or equality.
_set_one_sided_diff(posn: str, set1: AbstractSet[Any], set2: AbstractSet[Any], highlighter: _HighlightFunc) -> list[str]
Helper to report extra items in one set compared to another.
_compare_eq_dict(left: Mapping[Any, Any], right: Mapping[Any, Any], highlighter: _HighlightFunc, verbose: int = 0) -> list[str]
Compares two dictionaries:
Reports common keys with identical values (optionally suppressed at low verbosity).
Reports differing keys and their values side-by-side.
Reports keys exclusive to either dictionary.
_compare_eq_cls(left: Any, right: Any, highlighter: _HighlightFunc, verbose: int) -> list[str]
Compares two instances of dataclasses, attrs classes, or namedtuples field-by-field:
Shows which fields match and which differ.
Recursively drills into differing fields.
Suppresses identical fields at low verbosity.
_notin_text(term: str, text: str, verbose: int = 0) -> list[str]
Specialized explanation for `"not in"` operator when both operands are strings.
Locates the term in text.
Creates a diff showing where the term is contained.
Formats explanation showing the missing substring location.
running_on_ci() -> bool
Utility that returns `True` if the current environment appears to be a Continuous Integration (CI) system.
Checks environment variables like
CIandBUILD_NUMBER.
Important Implementation Details and Algorithms
Mini Formatting Language for Explanations:
Formatting of nested explanations uses a simple custom syntax with\n{,\n}, and\n~to denote nested blocks, block endings, and multi-line spans, respectively._split_explanationand_format_linesparse and render this structure with indentation and labels like "where", "and", and " +".Type-Directed Dispatch:
Theassertrepr_comparefunction dispatches to type-specific helpers based on operand types and operators, enabling tailored and meaningful explanations.Diff Generation for Strings:
Usesdifflib.ndiffto generate unified diffs for strings, skipping identical leading and trailing characters to reduce noise unless verbosity is increased.Recursive Comparison for Structured Data:
Dataclasses, attrs, and namedtuples are compared attribute-by-attribute, with recursion into differing attributes to provide layered insight.Verbosity Awareness:
Explanation detail adapts to pytest’s verbosity level (config.get_verbosity(Config.VERBOSITY_ASSERTIONS)), providing either concise summaries or full diffs.Safe Representation:
Uses pytest internal utilitiessafereprandsaferepr_unlimitedfor robust and readable string representations even for complex or faulty__repr__implementations.Highlighting Integration:
Supports terminal syntax highlighting by accepting a highlighting function from pytest’s terminal writer.Error Handling:
Wraps explanation generation in try-except to handle unexpected errors during representation, returning a safe fallback message.
Interaction with Other Components
pytest Assertion Rewriting:
This file provides hooks (via_reprcompareand_assertion_pass) that pytest’s assertion rewriting system uses to generate detailed assertion failure explanations.pytest Configuration and Terminal Output:
Utilizes pytest’sConfigobject and terminal writer for verbosity settings and syntax highlighting.Internal pytest Utilities:
Relies on internal modules like_pytest._code.ExceptionInfo,_pytest._io.saferepr, and_pytest._io.pprint.PrettyPrinterfor representation and formatting.Approximate Comparison:
Supports integration with pytest’sApproxBaseclass for approximate numeric comparisons.
Visual Diagram: Function Flowchart
flowchart TD
A[assertrepr_compare] --> B{Operator?}
B -->|==| C[_compare_eq_any]
B -->|not in| D[_notin_text]
B -->|!=| E[Set equality check]
B -->|>=| F[_compare_gte_set]
B -->|<=| G[_compare_lte_set]
B -->|>| H[_compare_gt_set]
B -->|<| I[_compare_lt_set]
C --> J{Type of operands?}
J -->|Strings| K[_diff_text]
J -->|Sequences| L[_compare_eq_sequence]
J -->|Sets| M[_compare_eq_set]
J -->|Dicts| N[_compare_eq_dict]
J -->|Dataclasses/Attrs/Namedtuples| O[_compare_eq_cls]
J -->|Iterable| P[_compare_eq_iterable]
K --> Q[Format & highlight diff]
L --> Q
M --> Q
N --> Q
O --> Q
P --> Q
Q --> R[Return explanation lines]
Summary
The [util.py](/projects/286/67331) module is a central utility for pytest’s enhanced assertion debugging. By providing detailed, type-aware, and visually formatted explanations of assertion failures, it significantly improves the clarity of test failure reports. It handles a broad variety of data types and operators, leverages pytest’s configuration for verbosity and highlighting, and integrates tightly with pytest’s internal assertion rewriting system.
This file enables pytest users to quickly identify the root cause of assertion failures, especially when dealing with complex or nested data structures, making debugging more efficient and less error-prone.