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.
diff --git a/Lib/asynchat.py b/Lib/asynchat.py
index a97de93..911833d 100644
--- a/Lib/asynchat.py
+++ b/Lib/asynchat.py
@@ -50,7 +50,6 @@
 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 3d3396c..cc69e47 100644
--- a/Lib/bsddb/test/test_early_close.py
+++ b/Lib/bsddb/test/test_early_close.py
@@ -168,9 +168,9 @@
         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 fc5a2a5..71ca8f8 100644
--- a/Lib/mimetools.py
+++ b/Lib/mimetools.py
@@ -5,7 +5,7 @@
 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 9bd0083..0a8c5d6 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 @@
 
     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 0576b62..06a2378 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -4,9 +4,9 @@
 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 @@
         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 c57ac7f..d97583f 100644
--- a/Lib/test/test_hmac.py
+++ b/Lib/test/test_hmac.py
@@ -211,7 +211,7 @@
             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 ed9c7af..644a473 100644
--- a/Lib/test/test_import.py
+++ b/Lib/test/test_import.py
@@ -5,7 +5,7 @@
 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 @@
         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 @@
         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 da982d0..5c83d28 100644
--- a/Lib/test/test_macostools.py
+++ b/Lib/test/test_macostools.py
@@ -52,7 +52,7 @@
     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 1867b9e..e335528 100644
--- a/Lib/test/test_pep352.py
+++ b/Lib/test/test_pep352.py
@@ -2,7 +2,7 @@
 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 @@
         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 @@
         # 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 @@
         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 @@
     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 @@
 
     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 @@
 
     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 780de74..aa1ecbb 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 @@
 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 @@
             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 @@
             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 @@
             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 @@
                     '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 @@
         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 @@
             __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 @@
         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 @@
             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 @@
     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 @@
         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 4d570d0..d82d391 100644
--- a/Lib/test/test_random.py
+++ b/Lib/test/test_random.py
@@ -191,7 +191,7 @@
 
     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 1da3205..70bd886 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 @@
         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 ec4a2db..232bffc 100644
--- a/Lib/test/test_struct.py
+++ b/Lib/test/test_struct.py
@@ -4,7 +4,7 @@
 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 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 6c1a947..e0e7e56 100644
--- a/Lib/test/test_structmembers.py
+++ b/Lib/test/test_structmembers.py
@@ -66,35 +66,35 @@
 
 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 404a405..49ec12c 100644
--- a/Lib/test/test_sundry.py
+++ b/Lib/test/test_sundry.py
@@ -8,7 +8,7 @@
 
 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 695bd6d..0bf22cf 100644
--- a/Lib/test/test_support.py
+++ b/Lib/test/test_support.py
@@ -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", "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 @@
 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 @@
     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 05b237a..b20f2b4 100644
--- a/Lib/test/test_symtable.py
+++ b/Lib/test/test_symtable.py
@@ -44,7 +44,7 @@
 
 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 @@
         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 1dcbbed..7b351b8 100644
--- a/Lib/test/test_urllib.py
+++ b/Lib/test/test_urllib.py
@@ -641,7 +641,7 @@
 
 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 0404b77..58fe282 100644
--- a/Lib/test/test_urllibnet.py
+++ b/Lib/test/test_urllibnet.py
@@ -182,8 +182,8 @@
 
 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 cae610e..4bb0c45 100755
--- a/Lib/test/test_userstring.py
+++ b/Lib/test/test_userstring.py
@@ -135,7 +135,7 @@
         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 9980f24..388b5e9 100644
--- a/Lib/test/test_warnings.py
+++ b/Lib/test/test_warnings.py
@@ -72,64 +72,69 @@
     """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 @@
             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 @@
     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 @@
     """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 @@
         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 @@
             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 @@
             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 @@
             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 @@
     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 @@
 
     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 @@
         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 @@
     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 @@
     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 @@
     module = py_warnings
 
 
-
 class CatchWarningTests(BaseTest):
 
     """Test catch_warnings()."""
@@ -511,12 +533,12 @@
             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 @@
     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 9ec04be..04e7b58 100644
--- a/Lib/warnings.py
+++ b/Lib/warnings.py
@@ -8,7 +8,7 @@
 import types
 
 __all__ = ["warn", "showwarning", "formatwarning", "filterwarnings",
-           "resetwarnings"]
+           "resetwarnings", "catch_warnings"]
 
 
 def warnpy3k(message, category=None, stacklevel=1):
@@ -304,37 +304,20 @@
                                     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 @@
         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