Repository: numpy/numpy

Issue:
 Convert WarningManager uses to warnings.catch_warnings.
Now that all supported Python versions have the `with` statement WarningManager should be dispensed with and deprecated.



Discussion:

Resolving patch:
diff --git a/numpy/lib/utils.py b/numpy/lib/utils.py
--- a/numpy/lib/utils.py
+++ b/numpy/lib/utils.py
@@ -1107,18 +1107,14 @@ def safe_eval(source):
     """
     # Local imports to speed up numpy's import time.
     import warnings
-    from numpy.testing.utils import WarningManager
-    warn_ctx = WarningManager()
-    warn_ctx.__enter__()
-    try:
+
+    with warnings.catch_warnings():
         # compiler package is deprecated for 3.x, which is already solved here
         warnings.simplefilter('ignore', DeprecationWarning)
         try:
             import compiler
         except ImportError:
             import ast as compiler
-    finally:
-        warn_ctx.__exit__()
 
     walker = SafeEval()
     try:
diff --git a/numpy/random/__init__.py b/numpy/random/__init__.py
--- a/numpy/random/__init__.py
+++ b/numpy/random/__init__.py
@@ -88,19 +88,15 @@
 """
 from __future__ import division, absolute_import, print_function
 
+import warnings
+
 # To get sub-modules
 from .info import __doc__, __all__
 
-import warnings
-from numpy.testing.utils import WarningManager
 
-warn_ctx = WarningManager()
-warn_ctx.__enter__()
-try:
+with warnings.catch_warnings():
     warnings.filterwarnings("ignore", message="numpy.ndarray size changed")
     from .mtrand import *
-finally:
-    warn_ctx.__exit__()
 
 # Some aliases:
 ranf = random = sample = random_sample


Test patch:
diff --git a/numpy/core/tests/test_api.py b/numpy/core/tests/test_api.py
--- a/numpy/core/tests/test_api.py
+++ b/numpy/core/tests/test_api.py
@@ -1,11 +1,10 @@
 from __future__ import division, absolute_import, print_function
 
 import sys
+import warnings
 
 import numpy as np
 from numpy.testing import *
-from numpy.testing.utils import WarningManager
-import warnings
 from numpy.compat import sixu
 
 # Switch between new behaviour when NPY_RELAXED_STRIDES_CHECKING is set.
diff --git a/numpy/core/tests/test_einsum.py b/numpy/core/tests/test_einsum.py
--- a/numpy/core/tests/test_einsum.py
+++ b/numpy/core/tests/test_einsum.py
@@ -1,12 +1,11 @@
 from __future__ import division, absolute_import, print_function
 
 import sys
+import warnings
 from decimal import Decimal
 
 import numpy as np
 from numpy.testing import *
-from numpy.testing.utils import WarningManager
-import warnings
 
 class TestEinSum(TestCase):
     def test_einsum_errors(self):
@@ -272,9 +271,7 @@ def check_einsum_sums(self, dtype):
             assert_equal(np.einsum(a, [0], b, [1]), np.outer(a, b))
 
         # Suppress the complex warnings for the 'as f8' tests
-        ctx = WarningManager()
-        ctx.__enter__()
-        try:
+        with warnings.catch_warnings():
             warnings.simplefilter('ignore', np.ComplexWarning)
 
             # matvec(a,b) / a.dot(b) where a is matrix, b is vector
@@ -379,8 +376,6 @@ def check_einsum_sums(self, dtype):
                                         dtype='f8', casting='unsafe')
                 assert_equal(c, np.tensordot(a.astype('f8'), b.astype('f8'),
                                         axes=([1,0],[0,1])).astype(dtype))
-        finally:
-            ctx.__exit__()
 
         # logical_and(logical_and(a!=0, b!=0), c!=0)
         a = np.array([1,   3,   -2,   0,   12,  13,   0,   1], dtype=dtype)
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -8,7 +8,6 @@
 import numpy as np
 from nose import SkipTest
 from numpy.core import *
