forward port r66386
This commit is contained in:
parent
d31fdc547b
commit
fcf5d639f5
@ -278,18 +278,26 @@ The :mod:`test.support` module defines the following functions:
|
||||
This will run all tests defined in the named module.
|
||||
|
||||
|
||||
.. function:: catch_warning(module=warnings, record=True)
|
||||
.. function:: check_warnings()
|
||||
|
||||
Return a context manager that guards the warnings filter from being
|
||||
permanently changed and optionally alters the :func:`showwarning`
|
||||
function to record the details of any warnings that are issued in the
|
||||
managed context. Attributes of the most recent warning are saved
|
||||
directly on the context manager, while details of previous warnings
|
||||
can be retrieved from the ``warnings`` list.
|
||||
A convenience wrapper for ``warnings.catch_warnings()`` that makes
|
||||
it easier to test that a warning was correctly raised with a single
|
||||
assertion. It is approximately equivalent to calling
|
||||
``warnings.catch_warnings(record=True)``.
|
||||
|
||||
The main difference is that on entry to the context manager, a
|
||||
:class:`WarningRecorder` instance is returned instead of a simple list.
|
||||
The underlying warnings list is available via the recorder object's
|
||||
:attr:`warnings` attribute, while the attributes of the last raised
|
||||
warning are also accessible directly on the object. If no warning has
|
||||
been raised, then the latter attributes will all be :const:`None`.
|
||||
|
||||
A :meth:`reset` method is also provided on the recorder object. This
|
||||
method simply clears the warning list.
|
||||
|
||||
The context manager is used like this::
|
||||
|
||||
with catch_warning() as w:
|
||||
with check_warnings() as w:
|
||||
warnings.simplefilter("always")
|
||||
warnings.warn("foo")
|
||||
assert str(w.message) == "foo"
|
||||
@ -297,15 +305,9 @@ The :mod:`test.support` module defines the following functions:
|
||||
assert str(w.message) == "bar"
|
||||
assert str(w.warnings[0].message) == "foo"
|
||||
assert str(w.warnings[1].message) == "bar"
|
||||
w.reset()
|
||||
assert len(w.warnings) == 0
|
||||
|
||||
By default, the real :mod:`warnings` module is affected - the ability
|
||||
to select a different module is provided for the benefit of the
|
||||
:mod:`warnings` module's own unit tests.
|
||||
The ``record`` argument specifies whether or not the :func:`showwarning`
|
||||
function is replaced. Note that recording the warnings in this fashion
|
||||
also prevents them from being written to sys.stderr. If set to ``False``,
|
||||
the standard handling of warning messages is left in place (however, the
|
||||
original handling is still restored at the end of the block).
|
||||
|
||||
.. function:: captured_stdout()
|
||||
|
||||
@ -346,4 +348,10 @@ The :mod:`test.support` module defines the following classes:
|
||||
|
||||
Temporarily unset the environment variable ``envvar``.
|
||||
|
||||
.. class:: WarningsRecorder()
|
||||
|
||||
Class used to record warnings for unit tests. See documentation of
|
||||
:func:`check_warnings` above for more details.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
|
@ -165,9 +165,9 @@ ImportWarning can also be enabled explicitly in Python code using::
|
||||
Temporarily Suppressing Warnings
|
||||
--------------------------------
|
||||
|
||||
If you are using code that you know will raise a warning, such some deprecated
|
||||
function, but do not want to see the warning, then suppress the warning using
|
||||
the :class:`catch_warnings` context manager::
|
||||
If you are using code that you know will raise a warning, such as a deprecated
|
||||
function, but do not want to see the warning, then it is possible to suppress
|
||||
the warning using the :class:`catch_warnings` context manager::
|
||||
|
||||
import warnings
|
||||
|
||||
@ -218,7 +218,15 @@ the warning has been cleared.
|
||||
Once the context manager exits, the warnings filter is restored to its state
|
||||
when the context was entered. This prevents tests from changing the warnings
|
||||
filter in unexpected ways between tests and leading to indeterminate test
|
||||
results.
|
||||
results. The :func:`showwarning` function in the module is also restored to
|
||||
its original value.
|
||||
|
||||
When testing multiple operations that raise the same kind of warning, it
|
||||
is important to test them in a manner that confirms each operation is raising
|
||||
a new warning (e.g. set warnings to be raised as exceptions and check the
|
||||
operations raise exceptions, check that the length of the warning list
|
||||
continues to increase after each operation, or else delete the previous
|
||||
entries from the warnings list before each new operation).
|
||||
|
||||
|
||||
.. _warning-functions:
|
||||
@ -314,20 +322,20 @@ Available Context Managers
|
||||
|
||||
.. class:: catch_warnings([\*, record=False, module=None])
|
||||
|
||||
A context manager that copies and, upon exit, restores the warnings filter.
|
||||
If the *record* argument is False (the default) the context manager returns
|
||||
:class:`None`. If *record* is true, a list is returned that is populated
|
||||
with objects as seen by a custom :func:`showwarning` function (which also
|
||||
suppresses output to ``sys.stdout``). Each object has attributes with the
|
||||
same names as the arguments to :func:`showwarning`.
|
||||
A context manager that copies and, upon exit, restores the warnings filter
|
||||
and the :func:`showwarning` function.
|
||||
If the *record* argument is :const:`False` (the default) the context manager
|
||||
returns :class:`None` on entry. If *record* is :const:`True`, a list is
|
||||
returned that is progressively populated with objects as seen by a custom
|
||||
:func:`showwarning` function (which also suppresses output to ``sys.stdout``).
|
||||
Each object in the list has attributes with the same names as the arguments to
|
||||
:func:`showwarning`.
|
||||
|
||||
The *module* argument takes a module that will be used instead of the
|
||||
module returned when you import :mod:`warnings` whose filter will be
|
||||
protected. This arguments exists primarily for testing the :mod:`warnings`
|
||||
protected. This argument exists primarily for testing the :mod:`warnings`
|
||||
module itself.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
.. versionchanged:: 3.0
|
||||
|
||||
Constructor arguments turned into keyword-only arguments.
|
||||
|
@ -19,7 +19,7 @@ __all__ = ["Error", "TestFailed", "TestSkipped", "ResourceDenied", "import_modul
|
||||
"is_resource_enabled", "requires", "find_unused_port", "bind_port",
|
||||
"fcmp", "is_jython", "TESTFN", "HOST", "FUZZ", "findfile", "verify",
|
||||
"vereq", "sortdict", "check_syntax_error", "open_urlresource",
|
||||
"catch_warning", "CleanImport", "EnvironmentVarGuard",
|
||||
"check_warnings", "CleanImport", "EnvironmentVarGuard",
|
||||
"TransientResource", "captured_output", "captured_stdout",
|
||||
"TransientResource", "transient_internet", "run_with_locale",
|
||||
"set_memlimit", "bigmemtest", "bigaddrspacetest", "BasicTestRunner",
|
||||
@ -53,7 +53,7 @@ class ResourceDenied(TestSkipped):
|
||||
def import_module(name, deprecated=False):
|
||||
"""Import the module to be tested, raising TestSkipped if it is not
|
||||
available."""
|
||||
with catch_warning(record=False):
|
||||
with warnings.catch_warnings():
|
||||
if deprecated:
|
||||
warnings.filterwarnings("ignore", ".+ (module|package)",
|
||||
DeprecationWarning)
|
||||
@ -368,17 +368,27 @@ def open_urlresource(url, *args, **kw):
|
||||
return open(fn, *args, **kw)
|
||||
|
||||
|
||||
def catch_warning(module=warnings, record=True):
|
||||
"""Guard the warnings filter from being permanently changed and
|
||||
optionally record the details of any warnings that are issued.
|
||||
|
||||
Use like this:
|
||||
|
||||
with catch_warning() as w:
|
||||
warnings.warn("foo")
|
||||
assert str(w.message) == "foo"
|
||||
class WarningsRecorder(object):
|
||||
"""Convenience wrapper for the warnings list returned on
|
||||
entry to the warnings.catch_warnings() context manager.
|
||||
"""
|
||||
return warnings.catch_warnings(record=record, module=module)
|
||||
def __init__(self, warnings_list):
|
||||
self.warnings = warnings_list
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if self.warnings:
|
||||
return getattr(self.warnings[-1], attr)
|
||||
elif attr in warnings.WarningMessage._WARNING_DETAILS:
|
||||
return None
|
||||
raise AttributeError("%r has no attribute %r" % (self, attr))
|
||||
|
||||
def reset(self):
|
||||
del self.warnings[:]
|
||||
|
||||
@contextlib.contextmanager
|
||||
def check_warnings():
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
yield WarningsRecorder(w)
|
||||
|
||||
|
||||
class CleanImport(object):
|
||||
|
@ -266,21 +266,24 @@ class RelativeImport(unittest.TestCase):
|
||||
self.assertTrue(hasattr(relimport, "RelativeImport"))
|
||||
|
||||
def test_issue3221(self):
|
||||
# Note for mergers: the 'absolute' tests from the 2.x branch
|
||||
# are missing in Py3k because implicit relative imports are
|
||||
# a thing of the past
|
||||
def check_relative():
|
||||
exec("from . import relimport", ns)
|
||||
# Check both OK with __package__ and __name__ correct
|
||||
# Check relative import OK with __package__ and __name__ correct
|
||||
ns = dict(__package__='test', __name__='test.notarealmodule')
|
||||
check_relative()
|
||||
# Check both OK with only __name__ wrong
|
||||
# Check relative import OK with only __name__ wrong
|
||||
ns = dict(__package__='test', __name__='notarealpkg.notarealmodule')
|
||||
check_relative()
|
||||
# Check relative fails with only __package__ wrong
|
||||
# Check relative import fails with only __package__ wrong
|
||||
ns = dict(__package__='foo', __name__='test.notarealmodule')
|
||||
self.assertRaises(SystemError, check_relative)
|
||||
# Check relative fails with __package__ and __name__ wrong
|
||||
# Check relative import fails with __package__ and __name__ wrong
|
||||
ns = dict(__package__='foo', __name__='notarealpkg.notarealmodule')
|
||||
self.assertRaises(SystemError, check_relative)
|
||||
# Check both fail with package set to a non-string
|
||||
# Check relative import fails with package set to a non-string
|
||||
ns = dict(__package__=object())
|
||||
self.assertRaises(ValueError, check_relative)
|
||||
|
||||
|
@ -66,35 +66,35 @@ class ReadWriteTests(unittest.TestCase):
|
||||
|
||||
class TestWarnings(unittest.TestCase):
|
||||
def has_warned(self, w):
|
||||
self.assertEqual(w[-1].category, RuntimeWarning)
|
||||
self.assertEqual(w.category, RuntimeWarning)
|
||||
|
||||
def test_byte_max(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
with support.check_warnings() as w:
|
||||
ts.T_BYTE = CHAR_MAX+1
|
||||
self.has_warned(w)
|
||||
|
||||
def test_byte_min(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
with support.check_warnings() as w:
|
||||
ts.T_BYTE = CHAR_MIN-1
|
||||
self.has_warned(w)
|
||||
|
||||
def test_ubyte_max(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
with support.check_warnings() as w:
|
||||
ts.T_UBYTE = UCHAR_MAX+1
|
||||
self.has_warned(w)
|
||||
|
||||
def test_short_max(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
with support.check_warnings() as w:
|
||||
ts.T_SHORT = SHRT_MAX+1
|
||||
self.has_warned(w)
|
||||
|
||||
def test_short_min(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
with support.check_warnings() as w:
|
||||
ts.T_SHORT = SHRT_MIN-1
|
||||
self.has_warned(w)
|
||||
|
||||
def test_ushort_max(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
with support.check_warnings() as w:
|
||||
ts.T_USHORT = USHRT_MAX+1
|
||||
self.has_warned(w)
|
||||
|
||||
|
@ -7,7 +7,7 @@ import warnings
|
||||
|
||||
class TestUntestedModules(unittest.TestCase):
|
||||
def test_at_least_import_untested_modules(self):
|
||||
with warnings.catch_warnings(record=True):
|
||||
with warnings.catch_warnings():
|
||||
import aifc
|
||||
import bdb
|
||||
import cgitb
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Very rudimentary test of threading module
|
||||
|
||||
import test.support
|
||||
from test.support import verbose, catch_warning
|
||||
from test.support import verbose
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
|
@ -214,7 +214,8 @@ class WarnTests(unittest.TestCase):
|
||||
def test_warn_nonstandard_types(self):
|
||||
# warn() should handle non-standard types without issue.
|
||||
for ob in (Warning, None, 42):
|
||||
with support.catch_warning(self.module) as w:
|
||||
with original_warnings.catch_warnings(record=True,
|
||||
module=self.module) as w:
|
||||
self.module.warn(ob)
|
||||
# Don't directly compare objects since
|
||||
# ``Warning() != Warning()``.
|
||||
@ -526,19 +527,23 @@ class CatchWarningTests(BaseTest):
|
||||
wmod = self.module
|
||||
orig_filters = wmod.filters
|
||||
orig_showwarning = wmod.showwarning
|
||||
with support.catch_warning(module=wmod):
|
||||
# Ensure both showwarning and filters are restored when recording
|
||||
with wmod.catch_warnings(module=wmod, record=True):
|
||||
wmod.filters = wmod.showwarning = object()
|
||||
self.assert_(wmod.filters is orig_filters)
|
||||
self.assert_(wmod.showwarning is orig_showwarning)
|
||||
with support.catch_warning(module=wmod, record=False):
|
||||
# Same test, but with recording disabled
|
||||
with wmod.catch_warnings(module=wmod, record=False):
|
||||
wmod.filters = wmod.showwarning = object()
|
||||
self.assert_(wmod.filters is orig_filters)
|
||||
self.assert_(wmod.showwarning is orig_showwarning)
|
||||
|
||||
def test_catch_warnings_recording(self):
|
||||
wmod = self.module
|
||||
with support.catch_warning(module=wmod) as w:
|
||||
# Ensure warnings are recorded when requested
|
||||
with wmod.catch_warnings(module=wmod, record=True) as w:
|
||||
self.assertEqual(w, [])
|
||||
self.assert_(type(w) is list)
|
||||
wmod.simplefilter("always")
|
||||
wmod.warn("foo")
|
||||
self.assertEqual(str(w[-1].message), "foo")
|
||||
@ -548,11 +553,59 @@ class CatchWarningTests(BaseTest):
|
||||
self.assertEqual(str(w[1].message), "bar")
|
||||
del w[:]
|
||||
self.assertEqual(w, [])
|
||||
# Ensure warnings are not recorded when not requested
|
||||
orig_showwarning = wmod.showwarning
|
||||
with support.catch_warning(module=wmod, record=False) as w:
|
||||
with wmod.catch_warnings(module=wmod, record=False) as w:
|
||||
self.assert_(w is None)
|
||||
self.assert_(wmod.showwarning is orig_showwarning)
|
||||
|
||||
def test_catch_warnings_reentry_guard(self):
|
||||
wmod = self.module
|
||||
# Ensure catch_warnings is protected against incorrect usage
|
||||
x = wmod.catch_warnings(module=wmod, record=True)
|
||||
self.assertRaises(RuntimeError, x.__exit__)
|
||||
with x:
|
||||
self.assertRaises(RuntimeError, x.__enter__)
|
||||
# Same test, but with recording disabled
|
||||
x = wmod.catch_warnings(module=wmod, record=False)
|
||||
self.assertRaises(RuntimeError, x.__exit__)
|
||||
with x:
|
||||
self.assertRaises(RuntimeError, x.__enter__)
|
||||
|
||||
def test_catch_warnings_defaults(self):
|
||||
wmod = self.module
|
||||
orig_filters = wmod.filters
|
||||
orig_showwarning = wmod.showwarning
|
||||
# Ensure default behaviour is not to record warnings
|
||||
with wmod.catch_warnings(module=wmod) as w:
|
||||
self.assert_(w is None)
|
||||
self.assert_(wmod.showwarning is orig_showwarning)
|
||||
self.assert_(wmod.filters is not orig_filters)
|
||||
self.assert_(wmod.filters is orig_filters)
|
||||
if wmod is sys.modules['warnings']:
|
||||
# Ensure the default module is this one
|
||||
with wmod.catch_warnings() as w:
|
||||
self.assert_(w is None)
|
||||
self.assert_(wmod.showwarning is orig_showwarning)
|
||||
self.assert_(wmod.filters is not orig_filters)
|
||||
self.assert_(wmod.filters is orig_filters)
|
||||
|
||||
def test_check_warnings(self):
|
||||
# Explicit tests for the test.support convenience wrapper
|
||||
wmod = self.module
|
||||
if wmod is sys.modules['warnings']:
|
||||
with support.check_warnings() as w:
|
||||
self.assertEqual(w.warnings, [])
|
||||
wmod.simplefilter("always")
|
||||
wmod.warn("foo")
|
||||
self.assertEqual(str(w.message), "foo")
|
||||
wmod.warn("bar")
|
||||
self.assertEqual(str(w.message), "bar")
|
||||
self.assertEqual(str(w.warnings[0].message), "foo")
|
||||
self.assertEqual(str(w.warnings[1].message), "bar")
|
||||
w.reset()
|
||||
self.assertEqual(w.warnings, [])
|
||||
|
||||
class CCatchWarningTests(CatchWarningTests):
|
||||
module = c_warnings
|
||||
|
||||
|
@ -301,8 +301,21 @@ class catch_warnings(object):
|
||||
"""
|
||||
self._record = record
|
||||
self._module = sys.modules['warnings'] if module is None else module
|
||||
self._entered = False
|
||||
|
||||
def __repr__(self):
|
||||
args = []
|
||||
if self._record:
|
||||
args.append("record=True")
|
||||
if self._module is not sys.modules['warnings']:
|
||||
args.append("module=%r" % self._module)
|
||||
name = type(self).__name__
|
||||
return "%s(%s)" % (name, ", ".join(args))
|
||||
|
||||
def __enter__(self):
|
||||
if self._entered:
|
||||
raise RuntimeError("Cannot enter %r twice" % self)
|
||||
self._entered = True
|
||||
self._filters = self._module.filters
|
||||
self._module.filters = self._filters[:]
|
||||
self._showwarning = self._module.showwarning
|
||||
@ -316,6 +329,8 @@ class catch_warnings(object):
|
||||
return None
|
||||
|
||||
def __exit__(self, *exc_info):
|
||||
if not self._entered:
|
||||
raise RuntimeError("Cannot exit %r without entering first" % self)
|
||||
self._module.filters = self._filters
|
||||
self._module.showwarning = self._showwarning
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user