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