Merge in r66135. Doing also required removing a stale DeprecationWarning along
with moving warnings.catch_warnings() over to keyword-only parameters for its constructor (as documented in the 2.6 docs).
This commit is contained in:
parent
3a2bd7d5c5
commit
ec92e181fb
|
@ -247,3 +247,52 @@ Available Functions
|
|||
:func:`filterwarnings`, including that of the :option:`-W` command line options
|
||||
and calls to :func:`simplefilter`.
|
||||
|
||||
|
||||
Available Classes
|
||||
-----------------
|
||||
|
||||
.. class:: catch_warnings([\*, record=False, module=None])
|
||||
|
||||
A context manager that guards the warnings filter from being permanentally
|
||||
mutated. The manager returns an instance of :class:`WarningsRecorder`. The
|
||||
*record* argument specifies whether warnings that would typically be
|
||||
handled by :func:`showwarning` should instead be recorded by the
|
||||
:class:`WarningsRecorder` instance. This argument is typically set when
|
||||
testing for expected warnings behavior. The *module* argument may be a
|
||||
module object that is to be used instead of the :mod:`warnings` module.
|
||||
This argument should only be set when testing the :mod:`warnings` module
|
||||
or some similar use-case.
|
||||
|
||||
Typical usage of the context manager is like so::
|
||||
|
||||
def fxn():
|
||||
warn("fxn is deprecated", DeprecationWarning)
|
||||
return "spam spam bacon spam"
|
||||
|
||||
# The function 'fxn' is known to raise a DeprecationWarning.
|
||||
with catch_warnings() as w:
|
||||
warnings.filterwarning('ignore', 'fxn is deprecated', DeprecationWarning)
|
||||
fxn() # DeprecationWarning is temporarily suppressed.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
.. versionchanged:: 3.0
|
||||
|
||||
Constructor arguments turned into keyword-only arguments.
|
||||
|
||||
|
||||
.. class:: WarningsRecorder()
|
||||
|
||||
A subclass of :class:`list` that stores all warnings passed to
|
||||
:func:`showwarning` when returned by a :class:`catch_warnings` context
|
||||
manager created with its *record* argument set to ``True``. Each recorded
|
||||
warning is represented by an object whose attributes correspond to the
|
||||
arguments to :func:`showwarning`. As a convenience, a
|
||||
:class:`WarningsRecorder` instance has the attributes of the last
|
||||
recorded warning set on the :class:`WarningsRecorder` instance as well.
|
||||
|
||||
.. method:: reset()
|
||||
|
||||
Delete all recorded warnings.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
|
|
@ -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",
|
||||
"WarningMessage", "catch_warning", "CleanImport", "EnvironmentVarGuard",
|
||||
"catch_warning", "CleanImport", "EnvironmentVarGuard",
|
||||
"TransientResource", "captured_output", "captured_stdout",
|
||||
"TransientResource", "transient_internet", "run_with_locale",
|
||||
"set_memlimit", "bigmemtest", "bigaddrspacetest", "BasicTestRunner",
|
||||
|
@ -368,47 +368,6 @@ def open_urlresource(url, *args, **kw):
|
|||
return open(fn, *args, **kw)
|
||||
|
||||
|
||||
class WarningMessage(object):
|
||||
"Holds the result of a single showwarning() call"
|
||||
_WARNING_DETAILS = "message category filename lineno line".split()
|
||||
def __init__(self, message, category, filename, lineno, line=None):
|
||||
for attr in self._WARNING_DETAILS:
|
||||
setattr(self, attr, locals()[attr])
|
||||
self._category_name = category.__name__ if category else None
|
||||
|
||||
def __str__(self):
|
||||
return ("{message : %r, category : %r, filename : %r, lineno : %s, "
|
||||
"line : %r}" % (self.message, self._category_name,
|
||||
self.filename, self.lineno, self.line))
|
||||
|
||||
class WarningRecorder(object):
|
||||
"Records the result of any showwarning calls"
|
||||
def __init__(self):
|
||||
self.warnings = []
|
||||
self._set_last(None)
|
||||
|
||||
def _showwarning(self, message, category, filename, lineno,
|
||||
file=None, line=None):
|
||||
wm = WarningMessage(message, category, filename, lineno, line)
|
||||
self.warnings.append(wm)
|
||||
self._set_last(wm)
|
||||
|
||||
def _set_last(self, last_warning):
|
||||
if last_warning is None:
|
||||
for attr in WarningMessage._WARNING_DETAILS:
|
||||
setattr(self, attr, None)
|
||||
else:
|
||||
for attr in WarningMessage._WARNING_DETAILS:
|
||||
setattr(self, attr, getattr(last_warning, attr))
|
||||
|
||||
def reset(self):
|
||||
self.warnings = []
|
||||
self._set_last(None)
|
||||
|
||||
def __str__(self):
|
||||
return '[%s]' % (', '.join(map(str, self.warnings)))
|
||||
|
||||
@contextlib.contextmanager
|
||||
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.
|
||||
|
@ -419,20 +378,7 @@ def catch_warning(module=warnings, record=True):
|
|||
warnings.warn("foo")
|
||||
assert str(w.message) == "foo"
|
||||
"""
|
||||
original_filters = module.filters
|
||||
original_showwarning = module.showwarning
|
||||
if record:
|
||||
recorder = WarningRecorder()
|
||||
module.showwarning = recorder._showwarning
|
||||
else:
|
||||
recorder = None
|
||||
try:
|
||||
# Replace the filters with a copy of the original
|
||||
module.filters = module.filters[:]
|
||||
yield recorder
|
||||
finally:
|
||||
module.showwarning = original_showwarning
|
||||
module.filters = original_filters
|
||||
return warnings.catch_warnings(record=record, module=module)
|
||||
|
||||
|
||||
class CleanImport(object):
|
||||
|
|
|
@ -79,20 +79,19 @@ class FilterTests(object):
|
|||
"FilterTests.test_error")
|
||||
|
||||
def test_ignore(self):
|
||||
with support.catch_warning(self.module) as w:
|
||||
with support.catch_warning(module=self.module) as w:
|
||||
self.module.resetwarnings()
|
||||
self.module.filterwarnings("ignore", category=UserWarning)
|
||||
self.module.warn("FilterTests.test_ignore", UserWarning)
|
||||
self.assert_(not w.message)
|
||||
self.assertEquals(len(w), 0)
|
||||
|
||||
def test_always(self):
|
||||
with support.catch_warning(self.module) as w:
|
||||
with support.catch_warning(module=self.module) as w:
|
||||
self.module.resetwarnings()
|
||||
self.module.filterwarnings("always", category=UserWarning)
|
||||
message = "FilterTests.test_always"
|
||||
self.module.warn(message, UserWarning)
|
||||
self.assert_(message, w.message)
|
||||
w.message = None # Reset.
|
||||
self.module.warn(message, UserWarning)
|
||||
self.assert_(w.message, message)
|
||||
|
||||
|
@ -107,7 +106,7 @@ class FilterTests(object):
|
|||
self.assertEquals(w.message, message)
|
||||
w.reset()
|
||||
elif x == 1:
|
||||
self.assert_(not w.message, "unexpected warning: " + str(w))
|
||||
self.assert_(not len(w), "unexpected warning: " + str(w))
|
||||
else:
|
||||
raise ValueError("loop variant unhandled")
|
||||
|
||||
|
@ -120,7 +119,7 @@ class FilterTests(object):
|
|||
self.assertEquals(w.message, message)
|
||||
w.reset()
|
||||
self.module.warn(message, UserWarning)
|
||||
self.assert_(not w.message, "unexpected message: " + str(w))
|
||||
self.assert_(not len(w), "unexpected message: " + str(w))
|
||||
|
||||
def test_once(self):
|
||||
with support.catch_warning(self.module) as w:
|
||||
|
@ -133,10 +132,10 @@ class FilterTests(object):
|
|||
w.reset()
|
||||
self.module.warn_explicit(message, UserWarning, "test_warnings.py",
|
||||
13)
|
||||
self.assert_(not w.message)
|
||||
self.assertEquals(len(w), 0)
|
||||
self.module.warn_explicit(message, UserWarning, "test_warnings2.py",
|
||||
42)
|
||||
self.assert_(not w.message)
|
||||
self.assertEquals(len(w), 0)
|
||||
|
||||
def test_inheritance(self):
|
||||
with support.catch_warning(self.module) as w:
|
||||
|
@ -156,7 +155,7 @@ class FilterTests(object):
|
|||
self.module.warn("FilterTests.test_ordering", UserWarning)
|
||||
except UserWarning:
|
||||
self.fail("order handling for actions failed")
|
||||
self.assert_(not w.message)
|
||||
self.assertEquals(len(w), 0)
|
||||
|
||||
def test_filterwarnings(self):
|
||||
# Test filterwarnings().
|
||||
|
@ -317,7 +316,6 @@ class WarnTests(unittest.TestCase):
|
|||
None, Warning, None, 1, registry=42)
|
||||
|
||||
|
||||
|
||||
class CWarnTests(BaseTest, WarnTests):
|
||||
module = c_warnings
|
||||
|
||||
|
@ -377,7 +375,7 @@ class _WarningsTests(BaseTest):
|
|||
self.failUnlessEqual(w.message, message)
|
||||
w.reset()
|
||||
self.module.warn_explicit(message, UserWarning, "file", 42)
|
||||
self.assert_(not w.message)
|
||||
self.assertEquals(len(w), 0)
|
||||
# Test the resetting of onceregistry.
|
||||
self.module.onceregistry = {}
|
||||
__warningregistry__ = {}
|
||||
|
@ -388,7 +386,7 @@ class _WarningsTests(BaseTest):
|
|||
del self.module.onceregistry
|
||||
__warningregistry__ = {}
|
||||
self.module.warn_explicit(message, UserWarning, "file", 42)
|
||||
self.failUnless(not w.message)
|
||||
self.assertEquals(len(w), 0)
|
||||
finally:
|
||||
self.module.onceregistry = original_registry
|
||||
|
||||
|
@ -487,45 +485,45 @@ class CWarningsDisplayTests(BaseTest, WarningsDisplayTests):
|
|||
class PyWarningsDisplayTests(BaseTest, WarningsDisplayTests):
|
||||
module = py_warnings
|
||||
|
||||
class WarningsSupportTests(object):
|
||||
"""Test the warning tools from test support module"""
|
||||
class CatchWarningTests(BaseTest):
|
||||
|
||||
def test_catch_warning_restore(self):
|
||||
"""Test catch_warnings()."""
|
||||
|
||||
def test_catch_warnings_restore(self):
|
||||
wmod = self.module
|
||||
orig_filters = wmod.filters
|
||||
orig_showwarning = wmod.showwarning
|
||||
with support.catch_warning(wmod):
|
||||
with support.catch_warning(module=wmod):
|
||||
wmod.filters = wmod.showwarning = object()
|
||||
self.assert_(wmod.filters is orig_filters)
|
||||
self.assert_(wmod.showwarning is orig_showwarning)
|
||||
with support.catch_warning(wmod, record=False):
|
||||
with support.catch_warning(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_warning_recording(self):
|
||||
def test_catch_warnings_recording(self):
|
||||
wmod = self.module
|
||||
with support.catch_warning(wmod) as w:
|
||||
self.assertEqual(w.warnings, [])
|
||||
with support.catch_warning(module=wmod) as w:
|
||||
self.assertEqual(w, [])
|
||||
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")
|
||||
self.assertEqual(str(w[0].message), "foo")
|
||||
self.assertEqual(str(w[1].message), "bar")
|
||||
w.reset()
|
||||
self.assertEqual(w.warnings, [])
|
||||
self.assertEqual(w, [])
|
||||
orig_showwarning = wmod.showwarning
|
||||
with support.catch_warning(wmod, record=False) as w:
|
||||
with support.catch_warning(module=wmod, record=False) as w:
|
||||
self.assert_(w is None)
|
||||
self.assert_(wmod.showwarning is orig_showwarning)
|
||||
|
||||
|
||||
class CWarningsSupportTests(BaseTest, WarningsSupportTests):
|
||||
class CCatchWarningTests(CatchWarningTests):
|
||||
module = c_warnings
|
||||
|
||||
class PyWarningsSupportTests(BaseTest, WarningsSupportTests):
|
||||
class PyCatchWarningTests(CatchWarningTests):
|
||||
module = py_warnings
|
||||
|
||||
|
||||
|
@ -539,7 +537,7 @@ def test_main():
|
|||
CWCmdLineTests, PyWCmdLineTests,
|
||||
_WarningsTests,
|
||||
CWarningsDisplayTests, PyWarningsDisplayTests,
|
||||
CWarningsSupportTests, PyWarningsSupportTests,
|
||||
CCatchWarningTests, PyCatchWarningTests,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -254,6 +254,76 @@ def warn_explicit(message, category, filename, lineno,
|
|||
showwarning(message, category, filename, lineno)
|
||||
|
||||
|
||||
class WarningMessage(object):
|
||||
|
||||
"""Holds the result of a single showwarning() call."""
|
||||
|
||||
_WARNING_DETAILS = ("message", "category", "filename", "lineno", "file",
|
||||
"line")
|
||||
|
||||
def __init__(self, message, category, filename, lineno, file=None,
|
||||
line=None):
|
||||
local_values = locals()
|
||||
for attr in self._WARNING_DETAILS:
|
||||
setattr(self, attr, local_values[attr])
|
||||
self._category_name = category.__name__ if category else None
|
||||
|
||||
def __str__(self):
|
||||
return ("{message : %r, category : %r, filename : %r, lineno : %s, "
|
||||
"line : %r}" % (self.message, self._category_name,
|
||||
self.filename, self.lineno, self.line))
|
||||
|
||||
|
||||
class WarningsRecorder(list):
|
||||
|
||||
"""Record the result of various showwarning() calls."""
|
||||
|
||||
def showwarning(self, *args, **kwargs):
|
||||
self.append(WarningMessage(*args, **kwargs))
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self[-1], attr)
|
||||
|
||||
def reset(self):
|
||||
del self[:]
|
||||
|
||||
|
||||
class catch_warnings(object):
|
||||
|
||||
"""Guard the warnings filter from being permanently changed and optionally
|
||||
record the details of any warnings that are issued.
|
||||
|
||||
Context manager returns an instance of warnings.WarningRecorder which is a
|
||||
list of WarningMessage instances. Attributes on WarningRecorder are
|
||||
redirected to the last created WarningMessage instance.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, *, record=False, module=None):
|
||||
"""Specify whether to record warnings and if an alternative module
|
||||
should be used other than sys.modules['warnings'].
|
||||
|
||||
For compatibility with Python 3.0, please consider all arguments to be
|
||||
keyword-only.
|
||||
|
||||
"""
|
||||
self._recorder = WarningsRecorder() if record else None
|
||||
self._module = sys.modules['warnings'] if module is None else module
|
||||
|
||||
def __enter__(self):
|
||||
self._filters = self._module.filters
|
||||
self._module.filters = self._filters[:]
|
||||
self._showwarning = self._module.showwarning
|
||||
if self._recorder is not None:
|
||||
self._recorder.reset() # In case the instance is being reused.
|
||||
self._module.showwarning = self._recorder.showwarning
|
||||
return self._recorder
|
||||
|
||||
def __exit__(self, *exc_info):
|
||||
self._module.filters = self._filters
|
||||
self._module.showwarning = self._showwarning
|
||||
|
||||
|
||||
# filters contains a sequence of filter 5-tuples
|
||||
# The components of the 5-tuple are:
|
||||
# - an action: error, ignore, always, default, module, or once
|
||||
|
|
|
@ -60,6 +60,9 @@ C API
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue 3602: As part of the merge of r66135, make the parameters on
|
||||
warnings.catch_warnings() keyword-only. Also remove a DeprecationWarning.
|
||||
|
||||
- The deprecation warnings for the camelCase threading API names were removed.
|
||||
|
||||
Extension Modules
|
||||
|
|
|
@ -386,49 +386,23 @@ warn_explicit(PyObject *category, PyObject *message,
|
|||
show_warning(filename, lineno, text, category, sourceline);
|
||||
}
|
||||
else {
|
||||
const char *msg = "functions overriding warnings.showwarning() "
|
||||
"must support the 'line' argument";
|
||||
const char *text_char = _PyUnicode_AsString(text);
|
||||
PyObject *res;
|
||||
|
||||
if (strcmp(msg, text_char) == 0) {
|
||||
/* Prevent infinite recursion by using built-in implementation
|
||||
of showwarning(). */
|
||||
show_warning(filename, lineno, text, category, sourceline);
|
||||
}
|
||||
else {
|
||||
PyObject *check_fxn;
|
||||
PyObject *defaults;
|
||||
PyObject *res;
|
||||
|
||||
if (PyMethod_Check(show_fxn))
|
||||
check_fxn = PyMethod_Function(show_fxn);
|
||||
else if (PyFunction_Check(show_fxn))
|
||||
check_fxn = show_fxn;
|
||||
else {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"warnings.showwarning() must be set to a "
|
||||
"function or method");
|
||||
Py_DECREF(show_fxn);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
defaults = PyFunction_GetDefaults(check_fxn);
|
||||
/* A proper implementation of warnings.showwarning() should
|
||||
have at least two default arguments. */
|
||||
if ((defaults == NULL) || (PyTuple_Size(defaults) < 2)) {
|
||||
if (PyErr_WarnEx(PyExc_DeprecationWarning, msg, 1) < 0) {
|
||||
Py_DECREF(show_fxn);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
res = PyObject_CallFunctionObjArgs(show_fxn, message, category,
|
||||
filename, lineno_obj,
|
||||
NULL);
|
||||
if (!PyMethod_Check(show_fxn) && !PyFunction_Check(show_fxn)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"warnings.showwarning() must be set to a "
|
||||
"function or method");
|
||||
Py_DECREF(show_fxn);
|
||||
Py_XDECREF(res);
|
||||
if (res == NULL)
|
||||
goto cleanup;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
res = PyObject_CallFunctionObjArgs(show_fxn, message, category,
|
||||
filename, lineno_obj,
|
||||
NULL);
|
||||
Py_DECREF(show_fxn);
|
||||
Py_XDECREF(res);
|
||||
if (res == NULL)
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
else /* if (rc == -1) */
|
||||
|
|
Loading…
Reference in New Issue