summaryrefslogtreecommitdiffstats
path: root/Lib/warnings.py
diff options
context:
space:
mode:
authorBrett Cannon <bcannon@gmail.com>2008-09-02 01:25:16 (GMT)
committerBrett Cannon <bcannon@gmail.com>2008-09-02 01:25:16 (GMT)
commit1eaf0742d877fd9d84d6ed82a04bc33b027e9ad0 (patch)
tree1c1ee3a5dee04f5f4657b707e9d54ca2e28b1505 /Lib/warnings.py
parent86533776c291c031853609ceaeda96eb2808e4ee (diff)
downloadcpython-1eaf0742d877fd9d84d6ed82a04bc33b027e9ad0.zip
cpython-1eaf0742d877fd9d84d6ed82a04bc33b027e9ad0.tar.gz
cpython-1eaf0742d877fd9d84d6ed82a04bc33b027e9ad0.tar.bz2
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.
Diffstat (limited to 'Lib/warnings.py')
-rw-r--r--Lib/warnings.py75
1 files changed, 74 insertions, 1 deletions
diff --git a/Lib/warnings.py b/Lib/warnings.py
index 2e5c512..b699c43 100644
--- a/Lib/warnings.py
+++ b/Lib/warnings.py
@@ -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