Coverage for /opt/homebrew/lib/python3.11/site-packages/_pytest/warnings.py: 74%

74 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-05-04 13:14 +0700

1import sys 

2import warnings 

3from contextlib import contextmanager 

4from typing import Generator 

5from typing import Optional 

6from typing import TYPE_CHECKING 

7 

8import pytest 

9from _pytest.config import apply_warning_filters 

10from _pytest.config import Config 

11from _pytest.config import parse_warning_filter 

12from _pytest.main import Session 

13from _pytest.nodes import Item 

14from _pytest.terminal import TerminalReporter 

15 

16if TYPE_CHECKING: 

17 from typing_extensions import Literal 

18 

19 

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

21 config.addinivalue_line( 

22 "markers", 

23 "filterwarnings(warning): add a warning filter to the given test. " 

24 "see https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings ", 

25 ) 

26 

27 

28@contextmanager 

29def catch_warnings_for_item( 

30 config: Config, 

31 ihook, 

32 when: "Literal['config', 'collect', 'runtest']", 

33 item: Optional[Item], 

34) -> Generator[None, None, None]: 

35 """Context manager that catches warnings generated in the contained execution block. 

36 

37 ``item`` can be None if we are not in the context of an item execution. 

38 

39 Each warning captured triggers the ``pytest_warning_recorded`` hook. 

40 """ 

41 config_filters = config.getini("filterwarnings") 

42 cmdline_filters = config.known_args_namespace.pythonwarnings or [] 

43 with warnings.catch_warnings(record=True) as log: 

44 # mypy can't infer that record=True means log is not None; help it. 

45 assert log is not None 

46 

47 if not sys.warnoptions: 

48 # If user is not explicitly configuring warning filters, show deprecation warnings by default (#2908). 

49 warnings.filterwarnings("always", category=DeprecationWarning) 

50 warnings.filterwarnings("always", category=PendingDeprecationWarning) 

51 

52 apply_warning_filters(config_filters, cmdline_filters) 

53 

54 # apply filters from "filterwarnings" marks 

55 nodeid = "" if item is None else item.nodeid 

56 if item is not None: 

57 for mark in item.iter_markers(name="filterwarnings"): 

58 for arg in mark.args: 

59 warnings.filterwarnings(*parse_warning_filter(arg, escape=False)) 

60 

61 yield 

62 

63 for warning_message in log: 

64 ihook.pytest_warning_recorded.call_historic( 

65 kwargs=dict( 

66 warning_message=warning_message, 

67 nodeid=nodeid, 

68 when=when, 

69 location=None, 

70 ) 

71 ) 

72 

73 

74def warning_record_to_str(warning_message: warnings.WarningMessage) -> str: 

75 """Convert a warnings.WarningMessage to a string.""" 

76 warn_msg = warning_message.message 

77 msg = warnings.formatwarning( 

78 str(warn_msg), 

79 warning_message.category, 

80 warning_message.filename, 

81 warning_message.lineno, 

82 warning_message.line, 

83 ) 

84 if warning_message.source is not None: 

85 try: 

86 import tracemalloc 

87 except ImportError: 

88 pass 

89 else: 

90 tb = tracemalloc.get_object_traceback(warning_message.source) 

91 if tb is not None: 

92 formatted_tb = "\n".join(tb.format()) 

93 # Use a leading new line to better separate the (large) output 

94 # from the traceback to the previous warning text. 

95 msg += f"\nObject allocated at:\n{formatted_tb}" 

96 else: 

97 # No need for a leading new line. 

98 url = "https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings" 

99 msg += "Enable tracemalloc to get traceback where the object was allocated.\n" 

100 msg += f"See {url} for more info." 

101 return msg 

102 

103 

104@pytest.hookimpl(hookwrapper=True, tryfirst=True) 

105def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]: 

106 with catch_warnings_for_item( 

107 config=item.config, ihook=item.ihook, when="runtest", item=item 

108 ): 

109 yield 

110 

111 

112@pytest.hookimpl(hookwrapper=True, tryfirst=True) 

113def pytest_collection(session: Session) -> Generator[None, None, None]: 

114 config = session.config 

115 with catch_warnings_for_item( 

116 config=config, ihook=config.hook, when="collect", item=None 

117 ): 

118 yield 

119 

120 

121@pytest.hookimpl(hookwrapper=True) 

122def pytest_terminal_summary( 

123 terminalreporter: TerminalReporter, 

124) -> Generator[None, None, None]: 

125 config = terminalreporter.config 

126 with catch_warnings_for_item( 

127 config=config, ihook=config.hook, when="config", item=None 

128 ): 

129 yield 

130 

131 

132@pytest.hookimpl(hookwrapper=True) 

133def pytest_sessionfinish(session: Session) -> Generator[None, None, None]: 

134 config = session.config 

135 with catch_warnings_for_item( 

136 config=config, ihook=config.hook, when="config", item=None 

137 ): 

138 yield 

139 

140 

141@pytest.hookimpl(hookwrapper=True) 

142def pytest_load_initial_conftests( 

143 early_config: "Config", 

144) -> Generator[None, None, None]: 

145 with catch_warnings_for_item( 

146 config=early_config, ihook=early_config.hook, when="config", item=None 

147 ): 

148 yield