Index: Python/_warnings.c =================================================================== --- Python/_warnings.c (revision 65979) +++ Python/_warnings.c (working copy) @@ -1,4 +1,5 @@ #include "Python.h" +#include "code.h" /* For DeprecationWarning about adding 'line'. */ #include "frameobject.h" #define MODULE_NAME "_warnings" @@ -416,11 +417,16 @@ /* 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; + PyCodeObject *code = (PyCodeObject *) + PyFunction_GetCode(check_fxn); + if (!(code->co_flags & CO_VARARGS)) { + 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); Index: Lib/asynchat.py =================================================================== --- Lib/asynchat.py (revision 65979) +++ Lib/asynchat.py (working copy) @@ -49,8 +49,9 @@ import socket import asyncore from collections import deque +from sys import py3kwarning from test.test_support import catch_warning -from warnings import filterwarnings +from warnings import filterwarnings, catch_warnings class async_chat (asyncore.dispatcher): """This is an abstract class. You must derive from this class, and add @@ -218,8 +219,9 @@ # handle classic producer behavior obs = self.ac_out_buffer_size try: - with catch_warning(record=False): - filterwarnings("ignore", ".*buffer", DeprecationWarning) + with catch_warnings(): + if py3kwarning: + filterwarnings("ignore", ".*buffer", DeprecationWarning) data = buffer(first, 0, obs) except TypeError: data = first.more() Index: Lib/warnings.py =================================================================== --- Lib/warnings.py (revision 65979) +++ Lib/warnings.py (working copy) @@ -272,7 +272,8 @@ 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 @@ 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): + self.pop() + + +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 Index: Lib/BaseHTTPServer.py =================================================================== --- Lib/BaseHTTPServer.py (revision 65979) +++ Lib/BaseHTTPServer.py (working copy) @@ -73,11 +73,11 @@ import sys import time import socket # For gethostbyaddr() -from test.test_support import catch_warning -from warnings import filterwarnings -with catch_warning(record=False): - filterwarnings("ignore", ".*mimetools has been removed", - DeprecationWarning) +from warnings import filterwarnings, catch_warnings +with catch_warnings(): + if sys.py3kwarning: + filterwarnings("ignore", ".*mimetools has been removed", + DeprecationWarning) import mimetools import SocketServer Index: Lib/mimetools.py =================================================================== --- Lib/mimetools.py (revision 65979) +++ Lib/mimetools.py (working copy) @@ -2,11 +2,12 @@ import os +import sys import tempfile -from test.test_support import catch_warning -from warnings import filterwarnings -with catch_warning(record=False): - filterwarnings("ignore", ".*rfc822 has been removed", DeprecationWarning) +from warnings import filterwarnings, catch_warnings +with catch_warnings(record=False): + if sys.py3kwarning: + filterwarnings("ignore", ".*rfc822 has been removed", DeprecationWarning) import rfc822 from warnings import warnpy3k Index: Lib/cgi.py =================================================================== --- Lib/cgi.py (revision 65979) +++ Lib/cgi.py (working copy) @@ -39,13 +39,14 @@ import os import urllib import UserDict -from test.test_support import catch_warning -from warnings import filterwarnings -with catch_warning(record=False): - filterwarnings("ignore", ".*mimetools has been removed", - DeprecationWarning) +from warnings import filterwarnings, catch_warnings +with catch_warnings(): + if sys.py3kwarning: + filterwarnings("ignore", ".*mimetools has been removed", + DeprecationWarning) import mimetools - filterwarnings("ignore", ".*rfc822 has been removed", DeprecationWarning) + if sys.py3kwarning: + filterwarnings("ignore", ".*rfc822 has been removed", DeprecationWarning) import rfc822 try: Index: Lib/httplib.py =================================================================== --- Lib/httplib.py (revision 65979) +++ Lib/httplib.py (working copy) @@ -67,12 +67,13 @@ """ import socket +from sys import py3kwarning from urlparse import urlsplit import warnings -from test.test_support import catch_warning -with catch_warning(record=False): - warnings.filterwarnings("ignore", ".*mimetools has been removed", - DeprecationWarning) +with warnings.catch_warnings(): + if py3kwarning: + warnings.filterwarnings("ignore", ".*mimetools has been removed", + DeprecationWarning) import mimetools try: Index: Lib/test/test_warnings.py =================================================================== --- Lib/test/test_warnings.py (revision 65979) +++ Lib/test/test_warnings.py (working copy) @@ -79,20 +79,19 @@ "FilterTests.test_error") def test_ignore(self): - with test_support.catch_warning(self.module) as w: + with test_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 test_support.catch_warning(self.module) as w: + with test_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 @@ 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 @@ 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 test_support.catch_warning(self.module) as w: @@ -133,10 +132,10 @@ 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 test_support.catch_warning(self.module) as w: @@ -156,7 +155,7 @@ 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 @@ None, Warning, None, 1, registry=42) - class CWarnTests(BaseTest, WarnTests): module = c_warnings @@ -377,7 +375,7 @@ 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 @@ 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 @@ -489,45 +487,45 @@ -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 test_support.catch_warning(wmod): + with wmod.catch_warnings(record=True, module=wmod): wmod.filters = wmod.showwarning = object() self.assert_(wmod.filters is orig_filters) self.assert_(wmod.showwarning is orig_showwarning) - with test_support.catch_warning(wmod, record=False): + 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_warning_recording(self): + def test_catch_warnings_recording(self): wmod = self.module - with test_support.catch_warning(wmod) as w: - self.assertEqual(w.warnings, []) + with wmod.catch_warnings(module=wmod, record=True) 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 test_support.catch_warning(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) - -class CWarningsSupportTests(BaseTest, WarningsSupportTests): +class CCatchWarningTests(CatchWarningTests): module = c_warnings -class PyWarningsSupportTests(BaseTest, WarningsSupportTests): +class PyCatchWarningTests(CatchWarningTests): module = py_warnings @@ -539,14 +537,24 @@ def bad_showwarning(message, category, filename, lineno, file=None): pass + @staticmethod + def ok_showwarning(*args): + pass + def test_deprecation(self): # message, category, filename, lineno[, file[, line]] args = ("message", UserWarning, "file name", 42) - with test_support.catch_warning(self.module): + with test_support.catch_warning(module=self.module): self.module.filterwarnings("error", category=DeprecationWarning) self.module.showwarning = self.bad_showwarning self.assertRaises(DeprecationWarning, self.module.warn_explicit, *args) + self.module.showwarning = self.ok_showwarning + try: + self.module.warn_explicit(*args) + except DeprecationWarning as exc: + self.fail('showwarning(*args) should not trigger a ' + 'DeprecationWarning') class CShowwarningDeprecationTests(ShowwarningDeprecationTests): module = c_warnings @@ -559,16 +567,14 @@ def test_main(): py_warnings.onceregistry.clear() c_warnings.onceregistry.clear() - test_support.run_unittest(CFilterTests, - PyFilterTests, - CWarnTests, - PyWarnTests, + test_support.run_unittest(CFilterTests, PyFilterTests, + CWarnTests, PyWarnTests, CWCmdLineTests, PyWCmdLineTests, _WarningsTests, CWarningsDisplayTests, PyWarningsDisplayTests, - CWarningsSupportTests, PyWarningsSupportTests, + CCatchWarningTests, PyCatchWarningTests, CShowwarningDeprecationTests, - PyShowwarningDeprecationTests, + PyShowwarningDeprecationTests, ) Index: Lib/test/test_support.py =================================================================== --- Lib/test/test_support.py (revision 65979) +++ Lib/test/test_support.py (working copy) @@ -18,7 +18,7 @@ "is_resource_enabled", "requires", "find_unused_port", "bind_port", "fcmp", "have_unicode", "is_jython", "TESTFN", "HOST", "FUZZ", "findfile", "verify", "vereq", "sortdict", "check_syntax_error", - "open_urlresource", "WarningMessage", "catch_warning", "CleanImport", + "open_urlresource", "catch_warning", "CleanImport", "EnvironmentVarGuard", "captured_output", "captured_stdout", "TransientResource", "transient_internet", "run_with_locale", "set_memlimit", "bigmemtest", "bigaddrspacetest", @@ -381,73 +381,10 @@ return open(fn) -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. + return warnings.catch_warnings(record=record, module=module) - Use like this: - with catch_warning() as w: - 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 - - class CleanImport(object): """Context manager to force import to return a new module reference.