Hide keyboard shortcuts

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 sys 

2import warnings 

3from contextlib import contextmanager 

4 

5import pytest 

6 

7 

8def _setoption(wmod, arg): 

9 """ 

10 Copy of the warning._setoption function but does not escape arguments. 

11 """ 

12 parts = arg.split(":") 

13 if len(parts) > 5: 

14 raise wmod._OptionError("too many fields (max 5): {!r}".format(arg)) 

15 while len(parts) < 5: 

16 parts.append("") 

17 action, message, category, module, lineno = [s.strip() for s in parts] 

18 action = wmod._getaction(action) 

19 category = wmod._getcategory(category) 

20 if lineno: 

21 try: 

22 lineno = int(lineno) 

23 if lineno < 0: 

24 raise ValueError 

25 except (ValueError, OverflowError): 

26 raise wmod._OptionError("invalid lineno {!r}".format(lineno)) 

27 else: 

28 lineno = 0 

29 wmod.filterwarnings(action, message, category, module, lineno) 

30 

31 

32def pytest_addoption(parser): 

33 group = parser.getgroup("pytest-warnings") 

34 group.addoption( 

35 "-W", 

36 "--pythonwarnings", 

37 action="append", 

38 help="set which warnings to report, see -W option of python itself.", 

39 ) 

40 parser.addini( 

41 "filterwarnings", 

42 type="linelist", 

43 help="Each line specifies a pattern for " 

44 "warnings.filterwarnings. " 

45 "Processed after -W/--pythonwarnings.", 

46 ) 

47 

48 

49def pytest_configure(config): 

50 config.addinivalue_line( 

51 "markers", 

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

53 "see https://docs.pytest.org/en/latest/warnings.html#pytest-mark-filterwarnings ", 

54 ) 

55 

56 

57@contextmanager 

58def catch_warnings_for_item(config, ihook, when, item): 

59 """ 

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

61 

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

63 

64 Each warning captured triggers the ``pytest_warning_captured`` hook. 

65 """ 

66 cmdline_filters = config.getoption("pythonwarnings") or [] 

67 inifilters = config.getini("filterwarnings") 

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

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

70 assert log is not None 

71 

72 if not sys.warnoptions: 

73 # if user is not explicitly configuring warning filters, show deprecation warnings by default (#2908) 

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

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

76 

77 # filters should have this precedence: mark, cmdline options, ini 

78 # filters should be applied in the inverse order of precedence 

79 for arg in inifilters: 

80 _setoption(warnings, arg) 

81 

82 for arg in cmdline_filters: 

83 warnings._setoption(arg) 

84 

85 if item is not None: 

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

87 for arg in mark.args: 

88 _setoption(warnings, arg) 

89 

90 yield 

91 

92 for warning_message in log: 

93 ihook.pytest_warning_captured.call_historic( 

94 kwargs=dict(warning_message=warning_message, when=when, item=item) 

95 ) 

96 

97 

98def warning_record_to_str(warning_message): 

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

100 warn_msg = warning_message.message 

101 msg = warnings.formatwarning( 

102 warn_msg, 

103 warning_message.category, 

104 warning_message.filename, 

105 warning_message.lineno, 

106 warning_message.line, 

107 ) 

108 return msg 

109 

110 

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

112def pytest_runtest_protocol(item): 

113 with catch_warnings_for_item( 

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

115 ): 

116 yield 

117 

118 

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

120def pytest_collection(session): 

121 config = session.config 

122 with catch_warnings_for_item( 

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

124 ): 

125 yield 

126 

127 

128@pytest.hookimpl(hookwrapper=True) 

129def pytest_terminal_summary(terminalreporter): 

130 config = terminalreporter.config 

131 with catch_warnings_for_item( 

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

133 ): 

134 yield 

135 

136 

137def _issue_warning_captured(warning, hook, stacklevel): 

138 """ 

139 This function should be used instead of calling ``warnings.warn`` directly when we are in the "configure" stage: 

140 at this point the actual options might not have been set, so we manually trigger the pytest_warning_captured 

141 hook so we can display these warnings in the terminal. This is a hack until we can sort out #2891. 

142 

143 :param warning: the warning instance. 

144 :param hook: the hook caller 

145 :param stacklevel: stacklevel forwarded to warnings.warn 

146 """ 

147 with warnings.catch_warnings(record=True) as records: 

148 warnings.simplefilter("always", type(warning)) 

149 warnings.warn(warning, stacklevel=stacklevel) 

150 # Mypy can't infer that record=True means records is not None; help it. 

151 assert records is not None 

152 hook.pytest_warning_captured.call_historic( 

153 kwargs=dict(warning_message=records[0], when="config", item=None) 

154 )