Commit 672237dc authored by Brett Cannon's avatar Brett Cannon

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.
parent 631be012
...@@ -158,6 +158,67 @@ ImportWarning can also be enabled explicitly in Python code using:: ...@@ -158,6 +158,67 @@ ImportWarning can also be enabled explicitly in Python code using::
warnings.simplefilter('default', ImportWarning) 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: .. _warning-functions:
Available Functions Available Functions
...@@ -264,31 +325,22 @@ Available Functions ...@@ -264,31 +325,22 @@ Available Functions
and calls to :func:`simplefilter`. 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 A context manager that copies and, upon exit, restores the warnings filter.
mutated. The manager returns an instance of :class:`WarningsRecorder`. The If the *record* argument is False (the default) the context manager returns
*record* argument specifies whether warnings that would typically be :class:`None`. If *record* is true, a list is returned that is populated
handled by :func:`showwarning` should instead be recorded by the with objects as seen by a custom :func:`showwarning` function (which also
:class:`WarningsRecorder` instance. This argument is typically set when suppresses output to ``sys.stdout``). Each object has attributes with the
testing for expected warnings behavior. The *module* argument may be a same names as the arguments to :func:`showwarning`.
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.
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. The *module* argument takes a module that will be used instead of the
with catch_warnings() as w: module returned when you import :mod:`warnings` whose filter will be
warnings.filterwarning('ignore', 'fxn is deprecated', DeprecationWarning) protected. This arguments exists primarily for testing the :mod:`warnings`
fxn() # DeprecationWarning is temporarily suppressed. module itself.
.. note:: .. note::
...@@ -297,19 +349,3 @@ Available Classes ...@@ -297,19 +349,3 @@ Available Classes
.. versionadded:: 2.6 .. 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
...@@ -50,7 +50,6 @@ import socket ...@@ -50,7 +50,6 @@ import socket
import asyncore import asyncore
from collections import deque from collections import deque
from sys import py3kwarning from sys import py3kwarning
from test.test_support import catch_warning
from warnings import filterwarnings, catch_warnings from warnings import filterwarnings, catch_warnings
class async_chat (asyncore.dispatcher): class async_chat (asyncore.dispatcher):
......
...@@ -168,9 +168,9 @@ class DBEnvClosedEarlyCrash(unittest.TestCase): ...@@ -168,9 +168,9 @@ class DBEnvClosedEarlyCrash(unittest.TestCase):
self.assertEquals(("XXX", "yyy"), c1.first()) self.assertEquals(("XXX", "yyy"), c1.first())
import warnings import warnings
# Not interested in warnings about implicit close. # Not interested in warnings about implicit close.
with warnings.catch_warnings():
warnings.simplefilter("ignore") warnings.simplefilter("ignore")
txn.commit() txn.commit()
warnings.resetwarnings()
self.assertRaises(db.DBCursorClosedError, c2.first) self.assertRaises(db.DBCursorClosedError, c2.first)
if db.version() > (4,3,0) : if db.version() > (4,3,0) :
......
...@@ -5,7 +5,7 @@ import os ...@@ -5,7 +5,7 @@ import os
import sys import sys
import tempfile import tempfile
from warnings import filterwarnings, catch_warnings from warnings import filterwarnings, catch_warnings
with catch_warnings(record=False): with catch_warnings():
if sys.py3kwarning: if sys.py3kwarning:
filterwarnings("ignore", ".*rfc822 has been removed", DeprecationWarning) filterwarnings("ignore", ".*rfc822 has been removed", DeprecationWarning)
import rfc822 import rfc822
......
import unittest import unittest
from test.test_support import run_unittest, catch_warning from test.test_support import run_unittest
import sys import sys
import warnings import warnings
...@@ -9,7 +9,7 @@ class AllTest(unittest.TestCase): ...@@ -9,7 +9,7 @@ class AllTest(unittest.TestCase):
def check_all(self, modname): def check_all(self, modname):
names = {} names = {}
with catch_warning(): with warnings.catch_warnings():
warnings.filterwarnings("ignore", ".* (module|package)", warnings.filterwarnings("ignore", ".* (module|package)",
DeprecationWarning) DeprecationWarning)
try: try:
......
...@@ -4,9 +4,9 @@ import os ...@@ -4,9 +4,9 @@ import os
import sys import sys
import unittest import unittest
import pickle, cPickle import pickle, cPickle
import warnings
from test.test_support import (TESTFN, unlink, run_unittest, from test.test_support import TESTFN, unlink, run_unittest, captured_output
catch_warning, captured_output)
from test.test_pep352 import ignore_message_warning from test.test_pep352 import ignore_message_warning
# XXX This is not really enough, each *operation* should be tested! # XXX This is not really enough, each *operation* should be tested!
...@@ -274,7 +274,7 @@ class ExceptionTests(unittest.TestCase): ...@@ -274,7 +274,7 @@ class ExceptionTests(unittest.TestCase):
except NameError: except NameError:
pass pass
with catch_warning(): with warnings.catch_warnings():
ignore_message_warning() ignore_message_warning()
for exc, args, expected in exceptionList: for exc, args, expected in exceptionList:
try: try:
......
...@@ -211,7 +211,7 @@ class TestVectorsTestCase(unittest.TestCase): ...@@ -211,7 +211,7 @@ class TestVectorsTestCase(unittest.TestCase):
def digest(self): def digest(self):
return self._x.digest() return self._x.digest()
with test_support.catch_warning(): with warnings.catch_warnings():
warnings.simplefilter('error', RuntimeWarning) warnings.simplefilter('error', RuntimeWarning)
try: try:
hmac.HMAC('a', 'b', digestmod=MockCrazyHash) hmac.HMAC('a', 'b', digestmod=MockCrazyHash)
......
...@@ -5,7 +5,7 @@ import shutil ...@@ -5,7 +5,7 @@ import shutil
import sys import sys
import py_compile import py_compile
import warnings 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): def remove_files(name):
...@@ -215,7 +215,7 @@ class ImportTest(unittest.TestCase): ...@@ -215,7 +215,7 @@ class ImportTest(unittest.TestCase):
self.assert_(y is test.test_support, y.__name__) self.assert_(y is test.test_support, y.__name__)
def test_import_initless_directory_warning(self): 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 # Just a random non-package directory we always expect to be
# somewhere in sys.path... # somewhere in sys.path...
warnings.simplefilter('error', ImportWarning) warnings.simplefilter('error', ImportWarning)
...@@ -279,17 +279,17 @@ class RelativeImport(unittest.TestCase): ...@@ -279,17 +279,17 @@ class RelativeImport(unittest.TestCase):
check_relative() check_relative()
# Check relative fails with only __package__ wrong # Check relative fails with only __package__ wrong
ns = dict(__package__='foo', __name__='test.notarealmodule') ns = dict(__package__='foo', __name__='test.notarealmodule')
with catch_warning() as w: with warnings.catch_warnings(record=True) as w:
check_absolute() check_absolute()
self.assert_('foo' in str(w.message)) self.assert_('foo' in str(w[-1].message))
self.assertEqual(w.category, RuntimeWarning) self.assertEqual(w[-1].category, RuntimeWarning)
self.assertRaises(SystemError, check_relative) self.assertRaises(SystemError, check_relative)
# Check relative fails with __package__ and __name__ wrong # Check relative fails with __package__ and __name__ wrong
ns = dict(__package__='foo', __name__='notarealpkg.notarealmodule') ns = dict(__package__='foo', __name__='notarealpkg.notarealmodule')
with catch_warning() as w: with warnings.catch_warnings(record=True) as w:
check_absolute() check_absolute()
self.assert_('foo' in str(w.message)) self.assert_('foo' in str(w[-1].message))
self.assertEqual(w.category, RuntimeWarning) self.assertEqual(w[-1].category, RuntimeWarning)
self.assertRaises(SystemError, check_relative) self.assertRaises(SystemError, check_relative)
# Check both fail with package set to a non-string # Check both fail with package set to a non-string
ns = dict(__package__=object()) ns = dict(__package__=object())
......
...@@ -52,7 +52,7 @@ class TestMacostools(unittest.TestCase): ...@@ -52,7 +52,7 @@ class TestMacostools(unittest.TestCase):
def test_touched(self): def test_touched(self):
# This really only tests that nothing unforeseen happens. # This really only tests that nothing unforeseen happens.
import warnings import warnings
with test_support.catch_warning(): with warnings.catch_warnings():
warnings.filterwarnings('ignore', 'macostools.touched*', warnings.filterwarnings('ignore', 'macostools.touched*',
DeprecationWarning) DeprecationWarning)
macostools.touched(test_support.TESTFN) macostools.touched(test_support.TESTFN)
......
...@@ -2,7 +2,7 @@ import unittest ...@@ -2,7 +2,7 @@ import unittest
import __builtin__ import __builtin__
import exceptions import exceptions
import warnings import warnings
from test.test_support import run_unittest, catch_warning from test.test_support import run_unittest
import os import os
from platform import system as platform_system from platform import system as platform_system
...@@ -22,7 +22,7 @@ class ExceptionClassTests(unittest.TestCase): ...@@ -22,7 +22,7 @@ class ExceptionClassTests(unittest.TestCase):
self.failUnless(issubclass(Exception, object)) self.failUnless(issubclass(Exception, object))
def verify_instance_interface(self, ins): def verify_instance_interface(self, ins):
with catch_warning(): with warnings.catch_warnings():
ignore_message_warning() ignore_message_warning()
for attr in ("args", "message", "__str__", "__repr__", for attr in ("args", "message", "__str__", "__repr__",
"__getitem__"): "__getitem__"):
...@@ -95,7 +95,7 @@ class ExceptionClassTests(unittest.TestCase): ...@@ -95,7 +95,7 @@ class ExceptionClassTests(unittest.TestCase):
# Make sure interface works properly when given a single argument # Make sure interface works properly when given a single argument
arg = "spam" arg = "spam"
exc = Exception(arg) exc = Exception(arg)
with catch_warning(): with warnings.catch_warnings():
ignore_message_warning() ignore_message_warning()
results = ([len(exc.args), 1], [exc.args[0], arg], results = ([len(exc.args), 1], [exc.args[0], arg],
[exc.message, arg], [exc.message, arg],
...@@ -109,7 +109,7 @@ class ExceptionClassTests(unittest.TestCase): ...@@ -109,7 +109,7 @@ class ExceptionClassTests(unittest.TestCase):
arg_count = 3 arg_count = 3
args = tuple(range(arg_count)) args = tuple(range(arg_count))
exc = Exception(*args) exc = Exception(*args)
with catch_warning(): with warnings.catch_warnings():
ignore_message_warning() ignore_message_warning()
results = ([len(exc.args), arg_count], [exc.args, args], results = ([len(exc.args), arg_count], [exc.args, args],
[exc.message, ''], [str(exc), str(args)], [exc.message, ''], [str(exc), str(args)],
...@@ -121,7 +121,7 @@ class ExceptionClassTests(unittest.TestCase): ...@@ -121,7 +121,7 @@ class ExceptionClassTests(unittest.TestCase):
def test_interface_no_arg(self): def test_interface_no_arg(self):
# Make sure that with no args that interface is correct # Make sure that with no args that interface is correct
exc = Exception() exc = Exception()
with catch_warning(): with warnings.catch_warnings():
ignore_message_warning() ignore_message_warning()
results = ([len(exc.args), 0], [exc.args, tuple()], results = ([len(exc.args), 0], [exc.args, tuple()],
[exc.message, ''], [exc.message, ''],
...@@ -132,7 +132,7 @@ class ExceptionClassTests(unittest.TestCase): ...@@ -132,7 +132,7 @@ class ExceptionClassTests(unittest.TestCase):
def test_message_deprecation(self): def test_message_deprecation(self):
# As of Python 2.6, BaseException.message is deprecated. # As of Python 2.6, BaseException.message is deprecated.
with catch_warning(): with warnings.catch_warnings():
warnings.resetwarnings() warnings.resetwarnings()
warnings.filterwarnings('error') warnings.filterwarnings('error')
...@@ -219,7 +219,7 @@ class UsageTests(unittest.TestCase): ...@@ -219,7 +219,7 @@ class UsageTests(unittest.TestCase):
def test_catch_string(self): def test_catch_string(self):
# Catching a string should trigger a DeprecationWarning. # Catching a string should trigger a DeprecationWarning.
with catch_warning(): with warnings.catch_warnings():
warnings.resetwarnings() warnings.resetwarnings()
warnings.filterwarnings("error") warnings.filterwarnings("error")
str_exc = "spam" str_exc = "spam"
......
This diff is collapsed.
...@@ -191,7 +191,7 @@ class WichmannHill_TestBasicOps(TestBasicOps): ...@@ -191,7 +191,7 @@ class WichmannHill_TestBasicOps(TestBasicOps):
def test_bigrand(self): def test_bigrand(self):
# Verify warnings are raised when randrange is too large for random() # 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") warnings.filterwarnings("error", "Underlying random")
self.assertRaises(UserWarning, self.gen.randrange, 2**60) self.assertRaises(UserWarning, self.gen.randrange, 2**60)
......
import sys import sys
sys.path = ['.'] + sys.path sys.path = ['.'] + sys.path
from test.test_support import verbose, run_unittest, catch_warning from test.test_support import verbose, run_unittest
import re import re
from re import Scanner from re import Scanner
import sys, os, traceback import sys, os, traceback
...@@ -447,7 +447,7 @@ class ReTests(unittest.TestCase): ...@@ -447,7 +447,7 @@ class ReTests(unittest.TestCase):
self.pickle_test(cPickle) self.pickle_test(cPickle)
# old pickles expect the _compile() reconstructor in sre module # old pickles expect the _compile() reconstructor in sre module
import warnings import warnings
with catch_warning(): with warnings.catch_warnings():
warnings.filterwarnings("ignore", "The sre module is deprecated", warnings.filterwarnings("ignore", "The sre module is deprecated",
DeprecationWarning) DeprecationWarning)
from sre import _compile from sre import _compile
......
...@@ -4,7 +4,7 @@ import struct ...@@ -4,7 +4,7 @@ import struct
import warnings import warnings
from functools import wraps 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 import sys
ISBIGENDIAN = sys.byteorder == "big" ISBIGENDIAN = sys.byteorder == "big"
...@@ -34,7 +34,7 @@ def bigendian_to_native(value): ...@@ -34,7 +34,7 @@ def bigendian_to_native(value):
def with_warning_restore(func): def with_warning_restore(func):
@wraps(func) @wraps(func)
def decorator(*args, **kw): def decorator(*args, **kw):
with catch_warning(): with warnings.catch_warnings():
# We need this function to warn every time, so stick an # We need this function to warn every time, so stick an
# unqualifed 'always' at the head of the filter list # unqualifed 'always' at the head of the filter list
warnings.simplefilter("always") warnings.simplefilter("always")
......
...@@ -66,35 +66,35 @@ class ReadWriteTests(unittest.TestCase): ...@@ -66,35 +66,35 @@ class ReadWriteTests(unittest.TestCase):
class TestWarnings(unittest.TestCase): class TestWarnings(unittest.TestCase):
def has_warned(self, w): def has_warned(self, w):
self.assertEqual(w.category, RuntimeWarning) self.assertEqual(w[-1].category, RuntimeWarning)
def test_byte_max(self): 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 ts.T_BYTE = CHAR_MAX+1
self.has_warned(w) self.has_warned(w)
def test_byte_min(self): 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 ts.T_BYTE = CHAR_MIN-1
self.has_warned(w) self.has_warned(w)
def test_ubyte_max(self): 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 ts.T_UBYTE = UCHAR_MAX+1
self.has_warned(w) self.has_warned(w)
def test_short_max(self): 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 ts.T_SHORT = SHRT_MAX+1
self.has_warned(w) self.has_warned(w)
def test_short_min(self): 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 ts.T_SHORT = SHRT_MIN-1
self.has_warned(w) self.has_warned(w)
def test_ushort_max(self): 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 ts.T_USHORT = USHRT_MAX+1
self.has_warned(w) self.has_warned(w)
......
...@@ -8,7 +8,7 @@ import warnings ...@@ -8,7 +8,7 @@ import warnings
class TestUntestedModules(unittest.TestCase): class TestUntestedModules(unittest.TestCase):
def test_at_least_import_untested_modules(self): def test_at_least_import_untested_modules(self):
with test_support.catch_warning(): with warnings.catch_warnings(record=True):
import CGIHTTPServer import CGIHTTPServer
import aifc import aifc
import audiodev import audiodev
......
...@@ -18,7 +18,7 @@ __all__ = ["Error", "TestFailed", "TestSkipped", "ResourceDenied", "import_modul ...@@ -18,7 +18,7 @@ __all__ = ["Error", "TestFailed", "TestSkipped", "ResourceDenied", "import_modul
"is_resource_enabled", "requires", "find_unused_port", "bind_port", "is_resource_enabled", "requires", "find_unused_port", "bind_port",
"fcmp", "have_unicode", "is_jython", "TESTFN", "HOST", "FUZZ", "fcmp", "have_unicode", "is_jython", "TESTFN", "HOST", "FUZZ",
"findfile", "verify", "vereq", "sortdict", "check_syntax_error", "findfile", "verify", "vereq", "sortdict", "check_syntax_error",
"open_urlresource", "catch_warning", "CleanImport", "open_urlresource", "CleanImport",
"EnvironmentVarGuard", "captured_output", "EnvironmentVarGuard", "captured_output",
"captured_stdout", "TransientResource", "transient_internet", "captured_stdout", "TransientResource", "transient_internet",
"run_with_locale", "set_memlimit", "bigmemtest", "bigaddrspacetest", "run_with_locale", "set_memlimit", "bigmemtest", "bigaddrspacetest",
...@@ -52,7 +52,7 @@ class ResourceDenied(TestSkipped): ...@@ -52,7 +52,7 @@ class ResourceDenied(TestSkipped):
def import_module(name, deprecated=False): def import_module(name, deprecated=False):
"""Import the module to be tested, raising TestSkipped if it is not """Import the module to be tested, raising TestSkipped if it is not
available.""" available."""
with catch_warning(record=False): with warnings.catch_warnings():
if deprecated: if deprecated:
warnings.filterwarnings("ignore", ".+ (module|package)", warnings.filterwarnings("ignore", ".+ (module|package)",
DeprecationWarning) DeprecationWarning)
...@@ -381,10 +381,6 @@ def open_urlresource(url): ...@@ -381,10 +381,6 @@ def open_urlresource(url):
return open(fn) return open(fn)
def catch_warning(module=warnings, record=True):
return warnings.catch_warnings(record=record, module=module)
class CleanImport(object): class CleanImport(object):
"""Context manager to force import to return a new module reference. """Context manager to force import to return a new module reference.
......
...@@ -44,7 +44,7 @@ def find_block(block, name): ...@@ -44,7 +44,7 @@ def find_block(block, name):
class SymtableTest(unittest.TestCase): class SymtableTest(unittest.TestCase):
with test_support.catch_warning(record=False): with warnings.catch_warnings():
# Ignore warnings about "from blank import *" # Ignore warnings about "from blank import *"
warnings.simplefilter("ignore", SyntaxWarning) warnings.simplefilter("ignore", SyntaxWarning)
top = symtable.symtable(TEST_CODE, "?", "exec") top = symtable.symtable(TEST_CODE, "?", "exec")
...@@ -60,16 +60,16 @@ class SymtableTest(unittest.TestCase): ...@@ -60,16 +60,16 @@ class SymtableTest(unittest.TestCase):
def check(w, msg): def check(w, msg):
self.assertEqual(str(w.message), msg) self.assertEqual(str(w.message), msg)
sym = self.top.lookup("glob") sym = self.top.lookup("glob")
with test_support.catch_warning() as w: with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always", DeprecationWarning) warnings.simplefilter("always", DeprecationWarning)
self.assertFalse(sym.is_vararg()) self.assertFalse(sym.is_vararg())
check(w, "is_vararg() is obsolete and will be removed") check(w[-1].message, "is_vararg() is obsolete and will be removed")
w.reset()
self.assertFalse(sym.is_keywordarg()) self.assertFalse(sym.is_keywordarg())
check(w, "is_keywordarg() is obsolete and will be removed") check(w[-1].message,
w.reset() "is_keywordarg() is obsolete and will be removed")
self.assertFalse(sym.is_in_tuple()) 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): def test_type(self):
self.assertEqual(self.top.get_type(), "module") self.assertEqual(self.top.get_type(), "module")
......
...@@ -641,7 +641,7 @@ class Pathname_Tests(unittest.TestCase): ...@@ -641,7 +641,7 @@ class Pathname_Tests(unittest.TestCase):
def test_main(): def test_main():
import warnings import warnings
with test_support.catch_warning(record=False): with warnings.catch_warnings():
warnings.filterwarnings('ignore', ".*urllib\.urlopen.*Python 3.0", warnings.filterwarnings('ignore', ".*urllib\.urlopen.*Python 3.0",
DeprecationWarning) DeprecationWarning)
test_support.run_unittest( test_support.run_unittest(
......
...@@ -182,8 +182,8 @@ class urlretrieveNetworkTests(unittest.TestCase): ...@@ -182,8 +182,8 @@ class urlretrieveNetworkTests(unittest.TestCase):
def test_main(): def test_main():
test_support.requires('network') test_support.requires('network')
from warnings import filterwarnings from warnings import filterwarnings, catch_warnings
with test_support.catch_warning(record=False): with catch_warnings():
filterwarnings('ignore', '.*urllib\.urlopen.*Python 3.0', filterwarnings('ignore', '.*urllib\.urlopen.*Python 3.0',
DeprecationWarning) DeprecationWarning)
test_support.run_unittest(URLTimeoutTest, test_support.run_unittest(URLTimeoutTest,
......
...@@ -135,7 +135,7 @@ class MutableStringTest(UserStringTest): ...@@ -135,7 +135,7 @@ class MutableStringTest(UserStringTest):
self.assertEqual(s, "") self.assertEqual(s, "")
def test_main(): def test_main():
with test_support.catch_warning(record=False): with warnings.catch_warnings():
warnings.filterwarnings("ignore", ".*MutableString", warnings.filterwarnings("ignore", ".*MutableString",
DeprecationWarning) DeprecationWarning)
test_support.run_unittest(UserStringTest, MutableStringTest) test_support.run_unittest(UserStringTest, MutableStringTest)
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ import sys ...@@ -8,7 +8,7 @@ import sys
import types import types
__all__ = ["warn", "showwarning", "formatwarning", "filterwarnings", __all__ = ["warn", "showwarning", "formatwarning", "filterwarnings",
"resetwarnings"] "resetwarnings", "catch_warnings"]
def warnpy3k(message, category=None, stacklevel=1): def warnpy3k(message, category=None, stacklevel=1):
...@@ -304,37 +304,20 @@ class WarningMessage(object): ...@@ -304,37 +304,20 @@ class WarningMessage(object):
self.filename, self.lineno, self.line)) 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): class catch_warnings(object):
"""Guard the warnings filter from being permanently changed and optionally """A context manager that copies and restores the warnings filter upon
record the details of any warnings that are issued. exiting the context.
Context manager returns an instance of warnings.WarningRecorder which is a The 'record' argument specifies whether warnings should be captured by a
list of WarningMessage instances. Attributes on WarningRecorder are custom implementation of warnings.showwarning() and be appended to a list
redirected to the last created WarningMessage instance. 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): ...@@ -346,17 +329,21 @@ class catch_warnings(object):
keyword-only. keyword-only.
""" """
self._recorder = WarningsRecorder() if record else None self._record = record
self._module = sys.modules['warnings'] if module is None else module self._module = sys.modules['warnings'] if module is None else module
def __enter__(self): def __enter__(self):
self._filters = self._module.filters self._filters = self._module.filters
self._module.filters = self._filters[:] self._module.filters = self._filters[:]
self._showwarning = self._module.showwarning self._showwarning = self._module.showwarning
if self._recorder is not None: if self._record:
self._recorder.reset() # In case the instance is being reused. log = []
self._module.showwarning = self._recorder.showwarning def showwarning(*args, **kwargs):
return self._recorder log.append(WarningMessage(*args, **kwargs))
self._module.showwarning = showwarning
return log
else:
return None
def __exit__(self, *exc_info): def __exit__(self, *exc_info):
self._module.filters = self._filters self._module.filters = self._filters
......
...@@ -60,6 +60,9 @@ C-API ...@@ -60,6 +60,9 @@ C-API
Library 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 #1638033: Cookie.Morsel gained the httponly attribute.
- Issue #3535: zipfile couldn't read some zip files larger than 2GB. - Issue #3535: zipfile couldn't read some zip files larger than 2GB.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment