Skip to content

Commit d6dfc7f

Browse files
author
Ben Cipollini
committed
New interface: suppress_warnings, clear_and_catch_warnings, and error_warnings.
Deprecate ErrorWarnings, IgnoreWarnings, and catch_warn_reset; nibabel.checkwarns.
1 parent 74b9db6 commit d6dfc7f

File tree

8 files changed

+163
-149
lines changed

8 files changed

+163
-149
lines changed

nibabel/checkwarns.py

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,39 +12,23 @@
1212

1313
import warnings
1414

15+
from .testing import (error_warnings, suppress_warnings)
1516

16-
class ErrorWarnings(warnings.catch_warnings):
17-
""" Context manager to check for warnings as errors. Usually used with
18-
``assert_raises`` in the with block
19-
20-
Examples
21-
--------
22-
>>> with ErrorWarnings():
23-
... try:
24-
... warnings.warn('Message', UserWarning)
25-
... except UserWarning:
26-
... print('I consider myself warned')
27-
I consider myself warned
28-
"""
29-
filter = 'error'
30-
31-
def __init__(self, record=True, module=None):
32-
super(ErrorWarnings, self).__init__(record=record, module=module)
33-
34-
def __enter__(self):
35-
mgr = super(ErrorWarnings, self).__enter__()
36-
warnings.simplefilter(self.filter)
37-
return mgr
38-
39-
40-
class IgnoreWarnings(ErrorWarnings):
41-
""" Context manager to ignore warnings
42-
43-
Examples
44-
--------
45-
>>> with IgnoreWarnings():
46-
... warnings.warn('Message', UserWarning)
47-
48-
(and you get no warning)
49-
"""
50-
filter = 'ignore'
17+
18+
warnings.warn('The checkwarns module is deprecated and will be removed in nibabel v3.0', FutureWarning)
19+
20+
21+
class ErrorWarnings(error_warnings):
22+
def __init__(self, *args, **kwargs):
23+
warnings.warn('ErrorWarnings is deprecated and will be removed in '
24+
'nibabel v3.0; use nibabel.testing.error_warnings.',
25+
FutureWarning)
26+
super(ErrorWarnings, self).__init__(*args, **kwargs)
27+
28+
29+
class IgnoreWarnings(suppress_warnings):
30+
def __init__(self, *args, **kwargs):
31+
warnings.warn('IgnoreWarnings is deprecated and will be removed in '
32+
'nibabel v3.0; use nibabel.testing.suppress_warnings.',
33+
FutureWarning)
34+
super(IgnoreWarnings, self).__init__(*args, **kwargs)

nibabel/testing/__init__.py

Lines changed: 84 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,22 @@
77
#
88
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
99
''' Utilities for testing '''
10+
from __future__ import division, print_function
11+
12+
import warnings
1013
from os.path import dirname, abspath, join as pjoin
1114

1215
import numpy as np
13-
from warnings import catch_warnings, simplefilter
14-
15-
# set path to example data
16-
data_path = abspath(pjoin(dirname(__file__), '..', 'tests', 'data'))
1716

1817
# Allow failed import of nose if not now running tests
1918
try:
20-
import nose.tools as nt
21-
except ImportError:
22-
pass
23-
else:
2419
from nose.tools import (assert_equal, assert_not_equal,
2520
assert_true, assert_false, assert_raises)
21+
except ImportError:
22+
pass
23+
24+
# set path to example data
25+
data_path = abspath(pjoin(dirname(__file__), '..', 'tests', 'data'))
2626

2727

2828
def assert_dt_equal(a, b):
@@ -56,35 +56,97 @@ def assert_allclose_safely(a, b, match_nans=True):
5656
assert_true(np.allclose(a, b))
5757

5858

