From 672237dc6ca1498eabac08554bcbc5bd0fd9ddaa Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Tue, 9 Sep 2008 00:49:16 +0000 Subject: [PATCH] warnings.catch_warnings() now returns a list or None instead of the custom WarningsRecorder object. This makes the API simpler to use as no special object must be learned. Closes issue 3781. Review by Benjamin Peterson. --- Doc/library/warnings.rst | 112 +++++++++++++++-------- Lib/asynchat.py | 1 - Lib/bsddb/test/test_early_close.py | 6 +- Lib/mimetools.py | 2 +- Lib/test/test___all__.py | 4 +- Lib/test/test_exceptions.py | 6 +- Lib/test/test_hmac.py | 2 +- Lib/test/test_import.py | 16 ++-- Lib/test/test_macostools.py | 2 +- Lib/test/test_pep352.py | 14 +-- Lib/test/test_py3kwarn.py | 100 ++++++++------------ Lib/test/test_random.py | 2 +- Lib/test/test_re.py | 4 +- Lib/test/test_struct.py | 4 +- Lib/test/test_structmembers.py | 14 +-- Lib/test/test_sundry.py | 2 +- Lib/test/test_support.py | 8 +- Lib/test/test_symtable.py | 14 +-- Lib/test/test_urllib.py | 2 +- Lib/test/test_urllibnet.py | 4 +- Lib/test/test_userstring.py | 2 +- Lib/test/test_warnings.py | 142 +++++++++++++++++------------ Lib/warnings.py | 55 +++++------ Misc/NEWS | 3 + 24 files changed, 268 insertions(+), 253 deletions(-) diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index 68bc26102c5..e9d018280f5 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -158,6 +158,67 @@ ImportWarning can also be enabled explicitly in Python code using:: warnings.simplefilter('default', ImportWarning) +.. _warning-suppress: + +Temporarily Suppressing Warnings +-------------------------------- + +If you are using code that you know will raise a warning, such some deprecated +function, but do not want to see the warning, then suppress the warning using +the :class:`catch_warnings` context manager:: + + import warnings + + def fxn(): + warnings.warn("deprecated", DeprecationWarning) + + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + fxn() + +While within the context manager all warnings will simply be ignored. This +allows you to use known-deprecated code without having to see the warning while +not suppressing the warning for other code that might not be aware of its use +of deprecated code. + + +.. _warning-testing: + +Testing Warnings +---------------- + +To test warnings raised by code, use the :class:`catch_warnings` context +manager. With it you can temporarily mutate the warnings filter to facilitate +your testing. For instance, do the following to capture all raised warnings to +check:: + + import warnings + + def fxn(): + warnings.warn("deprecated", DeprecationWarning) + + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Trigger a warning. + fxn() + # Verify some things + assert len(w) == 1 + assert isinstance(w[-1].category, DeprecationWarning) + assert "deprecated" in str(w[-1].message) + +One can also cause all warnings to be exceptions by using ``error`` instead of +``always``. One thing to be aware of is that if a warning has already been +raised because of a ``once``/``default`` rule, then no matter what filters are +set the warning will not be seen again unless the warnings registry related to +the warning has been cleared. + +Once the context manager exits, the warnings filter is restored to its state +when the context was entered. This prevents tests from changing the warnings +filter in unexpected ways between tests and leading to indeterminate test +results. + + .. _warning-functions: Available Functions @@ -264,31 +325,22 @@ Available Functions and calls to :func:`simplefilter`. -Available Classes ------------------ +Available Context Managers +-------------------------- -.. class:: catch_warnings([\*, record=False[, module=None]]) +.. class:: catch_warnings([\*, record=False, module=None]) - A context manager that guards the warnings filter from being permanently - 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. + A context manager that copies and, upon exit, restores the warnings filter. + If the *record* argument is False (the default) the context manager returns + :class:`None`. If *record* is true, a list is returned that is populated + with objects as seen by a custom :func:`showwarning` function (which also + suppresses output to ``sys.stdout``). Each object has attributes with the + same names as the arguments to :func:`showwarning`. - 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. + The *module* argument takes a module that will be used instead of the + module returned when you import :mod:`warnings` whose filter will be + protected. This arguments exists primarily for testing the :mod:`warnings` + module itself. .. note:: @@ -297,19 +349,3 @@ Available Classes .. versionadded:: 2.6 - -.. 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 diff --git a/Lib/asynchat.py b/Lib/asynchat.py index a97de93f270..911833d58ce 100644 --- a/Lib/asynchat.py +++ b/Lib/asynchat.py @@ -50,7 +50,6 @@ import socket import asyncore from collections import deque from sys import py3kwarning -from test.test_support import catch_warning from warnings import filterwarnings, catch_warnings class async_chat (asyncore.dispatcher): diff --git a/Lib/bsddb/test/test_early_close.py b/Lib/bsddb/test/test_early_close.py index 3d3396cb5b6..cc69e47a316 100644 --- a/Lib/bsddb/test/test_early_close.py +++ b/Lib/bsddb/test/test_early_close.py @@ -168,9 +168,9 @@ class DBEnvClosedEarlyCrash(unittest.TestCase): self.assertEquals(("XXX", "yyy"), c1.first()) import warnings # Not interested in warnings about implicit close. - warnings.simplefilter("ignore") - txn.commit() - warnings.resetwarnings() + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + txn.commit() self.assertRaises(db.DBCursorClosedError, c2.first) if db.version() > (4,3,0) : diff --git a/Lib/mimetools.py b/Lib/mimetools.py index fc5a2a5f186..71ca8f8593f 100644 --- a/Lib/mimetools.py +++ b/Lib/mimetools.py @@ -5,7 +5,7 @@ import os import sys import tempfile from warnings import filterwarnings, catch_warnings -with catch_warnings(record=False): +with catch_warnings(): if sys.py3kwarning: filterwarnings("ignore", ".*rfc822 has been removed", DeprecationWarning) import rfc822 diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py index 9bd00836da2..0a8c5d652bf 100644 --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -1,5 +1,5 @@ import unittest -from test.test_support import run_unittest, catch_warning +from test.test_support import run_unittest import sys import warnings @@ -9,7 +9,7 @@ class AllTest(unittest.TestCase): def check_all(self, modname): names = {} - with catch_warning(): + with warnings.catch_warnings(): warnings.filterwarnings("ignore", ".* (module|package)", DeprecationWarning) try: diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 0576b6249f2..06a23781c5d 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -4,9 +4,9 @@ import os import sys import unittest import pickle, cPickle +import warnings -from test.test_support import (TESTFN, unlink, run_unittest, - catch_warning, captured_output) +from test.test_support import TESTFN, unlink, run_unittest, captured_output from test.test_pep352 import ignore_message_warning # XXX This is not really enough, each *operation* should be tested! @@ -274,7 +274,7 @@ class ExceptionTests(unittest.TestCase): except NameError: pass - with catch_warning(): + with warnings.catch_warnings(): ignore_message_warning() for exc, args, expected in exceptionList: try: diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py index c57ac7f61e5..d97583fb432 100644 --- a/Lib/test/test_hmac.py +++ b/Lib/test/test_hmac.py @@ -211,7 +211,7 @@ class TestVectorsTestCase(unittest.TestCase): def digest(self): return self._x.digest() - with test_support.catch_warning(): + with warnings.catch_warnings(): warnings.simplefilter('error', RuntimeWarning) try: hmac.HMAC('a', 'b', digestmod=MockCrazyHash) diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py index ed9c7af4061..644a473b12b 100644 --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -5,7 +5,7 @@ import shutil import sys import py_compile import warnings -from test.test_support import unlink, TESTFN, unload, run_unittest, catch_warning +from test.test_support import unlink, TESTFN, unload, run_unittest def remove_files(name): @@ -215,7 +215,7 @@ class ImportTest(unittest.TestCase): self.assert_(y is test.test_support, y.__name__) def test_import_initless_directory_warning(self): - with catch_warning(): + with warnings.catch_warnings(): # Just a random non-package directory we always expect to be # somewhere in sys.path... warnings.simplefilter('error', ImportWarning) @@ -279,17 +279,17 @@ class RelativeImport(unittest.TestCase): check_relative() # Check relative fails with only __package__ wrong ns = dict(__package__='foo', __name__='test.notarealmodule') - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: check_absolute() - self.assert_('foo' in str(w.message)) - self.assertEqual(w.category, RuntimeWarning) + self.assert_('foo' in str(w[-1].message)) + self.assertEqual(w[-1].category, RuntimeWarning) self.assertRaises(SystemError, check_relative) # Check relative fails with __package__ and __name__ wrong ns = dict(__package__='foo', __name__='notarealpkg.notarealmodule') - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: check_absolute() - self.assert_('foo' in str(w.message)) - self.assertEqual(w.category, RuntimeWarning) + self.assert_('foo' in str(w[-1].message)) + self.assertEqual(w[-1].category, RuntimeWarning) self.assertRaises(SystemError, check_relative) # Check both fail with package set to a non-string ns = dict(__package__=object()) diff --git a/Lib/test/test_macostools.py b/Lib/test/test_macostools.py index da982d0bacd..5c83d28c47c 100644 --- a/Lib/test/test_macostools.py +++ b/Lib/test/test_macostools.py @@ -52,7 +52,7 @@ class TestMacostools(unittest.TestCase): def test_touched(self): # This really only tests that nothing unforeseen happens. import warnings - with test_support.catch_warning(): + with warnings.catch_warnings(): warnings.filterwarnings('ignore', 'macostools.touched*', DeprecationWarning) macostools.touched(test_support.TESTFN) diff --git a/Lib/test/test_pep352.py b/Lib/test/test_pep352.py index 1867b9ed633..e33552809f4 100644 --- a/Lib/test/test_pep352.py +++ b/Lib/test/test_pep352.py @@ -2,7 +2,7 @@ import unittest import __builtin__ import exceptions import warnings -from test.test_support import run_unittest, catch_warning +from test.test_support import run_unittest import os from platform import system as platform_system @@ -22,7 +22,7 @@ class ExceptionClassTests(unittest.TestCase): self.failUnless(issubclass(Exception, object)) def verify_instance_interface(self, ins): - with catch_warning(): + with warnings.catch_warnings(): ignore_message_warning() for attr in ("args", "message", "__str__", "__repr__", "__getitem__"): @@ -95,7 +95,7 @@ class ExceptionClassTests(unittest.TestCase): # Make sure interface works properly when given a single argument arg = "spam" exc = Exception(arg) - with catch_warning(): + with warnings.catch_warnings(): ignore_message_warning() results = ([len(exc.args), 1], [exc.args[0], arg], [exc.message, arg], @@ -109,7 +109,7 @@ class ExceptionClassTests(unittest.TestCase): arg_count = 3 args = tuple(range(arg_count)) exc = Exception(*args) - with catch_warning(): + with warnings.catch_warnings(): ignore_message_warning() results = ([len(exc.args), arg_count], [exc.args, args], [exc.message, ''], [str(exc), str(args)], @@ -121,7 +121,7 @@ class ExceptionClassTests(unittest.TestCase): def test_interface_no_arg(self): # Make sure that with no args that interface is correct exc = Exception() - with catch_warning(): + with warnings.catch_warnings(): ignore_message_warning() results = ([len(exc.args), 0], [exc.args, tuple()], [exc.message, ''], @@ -132,7 +132,7 @@ class ExceptionClassTests(unittest.TestCase): def test_message_deprecation(self): # As of Python 2.6, BaseException.message is deprecated. - with catch_warning(): + with warnings.catch_warnings(): warnings.resetwarnings() warnings.filterwarnings('error') @@ -219,7 +219,7 @@ class UsageTests(unittest.TestCase): def test_catch_string(self): # Catching a string should trigger a DeprecationWarning. - with catch_warning(): + with warnings.catch_warnings(): warnings.resetwarnings() warnings.filterwarnings("error") str_exc = "spam" diff --git a/Lib/test/test_py3kwarn.py b/Lib/test/test_py3kwarn.py index 780de740676..aa1ecbb588b 100644 --- a/Lib/test/test_py3kwarn.py +++ b/Lib/test/test_py3kwarn.py @@ -1,7 +1,6 @@ import unittest import sys -from test.test_support import (catch_warning, CleanImport, - TestSkipped, run_unittest) +from test.test_support import CleanImport, TestSkipped, run_unittest import warnings from contextlib import nested @@ -13,11 +12,11 @@ if not sys.py3kwarning: class TestPy3KWarnings(unittest.TestCase): def assertWarning(self, _, warning, expected_message): - self.assertEqual(str(warning.message), expected_message) + self.assertEqual(str(warning[-1].message), expected_message) def test_backquote(self): expected = 'backquote not supported in 3.x; use repr()' - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: exec "`2`" in {} self.assertWarning(None, w, expected) @@ -28,71 +27,55 @@ class TestPy3KWarnings(unittest.TestCase): exec expr in {'f' : f} expected = "assignment to True or False is forbidden in 3.x" - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: safe_exec("True = False") self.assertWarning(None, w, expected) - w.reset() safe_exec("False = True") self.assertWarning(None, w, expected) - w.reset() try: safe_exec("obj.False = True") except NameError: pass self.assertWarning(None, w, expected) - w.reset() try: safe_exec("obj.True = False") except NameError: pass self.assertWarning(None, w, expected) - w.reset() safe_exec("def False(): pass") self.assertWarning(None, w, expected) - w.reset() safe_exec("def True(): pass") self.assertWarning(None, w, expected) - w.reset() safe_exec("class False: pass") self.assertWarning(None, w, expected) - w.reset() safe_exec("class True: pass") self.assertWarning(None, w, expected) - w.reset() safe_exec("def f(True=43): pass") self.assertWarning(None, w, expected) - w.reset() safe_exec("def f(False=None): pass") self.assertWarning(None, w, expected) - w.reset() safe_exec("f(False=True)") self.assertWarning(None, w, expected) - w.reset() safe_exec("f(True=1)") self.assertWarning(None, w, expected) def test_type_inequality_comparisons(self): expected = 'type inequality comparisons not supported in 3.x' - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning(int < str, w, expected) - w.reset() self.assertWarning(type < object, w, expected) def test_object_inequality_comparisons(self): expected = 'comparing unequal types not supported in 3.x' - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning(str < [], w, expected) - w.reset() self.assertWarning(object() < (1, 2), w, expected) def test_dict_inequality_comparisons(self): expected = 'dict inequality comparisons not supported in 3.x' - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning({} < {2:3}, w, expected) - w.reset() self.assertWarning({} <= {}, w, expected) - w.reset() self.assertWarning({} > {2:3}, w, expected) - w.reset() self.assertWarning({2:3} >= {}, w, expected) def test_cell_inequality_comparisons(self): @@ -103,9 +86,8 @@ class TestPy3KWarnings(unittest.TestCase): return g cell0, = f(0).func_closure cell1, = f(1).func_closure - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning(cell0 == cell1, w, expected) - w.reset() self.assertWarning(cell0 < cell1, w, expected) def test_code_inequality_comparisons(self): @@ -114,13 +96,10 @@ class TestPy3KWarnings(unittest.TestCase): pass def g(x): pass - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning(f.func_code < g.func_code, w, expected) - w.reset() self.assertWarning(f.func_code <= g.func_code, w, expected) - w.reset() self.assertWarning(f.func_code >= g.func_code, w, expected) - w.reset() self.assertWarning(f.func_code > g.func_code, w, expected) def test_builtin_function_or_method_comparisons(self): @@ -128,13 +107,10 @@ class TestPy3KWarnings(unittest.TestCase): 'inequality comparisons not supported in 3.x') func = eval meth = {}.get - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning(func < meth, w, expected) - w.reset() self.assertWarning(func > meth, w, expected) - w.reset() self.assertWarning(meth <= func, w, expected) - w.reset() self.assertWarning(meth >= func, w, expected) def test_sort_cmp_arg(self): @@ -142,18 +118,15 @@ class TestPy3KWarnings(unittest.TestCase): lst = range(5) cmp = lambda x,y: -1 - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning(lst.sort(cmp=cmp), w, expected) - w.reset() self.assertWarning(sorted(lst, cmp=cmp), w, expected) - w.reset() self.assertWarning(lst.sort(cmp), w, expected) - w.reset() self.assertWarning(sorted(lst, cmp), w, expected) def test_sys_exc_clear(self): expected = 'sys.exc_clear() not supported in 3.x; use except clauses' - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning(sys.exc_clear(), w, expected) def test_methods_members(self): @@ -162,17 +135,17 @@ class TestPy3KWarnings(unittest.TestCase): __methods__ = ['a'] __members__ = ['b'] c = C() - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning(dir(c), w, expected) def test_softspace(self): expected = 'file.softspace not supported in 3.x' with file(__file__) as f: - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning(f.softspace, w, expected) def set(): f.softspace = 0 - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning(set(), w, expected) def test_slice_methods(self): @@ -188,60 +161,59 @@ class TestPy3KWarnings(unittest.TestCase): expected = "in 3.x, __{0}slice__ has been removed; use __{0}item__" for obj in (Spam(), Egg()): - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning(obj[1:2], w, expected.format('get')) - w.reset() del obj[3:4] self.assertWarning(None, w, expected.format('del')) - w.reset() obj[4:5] = "eggs" self.assertWarning(None, w, expected.format('set')) def test_tuple_parameter_unpacking(self): expected = "tuple parameter unpacking has been removed in 3.x" - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: exec "def f((a, b)): pass" self.assertWarning(None, w, expected) def test_buffer(self): expected = 'buffer() not supported in 3.x' - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning(buffer('a'), w, expected) def test_file_xreadlines(self): expected = ("f.xreadlines() not supported in 3.x, " "try 'for line in f' instead") with file(__file__) as f: - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: self.assertWarning(f.xreadlines(), w, expected) def test_hash_inheritance(self): - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: # With object as the base class class WarnOnlyCmp(object): def __cmp__(self, other): pass self.assertEqual(len(w), 1) self.assertWarning(None, w, "Overriding __cmp__ blocks inheritance of __hash__ in 3.x") - w.reset() + del w[:] class WarnOnlyEq(object): def __eq__(self, other): pass self.assertEqual(len(w), 1) self.assertWarning(None, w, "Overriding __eq__ blocks inheritance of __hash__ in 3.x") - w.reset() + del w[:] class WarnCmpAndEq(object): def __cmp__(self, other): pass def __eq__(self, other): pass self.assertEqual(len(w), 2) - self.assertWarning(None, w[-2], + self.assertWarning(None, w[:1], "Overriding __cmp__ blocks inheritance of __hash__ in 3.x") self.assertWarning(None, w, "Overriding __eq__ blocks inheritance of __hash__ in 3.x") - w.reset() + del w[:] class NoWarningOnlyHash(object): def __hash__(self): pass self.assertEqual(len(w), 0) + del w[:] # With an intermediate class in the heirarchy class DefinesAllThree(object): def __cmp__(self, other): pass @@ -252,22 +224,22 @@ class TestPy3KWarnings(unittest.TestCase): self.assertEqual(len(w), 1) self.assertWarning(None, w, "Overriding __cmp__ blocks inheritance of __hash__ in 3.x") - w.reset() + del w[:] class WarnOnlyEq(DefinesAllThree): def __eq__(self, other): pass self.assertEqual(len(w), 1) self.assertWarning(None, w, "Overriding __eq__ blocks inheritance of __hash__ in 3.x") - w.reset() + del w[:] class WarnCmpAndEq(DefinesAllThree): def __cmp__(self, other): pass def __eq__(self, other): pass self.assertEqual(len(w), 2) - self.assertWarning(None, w[-2], + self.assertWarning(None, w[:1], "Overriding __cmp__ blocks inheritance of __hash__ in 3.x") self.assertWarning(None, w, "Overriding __eq__ blocks inheritance of __hash__ in 3.x") - w.reset() + del w[:] class NoWarningOnlyHash(DefinesAllThree): def __hash__(self): pass self.assertEqual(len(w), 0) @@ -310,7 +282,7 @@ class TestStdlibRemovals(unittest.TestCase): def check_removal(self, module_name, optional=False): """Make sure the specified module, when imported, raises a DeprecationWarning and specifies itself in the message.""" - with nested(CleanImport(module_name), catch_warning(record=False)): + with nested(CleanImport(module_name), warnings.catch_warnings()): warnings.filterwarnings("error", ".+ removed", DeprecationWarning, __name__) try: @@ -348,36 +320,36 @@ class TestStdlibRemovals(unittest.TestCase): def dumbo(where, names, args): pass for path_mod in ("ntpath", "macpath", "os2emxpath", "posixpath"): mod = __import__(path_mod) - with catch_warning() as w: + with warnings.catch_warnings(record=True) as w: mod.walk("crashers", dumbo, None) - self.assertEquals(str(w.message), msg) + self.assertEquals(str(w[-1].message), msg) def test_commands_members(self): import commands members = {"mk2arg" : 2, "mkarg" : 1, "getstatus" : 1} for name, arg_count in members.items(): - with catch_warning(record=False): + with warnings.catch_warnings(): warnings.filterwarnings("error") func = getattr(commands, name) self.assertRaises(DeprecationWarning, func, *([None]*arg_count)) def test_reduce_move(self): from operator import add - with catch_warning(record=False): + with warnings.catch_warnings(): warnings.filterwarnings("error", "reduce") self.assertRaises(DeprecationWarning, reduce, add, range(10)) def test_mutablestring_removal(self): # UserString.MutableString has been removed in 3.0. import UserString - with catch_warning(record=False): + with warnings.catch_warnings(): warnings.filterwarnings("error", ".*MutableString", DeprecationWarning) self.assertRaises(DeprecationWarning, UserString.MutableString) def test_main(): - with catch_warning(): + with warnings.catch_warnings(): warnings.simplefilter("always") run_unittest(TestPy3KWarnings, TestStdlibRemovals) diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 4d570d07288..d82d391fe3e 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -191,7 +191,7 @@ class WichmannHill_TestBasicOps(TestBasicOps): def test_bigrand(self): # Verify warnings are raised when randrange is too large for random() - with test_support.catch_warning(): + with warnings.catch_warnings(): warnings.filterwarnings("error", "Underlying random") self.assertRaises(UserWarning, self.gen.randrange, 2**60) diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 1da3205e729..70bd88623d6 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1,7 +1,7 @@ import sys sys.path = ['.'] + sys.path -from test.test_support import verbose, run_unittest, catch_warning +from test.test_support import verbose, run_unittest import re from re import Scanner import sys, os, traceback @@ -447,7 +447,7 @@ class ReTests(unittest.TestCase): self.pickle_test(cPickle) # old pickles expect the _compile() reconstructor in sre module import warnings - with catch_warning(): + with warnings.catch_warnings(): warnings.filterwarnings("ignore", "The sre module is deprecated", DeprecationWarning) from sre import _compile diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index ec4a2dbc43e..232bffc275b 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -4,7 +4,7 @@ import struct import warnings from functools import wraps -from test.test_support import TestFailed, verbose, run_unittest, catch_warning +from test.test_support import TestFailed, verbose, run_unittest import sys ISBIGENDIAN = sys.byteorder == "big" @@ -34,7 +34,7 @@ def bigendian_to_native(value): def with_warning_restore(func): @wraps(func) def decorator(*args, **kw): - with catch_warning(): + with warnings.catch_warnings(): # We need this function to warn every time, so stick an # unqualifed 'always' at the head of the filter list warnings.simplefilter("always") diff --git a/Lib/test/test_structmembers.py b/Lib/test/test_structmembers.py index 6c1a947fe3f..e0e7e561300 100644 --- a/Lib/test/test_structmembers.py +++ b/Lib/test/test_structmembers.py @@ -66,35 +66,35 @@ class ReadWriteTests(unittest.TestCase): class TestWarnings(unittest.TestCase): def has_warned(self, w): - self.assertEqual(w.category, RuntimeWarning) + self.assertEqual(w[-1].category, RuntimeWarning) def test_byte_max(self): - with test_support.catch_warning() as w: + with warnings.catch_warnings(record=True) as w: ts.T_BYTE = CHAR_MAX+1 self.has_warned(w) def test_byte_min(self): - with test_support.catch_warning() as w: + with warnings.catch_warnings(record=True) as w: ts.T_BYTE = CHAR_MIN-1 self.has_warned(w) def test_ubyte_max(self): - with test_support.catch_warning() as w: + with warnings.catch_warnings(record=True) as w: ts.T_UBYTE = UCHAR_MAX+1 self.has_warned(w) def test_short_max(self): - with test_support.catch_warning() as w: + with warnings.catch_warnings(record=True) as w: ts.T_SHORT = SHRT_MAX+1 self.has_warned(w) def test_short_min(self): - with test_support.catch_warning() as w: + with warnings.catch_warnings(record=True) as w: ts.T_SHORT = SHRT_MIN-1 self.has_warned(w) def test_ushort_max(self): - with test_support.catch_warning() as w: + with warnings.catch_warnings(record=True) as w: ts.T_USHORT = USHRT_MAX+1 self.has_warned(w) diff --git a/Lib/test/test_sundry.py b/Lib/test/test_sundry.py index 404a405de5e..49ec12c944a 100644 --- a/Lib/test/test_sundry.py +++ b/Lib/test/test_sundry.py @@ -8,7 +8,7 @@ import warnings class TestUntestedModules(unittest.TestCase): def test_at_least_import_untested_modules(self): - with test_support.catch_warning(): + with warnings.catch_warnings(record=True): import CGIHTTPServer import aifc import audiodev diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 695bd6d0c3a..0bf22cf35db 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -18,7 +18,7 @@ __all__ = ["Error", "TestFailed", "TestSkipped", "ResourceDenied", "import_modul "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", "catch_warning", "CleanImport", + "open_urlresource", "CleanImport", "EnvironmentVarGuard", "captured_output", "captured_stdout", "TransientResource", "transient_internet", "run_with_locale", "set_memlimit", "bigmemtest", "bigaddrspacetest", @@ -52,7 +52,7 @@ class ResourceDenied(TestSkipped): def import_module(name, deprecated=False): """Import the module to be tested, raising TestSkipped if it is not available.""" - with catch_warning(record=False): + with warnings.catch_warnings(): if deprecated: warnings.filterwarnings("ignore", ".+ (module|package)", DeprecationWarning) @@ -381,10 +381,6 @@ def open_urlresource(url): return open(fn) -def catch_warning(module=warnings, record=True): - return warnings.catch_warnings(record=record, module=module) - - class CleanImport(object): """Context manager to force import to return a new module reference. diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index 05b237a97a2..b20f2b4e0e9 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -44,7 +44,7 @@ def find_block(block, name): class SymtableTest(unittest.TestCase): - with test_support.catch_warning(record=False): + with warnings.catch_warnings(): # Ignore warnings about "from blank import *" warnings.simplefilter("ignore", SyntaxWarning) top = symtable.symtable(TEST_CODE, "?", "exec") @@ -60,16 +60,16 @@ class SymtableTest(unittest.TestCase): def check(w, msg): self.assertEqual(str(w.message), msg) sym = self.top.lookup("glob") - with test_support.catch_warning() as w: + with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always", DeprecationWarning) self.assertFalse(sym.is_vararg()) - check(w, "is_vararg() is obsolete and will be removed") - w.reset() + check(w[-1].message, "is_vararg() is obsolete and will be removed") self.assertFalse(sym.is_keywordarg()) - check(w, "is_keywordarg() is obsolete and will be removed") - w.reset() + check(w[-1].message, + "is_keywordarg() is obsolete and will be removed") self.assertFalse(sym.is_in_tuple()) - check(w, "is_in_tuple() is obsolete and will be removed") + check(w[-1].message, + "is_in_tuple() is obsolete and will be removed") def test_type(self): self.assertEqual(self.top.get_type(), "module") diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 1dcbbedd707..7b351b8996b 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -641,7 +641,7 @@ class Pathname_Tests(unittest.TestCase): def test_main(): import warnings - with test_support.catch_warning(record=False): + with warnings.catch_warnings(): warnings.filterwarnings('ignore', ".*urllib\.urlopen.*Python 3.0", DeprecationWarning) test_support.run_unittest( diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py index 0404b77ac37..58fe282d311 100644 --- a/Lib/test/test_urllibnet.py +++ b/Lib/test/test_urllibnet.py @@ -182,8 +182,8 @@ class urlretrieveNetworkTests(unittest.TestCase): def test_main(): test_support.requires('network') - from warnings import filterwarnings - with test_support.catch_warning(record=False): + from warnings import filterwarnings, catch_warnings + with catch_warnings(): filterwarnings('ignore', '.*urllib\.urlopen.*Python 3.0', DeprecationWarning) test_support.run_unittest(URLTimeoutTest, diff --git a/Lib/test/test_userstring.py b/Lib/test/test_userstring.py index cae610ef73e..4bb0c457364 100755 --- a/Lib/test/test_userstring.py +++ b/Lib/test/test_userstring.py @@ -135,7 +135,7 @@ class MutableStringTest(UserStringTest): self.assertEqual(s, "") def test_main(): - with test_support.catch_warning(record=False): + with warnings.catch_warnings(): warnings.filterwarnings("ignore", ".*MutableString", DeprecationWarning) test_support.run_unittest(UserStringTest, MutableStringTest) diff --git a/Lib/test/test_warnings.py b/Lib/test/test_warnings.py index 9980f2497d6..388b5e9d3a6 100644 --- a/Lib/test/test_warnings.py +++ b/Lib/test/test_warnings.py @@ -72,64 +72,69 @@ class FilterTests(object): """Testing the filtering functionality.""" def test_error(self): - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(module=self.module) as w: self.module.resetwarnings() self.module.filterwarnings("error", category=UserWarning) self.assertRaises(UserWarning, self.module.warn, "FilterTests.test_error") def test_ignore(self): - with test_support.catch_warning(module=self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: self.module.resetwarnings() self.module.filterwarnings("ignore", category=UserWarning) self.module.warn("FilterTests.test_ignore", UserWarning) self.assertEquals(len(w), 0) def test_always(self): - with test_support.catch_warning(module=self.module) as w: + with original_warnings.catch_warnings(record=True, + 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) + self.assert_(message, w[-1].message) self.module.warn(message, UserWarning) - self.assert_(w.message, message) + self.assert_(w[-1].message, message) def test_default(self): - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: self.module.resetwarnings() self.module.filterwarnings("default", category=UserWarning) message = UserWarning("FilterTests.test_default") for x in xrange(2): self.module.warn(message, UserWarning) if x == 0: - self.assertEquals(w.message, message) - w.reset() + self.assertEquals(w[-1].message, message) + del w[:] elif x == 1: - self.assert_(not len(w), "unexpected warning: " + str(w)) + self.assertEquals(len(w), 0) else: raise ValueError("loop variant unhandled") def test_module(self): - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: self.module.resetwarnings() self.module.filterwarnings("module", category=UserWarning) message = UserWarning("FilterTests.test_module") self.module.warn(message, UserWarning) - self.assertEquals(w.message, message) - w.reset() + self.assertEquals(w[-1].message, message) + del w[:] self.module.warn(message, UserWarning) - self.assert_(not len(w), "unexpected message: " + str(w)) + self.assertEquals(len(w), 0) def test_once(self): - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: self.module.resetwarnings() self.module.filterwarnings("once", category=UserWarning) message = UserWarning("FilterTests.test_once") self.module.warn_explicit(message, UserWarning, "test_warnings.py", 42) - self.assertEquals(w.message, message) - w.reset() + self.assertEquals(w[-1].message, message) + del w[:] self.module.warn_explicit(message, UserWarning, "test_warnings.py", 13) self.assertEquals(len(w), 0) @@ -138,19 +143,20 @@ class FilterTests(object): self.assertEquals(len(w), 0) def test_inheritance(self): - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(module=self.module) as w: self.module.resetwarnings() self.module.filterwarnings("error", category=Warning) self.assertRaises(UserWarning, self.module.warn, "FilterTests.test_inheritance", UserWarning) def test_ordering(self): - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: self.module.resetwarnings() self.module.filterwarnings("ignore", category=UserWarning) self.module.filterwarnings("error", category=UserWarning, append=True) - w.reset() + del w[:] try: self.module.warn("FilterTests.test_ordering", UserWarning) except UserWarning: @@ -160,28 +166,29 @@ class FilterTests(object): def test_filterwarnings(self): # Test filterwarnings(). # Implicitly also tests resetwarnings(). - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: self.module.filterwarnings("error", "", Warning, "", 0) self.assertRaises(UserWarning, self.module.warn, 'convert to error') self.module.resetwarnings() text = 'handle normally' self.module.warn(text) - self.assertEqual(str(w.message), text) - self.assert_(w.category is UserWarning) + self.assertEqual(str(w[-1].message), text) + self.assert_(w[-1].category is UserWarning) self.module.filterwarnings("ignore", "", Warning, "", 0) text = 'filtered out' self.module.warn(text) - self.assertNotEqual(str(w.message), text) + self.assertNotEqual(str(w[-1].message), text) self.module.resetwarnings() self.module.filterwarnings("error", "hex*", Warning, "", 0) self.assertRaises(UserWarning, self.module.warn, 'hex/oct') text = 'nonmatching text' self.module.warn(text) - self.assertEqual(str(w.message), text) - self.assert_(w.category is UserWarning) + self.assertEqual(str(w[-1].message), text) + self.assert_(w[-1].category is UserWarning) class CFilterTests(BaseTest, FilterTests): module = c_warnings @@ -195,40 +202,51 @@ class WarnTests(unittest.TestCase): """Test warnings.warn() and warnings.warn_explicit().""" def test_message(self): - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: for i in range(4): text = 'multi %d' %i # Different text on each call. self.module.warn(text) - self.assertEqual(str(w.message), text) - self.assert_(w.category is UserWarning) + self.assertEqual(str(w[-1].message), text) + self.assert_(w[-1].category is UserWarning) def test_filename(self): with warnings_state(self.module): - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: warning_tests.inner("spam1") - self.assertEqual(os.path.basename(w.filename), "warning_tests.py") + self.assertEqual(os.path.basename(w[-1].filename), + "warning_tests.py") warning_tests.outer("spam2") - self.assertEqual(os.path.basename(w.filename), "warning_tests.py") + self.assertEqual(os.path.basename(w[-1].filename), + "warning_tests.py") def test_stacklevel(self): # Test stacklevel argument # make sure all messages are different, so the warning won't be skipped with warnings_state(self.module): - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: warning_tests.inner("spam3", stacklevel=1) - self.assertEqual(os.path.basename(w.filename), "warning_tests.py") + self.assertEqual(os.path.basename(w[-1].filename), + "warning_tests.py") warning_tests.outer("spam4", stacklevel=1) - self.assertEqual(os.path.basename(w.filename), "warning_tests.py") + self.assertEqual(os.path.basename(w[-1].filename), + "warning_tests.py") warning_tests.inner("spam5", stacklevel=2) - self.assertEqual(os.path.basename(w.filename), "test_warnings.py") + self.assertEqual(os.path.basename(w[-1].filename), + "test_warnings.py") warning_tests.outer("spam6", stacklevel=2) - self.assertEqual(os.path.basename(w.filename), "warning_tests.py") + self.assertEqual(os.path.basename(w[-1].filename), + "warning_tests.py") warning_tests.outer("spam6.5", stacklevel=3) - self.assertEqual(os.path.basename(w.filename), "test_warnings.py") + self.assertEqual(os.path.basename(w[-1].filename), + "test_warnings.py") warning_tests.inner("spam7", stacklevel=9999) - self.assertEqual(os.path.basename(w.filename), "sys") + self.assertEqual(os.path.basename(w[-1].filename), + "sys") def test_missing_filename_not_main(self): # If __file__ is not specified and __main__ is not the module name, @@ -237,9 +255,10 @@ class WarnTests(unittest.TestCase): try: del warning_tests.__file__ with warnings_state(self.module): - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: warning_tests.inner("spam8", stacklevel=1) - self.assertEqual(w.filename, warning_tests.__name__) + self.assertEqual(w[-1].filename, warning_tests.__name__) finally: warning_tests.__file__ = filename @@ -254,9 +273,10 @@ class WarnTests(unittest.TestCase): del warning_tests.__file__ warning_tests.__name__ = '__main__' with warnings_state(self.module): - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: warning_tests.inner('spam9', stacklevel=1) - self.assertEqual(w.filename, sys.argv[0]) + self.assertEqual(w[-1].filename, sys.argv[0]) finally: warning_tests.__file__ = filename warning_tests.__name__ = module_name @@ -272,9 +292,10 @@ class WarnTests(unittest.TestCase): warning_tests.__name__ = '__main__' del sys.argv with warnings_state(self.module): - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: warning_tests.inner('spam10', stacklevel=1) - self.assertEqual(w.filename, '__main__') + self.assertEqual(w[-1].filename, '__main__') finally: warning_tests.__file__ = filename warning_tests.__name__ = module_name @@ -292,9 +313,10 @@ class WarnTests(unittest.TestCase): warning_tests.__name__ = '__main__' sys.argv = [''] with warnings_state(self.module): - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: warning_tests.inner('spam11', stacklevel=1) - self.assertEqual(w.filename, '__main__') + self.assertEqual(w[-1].filename, '__main__') finally: warning_tests.__file__ = file_name warning_tests.__name__ = module_name @@ -328,7 +350,7 @@ class WCmdLineTests(unittest.TestCase): def test_improper_input(self): # Uses the private _setoption() function to test the parsing # of command-line warning arguments - with test_support.catch_warning(self.module): + with original_warnings.catch_warnings(module=self.module): self.assertRaises(self.module._OptionError, self.module._setoption, '1:2:3:4:5:6') self.assertRaises(self.module._OptionError, @@ -353,7 +375,7 @@ class _WarningsTests(BaseTest): def test_filter(self): # Everything should function even if 'filters' is not in warnings. - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(module=self.module) as w: self.module.filterwarnings("error", "", Warning, "", 0) self.assertRaises(UserWarning, self.module.warn, 'convert to error') @@ -368,21 +390,22 @@ class _WarningsTests(BaseTest): try: original_registry = self.module.onceregistry __warningregistry__ = {} - with test_support.catch_warning(self.module) as w: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: self.module.resetwarnings() self.module.filterwarnings("once", category=UserWarning) self.module.warn_explicit(message, UserWarning, "file", 42) - self.failUnlessEqual(w.message, message) - w.reset() + self.failUnlessEqual(w[-1].message, message) + del w[:] self.module.warn_explicit(message, UserWarning, "file", 42) self.assertEquals(len(w), 0) # Test the resetting of onceregistry. self.module.onceregistry = {} __warningregistry__ = {} self.module.warn('onceregistry test') - self.failUnlessEqual(w.message.args, message.args) + self.failUnlessEqual(w[-1].message.args, message.args) # Removal of onceregistry is okay. - w.reset() + del w[:] del self.module.onceregistry __warningregistry__ = {} self.module.warn_explicit(message, UserWarning, "file", 42) @@ -393,7 +416,7 @@ class _WarningsTests(BaseTest): def test_showwarning_missing(self): # Test that showwarning() missing is okay. text = 'del showwarning test' - with test_support.catch_warning(self.module): + with original_warnings.catch_warnings(module=self.module): self.module.filterwarnings("always", category=UserWarning) del self.module.showwarning with test_support.captured_output('stderr') as stream: @@ -414,7 +437,7 @@ class _WarningsTests(BaseTest): def test_show_warning_output(self): # With showarning() missing, make sure that output is okay. text = 'test show_warning' - with test_support.catch_warning(self.module): + with original_warnings.catch_warnings(module=self.module): self.module.filterwarnings("always", category=UserWarning) del self.module.showwarning with test_support.captured_output('stderr') as stream: @@ -486,7 +509,6 @@ class PyWarningsDisplayTests(BaseTest, WarningsDisplayTests): module = py_warnings - class CatchWarningTests(BaseTest): """Test catch_warnings().""" @@ -511,12 +533,12 @@ class CatchWarningTests(BaseTest): self.assertRaises(AttributeError, getattr, w, 'message') wmod.simplefilter("always") wmod.warn("foo") - self.assertEqual(str(w.message), "foo") + self.assertEqual(str(w[-1].message), "foo") wmod.warn("bar") - self.assertEqual(str(w.message), "bar") + self.assertEqual(str(w[-1].message), "bar") self.assertEqual(str(w[0].message), "foo") self.assertEqual(str(w[1].message), "bar") - w.reset() + del w[:] self.assertEqual(w, []) orig_showwarning = wmod.showwarning with wmod.catch_warnings(module=wmod, record=False) as w: @@ -545,7 +567,7 @@ class ShowwarningDeprecationTests(BaseTest): def test_deprecation(self): # message, category, filename, lineno[, file[, line]] args = ("message", UserWarning, "file name", 42) - with test_support.catch_warning(module=self.module): + with original_warnings.catch_warnings(module=self.module): self.module.filterwarnings("error", category=DeprecationWarning) self.module.showwarning = self.bad_showwarning self.assertRaises(DeprecationWarning, self.module.warn_explicit, diff --git a/Lib/warnings.py b/Lib/warnings.py index 9ec04be274b..04e7b5878c6 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -8,7 +8,7 @@ import sys import types __all__ = ["warn", "showwarning", "formatwarning", "filterwarnings", - "resetwarnings"] + "resetwarnings", "catch_warnings"] def warnpy3k(message, category=None, stacklevel=1): @@ -304,37 +304,20 @@ class WarningMessage(object): 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 attributes from the last caught warning, or raise - AttributeError.""" - try: - return getattr(self[-1], attr) - except IndexError: - raise AttributeError("no recorded warning to read " - "{0!r} attribute from".format(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. + """A context manager that copies and restores the warnings filter upon + exiting the context. - 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. + The 'record' argument specifies whether warnings should be captured by a + custom implementation of warnings.showwarning() and be appended to a list + returned by the context manager. Otherwise None is returned by the context + manager. The objects appended to the list are arguments whose attributes + mirror the arguments to showwarning(). + + The 'module' argument is to specify an alternative module to the module + named 'warnings' and imported under that name. This argument is only useful + when testing the warnings module itself. """ @@ -346,17 +329,21 @@ class catch_warnings(object): keyword-only. """ - self._recorder = WarningsRecorder() if record else None + self._record = record 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 + if self._record: + log = [] + def showwarning(*args, **kwargs): + log.append(WarningMessage(*args, **kwargs)) + self._module.showwarning = showwarning + return log + else: + return None def __exit__(self, *exc_info): self._module.filters = self._filters diff --git a/Misc/NEWS b/Misc/NEWS index b2da2c5993a..b7da5f1b890 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -60,6 +60,9 @@ C-API Library ------- +- Issue 3781: Clean up the API for warnings.catch_warnings() by having it + return a list or None rather than a custom object. + - Issue #1638033: Cookie.Morsel gained the httponly attribute. - Issue #3535: zipfile couldn't read some zip files larger than 2GB.