Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/_pytest/faulthandler.py : 23%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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.store import StoreKey
14fault_handler_stderr_key = StoreKey[TextIO]()
17def pytest_addoption(parser: Parser) -> None:
18 help = (
19 "Dump the traceback of all threads if a test takes "
20 "more than TIMEOUT seconds to finish."
21 )
22 parser.addini("faulthandler_timeout", help, default=0.0)
25def pytest_configure(config: Config) -> None:
26 import faulthandler
28 if not faulthandler.is_enabled():
29 # faulthhandler is not enabled, so install plugin that does the actual work
30 # of enabling faulthandler before each test executes.
31 config.pluginmanager.register(FaultHandlerHooks(), "faulthandler-hooks")
32 else:
33 from _pytest.warnings import _issue_warning_captured
35 # Do not handle dumping to stderr if faulthandler is already enabled, so warn
36 # users that the option is being ignored.
37 timeout = FaultHandlerHooks.get_timeout_config_value(config)
38 if timeout > 0:
39 _issue_warning_captured(
40 pytest.PytestConfigWarning(
41 "faulthandler module enabled before pytest configuration step, "
42 "'faulthandler_timeout' option ignored"
43 ),
44 config.hook,
45 stacklevel=2,
46 )
49class FaultHandlerHooks:
50 """Implements hooks that will actually install fault handler before tests execute,
51 as well as correctly handle pdb and internal errors."""
53 def pytest_configure(self, config: Config) -> None:
54 import faulthandler
56 stderr_fd_copy = os.dup(self._get_stderr_fileno())
57 config._store[fault_handler_stderr_key] = open(stderr_fd_copy, "w")
58 faulthandler.enable(file=config._store[fault_handler_stderr_key])
60 def pytest_unconfigure(self, config: Config) -> None:
61 import faulthandler
63 faulthandler.disable()
64 # close our dup file installed during pytest_configure
65 # re-enable the faulthandler, attaching it to the default sys.stderr
66 # so we can see crashes after pytest has finished, usually during
67 # garbage collection during interpreter shutdown
68 config._store[fault_handler_stderr_key].close()
69 del config._store[fault_handler_stderr_key]
70 faulthandler.enable(file=self._get_stderr_fileno())
72 @staticmethod
73 def _get_stderr_fileno():
74 try:
75 return sys.stderr.fileno()
76 except (AttributeError, io.UnsupportedOperation):
77 # pytest-xdist monkeypatches sys.stderr with an object that is not an actual file.
78 # https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors
79 # This is potentially dangerous, but the best we can do.
80 return sys.__stderr__.fileno()
82 @staticmethod
83 def get_timeout_config_value(config):
84 return float(config.getini("faulthandler_timeout") or 0.0)
86 @pytest.hookimpl(hookwrapper=True, trylast=True)
87 def pytest_runtest_protocol(self, item: Item) -> Generator[None, None, None]:
88 timeout = self.get_timeout_config_value(item.config)
89 stderr = item.config._store[fault_handler_stderr_key]
90 if timeout > 0 and stderr is not None:
91 import faulthandler
93 faulthandler.dump_traceback_later(timeout, file=stderr)
94 try:
95 yield
96 finally:
97 faulthandler.cancel_dump_traceback_later()
98 else:
99 yield
101 @pytest.hookimpl(tryfirst=True)
102 def pytest_enter_pdb(self) -> None:
103 """Cancel any traceback dumping due to timeout before entering pdb.
104 """
105 import faulthandler
107 faulthandler.cancel_dump_traceback_later()
109 @pytest.hookimpl(tryfirst=True)
110 def pytest_exception_interact(self) -> None:
111 """Cancel any traceback dumping due to an interactive exception being
112 raised.
113 """
114 import faulthandler
116 faulthandler.cancel_dump_traceback_later()