-from numpy.testing.utils import WarningManager
 from numpy.compat import asbytes, getexception, strchar, sixu
 from test_print import in_foreign_locale
 from numpy.core.multiarray_tests import (
@@ -958,17 +957,13 @@ def test_diagonal(self):
         assert_equal(b.diagonal(0, 2, 1), [[0, 3], [4, 7]])
 
     def test_diagonal_deprecation(self):
-        import warnings
-        from numpy.testing.utils import WarningManager
+
         def collect_warning_types(f, *args, **kwargs):
-            ctx = WarningManager(record=True)
-            warning_log = ctx.__enter__()
-            warnings.simplefilter("always")
-            try:
+            with warnings.catch_warnings(record=True) as log:
+                warnings.simplefilter("always")
                 f(*args, **kwargs)
-            finally:
-                ctx.__exit__()
-            return [w.category for w in warning_log]
+            return [w.category for w in log]
+
         a = np.arange(9).reshape(3, 3)
         # All the different functions raise a warning, but not an error, and
         # 'a' is not modified:
@@ -2028,17 +2023,13 @@ def test_field_names(self):
             assert_raises(ValueError, a.__getitem__, sixu('\u03e0'))
 
     def test_field_names_deprecation(self):
-        import warnings
-        from numpy.testing.utils import WarningManager
+
         def collect_warning_types(f, *args, **kwargs):
-            ctx = WarningManager(record=True)
-            warning_log = ctx.__enter__()
-            warnings.simplefilter("always")
-            try:
+            with warnings.catch_warnings(record=True) as log:
+                warnings.simplefilter("always")
                 f(*args, **kwargs)
-            finally:
-                ctx.__exit__()
-            return [w.category for w in warning_log]
+            return [w.category for w in log]
+
         a = np.zeros((1,), dtype=[('f1', 'i4'),
                                   ('f2', 'i4'),
                                   ('f3', [('sf1', 'i4')])])
@@ -2500,36 +2491,38 @@ def test_simple_strict_within(self):
         assert_array_equal(l, r)
 
 class TestWarnings(object):
+
     def test_complex_warning(self):
         x = np.array([1,2])
         y = np.array([1-2j,1+2j])
 
-        warn_ctx = WarningManager()
-        warn_ctx.__enter__()
-        try:
+        with warnings.catch_warnings():
             warnings.simplefilter("error", np.ComplexWarning)
             assert_raises(np.ComplexWarning, x.__setitem__, slice(None), y)
             assert_equal(x, [1,2])
-        finally:
-            warn_ctx.__exit__()
 
 class TestMinScalarType(object):
+
     def test_usigned_shortshort(self):
         dt = np.min_scalar_type(2**8-1)
         wanted = np.dtype('uint8')
         assert_equal(wanted, dt)
+
     def test_usigned_short(self):
         dt = np.min_scalar_type(2**16-1)
         wanted = np.dtype('uint16')
         assert_equal(wanted, dt)
+
     def test_usigned_int(self):
         dt = np.min_scalar_type(2**32-1)
         wanted = np.dtype('uint32')
         assert_equal(wanted, dt)
+
     def test_usigned_longlong(self):
         dt = np.min_scalar_type(2**63-1)
         wanted = np.dtype('uint64')
         assert_equal(wanted, dt)
+
     def test_object(self):
         dt = np.min_scalar_type(2**64)
         wanted = np.dtype('O')
diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py
--- a/numpy/core/tests/test_regression.py
+++ b/numpy/core/tests/test_regression.py
@@ -16,7 +16,7 @@
         assert_almost_equal, assert_array_equal, assert_array_almost_equal,
         assert_raises, assert_warns, dec
         )
-from numpy.testing.utils import _assert_valid_refcount, WarningManager
+from numpy.testing.utils import _assert_valid_refcount
 from numpy.compat import asbytes, asunicode, asbytes_nested, long, sixu
 
 rlevel = 1
@@ -1545,13 +1545,9 @@ def test_complex_scalar_warning(self):
         for tp in [np.csingle, np.cdouble, np.clongdouble]:
             x = tp(1+2j)
             assert_warns(np.ComplexWarning, float, x)
-            warn_ctx = WarningManager()
-            warn_ctx.__enter__()
-            try:
+            with warnings.catch_warnings():
                 warnings.simplefilter('ignore')
                 assert_equal(float(x), float(x.real))
-            finally:
-                warn_ctx.__exit__()
 
     def test_complex_scalar_complex_cast(self):
         for tp in [np.csingle, np.cdouble, np.clongdouble]:
diff --git a/numpy/lib/tests/test_io.py b/numpy/lib/tests/test_io.py
--- a/numpy/lib/tests/test_io.py
+++ b/numpy/lib/tests/test_io.py
@@ -10,7 +10,6 @@
 import gc
 from io import BytesIO
 from datetime import datetime
