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

1import io 

2import os 

3import sys 

4from typing import Generator 

5from typing import TextIO 

6 

7import pytest 

8from _pytest.config import Config 

9from _pytest.config.argparsing import Parser 

10from _pytest.nodes import Item 

11from _pytest.stash import StashKey 

12 

13 

14fault_handler_stderr_key = StashKey[TextIO]() 

15fault_handler_originally_enabled_key = StashKey[bool]() 

16 

17 

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) 

24 

25 

26def pytest_configure(config: Config) -> None: 

27 import faulthandler 

28 

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]) 

33 

34 

35def pytest_unconfigure(config: Config) -> None: 

36 import faulthandler 

37 

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()) 

46 

47 

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() 

61 

62 

63def get_timeout_config_value(config: Config) -> float: 

64 return float(config.getini("faulthandler_timeout") or 0.0) 

65 

66 

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 

73 

74 faulthandler.dump_traceback_later(timeout, file=stderr) 

75 try: 

76 yield 

77 finally: 

78 faulthandler.cancel_dump_traceback_later() 

79 else: 

80 yield 

81 

82 

83@pytest.hookimpl(tryfirst=True) 

84def pytest_enter_pdb() -> None: 

85 """Cancel any traceback dumping due to timeout before entering pdb.""" 

86 import faulthandler 

87 

88 faulthandler.cancel_dump_traceback_later() 

89 

90 

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 

96 

97 faulthandler.cancel_dump_traceback_later()