From 6d9520c4f0e01ff87ff3d74166f508df0a215261 Mon Sep 17 00:00:00 2001
From: Brett Cannon <bcannon@gmail.com>
Date: Wed, 13 Dec 2006 23:09:53 +0000
Subject: Add test.test_support.guard_warnings_filter .  This function returns
 a context manager that protects warnings.filter from being modified once the
 context is exited.

---
 Doc/lib/libtest.tex      |  4 ++++
 Lib/test/test_import.py  | 11 ++++-------
 Lib/test/test_random.py  |  7 +++----
 Lib/test/test_struct.py  | 25 ++++++++++---------------
 Lib/test/test_support.py | 12 ++++++++++++
 Misc/NEWS                |  4 ++++
 6 files changed, 37 insertions(+), 26 deletions(-)

diff --git a/Doc/lib/libtest.tex b/Doc/lib/libtest.tex
index 54a24b1..d13bfff 100644
--- a/Doc/lib/libtest.tex
+++ b/Doc/lib/libtest.tex
@@ -263,6 +263,10 @@ If no match is found \var{filename} is returned.
 This does not equal a failure since it could be the path to the file.
 \end{funcdesc}
 
+\begin{funcdesc}{guard_warnings_filter}{}
+Returns a context manager that guards the \module{warnings} module's
+filter settings.
+
 \begin{funcdesc}{run_unittest}{*classes}
 Execute \class{unittest.TestCase} subclasses passed to the function.
 The function scans the classes for methods starting with the prefix
diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py
index 462e266..a6db281 100644
--- a/Lib/test/test_import.py
+++ b/Lib/test/test_import.py
@@ -1,10 +1,11 @@
-from test.test_support import TESTFN, run_unittest
+from test.test_support import TESTFN, run_unittest, guard_warnings_filter
 
 import unittest
 import os
 import random
 import sys
 import py_compile
+import warnings
 
 
 def remove_files(name):
@@ -204,15 +205,11 @@ class ImportTest(unittest.TestCase):
         self.assert_(y is test.test_support, y.__name__)
 
     def test_import_initless_directory_warning(self):
-        import warnings
-        oldfilters = warnings.filters[:]
-        warnings.simplefilter('error', ImportWarning);
-        try:
+        with guard_warnings_filter():
             # Just a random non-package directory we always expect to be
             # somewhere in sys.path...
+            warnings.simplefilter('error', ImportWarning)
             self.assertRaises(ImportWarning, __import__, "site-packages")
-        finally:
-            warnings.filters = oldfilters
 
 def test_main(verbose=None):
     run_unittest(ImportTest)
diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py
index bba4c7c..e3f05a0 100644
--- a/Lib/test/test_random.py
+++ b/Lib/test/test_random.py
@@ -178,10 +178,9 @@ class WichmannHill_TestBasicOps(TestBasicOps):
 
     def test_bigrand(self):
         # Verify warnings are raised when randrange is too large for random()
-        oldfilters = warnings.filters[:]
-        warnings.filterwarnings("error", "Underlying random")
-        self.assertRaises(UserWarning, self.gen.randrange, 2**60)
-        warnings.filters[:] = oldfilters
+        with test_support.guard_warnings_filter():
+            warnings.filterwarnings("error", "Underlying random")
+            self.assertRaises(UserWarning, self.gen.randrange, 2**60)
 
 class SystemRandom_TestBasicOps(TestBasicOps):
     gen = random.SystemRandom()
diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py
index 66fd667..0144a0f 100644
--- a/Lib/test/test_struct.py
+++ b/Lib/test/test_struct.py
@@ -50,22 +50,17 @@ def any_err(func, *args):
 
 def with_warning_restore(func):
     def _with_warning_restore(*args, **kw):
-        # The `warnings` module doesn't have an advertised way to restore
-        # its filter list.  Cheat.
-        save_warnings_filters = warnings.filters[:]
-        # Grrr, we need this function to warn every time.  Without removing
-        # the warningregistry, running test_tarfile then test_struct would fail
-        # on 64-bit platforms.
-        globals = func.func_globals
-        if '__warningregistry__' in globals:
-            del globals['__warningregistry__']
-        warnings.filterwarnings("error", r"""^struct.*""", DeprecationWarning)
-        warnings.filterwarnings("error", r""".*format requires.*""",
-                                DeprecationWarning)
-        try:
+        with test.test_support.guard_warnings_filter():
+            # Grrr, we need this function to warn every time.  Without removing
+            # the warningregistry, running test_tarfile then test_struct would fail
+            # on 64-bit platforms.
+            globals = func.func_globals
+            if '__warningregistry__' in globals:
+                del globals['__warningregistry__']
+            warnings.filterwarnings("error", r"""^struct.*""", DeprecationWarning)
+            warnings.filterwarnings("error", r""".*format requires.*""",
+                                    DeprecationWarning)
             return func(*args, **kw)
-        finally:
-            warnings.filters[:] = save_warnings_filters[:]
     return _with_warning_restore
 
 def deprecated_err(func, *args):
diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py
index ae39aa1..4939410 100644
--- a/Lib/test/test_support.py
+++ b/Lib/test/test_support.py
@@ -3,7 +3,9 @@
 if __name__ != 'test.test_support':
     raise ImportError, 'test_support must be imported from the test package'
 
+from contextlib import contextmanager
 import sys
+import warnings
 
 class Error(Exception):
     """Base class for regression test exceptions."""
@@ -268,6 +270,16 @@ def open_urlresource(url):
     print >> get_original_stdout(), '\tfetching %s ...' % url
     fn, _ = urllib.urlretrieve(url, filename)
     return open(fn)
+    
+@contextmanager
+def guard_warnings_filter():
+    """Guard the warnings filter from being permanently changed."""
+    original_filters = warnings.filters[:]
+    try:
+        yield
+    finally:
+        warnings.filters = original_filters
+    
 
 #=======================================================================
 # Decorator for running a function in a different locale, correctly resetting
diff --git a/Misc/NEWS b/Misc/NEWS
index 667ccaf..4d9c899 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -302,6 +302,10 @@ Extension Modules
 Tests
 -----
 
+- Added guard_warnings_filter to test.test_support.  It returns a context
+  manager that protects the 'warnings' module's filter from being mutated
+  once the context has been exited.
+
 - Added some tests for modulefinder.
 
 - Converted test_imp to use unittest.
-- 
cgit v0.12