-from numpy.testing.utils import WarningManager
 
 import numpy as np
 import numpy.ma as ma
@@ -571,14 +570,13 @@ def test_3d_shaped_dtype(self):
         dt = np.dtype([('name', 'S4'), ('x', float), ('y', float),
                        ('block', int, (2, 2, 3))])
         x = np.loadtxt(c, dtype=dt)
-        a = np.array([('aaaa', 1.0, 8.0, [[[1, 2, 3], [4, 5, 6]],[[7, 8, 9], [10, 11, 12]]])],
-                     dtype=dt)
+        a = np.array([('aaaa', 1.0, 8.0,
+                        [[[1, 2, 3], [4, 5, 6]],[[7, 8, 9], [10, 11, 12]]])],
+                    dtype=dt)
         assert_array_equal(x, a)
 
     def test_empty_file(self):
-        warn_ctx = WarningManager()
-        warn_ctx.__enter__()
-        try:
+        with warnings.catch_warnings():
             warnings.filterwarnings("ignore",
                     message="loadtxt: Empty input file:")
             c = TextIO()
@@ -587,8 +585,6 @@ def test_empty_file(self):
             x = np.loadtxt(c, dtype=np.int64)
             assert_equal(x.shape, (0,))
             assert_(x.dtype == np.int64)
-        finally:
-            warn_ctx.__exit__()
 
 
     def test_unused_converter(self):
@@ -704,16 +700,12 @@ def test_ndmin_keyword(self):
         assert_(x.shape == (3,))
 
         # Test ndmin kw with empty file.
-        warn_ctx = WarningManager()
-        warn_ctx.__enter__()
-        try:
+        with warnings.catch_warnings():
             warnings.filterwarnings("ignore",
                     message="loadtxt: Empty input file:")
             f = TextIO()
             assert_(np.loadtxt(f, ndmin=2).shape == (0, 1,))
             assert_(np.loadtxt(f, ndmin=1).shape == (0,))
-        finally:
-            warn_ctx.__exit__()
 
     def test_generator_source(self):
         def count():
@@ -841,11 +833,9 @@ def test_skip_footer(self):
         assert_equal(test, ctrl)
 
     def test_skip_footer_with_invalid(self):
-        warn_ctx = WarningManager()
-        warn_ctx.__enter__()
-        try:
-            basestr = '1 1\n2 2\n3 3\n4 4\n5  \n6  \n7  \n'
+        with warnings.catch_warnings():
             warnings.filterwarnings("ignore")
+            basestr = '1 1\n2 2\n3 3\n4 4\n5  \n6  \n7  \n'
             # Footer too small to get rid of all invalid values
             assert_raises(ValueError, np.genfromtxt,
                           TextIO(basestr), skip_footer=1)
@@ -862,9 +852,6 @@ def test_skip_footer_with_invalid(self):
             assert_equal(a, np.array([[1., 1.], [3., 3.], [4., 4.], [6., 6.]]))
             a = np.genfromtxt(TextIO(basestr), skip_footer=3, invalid_raise=False)
             assert_equal(a, np.array([[1., 1.], [3., 3.], [4., 4.]]))
-        finally:
-            warn_ctx.__exit__()
-
 
     def test_header(self):
         "Test retrieving a header"
@@ -1168,15 +1155,12 @@ def test_usecols_with_named_columns(self):
 
     def test_empty_file(self):
         "Test that an empty file raises the proper warning."
-        warn_ctx = WarningManager()
-        warn_ctx.__enter__()
-        try:
-            warnings.filterwarnings("ignore", message="genfromtxt: Empty input file:")
+        with warnings.catch_warnings():
+            warnings.filterwarnings("ignore",
+                                    message="genfromtxt: Empty input file:")
             data = TextIO()
             test = np.genfromtxt(data)
             assert_equal(test, np.array([]))
-        finally:
-            warn_ctx.__exit__()
 
     def test_fancy_dtype_alt(self):
         "Check that a nested dtype isn't MIA"
diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py
--- a/numpy/ma/tests/test_core.py
+++ b/numpy/ma/tests/test_core.py
@@ -20,7 +20,6 @@
 from numpy.ma.testutils import *
 from numpy.ma.core import *
 from numpy.compat import asbytes, asbytes_nested
-from numpy.testing.utils import WarningManager
 
 pi = np.pi
 
@@ -422,13 +421,9 @@ def test_topython(self):
         assert_equal(1.0, float(array([[1]])))
         self.assertRaises(TypeError, float, array([1, 1]))
         #
