1 import contextlib
2 import functools
3 import importlib
4 import re
5 import sys
6 import warnings
7
8
9 def import_deprecated(name):
10 """Import *name* while suppressing DeprecationWarning."""
11 with warnings.catch_warnings():
12 warnings.simplefilter('ignore', category=DeprecationWarning)
13 return importlib.import_module(name)
14
15
16 def check_syntax_warning(testcase, statement, errtext='',
17 *, lineno=1, offset=None):
18 # Test also that a warning is emitted only once.
19 from test.support import check_syntax_error
20 with warnings.catch_warnings(record=True) as warns:
21 warnings.simplefilter('always', SyntaxWarning)
22 compile(statement, '<testcase>', 'exec')
23 testcase.assertEqual(len(warns), 1, warns)
24
25 warn, = warns
26 testcase.assertTrue(issubclass(warn.category, SyntaxWarning),
27 warn.category)
28 if errtext:
29 testcase.assertRegex(str(warn.message), errtext)
30 testcase.assertEqual(warn.filename, '<testcase>')
31 testcase.assertIsNotNone(warn.lineno)
32 if lineno is not None:
33 testcase.assertEqual(warn.lineno, lineno)
34
35 # SyntaxWarning should be converted to SyntaxError when raised,
36 # since the latter contains more information and provides better
37 # error report.
38 with warnings.catch_warnings(record=True) as warns:
39 warnings.simplefilter('error', SyntaxWarning)
40 check_syntax_error(testcase, statement, errtext,
41 lineno=lineno, offset=offset)
42 # No warnings are leaked when a SyntaxError is raised.
43 testcase.assertEqual(warns, [])
44
45
46 def ignore_warnings(*, category):
47 """Decorator to suppress warnings.
48
49 Use of context managers to hide warnings make diffs
50 more noisy and tools like 'git blame' less useful.
51 """
52 def decorator(test):
53 @functools.wraps(test)
54 def wrapper(self, *args, **kwargs):
55 with warnings.catch_warnings():
56 warnings.simplefilter('ignore', category=category)
57 return test(self, *args, **kwargs)
58 return wrapper
59 return decorator
60
61
62 class ESC[4;38;5;81mWarningsRecorder(ESC[4;38;5;149mobject):
63 """Convenience wrapper for the warnings list returned on
64 entry to the warnings.catch_warnings() context manager.
65 """
66 def __init__(self, warnings_list):
67 self._warnings = warnings_list
68 self._last = 0
69
70 def __getattr__(self, attr):
71 if len(self._warnings) > self._last:
72 return getattr(self._warnings[-1], attr)
73 elif attr in warnings.WarningMessage._WARNING_DETAILS:
74 return None
75 raise AttributeError("%r has no attribute %r" % (self, attr))
76
77 @property
78 def warnings(self):
79 return self._warnings[self._last:]
80
81 def reset(self):
82 self._last = len(self._warnings)
83
84
85 @contextlib.contextmanager
86 def check_warnings(*filters, **kwargs):
87 """Context manager to silence warnings.
88
89 Accept 2-tuples as positional arguments:
90 ("message regexp", WarningCategory)
91
92 Optional argument:
93 - if 'quiet' is True, it does not fail if a filter catches nothing
94 (default True without argument,
95 default False if some filters are defined)
96
97 Without argument, it defaults to:
98 check_warnings(("", Warning), quiet=True)
99 """
100 quiet = kwargs.get('quiet')
101 if not filters:
102 filters = (("", Warning),)
103 # Preserve backward compatibility
104 if quiet is None:
105 quiet = True
106 return _filterwarnings(filters, quiet)
107
108
109 @contextlib.contextmanager
110 def check_no_warnings(testcase, message='', category=Warning, force_gc=False):
111 """Context manager to check that no warnings are emitted.
112
113 This context manager enables a given warning within its scope
114 and checks that no warnings are emitted even with that warning
115 enabled.
116
117 If force_gc is True, a garbage collection is attempted before checking
118 for warnings. This may help to catch warnings emitted when objects
119 are deleted, such as ResourceWarning.
120
121 Other keyword arguments are passed to warnings.filterwarnings().
122 """
123 from test.support import gc_collect
124 with warnings.catch_warnings(record=True) as warns:
125 warnings.filterwarnings('always',
126 message=message,
127 category=category)
128 yield
129 if force_gc:
130 gc_collect()
131 testcase.assertEqual(warns, [])
132
133
134 @contextlib.contextmanager
135 def check_no_resource_warning(testcase):
136 """Context manager to check that no ResourceWarning is emitted.
137
138 Usage:
139
140 with check_no_resource_warning(self):
141 f = open(...)
142 ...
143 del f
144
145 You must remove the object which may emit ResourceWarning before
146 the end of the context manager.
147 """
148 with check_no_warnings(testcase, category=ResourceWarning, force_gc=True):
149 yield
150
151
152 def _filterwarnings(filters, quiet=False):
153 """Catch the warnings, then check if all the expected
154 warnings have been raised and re-raise unexpected warnings.
155 If 'quiet' is True, only re-raise the unexpected warnings.
156 """
157 # Clear the warning registry of the calling module
158 # in order to re-raise the warnings.
159 frame = sys._getframe(2)
160 registry = frame.f_globals.get('__warningregistry__')
161 if registry:
162 registry.clear()
163 with warnings.catch_warnings(record=True) as w:
164 # Set filter "always" to record all warnings. Because
165 # test_warnings swap the module, we need to look up in
166 # the sys.modules dictionary.
167 sys.modules['warnings'].simplefilter("always")
168 yield WarningsRecorder(w)
169 # Filter the recorded warnings
170 reraise = list(w)
171 missing = []
172 for msg, cat in filters:
173 seen = False
174 for w in reraise[:]:
175 warning = w.message
176 # Filter out the matching messages
177 if (re.match(msg, str(warning), re.I) and
178 issubclass(warning.__class__, cat)):
179 seen = True
180 reraise.remove(w)
181 if not seen and not quiet:
182 # This filter caught nothing
183 missing.append((msg, cat.__name__))
184 if reraise:
185 raise AssertionError("unhandled warning %s" % reraise[0])
186 if missing:
187 raise AssertionError("filter (%r, %s) did not catch any warning" %
188 missing[0])
189
190
191 @contextlib.contextmanager
192 def save_restore_warnings_filters():
193 old_filters = warnings.filters[:]
194 try:
195 yield
196 finally:
197 warnings.filters[:] = old_filters
198
199
200 def _warn_about_deprecation():
201 warnings.warn(
202 "This is used in test_support test to ensure"
203 " support.ignore_deprecations_from() works as expected."
204 " You should not be seeing this.",
205 DeprecationWarning,
206 stacklevel=0,
207 )