test_unraisableexception.py
Overview
`test_unraisableexception.py` is a test suite designed to verify the behavior of the pytest framework when handling *unraisable exceptions*. Unraisable exceptions occur when an exception is raised during object finalization (such as in a [__del__](/projects/286/67223) method) or during other contexts where exceptions cannot propagate normally, like garbage collection or asyncio task creation failures.
This file contains multiple test cases that:
Confirm that unraisable exceptions trigger the appropriate
PytestUnraisableExceptionWarning.Validate correct reporting and handling of these warnings in different test lifecycle stages (during tests, setup, teardown).
Test behavior when the warnings are escalated to errors.
Ensure compatibility with reference cycles and garbage collection states.
Check robustness in edge cases such as multiple simultaneous unraisable exceptions and failures in the traceback formatting process.
Verify integration with asyncio task scheduling and system warning filters.
These tests use the `pytest` testing framework along with its `Pytester` utility for creating test files dynamically and running pytest programmatically.
Detailed Explanations
Constants and Imports
PYPY: A boolean flag indicating if the current interpreter is PyPy. Tests marked as flaky under PyPy are skipped.UNRAISABLE_LINE: The expected warning line format forPytestUnraisableExceptionWarning, which differs depending on Python version (3.14+ vs earlier).TRACEMALLOC_LINES: Additional traceback lines shown when tracemalloc is not enabled, relevant for Python versions before 3.14.
Test Functions
All test functions use the `pytest` marker decorators to skip tests on PyPy (due to GC differences) and to filter or escalate the `PytestUnraisableExceptionWarning`.
test_unraisable(pytester: Pytester) -> None
Purpose: Tests that an unraisable exception raised in a del method during a test triggers a warning.
Implementation:
Creates a test file dynamically containing a class
BrokenDelwhose destructor raisesValueError.Runs pytest and asserts:
Exit code is 0.
Two tests passed.
One warning was emitted.
The warning message matches the expected pattern including the unraisable exception traceback.
Usage Example:
def test_unraisable(pytester):
# pytester.makepyfile(...) creates the test file.
# pytester.runpytest() runs pytest on the created file.
# Assertions check that unraisable exceptions are correctly warned.
test_unraisable_in_setup(pytester: Pytester) -> None
Purpose: Checks that unraisable exceptions raised during fixture setup raise warnings correctly.
Key difference: The
BrokenDelobject is created and deleted inside a fixture, before tests run.
test_unraisable_in_teardown(pytester: Pytester) -> None
Purpose: Validates unraisable exceptions during fixture teardown trigger the warning.
Key difference: The
BrokenDelobject is created and deleted in the fixture’s teardown phase (after the yield statement).
test_unraisable_warning_error(pytester: Pytester) -> None
Purpose: Tests that when
PytestUnraisableExceptionWarningwarnings are escalated to errors, the test run fails.Behavior: The test triggers the unraisable exception and expects pytest to exit with a failure code.
test_unraisable_warning_multiple_errors(pytester: Pytester) -> None
Purpose: Checks pytest’s handling of multiple simultaneous unraisable exceptions.
Implementation: Creates multiple
BrokenDelinstances that raise different messages on deletion.Validation: Asserts that the output contains an
ExceptionGroupmentioning multiple unraisable exception warnings.
test_unraisable_collection_failure(pytester: Pytester) -> None
Purpose: Simulates a failure in handling unraisable exceptions by mocking
traceback.format_exceptionto raise a custom error.Expected Result: Pytest run fails with a
RuntimeErrorindicating failure to process the unraisable exception.
_set_gc_state(enabled: bool) -> bool
Utility function: Enables or disables garbage collection (GC) and returns the previous state.
Used internally to control GC state during tests involving reference cycles.
test_refcycle_unraisable(pytester: Pytester) -> None
Purpose: Verifies unraisable exceptions raised in destructors of objects involved in reference cycles are caught and warned even if GC is disabled.
Implementation: Disables GC, creates a cyclic reference in
BrokenDel, and ensures the warning appears.
test_refcycle_unraisable_warning_filter(pytester: Pytester) -> None
Similar to
test_refcycle_unraisablebut with warnings escalated to errors.
test_create_task_raises_unraisable_warning_filter(pytester: Pytester) -> None
Purpose: Tests unraisable exceptions from asyncio task creation in absence of a running event loop.
Expected: A
RuntimeWarningabout a coroutine never awaited appears, and the test run exits with failure.
test_refcycle_unraisable_warning_filter_default(pytester: Pytester) -> None
Purpose: Runs a test with GC disabled in a subprocess with default warning filters to ensure unraisable exceptions are still caught and reported.
Note: The warning may appear on stderr rather than the terminal reporter.
test_possibly_none_excinfo(pytester: Pytester) -> None
Purpose: Tests robustness of the
sys.unraisablehookwhen receiving an exception info object with potentiallyNonefields.Expected:
PytestUnraisableExceptionWarningis raised with message indicating "Exception ignored in: None".
Important Implementation Details and Algorithms
Tests dynamically create Python test modules using
pytester.makepyfilewith embedded code snippets that induce unraisable exceptions.Use of
pytest.mark.filterwarningscontrols how warnings are handled, either allowing them to pass, fail the test, or be ignored.Conditional test skipping on PyPy due to differences in garbage collection behavior affecting reproducibility.
Use of
pytester.runpytest()orpytester.runpytest_subprocess()to run the generated tests, capturing results and outputs for assertions.Some tests simulate failures in internal traceback formatting by mocking
traceback.format_exceptionto raise exceptions, verifying pytest’s error handling.Testing of unraisable exceptions raised both in normal test execution as well as in fixtures' setup and teardown phases.
Special handling for Python version differences in warning message formats and presence of tracemalloc hints.
Interaction with Other Parts of the System
The file tests the pytest framework’s internal handling of unraisable exceptions, specifically the custom warning class
PytestUnraisableExceptionWarning.Makes use of
pytester.Pytester, a pytest plugin utility for creating and running test files programmatically.Uses standard Python modules
sys,gc, andmockfor environment control and mocking.Interacts with Python’s unraisable exception hook (
sys.unraisablehook) and warning system.Verifies behavior in context of Python's garbage collector and asyncio event loop.
Helps ensure robust integration of pytest with Python’s low-level exception and resource management mechanisms.
Visual Diagram
flowchart TD
A[test_unraisableexception.py Tests]
A --> B[test_unraisable]
A --> C[test_unraisable_in_setup]
A --> D[test_unraisable_in_teardown]
A --> E[test_unraisable_warning_error]
A --> F[test_unraisable_warning_multiple_errors]
A --> G[test_unraisable_collection_failure]
A --> H[test_refcycle_unraisable]
A --> I[test_refcycle_unraisable_warning_filter]
A --> J[test_create_task_raises_unraisable_warning_filter]
A --> K[test_refcycle_unraisable_warning_filter_default]
A --> L[test_possibly_none_excinfo]
subgraph Setup/Teardown Tests
C
D
end
subgraph Warning/Error Handling
E
F
K
L
end
subgraph GC & Reference Cycles
H
I
end
gmock(Mocking traceback.format_exception) --> G
style A fill:#f9f,stroke:#333,stroke-width:2px
style B,C,D,E,F,G,H,I,J,K,L fill:#bbf,stroke:#333,stroke-width:1px
Summary
`test_unraisableexception.py` is a comprehensive test suite validating how pytest detects, warns, and handles unraisable exceptions in various scenarios. It ensures that pytest's warning mechanism behaves correctly across different Python versions, GC states, and execution contexts, thereby improving pytest’s reliability and developer feedback for hard-to-catch errors.