-        warn_ctx = WarningManager()
-        warn_ctx.__enter__()
-        try:
+        with warnings.catch_warnings():
             warnings.simplefilter('ignore', UserWarning)
             assert_(np.isnan(float(array([1], mask=[1]))))
-        finally:
-            warn_ctx.__exit__()
         #
         a = array([1, 2, 3], mask=[1, 0, 0])
         self.assertRaises(TypeError, lambda:float(a))
@@ -2764,23 +2759,15 @@ def test_varstd_specialcases(self):
             self.assertTrue(method(0) is masked)
             self.assertTrue(method(-1) is masked)
             # Using a masked array as explicit output
-            warn_ctx = WarningManager()
-            warn_ctx.__enter__()
-            try:
+            with warnings.catch_warnings():
                 warnings.simplefilter('ignore')
                 _ = method(out=mout)
-            finally:
-                warn_ctx.__exit__()
             self.assertTrue(mout is not masked)
             assert_equal(mout.mask, True)
             # Using a ndarray as explicit output
-            warn_ctx = WarningManager()
-            warn_ctx.__enter__()
-            try:
+            with warnings.catch_warnings():
                 warnings.simplefilter('ignore')
                 _ = method(out=nout)
-            finally:
-                warn_ctx.__exit__()
             self.assertTrue(np.isnan(nout))
         #
         x = array(arange(10), mask=True)
diff --git a/numpy/ma/tests/test_mrecords.py b/numpy/ma/tests/test_mrecords.py
--- a/numpy/ma/tests/test_mrecords.py
+++ b/numpy/ma/tests/test_mrecords.py
@@ -21,7 +21,6 @@
 from numpy.compat import asbytes, asbytes_nested
 from numpy.ma.testutils import *
 from numpy.ma import masked, nomask
-from numpy.testing.utils import WarningManager
 from numpy.ma.mrecords import MaskedRecords, mrecarray, fromarrays, \
                               fromtextfile, fromrecords, addfield
 
@@ -142,15 +141,11 @@ def test_set_fields(self):
         rdata = data.view(MaskedRecords)
         val = ma.array([10,20,30], mask=[1,0,0])
         #
-        warn_ctx = WarningManager()
-        warn_ctx.__enter__()
-        try:
+        with warnings.catch_warnings():
             warnings.simplefilter("ignore")
             rdata['num'] = val
             assert_equal(rdata.num, val)
             assert_equal(rdata.num.mask, [1,0,0])
-        finally:
-            warn_ctx.__exit__()
 
     def test_set_fields_mask(self):
         "Tests setting the mask of a field."
