diff options
author | Brett Cannon <bcannon@gmail.com> | 2008-09-02 01:25:16 (GMT) |
---|---|---|
committer | Brett Cannon <bcannon@gmail.com> | 2008-09-02 01:25:16 (GMT) |
commit | 1eaf0742d877fd9d84d6ed82a04bc33b027e9ad0 (patch) | |
tree | 1c1ee3a5dee04f5f4657b707e9d54ca2e28b1505 /Lib/warnings.py | |
parent | 86533776c291c031853609ceaeda96eb2808e4ee (diff) | |
download | cpython-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.py | 75 |
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 |