59-
class suppress_warnings(catch_warnings):
60-
""" Version of ``catch_warnings`` class that suppresses warnings
61-
"""
62-
def __enter__(self):
63-
res = super(suppress_warnings, self).__enter__()
64-
simplefilter('ignore')
65-
return res
59+
class clear_and_catch_warnings(warnings.catch_warnings):
60+
""" Context manager that resets warning registry for catching warnings
61+
62+
Warnings can be slippery, because, whenever a warning is triggered, Python
63+
adds a ``__warningregistry__`` member to the *calling* module. This makes
64+
it impossible to retrigger the warning in this module, whatever you put in
65+
the warnings filters. This context manager accepts a sequence of `modules`
66+
as a keyword argument to its constructor and:
67+
68+
* stores and removes any ``__warningregistry__`` entries in given `modules`
69+
on entry;
70+
* resets ``__warningregistry__`` to its previous state on exit.
71+
72+
This makes it possible to trigger any warning afresh inside the context
73+
manager without disturbing the state of warnings outside.
6674
75+
For compatibility with Python 3.0, please consider all arguments to be
76+
keyword-only.
6777
68-
class catch_warn_reset(catch_warnings):
69-
""" Version of ``catch_warnings`` class that resets warning registry
78+
Parameters
79+
----------
80+
record : bool, optional
81+
Specifies whether warnings should be captured by a custom
82+
implementation of ``warnings.showwarning()`` and be appended to a list
83+
returned by the context manager. Otherwise None is returned by the
84+
context manager. The objects appended to the list are arguments whose
85+
attributes mirror the arguments to ``showwarning()``.
86+
modules : sequence, optional
87+
Sequence of modules for which to reset warnings registry on entry and
88+
restore on exit
89+
90+
Examples
91+
--------
92+
>>> import warnings
93+
>>> with clear_and_catch_warnings(modules=[np.core.fromnumeric]):
94+
... warnings.simplefilter('always')
95+
... # do something that raises a warning in np.core.fromnumeric
7096
"""
71-
def __init__(self, *args, **kwargs):
72-
self.modules = kwargs.pop('modules', [])
97+
class_modules = ()
98+
99+
def __init__(self, record=False, modules=()):
100+
self.modules = set(modules).union(self.class_modules)
73101
self._warnreg_copies = {}
74-
super(catch_warn_reset, self).__init__(*args, **kwargs)
102+
super(clear_and_catch_warnings, self).__init__(record=record)
75103

76104
def __enter__(self):
77105
for mod in self.modules:
78106
if hasattr(mod, '__warningregistry__'):
79107
mod_reg = mod.__warningregistry__
80108
self._warnreg_copies[mod] = mod_reg.copy()
81109
mod_reg.clear()
82-
return super(catch_warn_reset, self).__enter__()
110+
return super(clear_and_catch_warnings, self).__enter__()
83111

84112
def __exit__(self, *exc_info):
85-
super(catch_warn_reset, self).__exit__(*exc_info)
113+
super(clear_and_catch_warnings, self).__exit__(*exc_info)
86114
for mod in self.modules:
87115
if hasattr(mod, '__warningregistry__'):
88116
mod.__warningregistry__.clear()
89117
if mod in self._warnreg_copies:
90118
mod.__warningregistry__.update(self._warnreg_copies[mod])
119+
120+
121+
class error_warnings(clear_and_catch_warnings):
122+
""" Context manager to check for warnings as errors. Usually used with
123+
``assert_raises`` in the with block
124+
125+
Examples
126+
--------
127+
>>> with error_warnings():
128+
... try:
129+
... warnings.warn('Message', UserWarning)
130+
... except UserWarning:
131+
... print('I consider myself warned')
132+
I consider myself warned
133+
"""
134+
filter = 'error'
135+
136+
def __enter__(self):
137+
mgr = super(error_warnings, self).__enter__()
138+
warnings.simplefilter(self.filter)
139+
return mgr
140+
141+
142+
class suppress_warnings(error_warnings):
143+
""" Version of ``catch_warnings`` class that suppresses warnings
144+
"""
145+
filter = 'ignore'
146+
147+
148+
class catch_warn_reset(clear_and_catch_warnings):
149+
def __init__(self, *args, **kwargs):
150+
warnings.warn('catch_warn_reset is deprecated and will be removed in '
151+
'nibabel v3.0; use nibabel.testing.clear_and_catch_warnings.',
152+
FutureWarning)

nibabel/tests/test_arraywriters.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,19 @@
1111
import numpy as np
1212

1313
from ..externals.six import BytesIO
14-
1514
from ..arraywriters import (SlopeInterArrayWriter, SlopeArrayWriter,
1615
WriterError, ScalingError, ArrayWriter,
1716
make_array_writer, get_slope_inter)
18-
1917
from ..casting import int_abs, type_info, shared_range, on_powerpc
20-
2118
from ..volumeutils import array_from_file, apply_read_scaling, _dt_min_max
2219

2320
from numpy.testing import (assert_array_almost_equal,
2421
assert_array_equal)
25-
2622
from nose.tools import (assert_true, assert_false,
2723
assert_equal, assert_not_equal,
2824
assert_raises)
29-
30-
from ..testing import assert_allclose_safely, suppress_warnings
31-
from ..checkwarns import ErrorWarnings
25+
from ..testing import (assert_allclose_safely, suppress_warnings,
26+
error_warnings)
3227

3328

3429
FLOAT_TYPES = np.sctypes['float']
@@ -524,7 +519,7 @@ def test_nan2zero():
524519
data_back = round_trip(aw)
525520
assert_array_equal(np.isnan(data_back), [True, False])
526521
# Deprecation warning for nan2zero as argument to `to_fileobj`
527-
with ErrorWarnings():
522+
with error_warnings():
528523
assert_raises(DeprecationWarning,
529524
aw.to_fileobj, BytesIO(), 'F', True)
530525
assert_raises(DeprecationWarning,
@@ -545,7 +540,7 @@ def test_nan2zero():
545540
astype_res = np.array(np.nan).astype(np.int32)
546541
assert_array_equal(data_back, [astype_res, 99])
547542
# Deprecation warning for nan2zero as argument to `to_fileobj`
548-
with ErrorWarnings():
543+
with error_warnings():
549544
assert_raises(DeprecationWarning,
550545
aw.to_fileobj, BytesIO(), 'F', False)
551546
assert_raises(DeprecationWarning,

nibabel/tests/test_checkwarns.py

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,9 @@
11
""" Tests for warnings context managers
22
"""
3-
43
from __future__ import division, print_function, absolute_import
54

6-
from warnings import warn, simplefilter, filters
7-
85
from ..checkwarns import ErrorWarnings, IgnoreWarnings
96

107
from nose.tools import assert_true, assert_equal, assert_raises
11-
12-
13-
def test_warn_error():
14-
# Check warning error context manager
15-
n_warns = len(filters)
16-
with ErrorWarnings():
17-
assert_raises(UserWarning, warn, 'A test')
18-
with ErrorWarnings() as w: # w not used for anything
19-
assert_raises(UserWarning, warn, 'A test')
20-
assert_equal(n_warns, len(filters))
21-
# Check other errors are propagated
22-
def f():
23-
with ErrorWarnings():
24-
raise ValueError('An error')
25-
assert_raises(ValueError, f)
26-
27-
28-
def test_warn_ignore():
29-
# Check warning ignore context manager
30-
n_warns = len(filters)
31-
with IgnoreWarnings():
32-
warn('Here is a warning, you will not see it')
33-
warn('Nor this one', DeprecationWarning)
34-
with IgnoreWarnings() as w: # w not used
35-
warn('Here is a warning, you will not see it')
36-
warn('Nor this one', DeprecationWarning)
37-
assert_equal(n_warns, len(filters))
38-
# Check other errors are propagated
39-
def f():
40-
with IgnoreWarnings():
41-
raise ValueError('An error')
42-
assert_raises(ValueError, f)
8+
from ..testing import (error_warnings, suppress_warnings,
9+
clear_and_catch_warnings)

nibabel/tests/test_openers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
from bz2 import BZ2File
1313
from io import BytesIO, UnsupportedOperation
1414

15-
from ..checkwarns import ErrorWarnings
1615
from ..py3k import asstr, asbytes
1716
from ..openers import Opener, ImageOpener
1817
from ..tmpdirs import InTemporaryDirectory
1918
from ..volumeutils import BinOpener
2019

2120
from nose.tools import (assert_true, assert_false, assert_equal,
2221
assert_not_equal, assert_raises)
22+
from ..testing import error_warnings
23+
2324

2425
class Lunk(object):
2526
# bare file-like for testing
@@ -84,7 +85,7 @@ def test_Opener_various():
8485
assert_not_equal(fobj.fileno(), 0)
8586

8687
def test_BinOpener():
87-
with ErrorWarnings():
88+
with error_warnings():
8889
assert_raises(DeprecationWarning,
8990
BinOpener, 'test.txt', 'r')
9091

nibabel/tests/test_parrec.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from nose.tools import (assert_true, assert_false, assert_raises,
2424
assert_equal)
2525

26-
from ..testing import catch_warn_reset, suppress_warnings
26+
from ..testing import clear_and_catch_warnings, suppress_warnings
2727

2828
from .test_arrayproxy import check_mmap
2929
from . import test_spatialimages as tsi
@@ -237,7 +237,7 @@ def test_affine_regression():
237237

238238
def test_get_voxel_size_deprecated():
239239
hdr = PARRECHeader(HDR_INFO, HDR_DEFS)
240-
with catch_warn_reset(modules=[parrec], record=True) as wlist:
240+
with clear_and_catch_warnings(modules=[parrec], record=True) as wlist:
241241
simplefilter('always')
242242
hdr.get_voxel_size()
243243
assert_equal(wlist[0].category, DeprecationWarning)
@@ -255,7 +255,7 @@ def test_get_sorted_slice_indices():
255255
17, 16, 15, 14, 13, 12, 11, 10, 9,
256256
26, 25, 24, 23, 22, 21, 20, 19, 18])
257257
# Omit last slice, only two volumes
258-
with catch_warn_reset(modules=[parrec], record=True):
258+
with clear_and_catch_warnings(modules=[parrec], record=True):
259259
hdr = PARRECHeader(HDR_INFO, HDR_DEFS[:-1], permit_truncated=True)
260260
assert_array_equal(hdr.get_sorted_slice_indices(), range(n_slices - 9))
261261

@@ -300,7 +300,7 @@ def test_truncated_load():
300300
with open(TRUNC_PAR, 'rt') as fobj:
301301
gen_info, slice_info = parse_PAR_header(fobj)
302302
assert_raises(PARRECError, PARRECHeader, gen_info, slice_info)
303-
with catch_warn_reset(record=True) as wlist:
303+
with clear_and_catch_warnings(record=True) as wlist:
304304
hdr = PARRECHeader(gen_info, slice_info, True)
305305
assert_equal(len(wlist), 1)
306306

@@ -373,7 +373,7 @@ def test_truncations():
373373
# Drop one line, raises error
374374
assert_raises(PARRECError, PARRECHeader, gen_info, slice_info[:-1])
375375
# When we are permissive, we raise a warning, and drop a volume
376-
with catch_warn_reset(modules=[parrec], record=True) as wlist:
376+
with clear_and_catch_warnings(modules=[parrec], record=True) as wlist:
377377
hdr = PARRECHeader(gen_info, slice_info[:-1], permit_truncated=True)
378378
assert_equal(len(wlist), 1)
379379
assert_equal(hdr.get_data_shape(), (80, 80, 10))

0 commit comments

Comments
 (0)