Move test.test_support.catch_warning() to the warnings module, rename it

catch_warnings(), and clean up the API.

While expanding the test suite, a bug was found where a warning about the
'line' argument to showwarning() was not letting functions with '*args' go
without a warning.

Closes issue 3602.
Code review by Benjamin Peterson.
This commit is contained in:
Brett Cannon 2008-09-02 01:25:16 +00:00
parent 86533776c2
commit 1eaf0742d8
11 changed files with 208 additions and 126 deletions

View file

@ -272,7 +272,8 @@ def warn_explicit(message, category, filename, lineno,
fxn_code = showwarning.__func__.func_code
if fxn_code:
args = fxn_code.co_varnames[:fxn_code.co_argcount]
if 'line' not in args:
CO_VARARGS = 0x4
if 'line' not in args and not fxn_code.co_flags & CO_VARARGS:
showwarning_msg = ("functions overriding warnings.showwarning() "
"must support the 'line' argument")
if message == showwarning_msg:
@ -283,6 +284,78 @@ 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."""
# Explicitly stated arguments so as to not trigger DeprecationWarning
# about adding 'line'.
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