|
7 | 7 | #
|
8 | 8 | ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
|
9 | 9 | ''' Utilities for testing '''
|
| 10 | +from __future__ import division, print_function |
| 11 | + |
| 12 | +import sys |
| 13 | +import warnings |
10 | 14 | from os.path import dirname, abspath, join as pjoin
|
11 | 15 |
|
12 | 16 | 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')) |
17 | 17 |
|
18 | 18 | # Allow failed import of nose if not now running tests
|
19 | 19 | try:
|
20 |
| - import nose.tools as nt |
21 |
| -except ImportError: |
22 |
| - pass |
23 |
| -else: |
24 | 20 | from nose.tools import (assert_equal, assert_not_equal,
|
25 | 21 | assert_true, assert_false, assert_raises)
|
| 22 | +except ImportError: |
| 23 | + pass |
| 24 | + |
| 25 | +# set path to example data |
| 26 | +data_path = abspath(pjoin(dirname(__file__), '..', 'tests', 'data')) |
26 | 27 |
|
27 | 28 |
|
28 | 29 | def assert_dt_equal(a, b):
|
@@ -56,35 +57,110 @@ def assert_allclose_safely(a, b, match_nans=True):
|
56 | 57 | assert_true(np.allclose(a, b))
|
57 | 58 |
|
58 | 59 |
|
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 |
| 60 | +def get_fresh_mod(mod_name=__name__): |
| 61 | + # Get this module, with warning registry empty |
| 62 | + my_mod = sys.modules[mod_name] |
| 63 | + try: |
| 64 | + my_mod.__warningregistry__.clear() |
| 65 | + except AttributeError: |
| 66 | + pass |
| 67 | + return my_mod |
| 68 | + |
| 69 | + |
| 70 | +class clear_and_catch_warnings(warnings.catch_warnings): |
| 71 | + """ Context manager that resets warning registry for catching warnings |
| 72 | +
|
| 73 | + Warnings can be slippery, because, whenever a warning is triggered, Python |
| 74 | + adds a ``__warningregistry__`` member to the *calling* module. This makes |
| 75 | + it impossible to retrigger the warning in this module, whatever you put in |
| 76 | + the warnings filters. This context manager accepts a sequence of `modules` |
| 77 | + as a keyword argument to its constructor and: |
| 78 | +
|
| 79 | + * stores and removes any ``__warningregistry__`` entries in given `modules` |
| 80 | + on entry; |
| 81 | + * resets ``__warningregistry__`` to its previous state on exit. |
| 82 | +
|
| 83 | + This makes it possible to trigger any warning afresh inside the context |
| 84 | + manager without disturbing the state of warnings outside. |
66 | 85 |
|
| 86 | + For compatibility with Python 3.0, please consider all arguments to be |
| 87 | + keyword-only. |
67 | 88 |
|
68 |
| -class catch_warn_reset(catch_warnings): |
69 |
| - """ Version of ``catch_warnings`` class that resets warning registry |
| 89 | + Parameters |
| 90 | + ---------- |
| 91 | + record : bool, optional |
| 92 | + Specifies whether warnings should be captured by a custom |
| 93 | + implementation of ``warnings.showwarning()`` and be appended to a list |
| 94 | + returned by the context manager. Otherwise None is returned by the |
| 95 | + context manager. The objects appended to the list are arguments whose |
| 96 | + attributes mirror the arguments to ``showwarning()``. |
| 97 | +
|
| 98 | + NOTE: nibabel difference from numpy: default is True |
| 99 | +
|
| 100 | + modules : sequence, optional |
| 101 | + Sequence of modules for which to reset warnings registry on entry and |
| 102 | + restore on exit |
| 103 | +
|
| 104 | + Examples |
| 105 | + -------- |
| 106 | + >>> import warnings |
| 107 | + >>> with clear_and_catch_warnings(modules=[np.core.fromnumeric]): |
| 108 | + ... warnings.simplefilter('always') |
| 109 | + ... # do something that raises a warning in np.core.fromnumeric |
70 | 110 | """
|
71 |
| - def __init__(self, *args, **kwargs): |
72 |
| - self.modules = kwargs.pop('modules', []) |
| 111 | + class_modules = () |
| 112 | + |
| 113 | + def __init__(self, record=True, modules=()): |
| 114 | + self.modules = set(modules).union(self.class_modules) |
73 | 115 | self._warnreg_copies = {}
|
74 |
| - super(catch_warn_reset, self).__init__(*args, **kwargs) |
| 116 | + super(clear_and_catch_warnings, self).__init__(record=record) |
75 | 117 |
|
76 | 118 | def __enter__(self):
|
77 | 119 | for mod in self.modules:
|
78 | 120 | if hasattr(mod, '__warningregistry__'):
|
79 | 121 | mod_reg = mod.__warningregistry__
|
80 | 122 | self._warnreg_copies[mod] = mod_reg.copy()
|
81 | 123 | mod_reg.clear()
|
82 |
| - return super(catch_warn_reset, self).__enter__() |
| 124 | + return super(clear_and_catch_warnings, self).__enter__() |
83 | 125 |
|
84 | 126 | def __exit__(self, *exc_info):
|
85 |
| - super(catch_warn_reset, self).__exit__(*exc_info) |
| 127 | + super(clear_and_catch_warnings, self).__exit__(*exc_info) |
86 | 128 | for mod in self.modules:
|
87 | 129 | if hasattr(mod, '__warningregistry__'):
|
88 | 130 | mod.__warningregistry__.clear()
|
89 | 131 | if mod in self._warnreg_copies:
|
90 | 132 | mod.__warningregistry__.update(self._warnreg_copies[mod])
|
| 133 | + |
| 134 | + |
| 135 | +class error_warnings(clear_and_catch_warnings): |
| 136 | + """ Context manager to check for warnings as errors. Usually used with |
| 137 | + ``assert_raises`` in the with block |
| 138 | +
|
| 139 | + Examples |
| 140 | + -------- |
| 141 | + >>> with error_warnings(): |
| 142 | + ... try: |
| 143 | + ... warnings.warn('Message', UserWarning) |
| 144 | + ... except UserWarning: |
| 145 | + ... print('I consider myself warned') |
| 146 | + I consider myself warned |
| 147 | + """ |
| 148 | + filter = 'error' |
| 149 | + |
| 150 | + def __enter__(self): |
| 151 | + mgr = super(error_warnings, self).__enter__() |
| 152 | + warnings.simplefilter(self.filter) |
| 153 | + return mgr |
| 154 | + |
| 155 | + |
| 156 | +class suppress_warnings(error_warnings): |
| 157 | + """ Version of ``catch_warnings`` class that suppresses warnings |
| 158 | + """ |
| 159 | + filter = 'ignore' |
| 160 | + |
| 161 | + |
| 162 | +class catch_warn_reset(clear_and_catch_warnings): |
| 163 | + def __init__(self, *args, **kwargs): |
| 164 | + warnings.warn('catch_warn_reset is deprecated and will be removed in ' |
| 165 | + 'nibabel v3.0; use nibabel.testing.clear_and_catch_warnings.', |
| 166 | + FutureWarning) |
0 commit comments