bpo-40275: Add warnings_helper submodule in test.support (GH-20797)
This commit is contained in:
parent
bdfe9b633a
commit
10e6506aa8
|
@ -497,79 +497,6 @@ The :mod:`test.support` module defines the following functions:
|
||||||
check_impl_detail(cpython=False) # Everywhere except CPython.
|
check_impl_detail(cpython=False) # Everywhere except CPython.
|
||||||
|
|
||||||
|
|
||||||
.. function:: check_warnings(\*filters, quiet=True)
|
|
||||||
|
|
||||||
A convenience wrapper for :func:`warnings.catch_warnings()` that makes it
|
|
||||||
easier to test that a warning was correctly raised. It is approximately
|
|
||||||
equivalent to calling ``warnings.catch_warnings(record=True)`` with
|
|
||||||
:meth:`warnings.simplefilter` set to ``always`` and with the option to
|
|
||||||
automatically validate the results that are recorded.
|
|
||||||
|
|
||||||
``check_warnings`` accepts 2-tuples of the form ``("message regexp",
|
|
||||||
WarningCategory)`` as positional arguments. If one or more *filters* are
|
|
||||||
provided, or if the optional keyword argument *quiet* is ``False``,
|
|
||||||
it checks to make sure the warnings are as expected: each specified filter
|
|
||||||
must match at least one of the warnings raised by the enclosed code or the
|
|
||||||
test fails, and if any warnings are raised that do not match any of the
|
|
||||||
specified filters the test fails. To disable the first of these checks,
|
|
||||||
set *quiet* to ``True``.
|
|
||||||
|
|
||||||
If no arguments are specified, it defaults to::
|
|
||||||
|
|
||||||
check_warnings(("", Warning), quiet=True)
|
|
||||||
|
|
||||||
In this case all warnings are caught and no errors are raised.
|
|
||||||
|
|
||||||
On entry to the context manager, a :class:`WarningRecorder` instance is
|
|
||||||
returned. The underlying warnings list from
|
|
||||||
:func:`~warnings.catch_warnings` is available via the recorder object's
|
|
||||||
:attr:`warnings` attribute. As a convenience, the attributes of the object
|
|
||||||
representing the most recent warning can also be accessed directly through
|
|
||||||
the recorder object (see example below). If no warning has been raised,
|
|
||||||
then any of the attributes that would otherwise be expected on an object
|
|
||||||
representing a warning will return ``None``.
|
|
||||||
|
|
||||||
The recorder object also has a :meth:`reset` method, which clears the
|
|
||||||
warnings list.
|
|
||||||
|
|
||||||
The context manager is designed to be used like this::
|
|
||||||
|
|
||||||
with check_warnings(("assertion is always true", SyntaxWarning),
|
|
||||||
("", UserWarning)):
|
|
||||||
exec('assert(False, "Hey!")')
|
|
||||||
warnings.warn(UserWarning("Hide me!"))
|
|
||||||
|
|
||||||
In this case if either warning was not raised, or some other warning was
|
|
||||||
raised, :func:`check_warnings` would raise an error.
|
|
||||||
|
|
||||||
When a test needs to look more deeply into the warnings, rather than
|
|
||||||
just checking whether or not they occurred, code like this can be used::
|
|
||||||
|
|
||||||
with check_warnings(quiet=True) as w:
|
|
||||||
warnings.warn("foo")
|
|
||||||
assert str(w.args[0]) == "foo"
|
|
||||||
warnings.warn("bar")
|
|
||||||
assert str(w.args[0]) == "bar"
|
|
||||||
assert str(w.warnings[0].args[0]) == "foo"
|
|
||||||
assert str(w.warnings[1].args[0]) == "bar"
|
|
||||||
w.reset()
|
|
||||||
assert len(w.warnings) == 0
|
|
||||||
|
|
||||||
|
|
||||||
Here all warnings will be caught, and the test code tests the captured
|
|
||||||
warnings directly.
|
|
||||||
|
|
||||||
.. versionchanged:: 3.2
|
|
||||||
New optional arguments *filters* and *quiet*.
|
|
||||||
|
|
||||||
|
|
||||||
.. function:: check_no_resource_warning(testcase)
|
|
||||||
|
|
||||||
Context manager to check that no :exc:`ResourceWarning` was raised. You
|
|
||||||
must remove the object which may emit :exc:`ResourceWarning` before the
|
|
||||||
end of the context manager.
|
|
||||||
|
|
||||||
|
|
||||||
.. function:: set_memlimit(limit)
|
.. function:: set_memlimit(limit)
|
||||||
|
|
||||||
Set the values for :data:`max_memuse` and :data:`real_max_memuse` for big
|
Set the values for :data:`max_memuse` and :data:`real_max_memuse` for big
|
||||||
|
@ -851,20 +778,6 @@ The :mod:`test.support` module defines the following functions:
|
||||||
the offset of the exception.
|
the offset of the exception.
|
||||||
|
|
||||||
|
|
||||||
.. function:: check_syntax_warning(testcase, statement, errtext='', *, lineno=1, offset=None)
|
|
||||||
|
|
||||||
Test for syntax warning in *statement* by attempting to compile *statement*.
|
|
||||||
Test also that the :exc:`SyntaxWarning` is emitted only once, and that it
|
|
||||||
will be converted to a :exc:`SyntaxError` when turned into error.
|
|
||||||
*testcase* is the :mod:`unittest` instance for the test. *errtext* is the
|
|
||||||
regular expression which should match the string representation of the
|
|
||||||
emitted :exc:`SyntaxWarning` and raised :exc:`SyntaxError`. If *lineno*
|
|
||||||
is not ``None``, compares to the line of the warning and exception.
|
|
||||||
If *offset* is not ``None``, compares to the offset of the exception.
|
|
||||||
|
|
||||||
.. versionadded:: 3.8
|
|
||||||
|
|
||||||
|
|
||||||
.. function:: open_urlresource(url, *args, **kw)
|
.. function:: open_urlresource(url, *args, **kw)
|
||||||
|
|
||||||
Open *url*. If open fails, raises :exc:`TestFailed`.
|
Open *url*. If open fails, raises :exc:`TestFailed`.
|
||||||
|
@ -1051,12 +964,6 @@ The :mod:`test.support` module defines the following classes:
|
||||||
Try to match a single stored value (*dv*) with a supplied value (*v*).
|
Try to match a single stored value (*dv*) with a supplied value (*v*).
|
||||||
|
|
||||||
|
|
||||||
.. class:: WarningsRecorder()
|
|
||||||
|
|
||||||
Class used to record warnings for unit tests. See documentation of
|
|
||||||
:func:`check_warnings` above for more details.
|
|
||||||
|
|
||||||
|
|
||||||
.. class:: BasicTestRunner()
|
.. class:: BasicTestRunner()
|
||||||
|
|
||||||
.. method:: run(test)
|
.. method:: run(test)
|
||||||
|
@ -1659,3 +1566,105 @@ The :mod:`test.support.import_helper` module provides support for import tests.
|
||||||
will be reverted at the end of the block.
|
will be reverted at the end of the block.
|
||||||
|
|
||||||
|
|
||||||
|
:mod:`test.support.warnings_helper` --- Utilities for warnings tests
|
||||||
|
====================================================================
|
||||||
|
|
||||||
|
.. module:: test.support.warnings_helper
|
||||||
|
:synopsis: Support for warnings tests.
|
||||||
|
|
||||||
|
The :mod:`test.support.warnings_helper` module provides support for warnings tests.
|
||||||
|
|
||||||
|
.. versionadded:: 3.10
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: check_no_resource_warning(testcase)
|
||||||
|
|
||||||
|
Context manager to check that no :exc:`ResourceWarning` was raised. You
|
||||||
|
must remove the object which may emit :exc:`ResourceWarning` before the
|
||||||
|
end of the context manager.
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: check_syntax_warning(testcase, statement, errtext='', *, lineno=1, offset=None)
|
||||||
|
|
||||||
|
Test for syntax warning in *statement* by attempting to compile *statement*.
|
||||||
|
Test also that the :exc:`SyntaxWarning` is emitted only once, and that it
|
||||||
|
will be converted to a :exc:`SyntaxError` when turned into error.
|
||||||
|
*testcase* is the :mod:`unittest` instance for the test. *errtext* is the
|
||||||
|
regular expression which should match the string representation of the
|
||||||
|
emitted :exc:`SyntaxWarning` and raised :exc:`SyntaxError`. If *lineno*
|
||||||
|
is not ``None``, compares to the line of the warning and exception.
|
||||||
|
If *offset* is not ``None``, compares to the offset of the exception.
|
||||||
|
|
||||||
|
.. versionadded:: 3.8
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: check_warnings(\*filters, quiet=True)
|
||||||
|
|
||||||
|
A convenience wrapper for :func:`warnings.catch_warnings()` that makes it
|
||||||
|
easier to test that a warning was correctly raised. It is approximately
|
||||||
|
equivalent to calling ``warnings.catch_warnings(record=True)`` with
|
||||||
|
:meth:`warnings.simplefilter` set to ``always`` and with the option to
|
||||||
|
automatically validate the results that are recorded.
|
||||||
|
|
||||||
|
``check_warnings`` accepts 2-tuples of the form ``("message regexp",
|
||||||
|
WarningCategory)`` as positional arguments. If one or more *filters* are
|
||||||
|
provided, or if the optional keyword argument *quiet* is ``False``,
|
||||||
|
it checks to make sure the warnings are as expected: each specified filter
|
||||||
|
must match at least one of the warnings raised by the enclosed code or the
|
||||||
|
test fails, and if any warnings are raised that do not match any of the
|
||||||
|
specified filters the test fails. To disable the first of these checks,
|
||||||
|
set *quiet* to ``True``.
|
||||||
|
|
||||||
|
If no arguments are specified, it defaults to::
|
||||||
|
|
||||||
|
check_warnings(("", Warning), quiet=True)
|
||||||
|
|
||||||
|
In this case all warnings are caught and no errors are raised.
|
||||||
|
|
||||||
|
On entry to the context manager, a :class:`WarningRecorder` instance is
|
||||||
|
returned. The underlying warnings list from
|
||||||
|
:func:`~warnings.catch_warnings` is available via the recorder object's
|
||||||
|
:attr:`warnings` attribute. As a convenience, the attributes of the object
|
||||||
|
representing the most recent warning can also be accessed directly through
|
||||||
|
the recorder object (see example below). If no warning has been raised,
|
||||||
|
then any of the attributes that would otherwise be expected on an object
|
||||||
|
representing a warning will return ``None``.
|
||||||
|
|
||||||
|
The recorder object also has a :meth:`reset` method, which clears the
|
||||||
|
warnings list.
|
||||||
|
|
||||||
|
The context manager is designed to be used like this::
|
||||||
|
|
||||||
|
with check_warnings(("assertion is always true", SyntaxWarning),
|
||||||
|
("", UserWarning)):
|
||||||
|
exec('assert(False, "Hey!")')
|
||||||
|
warnings.warn(UserWarning("Hide me!"))
|
||||||
|
|
||||||
|
In this case if either warning was not raised, or some other warning was
|
||||||
|
raised, :func:`check_warnings` would raise an error.
|
||||||
|
|
||||||
|
When a test needs to look more deeply into the warnings, rather than
|
||||||
|
just checking whether or not they occurred, code like this can be used::
|
||||||
|
|
||||||
|
with check_warnings(quiet=True) as w:
|
||||||
|
warnings.warn("foo")
|
||||||
|
assert str(w.args[0]) == "foo"
|
||||||
|
warnings.warn("bar")
|
||||||
|
assert str(w.args[0]) == "bar"
|
||||||
|
assert str(w.warnings[0].args[0]) == "foo"
|
||||||
|
assert str(w.warnings[1].args[0]) == "bar"
|
||||||
|
w.reset()
|
||||||
|
assert len(w.warnings) == 0
|
||||||
|
|
||||||
|
|
||||||
|
Here all warnings will be caught, and the test code tests the captured
|
||||||
|
warnings directly.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.2
|
||||||
|
New optional arguments *filters* and *quiet*.
|
||||||
|
|
||||||
|
|
||||||
|
.. class:: WarningsRecorder()
|
||||||
|
|
||||||
|
Class used to record warnings for unit tests. See documentation of
|
||||||
|
:func:`check_warnings` above for more details.
|
||||||
|
|
|
@ -14,7 +14,6 @@ import sysconfig
|
||||||
import time
|
import time
|
||||||
import types
|
import types
|
||||||
import unittest
|
import unittest
|
||||||
import warnings
|
|
||||||
|
|
||||||
from .import_helper import (
|
from .import_helper import (
|
||||||
CleanImport, DirsOnSysPath, _ignore_deprecated_imports,
|
CleanImport, DirsOnSysPath, _ignore_deprecated_imports,
|
||||||
|
@ -30,6 +29,10 @@ from .os_helper import (
|
||||||
rmtree, skip_unless_symlink, skip_unless_xattr,
|
rmtree, skip_unless_symlink, skip_unless_xattr,
|
||||||
temp_cwd, temp_dir, temp_umask, unlink,
|
temp_cwd, temp_dir, temp_umask, unlink,
|
||||||
EnvironmentVarGuard, FakePath, _longpath)
|
EnvironmentVarGuard, FakePath, _longpath)
|
||||||
|
from .warnings_helper import (
|
||||||
|
WarningsRecorder, _filterwarnings,
|
||||||
|
check_no_resource_warning, check_no_warnings,
|
||||||
|
check_syntax_warning, check_warnings, ignore_warnings)
|
||||||
|
|
||||||
from .testresult import get_test_runner
|
from .testresult import get_test_runner
|
||||||
|
|
||||||
|
@ -45,7 +48,7 @@ __all__ = [
|
||||||
# unittest
|
# unittest
|
||||||
"is_resource_enabled", "requires", "requires_freebsd_version",
|
"is_resource_enabled", "requires", "requires_freebsd_version",
|
||||||
"requires_linux_version", "requires_mac_ver",
|
"requires_linux_version", "requires_mac_ver",
|
||||||
"check_syntax_error", "check_syntax_warning",
|
"check_syntax_error",
|
||||||
"TransientResource", "time_out", "socket_peer_reset", "ioerror_peer_reset",
|
"TransientResource", "time_out", "socket_peer_reset", "ioerror_peer_reset",
|
||||||
"BasicTestRunner", "run_unittest", "run_doctest",
|
"BasicTestRunner", "run_unittest", "run_doctest",
|
||||||
"requires_gzip", "requires_bz2", "requires_lzma",
|
"requires_gzip", "requires_bz2", "requires_lzma",
|
||||||
|
@ -53,7 +56,6 @@ __all__ = [
|
||||||
"requires_IEEE_754", "requires_zlib",
|
"requires_IEEE_754", "requires_zlib",
|
||||||
"anticipate_failure", "load_package_tests", "detect_api_mismatch",
|
"anticipate_failure", "load_package_tests", "detect_api_mismatch",
|
||||||
"check__all__", "skip_if_buggy_ucrt_strfptime",
|
"check__all__", "skip_if_buggy_ucrt_strfptime",
|
||||||
"ignore_warnings",
|
|
||||||
# sys
|
# sys
|
||||||
"is_jython", "is_android", "check_impl_detail", "unix_shell",
|
"is_jython", "is_android", "check_impl_detail", "unix_shell",
|
||||||
"setswitchinterval",
|
"setswitchinterval",
|
||||||
|
@ -62,7 +64,6 @@ __all__ = [
|
||||||
# processes
|
# processes
|
||||||
"reap_children",
|
"reap_children",
|
||||||
# miscellaneous
|
# miscellaneous
|
||||||
"check_warnings", "check_no_resource_warning", "check_no_warnings",
|
|
||||||
"run_with_locale", "swap_item", "findfile",
|
"run_with_locale", "swap_item", "findfile",
|
||||||
"swap_attr", "Matcher", "set_memlimit", "SuppressCrashReport", "sortdict",
|
"swap_attr", "Matcher", "set_memlimit", "SuppressCrashReport", "sortdict",
|
||||||
"run_with_tz", "PGO", "missing_compiler_executable",
|
"run_with_tz", "PGO", "missing_compiler_executable",
|
||||||
|
@ -128,22 +129,6 @@ class ResourceDenied(unittest.SkipTest):
|
||||||
and unexpected skips.
|
and unexpected skips.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def ignore_warnings(*, category):
|
|
||||||
"""Decorator to suppress deprecation warnings.
|
|
||||||
|
|
||||||
Use of context managers to hide warnings make diffs
|
|
||||||
more noisy and tools like 'git blame' less useful.
|
|
||||||
"""
|
|
||||||
def decorator(test):
|
|
||||||
@functools.wraps(test)
|
|
||||||
def wrapper(self, *args, **kwargs):
|
|
||||||
with warnings.catch_warnings():
|
|
||||||
warnings.simplefilter('ignore', category=category)
|
|
||||||
return test(self, *args, **kwargs)
|
|
||||||
return wrapper
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def anticipate_failure(condition):
|
def anticipate_failure(condition):
|
||||||
"""Decorator to mark a test that is known to be broken in some cases
|
"""Decorator to mark a test that is known to be broken in some cases
|
||||||
|
|
||||||
|
@ -511,32 +496,6 @@ def check_syntax_error(testcase, statement, errtext='', *, lineno=None, offset=N
|
||||||
if offset is not None:
|
if offset is not None:
|
||||||
testcase.assertEqual(err.offset, offset)
|
testcase.assertEqual(err.offset, offset)
|
||||||
|
|
||||||
def check_syntax_warning(testcase, statement, errtext='', *, lineno=1, offset=None):
|
|
||||||
# Test also that a warning is emitted only once.
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('always', SyntaxWarning)
|
|
||||||
compile(statement, '<testcase>', 'exec')
|
|
||||||
testcase.assertEqual(len(warns), 1, warns)
|
|
||||||
|
|
||||||
warn, = warns
|
|
||||||
testcase.assertTrue(issubclass(warn.category, SyntaxWarning), warn.category)
|
|
||||||
if errtext:
|
|
||||||
testcase.assertRegex(str(warn.message), errtext)
|
|
||||||
testcase.assertEqual(warn.filename, '<testcase>')
|
|
||||||
testcase.assertIsNotNone(warn.lineno)
|
|
||||||
if lineno is not None:
|
|
||||||
testcase.assertEqual(warn.lineno, lineno)
|
|
||||||
|
|
||||||
# SyntaxWarning should be converted to SyntaxError when raised,
|
|
||||||
# since the latter contains more information and provides better
|
|
||||||
# error report.
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('error', SyntaxWarning)
|
|
||||||
check_syntax_error(testcase, statement, errtext,
|
|
||||||
lineno=lineno, offset=offset)
|
|
||||||
# No warnings are leaked when a SyntaxError is raised.
|
|
||||||
testcase.assertEqual(warns, [])
|
|
||||||
|
|
||||||
|
|
||||||
def open_urlresource(url, *args, **kw):
|
def open_urlresource(url, *args, **kw):
|
||||||
import urllib.request, urllib.parse
|
import urllib.request, urllib.parse
|
||||||
|
@ -592,134 +551,6 @@ def open_urlresource(url, *args, **kw):
|
||||||
raise TestFailed('invalid resource %r' % fn)
|
raise TestFailed('invalid resource %r' % fn)
|
||||||
|
|
||||||
|
|
||||||
class WarningsRecorder(object):
|
|
||||||
"""Convenience wrapper for the warnings list returned on
|
|
||||||
entry to the warnings.catch_warnings() context manager.
|
|
||||||
"""
|
|
||||||
def __init__(self, warnings_list):
|
|
||||||
self._warnings = warnings_list
|
|
||||||
self._last = 0
|
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
|
||||||
if len(self._warnings) > self._last:
|
|
||||||
return getattr(self._warnings[-1], attr)
|
|
||||||
elif attr in warnings.WarningMessage._WARNING_DETAILS:
|
|
||||||
return None
|
|
||||||
raise AttributeError("%r has no attribute %r" % (self, attr))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def warnings(self):
|
|
||||||
return self._warnings[self._last:]
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
self._last = len(self._warnings)
|
|
||||||
|
|
||||||
|
|
||||||
def _filterwarnings(filters, quiet=False):
|
|
||||||
"""Catch the warnings, then check if all the expected
|
|
||||||
warnings have been raised and re-raise unexpected warnings.
|
|
||||||
If 'quiet' is True, only re-raise the unexpected warnings.
|
|
||||||
"""
|
|
||||||
# Clear the warning registry of the calling module
|
|
||||||
# in order to re-raise the warnings.
|
|
||||||
frame = sys._getframe(2)
|
|
||||||
registry = frame.f_globals.get('__warningregistry__')
|
|
||||||
if registry:
|
|
||||||
registry.clear()
|
|
||||||
with warnings.catch_warnings(record=True) as w:
|
|
||||||
# Set filter "always" to record all warnings. Because
|
|
||||||
# test_warnings swap the module, we need to look up in
|
|
||||||
# the sys.modules dictionary.
|
|
||||||
sys.modules['warnings'].simplefilter("always")
|
|
||||||
yield WarningsRecorder(w)
|
|
||||||
# Filter the recorded warnings
|
|
||||||
reraise = list(w)
|
|
||||||
missing = []
|
|
||||||
for msg, cat in filters:
|
|
||||||
seen = False
|
|
||||||
for w in reraise[:]:
|
|
||||||
warning = w.message
|
|
||||||
# Filter out the matching messages
|
|
||||||
if (re.match(msg, str(warning), re.I) and
|
|
||||||
issubclass(warning.__class__, cat)):
|
|
||||||
seen = True
|
|
||||||
reraise.remove(w)
|
|
||||||
if not seen and not quiet:
|
|
||||||
# This filter caught nothing
|
|
||||||
missing.append((msg, cat.__name__))
|
|
||||||
if reraise:
|
|
||||||
raise AssertionError("unhandled warning %s" % reraise[0])
|
|
||||||
if missing:
|
|
||||||
raise AssertionError("filter (%r, %s) did not catch any warning" %
|
|
||||||
missing[0])
|
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def check_warnings(*filters, **kwargs):
|
|
||||||
"""Context manager to silence warnings.
|
|
||||||
|
|
||||||
Accept 2-tuples as positional arguments:
|
|
||||||
("message regexp", WarningCategory)
|
|
||||||
|
|
||||||
Optional argument:
|
|
||||||
- if 'quiet' is True, it does not fail if a filter catches nothing
|
|
||||||
(default True without argument,
|
|
||||||
default False if some filters are defined)
|
|
||||||
|
|
||||||
Without argument, it defaults to:
|
|
||||||
check_warnings(("", Warning), quiet=True)
|
|
||||||
"""
|
|
||||||
quiet = kwargs.get('quiet')
|
|
||||||
if not filters:
|
|
||||||
filters = (("", Warning),)
|
|
||||||
# Preserve backward compatibility
|
|
||||||
if quiet is None:
|
|
||||||
quiet = True
|
|
||||||
return _filterwarnings(filters, quiet)
|
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def check_no_warnings(testcase, message='', category=Warning, force_gc=False):
|
|
||||||
"""Context manager to check that no warnings are emitted.
|
|
||||||
|
|
||||||
This context manager enables a given warning within its scope
|
|
||||||
and checks that no warnings are emitted even with that warning
|
|
||||||
enabled.
|
|
||||||
|
|
||||||
If force_gc is True, a garbage collection is attempted before checking
|
|
||||||
for warnings. This may help to catch warnings emitted when objects
|
|
||||||
are deleted, such as ResourceWarning.
|
|
||||||
|
|
||||||
Other keyword arguments are passed to warnings.filterwarnings().
|
|
||||||
"""
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.filterwarnings('always',
|
|
||||||
message=message,
|
|
||||||
category=category)
|
|
||||||
yield
|
|
||||||
if force_gc:
|
|
||||||
gc_collect()
|
|
||||||
testcase.assertEqual(warns, [])
|
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def check_no_resource_warning(testcase):
|
|
||||||
"""Context manager to check that no ResourceWarning is emitted.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
with check_no_resource_warning(self):
|
|
||||||
f = open(...)
|
|
||||||
...
|
|
||||||
del f
|
|
||||||
|
|
||||||
You must remove the object which may emit ResourceWarning before
|
|
||||||
the end of the context manager.
|
|
||||||
"""
|
|
||||||
with check_no_warnings(testcase, category=ResourceWarning, force_gc=True):
|
|
||||||
yield
|
|
||||||
|
|
||||||
|
|
||||||
class TransientResource(object):
|
class TransientResource(object):
|
||||||
|
|
||||||
"""Raise ResourceDenied if an exception is raised while the context manager
|
"""Raise ResourceDenied if an exception is raised while the context manager
|
||||||
|
@ -978,6 +809,7 @@ class _MemoryWatchdog:
|
||||||
self.started = False
|
self.started = False
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
import warnings
|
||||||
try:
|
try:
|
||||||
f = open(self.procfile, 'r')
|
f = open(self.procfile, 'r')
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
import contextlib
|
||||||
|
import functools
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
|
def check_syntax_warning(testcase, statement, errtext='',
|
||||||
|
*, lineno=1, offset=None):
|
||||||
|
# Test also that a warning is emitted only once.
|
||||||
|
from test.support import check_syntax_error
|
||||||
|
with warnings.catch_warnings(record=True) as warns:
|
||||||
|
warnings.simplefilter('always', SyntaxWarning)
|
||||||
|
compile(statement, '<testcase>', 'exec')
|
||||||
|
testcase.assertEqual(len(warns), 1, warns)
|
||||||
|
|
||||||
|
warn, = warns
|
||||||
|
testcase.assertTrue(issubclass(warn.category, SyntaxWarning),
|
||||||
|
warn.category)
|
||||||
|
if errtext:
|
||||||
|
testcase.assertRegex(str(warn.message), errtext)
|
||||||
|
testcase.assertEqual(warn.filename, '<testcase>')
|
||||||
|
testcase.assertIsNotNone(warn.lineno)
|
||||||
|
if lineno is not None:
|
||||||
|
testcase.assertEqual(warn.lineno, lineno)
|
||||||
|
|
||||||
|
# SyntaxWarning should be converted to SyntaxError when raised,
|
||||||
|
# since the latter contains more information and provides better
|
||||||
|
# error report.
|
||||||
|
with warnings.catch_warnings(record=True) as warns:
|
||||||
|
warnings.simplefilter('error', SyntaxWarning)
|
||||||
|
check_syntax_error(testcase, statement, errtext,
|
||||||
|
lineno=lineno, offset=offset)
|
||||||
|
# No warnings are leaked when a SyntaxError is raised.
|
||||||
|
testcase.assertEqual(warns, [])
|
||||||
|
|
||||||
|
|
||||||
|
def ignore_warnings(*, category):
|
||||||
|
"""Decorator to suppress deprecation warnings.
|
||||||
|
|
||||||
|
Use of context managers to hide warnings make diffs
|
||||||
|
more noisy and tools like 'git blame' less useful.
|
||||||
|
"""
|
||||||
|
def decorator(test):
|
||||||
|
@functools.wraps(test)
|
||||||
|
def wrapper(self, *args, **kwargs):
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter('ignore', category=category)
|
||||||
|
return test(self, *args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
class WarningsRecorder(object):
|
||||||
|
"""Convenience wrapper for the warnings list returned on
|
||||||
|
entry to the warnings.catch_warnings() context manager.
|
||||||
|
"""
|
||||||
|
def __init__(self, warnings_list):
|
||||||
|
self._warnings = warnings_list
|
||||||
|
self._last = 0
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
if len(self._warnings) > self._last:
|
||||||
|
return getattr(self._warnings[-1], attr)
|
||||||
|
elif attr in warnings.WarningMessage._WARNING_DETAILS:
|
||||||
|
return None
|
||||||
|
raise AttributeError("%r has no attribute %r" % (self, attr))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def warnings(self):
|
||||||
|
return self._warnings[self._last:]
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self._last = len(self._warnings)
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def check_warnings(*filters, **kwargs):
|
||||||
|
"""Context manager to silence warnings.
|
||||||
|
|
||||||
|
Accept 2-tuples as positional arguments:
|
||||||
|
("message regexp", WarningCategory)
|
||||||
|
|
||||||
|
Optional argument:
|
||||||
|
- if 'quiet' is True, it does not fail if a filter catches nothing
|
||||||
|
(default True without argument,
|
||||||
|
default False if some filters are defined)
|
||||||
|
|
||||||
|
Without argument, it defaults to:
|
||||||
|
check_warnings(("", Warning), quiet=True)
|
||||||
|
"""
|
||||||
|
quiet = kwargs.get('quiet')
|
||||||
|
if not filters:
|
||||||
|
filters = (("", Warning),)
|
||||||
|
# Preserve backward compatibility
|
||||||
|
if quiet is None:
|
||||||
|
quiet = True
|
||||||
|
return _filterwarnings(filters, quiet)
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def check_no_warnings(testcase, message='', category=Warning, force_gc=False):
|
||||||
|
"""Context manager to check that no warnings are emitted.
|
||||||
|
|
||||||
|
This context manager enables a given warning within its scope
|
||||||
|
and checks that no warnings are emitted even with that warning
|
||||||
|
enabled.
|
||||||
|
|
||||||
|
If force_gc is True, a garbage collection is attempted before checking
|
||||||
|
for warnings. This may help to catch warnings emitted when objects
|
||||||
|
are deleted, such as ResourceWarning.
|
||||||
|
|
||||||
|
Other keyword arguments are passed to warnings.filterwarnings().
|
||||||
|
"""
|
||||||
|
from test.support import gc_collect
|
||||||
|
with warnings.catch_warnings(record=True) as warns:
|
||||||
|
warnings.filterwarnings('always',
|
||||||
|
message=message,
|
||||||
|
category=category)
|
||||||
|
yield
|
||||||
|
if force_gc:
|
||||||
|
gc_collect()
|
||||||
|
testcase.assertEqual(warns, [])
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def check_no_resource_warning(testcase):
|
||||||
|
"""Context manager to check that no ResourceWarning is emitted.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
with check_no_resource_warning(self):
|
||||||
|
f = open(...)
|
||||||
|
...
|
||||||
|
del f
|
||||||
|
|
||||||
|
You must remove the object which may emit ResourceWarning before
|
||||||
|
the end of the context manager.
|
||||||
|
"""
|
||||||
|
with check_no_warnings(testcase, category=ResourceWarning, force_gc=True):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
def _filterwarnings(filters, quiet=False):
|
||||||
|
"""Catch the warnings, then check if all the expected
|
||||||
|
warnings have been raised and re-raise unexpected warnings.
|
||||||
|
If 'quiet' is True, only re-raise the unexpected warnings.
|
||||||
|
"""
|
||||||
|
# Clear the warning registry of the calling module
|
||||||
|
# in order to re-raise the warnings.
|
||||||
|
frame = sys._getframe(2)
|
||||||
|
registry = frame.f_globals.get('__warningregistry__')
|
||||||
|
if registry:
|
||||||
|
registry.clear()
|
||||||
|
with warnings.catch_warnings(record=True) as w:
|
||||||
|
# Set filter "always" to record all warnings. Because
|
||||||
|
# test_warnings swap the module, we need to look up in
|
||||||
|
# the sys.modules dictionary.
|
||||||
|
sys.modules['warnings'].simplefilter("always")
|
||||||
|
yield WarningsRecorder(w)
|
||||||
|
# Filter the recorded warnings
|
||||||
|
reraise = list(w)
|
||||||
|
missing = []
|
||||||
|
for msg, cat in filters:
|
||||||
|
seen = False
|
||||||
|
for w in reraise[:]:
|
||||||
|
warning = w.message
|
||||||
|
# Filter out the matching messages
|
||||||
|
if (re.match(msg, str(warning), re.I) and
|
||||||
|
issubclass(warning.__class__, cat)):
|
||||||
|
seen = True
|
||||||
|
reraise.remove(w)
|
||||||
|
if not seen and not quiet:
|
||||||
|
# This filter caught nothing
|
||||||
|
missing.append((msg, cat.__name__))
|
||||||
|
if reraise:
|
||||||
|
raise AssertionError("unhandled warning %s" % reraise[0])
|
||||||
|
if missing:
|
||||||
|
raise AssertionError("filter (%r, %s) did not catch any warning" %
|
||||||
|
missing[0])
|
Loading…
Reference in New Issue