import io import os import sys from typing import Generator import pytest from _pytest.config import Config from _pytest.config.argparsing import Parser from _pytest.nodes import Item from _pytest.stash import StashKey fault_handler_stderr_fd_key = StashKey[int]() fault_handler_originally_enabled_key = StashKey[bool]() def pytest_addoption(parser: Parser) -> None: help = ( "Dump the traceback of all threads if a test takes " "more than TIMEOUT seconds to finish" ) parser.addini("faulthandler_timeout", help, default=0.0) def pytest_configure(config: Config) -> None: import faulthandler config.stash[fault_handler_stderr_fd_key] = os.dup(get_stderr_fileno()) config.stash[fault_handler_originally_enabled_key] = faulthandler.is_enabled() faulthandler.enable(file=config.stash[fault_handler_stderr_fd_key]) def pytest_unconfigure(config: Config) -> None: import faulthandler faulthandler.disable() # Close the dup file installed during pytest_configure. if fault_handler_stderr_fd_key in config.stash: os.close(config.stash[fault_handler_stderr_fd_key]) del config.stash[fault_handler_stderr_fd_key] if config.stash.get(fault_handler_originally_enabled_key, False): # Re-enable the faulthandler if it was originally enabled. faulthandler.enable(file=get_stderr_fileno()) def get_stderr_fileno() -> int: try: fileno = sys.stderr.fileno() # The Twisted Logger will return an invalid file descriptor since it is not backed # by an FD. So, let's also forward this to the same code path as with pytest-xdist. if fileno == -1: raise AttributeError() return fileno except (AttributeError, io.UnsupportedOperation): # pytest-xdist monkeypatches sys.stderr with an object that is not an actual file. # https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors # This is potentially dangerous, but the best we can do. return sys.__stderr__.fileno() def get_timeout_config_value(config: Config) -> float: return float(config.getini("faulthandler_timeout") or 0.0) @pytest.hookimpl(hookwrapper=True, trylast=True) def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]: timeout = get_timeout_config_value(item.config) if timeout > 0: import faulthandler stderr = item.config.stash[fault_handler_stderr_fd_key] faulthandler.dump_traceback_later(timeout, file=stderr) try: yield finally: faulthandler.cancel_dump_traceback_later() else: yield @pytest.hookimpl(tryfirst=True) def pytest_enter_pdb() -> None: """Cancel any traceback dumping due to timeout before entering pdb.""" import faulthandler faulthandler.cancel_dump_traceback_later() @pytest.hookimpl(tryfirst=True) def pytest_exception_interact() -> None: """Cancel any traceback dumping due to an interactive exception being raised.""" import faulthandler faulthandler.cancel_dump_traceback_later()
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
__pycache__ | Folder | 0755 |
|
|
_code | Folder | 0755 |
|
|
_io | Folder | 0755 |
|
|
_py | Folder | 0755 |
|
|
assertion | Folder | 0755 |
|
|
config | Folder | 0755 |
|
|
mark | Folder | 0755 |
|
|
__init__.py | File | 356 B | 0644 |
|
_argcomplete.py | File | 3.71 KB | 0644 |
|
_version.py | File | 160 B | 0644 |
|
cacheprovider.py | File | 20.89 KB | 0644 |
|
capture.py | File | 33.92 KB | 0644 |
|
compat.py | File | 12.89 KB | 0644 |
|
debugging.py | File | 13.18 KB | 0644 |
|
deprecated.py | File | 5.36 KB | 0644 |
|
doctest.py | File | 25.35 KB | 0644 |
|
faulthandler.py | File | 3.04 KB | 0644 |
|
fixtures.py | File | 65.51 KB | 0644 |
|
freeze_support.py | File | 1.31 KB | 0644 |
|
helpconfig.py | File | 8.34 KB | 0644 |
|
hookspec.py | File | 31.79 KB | 0644 |
|
junitxml.py | File | 25.11 KB | 0644 |
|
legacypath.py | File | 16.53 KB | 0644 |
|
logging.py | File | 33.23 KB | 0644 |
|
main.py | File | 31.73 KB | 0644 |
|
monkeypatch.py | File | 14.51 KB | 0644 |
|
nodes.py | File | 25.94 KB | 0644 |
|
nose.py | File | 1.65 KB | 0644 |
|
outcomes.py | File | 10.02 KB | 0644 |
|
pastebin.py | File | 3.86 KB | 0644 |
|
pathlib.py | File | 25.22 KB | 0644 |
|
py.typed | File | 0 B | 0644 |
|
pytester.py | File | 60.52 KB | 0644 |
|
pytester_assertions.py | File | 2.27 KB | 0644 |
|
python.py | File | 69.49 KB | 0644 |
|
python_api.py | File | 37.5 KB | 0644 |
|
python_path.py | File | 709 B | 0644 |
|
recwarn.py | File | 10.67 KB | 0644 |
|
reports.py | File | 20.35 KB | 0644 |
|
runner.py | File | 18.01 KB | 0644 |
|
scope.py | File | 2.81 KB | 0644 |
|
setuponly.py | File | 3.18 KB | 0644 |
|
setupplan.py | File | 1.19 KB | 0644 |
|
skipping.py | File | 9.96 KB | 0644 |
|
stash.py | File | 2.98 KB | 0644 |
|
stepwise.py | File | 4.6 KB | 0644 |
|
terminal.py | File | 52.25 KB | 0644 |
|
threadexception.py | File | 2.85 KB | 0644 |
|
timing.py | File | 375 B | 0644 |
|
tmpdir.py | File | 11.43 KB | 0644 |
|
unittest.py | File | 14.46 KB | 0644 |
|
unraisableexception.py | File | 3.12 KB | 0644 |
|
warning_types.py | File | 4.37 KB | 0644 |
|
warnings.py | File | 4.95 KB | 0644 |
|