faulthandler.py
Overview
The [faulthandler.py](/projects/286/67477) file is a plugin module for the pytest testing framework that integrates Python's built-in `faulthandler` module to improve debugging of test timeouts and crashes. Its primary purpose is to enable automatic dumping of Python traceback information for all threads when a test exceeds a configured timeout. This helps developers quickly identify deadlocks, infinite loops, or other issues causing tests to hang.
Key functionalities include:
Configuring a timeout threshold after which faulthandler dumps stack traces.
Managing faulthandler's output file descriptor safely despite possible monkeypatching of
sys.stderr.Enabling and disabling faulthandler at pytest startup and teardown.
Cancelling scheduled traceback dumps when entering the debugger or interacting with exceptions.
This integration enhances pytest's ability to diagnose problematic tests by leveraging faulthandler's capability to dump tracebacks of all threads to help uncover deadlocks or stuck states.
Detailed Component Descriptions
Constants / Keys
fault_handler_original_stderr_fd_key: StashKey[int]Stores the original file descriptor of
sys.stderrbefore pytest reconfigures faulthandler output. Used to restore faulthandler state on teardown.fault_handler_stderr_fd_key: StashKey[int]Stores a duplicated file descriptor of
sys.stderrfor faulthandler to write to safely throughout the test session.
Functions
pytest_addoption(parser: Parser) -> None
Registers a pytest ini configuration option:
Purpose: Adds the
faulthandler_timeoutini option to specify a timeout in seconds. If a test runs longer than this, faulthandler will dump tracebacks.Parameters:
parser: pytest's command-line argument parser.
Usage Example:
[pytest] faulthandler_timeout = 10.0Effect: Enables users to configure automatic traceback dumping on long-running tests.
pytest_configure(config: Config) -> None
Configures the faulthandler plugin during pytest startup:
Purpose: Enables faulthandler with a duplicated stderr file descriptor, and stashes original and duplicate fds in pytest's stash for later restoration.
Parameters:
config: pytest configuration object.
Implementation Details:
Uses
get_stderr_fileno()to safely get a writable file descriptor.Duplicates the file descriptor with
os.dup()to avoid issues ifsys.stderris replaced or closed.Enables faulthandler to write to this duplicated fd.
Usage: Automatically invoked by pytest during initialization.
pytest_unconfigure(config: Config) -> None
Restores faulthandler state during pytest teardown:
Purpose: Disables faulthandler, closes duplicated file descriptors, and restores faulthandler to original stderr if it was originally enabled.
Parameters:
config: pytest configuration object.
Implementation Details:
Checks the stash keys set in
pytest_configure.Properly closes the duplicated file descriptor to avoid resource leaks.
Re-enables faulthandler on the original stderr fd if necessary.
Usage: Automatically invoked by pytest during shutdown.
get_stderr_fileno() -> int
Safely obtains a usable file descriptor for `sys.stderr` for faulthandler output:
Purpose: Handles cases when
sys.stderris replaced by non-file-like objects (e.g., by pytest-xdist or Twisted Logger).Returns: Integer file descriptor corresponding to a valid stderr.
Implementation Details:
Attempts to call
sys.stderr.fileno().If returns
-1or raises an error, falls back tosys.__stderr__.fileno().Includes an assertion that
sys.__stderr__is notNone.
Usage Example:
stderr_fd = get_stderr_fileno()Notes: Ensures faulthandler writes to a real file descriptor, avoiding errors.
get_timeout_config_value(config: Config) -> float
Retrieves the configured faulthandler timeout value:
Purpose: Gets the float timeout value from pytest ini config, defaults to 0.0 if unset.
Parameters:
config: pytest configuration object.
Returns: Timeout in seconds as
float.Usage Example:
timeout = get_timeout_config_value(config)Notes: Used internally to determine if faulthandler timeout should be enabled.
Pytest Hook Implementations
pytest_runtest_protocol(item: Item) -> Generator[None, object, object]
Wraps the execution of a single test item to enable faulthandler timeout dumping:
Purpose: If timeout is > 0, schedules a faulthandler traceback dump after the timeout for the current test.
Parameters:
item: pytest test item object.
Returns: A generator yielding control to pytest's runtest machinery.
Implementation Details:
Schedules
faulthandler.dump_traceback_later(timeout)to dump all threads' tracebacks if the timeout is exceeded.Cancels scheduled dump on test completion or interruption.
Usage: Automatically invoked by pytest during test runtest protocol.
Example: If
faulthandler_timeout=5.0, and a test hangs longer than 5 seconds, faulthandler will dump tracebacks.
pytest_enter_pdb() -> None
Cancels any pending faulthandler traceback dumps when entering the interactive debugger:
Purpose: Prevents faulthandler from dumping tracebacks if the user enters pdb.
Usage: Automatically called by pytest before starting pdb.
Implementation: Calls
faulthandler.cancel_dump_traceback_later().
pytest_exception_interact() -> None
Cancels any pending faulthandler tracebacks when pytest enters exception interaction mode:
Purpose: Prevents faulthandler dumping tracebacks when an interactive exception prompt appears.
Usage: Automatically called by pytest on interactive exception handling.
Implementation: Calls
faulthandler.cancel_dump_traceback_later().
Important Implementation Details
The file uses pytest's
StashKeymechanism to store file descriptors safely in the config stash, avoiding clashes.Duplication of
sys.stderrfile descriptor ensures faulthandler output remains valid even ifsys.stderris monkeypatched or closed during tests.The timeout feature leverages
faulthandler.dump_traceback_later()for asynchronous dumping of all thread tracebacks.Cancelling the scheduled dump is critical to avoid unwanted tracebacks after tests complete or debugger is entered.
Careful handling of file descriptors prevents resource leaks and preserves original faulthandler state.
Interaction with Other Components
pytest core: Implements pytest hook functions (
pytest_addoption,pytest_configure,pytest_unconfigure,pytest_runtest_protocol, etc.) to integrate with pytest lifecycle.faulthandler stdlib module: Directly calls faulthandler APIs to enable, disable, schedule dump, and cancel dump.
pytest config and stash: Uses
ConfigandStashKeyto store file descriptor info persistently during the test session.sys and os modules: Uses
sys.stderrandos.dup()for safe file descriptor handling.pytest-xdist or other plugins: Accounts for possible monkeypatching of
sys.stderrby external plugins, ensuring robustness.
Usage Example
Assuming you have this plugin enabled in your pytest environment, you can configure a timeout in your `pytest.ini`:
[pytest]
faulthandler_timeout = 10.0
When you run `pytest`, any test running longer than 10 seconds will trigger faulthandler dumping stack traces of all threads to stderr, aiding in diagnosing hangs or deadlocks.
Visual Diagram
classDiagram
class FaulthandlerPlugin {
<<pytest plugin>>
+pytest_addoption(parser: Parser) void
+pytest_configure(config: Config) void
+pytest_unconfigure(config: Config) void
+pytest_runtest_protocol(item: Item) Generator
+pytest_enter_pdb() void
+pytest_exception_interact() void
+get_stderr_fileno() int
+get_timeout_config_value(config: Config) float
}
FaulthandlerPlugin : +fault_handler_original_stderr_fd_key: StashKey[int]
FaulthandlerPlugin : +fault_handler_stderr_fd_key: StashKey[int]
FaulthandlerPlugin ..> "faulthandler" : uses
FaulthandlerPlugin ..> "pytest.config.Config" : interacts
FaulthandlerPlugin ..> "sys.stderr" : reads fileno
FaulthandlerPlugin ..> "os" : duplicates and closes fds
FaulthandlerPlugin ..> "pytest.nodes.Item" : receives test item
Summary
The [faulthandler.py](/projects/286/67477) file is a pytest plugin module that leverages Python's `faulthandler` to improve test debugging by dumping thread tracebacks on test timeouts. It carefully manages file descriptors to ensure faulthandler output reliability even under complex test environments and integrates tightly with pytest's lifecycle hooks to enable, disable, and cancel faulthandler tracebacks as appropriate.
This plugin significantly aids developers in diagnosing stuck or hanging tests by providing detailed runtime traceback information automatically.