Coverage for /opt/homebrew/lib/python3.11/site-packages/_pytest/faulthandler.py: 78%
58 statements
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 13:14 +0700
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 13:14 +0700
1import io
2import os
3import sys
4from typing import Generator
5from typing import TextIO
7import pytest
8from _pytest.config import Config
9from _pytest.config.argparsing import Parser
10from _pytest.nodes import Item
11from _pytest.stash import StashKey
14fault_handler_stderr_key = StashKey[TextIO]()
15fault_handler_originally_enabled_key = StashKey[bool]()
18def pytest_addoption(parser: Parser) -> None:
19 help = (
20 "Dump the traceback of all threads if a test takes "
21 "more than TIMEOUT seconds to finish"
22 )
23 parser.addini("faulthandler_timeout", help, default=0.0)
26def pytest_configure(config: Config) -> None:
27 import faulthandler
29 stderr_fd_copy = os.dup(get_stderr_fileno())
30 config.stash[fault_handler_stderr_key] = open(stderr_fd_copy, "w")
31 config.stash[fault_handler_originally_enabled_key] = faulthandler.is_enabled()
32 faulthandler.enable(file=config.stash[fault_handler_stderr_key])
35def pytest_unconfigure(config: Config) -> None:
36 import faulthandler
38 faulthandler.disable()
39 # Close the dup file installed during pytest_configure.
40 if fault_handler_stderr_key in config.stash:
41 config.stash[fault_handler_stderr_key].close()
42 del config.stash[fault_handler_stderr_key]
43 if config.stash.get(fault_handler_originally_enabled_key, False):
44 # Re-enable the faulthandler if it was originally enabled.
45 faulthandler.enable(file=get_stderr_fileno())
48def get_stderr_fileno() -> int:
49 try:
50 fileno = sys.stderr.fileno()
51 # The Twisted Logger will return an invalid file descriptor since it is not backed
52 # by an FD. So, let's also forward this to the same code path as with pytest-xdist.
53 if fileno == -1:
54 raise AttributeError()
55 return fileno
56 except (AttributeError, io.UnsupportedOperation):
57 # pytest-xdist monkeypatches sys.stderr with an object that is not an actual file.
58 # https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors
59 # This is potentially dangerous, but the best we can do.
60 return sys.__stderr__.fileno()
63def get_timeout_config_value(config: Config) -> float:
64 return float(config.getini("faulthandler_timeout") or 0.0)
67@pytest.hookimpl(hookwrapper=True, trylast=True)
68def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
69 timeout = get_timeout_config_value(item.config)
70 stderr = item.config.stash[fault_handler_stderr_key]
71 if timeout > 0 and stderr is not None:
72 import faulthandler
74 faulthandler.dump_traceback_later(timeout, file=stderr)
75 try:
76 yield
77 finally:
78 faulthandler.cancel_dump_traceback_later()
79 else:
80 yield
83@pytest.hookimpl(tryfirst=True)
84def pytest_enter_pdb() -> None:
85 """Cancel any traceback dumping due to timeout before entering pdb."""
86 import faulthandler
88 faulthandler.cancel_dump_traceback_later()
91@pytest.hookimpl(tryfirst=True)
92def pytest_exception_interact() -> None:
93 """Cancel any traceback dumping due to an interactive exception being
94 raised."""
95 import faulthandler
97 faulthandler.cancel_dump_traceback_later()