• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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