diff --git a/numpy/testing/decorators.py b/numpy/testing/decorators.py
--- a/numpy/testing/decorators.py
+++ b/numpy/testing/decorators.py
@@ -15,11 +15,8 @@
 """
 from __future__ import division, absolute_import, print_function
 
-import warnings
 import sys
-
-from numpy.testing.utils import \
-        WarningManager, WarningMessage
+import warnings
 import collections
 
 def slow(t):
@@ -253,10 +250,8 @@ def deprecate_decorator(f):
 
         def _deprecated_imp(*args, **kwargs):
             # Poor man's replacement for the with statement
-            ctx = WarningManager(record=True)
-            l = ctx.__enter__()
-            warnings.simplefilter('always')
-            try:
+            with warnings.catch_warnings(record=True) as l:
+                warnings.simplefilter('always')
                 f(*args, **kwargs)
                 if not len(l) > 0:
                     raise AssertionError("No warning raised when calling %s"
@@ -264,8 +259,6 @@ def _deprecated_imp(*args, **kwargs):
                 if not l[0].category is DeprecationWarning:
                     raise AssertionError("First warning for %s is not a " \
                             "DeprecationWarning( is %s)" % (f.__name__, l[0]))
-            finally:
-                ctx.__exit__()
 
         if isinstance(conditional, collections.Callable):
             cond = conditional()
diff --git a/numpy/testing/nosetester.py b/numpy/testing/nosetester.py
--- a/numpy/testing/nosetester.py
+++ b/numpy/testing/nosetester.py
@@ -363,32 +363,27 @@ def test(self, label='fast', verbose=1, extra_argv=None,
         if raise_warnings in _warn_opts.keys():
             raise_warnings = _warn_opts[raise_warnings]
 
-        # Preserve the state of the warning filters
-        warn_ctx = numpy.testing.utils.WarningManager()
-        warn_ctx.__enter__()
-        # Reset the warning filters to the default state,
-        # so that running the tests is more repeatable.
-        warnings.resetwarnings()
-        # If deprecation warnings are not set to 'error' below,
-        # at least set them to 'warn'.
-        warnings.filterwarnings('always', category=DeprecationWarning)
-        # Force the requested warnings to raise
-        for warningtype in raise_warnings:
-            warnings.filterwarnings('error', category=warningtype)
-        # Filter out annoying import messages.
-        warnings.filterwarnings('ignore', message='Not importing directory')
-        warnings.filterwarnings("ignore", message="numpy.dtype size changed")
-        warnings.filterwarnings("ignore", message="numpy.ufunc size changed")
-        warnings.filterwarnings("ignore", category=ModuleDeprecationWarning)
-
-        try:
+        with warnings.catch_warnings():
+            # Reset the warning filters to the default state,
+            # so that running the tests is more repeatable.
+            warnings.resetwarnings()
+            # If deprecation warnings are not set to 'error' below,
+            # at least set them to 'warn'.
+            warnings.filterwarnings('always', category=DeprecationWarning)
+            # Force the requested warnings to raise
+            for warningtype in raise_warnings:
+                warnings.filterwarnings('error', category=warningtype)
+            # Filter out annoying import messages.
+            warnings.filterwarnings('ignore', message='Not importing directory')
+            warnings.filterwarnings("ignore", message="numpy.dtype size changed")
+            warnings.filterwarnings("ignore", message="numpy.ufunc size changed")
+            warnings.filterwarnings("ignore", category=ModuleDeprecationWarning)
+
             from .noseclasses import NumpyTestProgram
 
-            argv, plugins = self.prepare_test_args(label,
-                    verbose, extra_argv, doctests, coverage)
+            argv, plugins = self.prepare_test_args(
+                    label, verbose, extra_argv, doctests, coverage)
             t = NumpyTestProgram(argv=argv, exit=False, plugins=plugins)
-        finally:
-            warn_ctx.__exit__()
 
         return t.result
 
diff --git a/numpy/testing/utils.py b/numpy/testing/utils.py
--- a/numpy/testing/utils.py
+++ b/numpy/testing/utils.py
@@ -1365,6 +1365,8 @@ class WarningMessage(object):
     """
     Holds the result of a single showwarning() call.
 
+    Deprecated in 1.8.0
+
     Notes
     -----
     `WarningMessage` is copied from the Python 2.6 warnings module,
@@ -1405,6 +1407,8 @@ class WarningManager(object):
     named 'warnings' and imported under that name. This argument is only useful
     when testing the warnings module itself.
 
+    Deprecated in 1.8.0
+
     Notes
     -----
     `WarningManager` is a copy of the ``catch_warnings`` context manager
@@ -1467,13 +1471,8 @@ def assert_warns(warning_class, func, *args, **kw):
     The value returned by `func`.
 
     """
-
-    # XXX: once we may depend on python >= 2.6, this can be replaced by the
-    # warnings module context manager.
-    ctx = WarningManager(record=True)
-    l = ctx.__enter__()
-    warnings.simplefilter('always')
-    try:
+    with warnings.catch_warnings(record=True) as l:
+        warnings.simplefilter('always')
         result = func(*args, **kw)
         if not len(l) > 0:
             raise AssertionError("No warning raised when calling %s"
@@ -1481,8 +1480,6 @@ def assert_warns(warning_class, func, *args, **kw):
         if not l[0].category is warning_class:
             raise AssertionError("First warning for %s is not a " \
                     "%s( is %s)" % (func.__name__, warning_class, l[0]))
-    finally:
-        ctx.__exit__()
     return result
 
 def assert_no_warnings(func, *args, **kw):
@@ -1503,18 +1500,12 @@ def assert_no_warnings(func, *args, **kw):
     The value returned by `func`.
 
     """
-    # XXX: once we may depend on python >= 2.6, this can be replaced by the
-    # warnings module context manager.
-    ctx = WarningManager(record=True)
-    l = ctx.__enter__()
-    warnings.simplefilter('always')
-    try:
+    with warnings.catch_warnings(record=True) as l:
+        warnings.simplefilter('always')
         result = func(*args, **kw)
         if len(l) > 0:
             raise AssertionError("Got warnings when calling %s: %s"
                     % (func.__name__, l))
-    finally:
-        ctx.__exit__()
     return result