test_compat.py
Overview
`test_compat.py` is a test suite designed to validate utility functions and compatibility helpers primarily related to function unwrapping, safe attribute access, class type checks, cached properties, and exhaustive type checking assertions. The file contains multiple test cases that ensure the correctness and robustness of these utilities, many of which are imported from the `_pytest.compat` module. The tests cover scenarios such as handling decorator wrappers, partial functions, exception safety in attribute access, verifying `cached_property` behavior, and asserting unreachable code paths with `assert_never`.
This file plays a critical role in verifying foundational helper functions that are leveraged across the pytest codebase to maintain compatibility and correctness in complex runtime scenarios, such as decorator chains and safe introspection.
Detailed Explanation of Components
Functions
test_real_func_loop_limit() -> None
Purpose:
Tests thatget_real_funcdetects and raises an error on infinite wrapper loops when unwrapping a heavily self-referential object.How it works:
Defines a local class Evil that returns itself repeatedly via__getattr__, simulating an infinite recursive wrapper chain. The test verifies that get_real_func(evil) raises aValueErrorindicating a wrapper loop.Usage Example:
test_real_func_loop_limit()Important details:
This test ensures thatget_real_funccontains a loop detection mechanism to prevent infinite recursion when unwrapping decorators or proxies.
test_get_real_func() -> None
Purpose:
Validates thatget_real_funccorrectly unwraps multiple layers of decorators to retrieve the original undecorated function.How it works:
Defines a simple decorator usingwraps, wraps a function multiple times, then asserts thatget_real_funcreturns the original function. It also tests compatibility with a pytest fixture wrapper.Usage Example:
test_get_real_func()Important details:
This test confirmsget_real_funcworks with both standard decorator chains and pytest's internal fixture wrappers.
test_get_real_func_partial() -> None
Purpose:
Tests thatget_real_funchandlesfunctools.partialobjects by unwrapping them correctly to their base function.How it works:
Defines a functionfoo, wraps it withpartial, and verifies thatget_real_funcreturns the originalfoofunction.Usage Example:
test_get_real_func_partial()
test_helper_failures() -> None
Purpose:
Verifies that exceptions raised by properties in theErrorsHelperclass behave as expected, specifically which exceptions are caught and which propagate.How it works:
InstantiatesErrorsHelperand asserts that:Accessing
raise_exceptionraises anException.Accessing
raise_fail_outcomeraisesOutcomeException(pytest's failure exception).Accessing
raise_baseexceptionis not tested here because it raisesBaseExceptionwhich should propagate.
Usage Example:
test_helper_failures()
test_safe_getattr() -> None
Purpose:
Tests thesafe_getattrutility which safely gets an attribute but returns a default if certain exceptions are raised.How it works:
UsesErrorsHelperproperties that raise exceptions. Verifies thatsafe_getattrreturns the default value forExceptionandOutcomeExceptionbut propagatesBaseException.Usage Example:
test_safe_getattr()
test_safe_isclass() -> None
Purpose:
Tests thesafe_isclassfunction which determines if an object is a class, handling cases where__class__is overridden unsafely.How it works:
Checks thatsafe_isclass(type)returnsTrue. Defines a class with a__class__property that raises an assertion failure if accessed, and confirmssafe_isclassreturnsFalsefor its instance without triggering the assertion.Usage Example:
test_safe_isclass()
test_cached_property() -> None
Purpose:
Tests the behavior of Python'sfunctools.cached_propertydecorator.How it works:
Defines a class with acached_propertythat increments a counter each time it is computed. Verifies that the property is computed once per instance and cached thereafter.Usage Example:
test_cached_property()
test_assert_never_union() -> None
Purpose:
Tests theassert_neverfunction to ensure it raisesAssertionErrorfor unexpected union type values.How it works:
Defines a variable with typeint | str. Checks that when the value is anintorstr, no assertion is raised, but if a branch is missing,assert_nevertriggers an error.Usage Example:
test_assert_never_union()
test_assert_never_enum() -> None
Purpose:
Validatesassert_neverbehavior with enum types.How it works:
Defines an enumEwith two membersaandb. Tests thatassert_neverraises when an enum value is not handled explicitly.Usage Example:
test_assert_never_enum()
test_assert_never_literal() -> None
Purpose:
Testsassert_neveronLiteraltypes.How it works:
Uses a variable typed asLiteral["a", "b"]. Verifies that missing branches raiseAssertionErrorviaassert_never.Usage Example:
test_assert_never_literal()
Class
ErrorsHelper
Description:
A utility class used to raise different types of exceptions from properties to test exception handling in safe attribute access and failure scenarios.Properties:
raise_baseexception: property
Raises aBaseExceptionwhen accessed. Represents critical exceptions that should not be caught by safe attribute access.raise_exception: property
Raises a generalExceptionwhen accessed. Simulates typical exceptions that should be caught.raise_fail_outcome: property
Callspytest.fail(), raising anOutcomeExceptionto simulate pytest test failure exceptions.
Usage Example:
helper = ErrorsHelper() try: helper.raise_exception except Exception: print("Exception caught") try: helper.raise_fail_outcome except OutcomeException: print("Test failure caught") # Accessing raise_baseexception will propagate BaseException
Important Implementation Details and Algorithms
Function Unwrapping (
get_real_func):
The tests emphasize the importance of correctly unwrapping wrapped functions, including chained decorators and partial objects, without causing infinite recursion. Thetest_real_func_loop_limithighlights a safeguard against infinite loops by raising aValueError.Safe Attribute Access (
safe_getattr):
Designed to safely access object attributes while catching non-critical exceptions (ExceptionandOutcomeException) but allowing critical exceptions (BaseException) to propagate.Safe Class Detection (
safe_isclass):
Avoids unsafe__class__attribute access that could raise errors; useful in dynamically typed or proxy-heavy environments.Exhaustive Type Checking (
assert_never):
Used to assert that all possible cases of a union, enum, or literal type are handled. If an unexpected value is encountered, it raises anAssertionError. This provides a runtime safety net for exhaustive type checks that static type checkers enforce at type-checking time.
Interaction with Other Parts of the System
Imports multiple compatibility utilities from
_pytest.compat, such asassert_never,get_real_func,safe_getattr, andsafe_isclass. These are core helpers used throughout the pytest framework for safe introspection and compatibility.Uses
pytestandpytest.failto verify behavior related to test outcomes and exceptions.OutcomeExceptionfrom_pytest.outcomesis used to distinguish pytest test failure exceptions from other exceptions.This file itself is a test module and thus interacts indirectly with the rest of the pytest test suite, ensuring the reliability of helper functions used elsewhere.
Visual Diagram: Function Relationships Flowchart
flowchart TD
A[get_real_func] -->|unwraps| B[decorator chain]
A -->|unwraps| C[functools.partial]
D[test_real_func_loop_limit] --> A
E[test_get_real_func] --> A
F[test_get_real_func_partial] --> A
G[safe_getattr] --> H[ErrorsHelper properties]
I[test_safe_getattr] --> G
I --> H
J[safe_isclass] --> K[Class or Instance]
L[test_safe_isclass] --> J
L --> K
M[assert_never] --> N[Union, Enum, Literal]
O[test_assert_never_union] --> M
P[test_assert_never_enum] --> M
Q[test_assert_never_literal] --> M
R[cached_property] --> S[Class property caching]
T[test_cached_property] --> R
U[ErrorsHelper] --> H
V[test_helper_failures] --> H
Summary
`test_compat.py` is a comprehensive test module validating critical compatibility utilities in the pytest framework. It covers safe function unwrapping, handling of partial functions, safe attribute access amidst exceptions, proper class detection, cached property behavior, and exhaustive type checking assertions. The file ensures these utilities perform reliably under edge cases such as infinite decorator loops, unusual class implementations, and various exception types. This testing is vital for the robustness of pytest's internal compatibility layer, influencing many areas of the testing framework.