10000 Rebase 7763, ENH: Add new warning suppression/filtering context by charris · Pull Request #7985 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

Rebase 7763, ENH: Add new warning suppression/filtering context #7985

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 28, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
ENH,TST: Add new warning suppression/filtering context
This context has a couple of advantages over the typical one, and can
ensure that warnings are cleanly filtered without side effect for
later tests.
  • Loading branch information
seberg authored and charris committed Aug 28, 2016
commit 3c34457ac9e6c11f9197f513013d8ab1ee9d9236
17 changes: 17 additions & 0 deletions doc/release/1.12.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,23 @@ to ``logspace``, but with start and stop specified directly:
``geomspace(start, stop)`` behaves the same as
``logspace(log10(start), log10(stop))``.

New context manager for testing warnings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A new context manager ``suppress_warnings`` has been added to the testing
utils. This context manager is designed to help reliably test warnings.
Specifically to reliably filter/ignore warnings. Ignoring warnings
by using an "ignore" filter in Python versions before 3.4.x can quickly
result in these (or similar) warnings not being tested reliably.

The context manager allows to filter (as well as record) warnings similar
to the ``catch_warnings`` context, but allows for easier specificity.
Also printing warnings that have not been filtered or nesting the
context manager will work as expected. Additionally, it is possible
to use the context manager as a decorator which can be useful when
multiple tests give need to hide the same warning.


Improvements
============

Expand Down
23 changes: 2 additions & 21 deletions numpy/testing/nosetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from numpy.compat import basestring
import numpy as np

from .utils import import_nose, suppress_warnings


def get_package_name(filepath):
"""
Expand Down Expand Up @@ -53,27 +55,6 @@ def get_package_name(filepath):

return '.'.join(pkg_name)

def import_nose():
""" Import nose only when needed.
"""
fine_nose = True
minimum_nose_version = (1, 0, 0)
try:
import nose
except ImportError:
fine_nose = False
else:
if nose.__versioninfo__ < minimum_nose_version:
fine_nose = False

if not fine_nose:
msg = ('Need nose >= %d.%d.%d for tests - see '
'http://somethingaboutorange.com/mrl/projects/nose' %
minimum_nose_version)
raise ImportError(msg)

return nose

def run_module_suite(file_to_run=None, argv=None):
"""
Run a test module.
Expand Down
186 changes: 182 additions & 4 deletions numpy/testing/tests/test_utils.py
8000
8000
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
assert_array_almost_equal, build_err_msg, raises, assert_raises,
assert_warns, assert_no_warnings, assert_allclose, assert_approx_equal,
assert_array_almost_equal_nulp, assert_array_max_ulp,
clear_and_catch_warnings, run_module_suite,
clear_and_catch_warnings, suppress_warnings, run_module_suite,
assert_string_equal, assert_, tempdir, temppath,
)
import unittest
Expand Down Expand Up @@ -795,14 +795,16 @@ def test_simple(self):
lambda: assert_string_equal("foo", "hello"))


def assert_warn_len_equal(mod, n_in_context):
def assert_warn_len_equal(mod, n_in_context, py3_n_in_context=None):
mod_warns = mod.__warningregistry__
# Python 3.4 appears to clear any pre-existing warnings of the same type,
# when raising warnings inside a catch_warnings block. So, there is a
# warning generated by the tests within the context manager, but no
# previous warnings.
if 'version' in mod_warns:
assert_equal(len(mod_warns), 2) # including 'version'
if py3_n_in_context is None:
py3_n_in_context = n_in_context
assert_equal(len(mod_warns)-1, py3_n_in_context)
else:
assert_equal(len(mod_warns), n_in_context)

Expand Down Expand Up @@ -840,7 +842,183 @@ def test_clear_and_catch_warnings():
with clear_and_catch_warnings():
warnings.simplefilter('ignore')
warnings.warn('Another warning')
assert_warn_len_equal(my_mod, 2)
assert_warn_len_equal(my_mod, 2, 1)


def test_suppress_warnings_module():
# Initial state of module, no warnings
my_mod = _get_fresh_mod()
assert_equal(getattr(my_mod, '__warningregistry__', {}), {})

def warn_other_module():
# Apply along axis is implemented in python; stacklevel=2 means
# we end up inside its module, not ours.
def warn(arr):
warnings.warn("Some warning 2", stacklevel=2)
return arr
np.apply_along_axis(warn, 0, [0])

# Test module based warning suppression:
with suppress_warnings() as sup:
sup.record(UserWarning)
# suppress warning from other module (may have .pyc ending),
# if apply_along_axis is moved, had to be changed.
sup.filter(module=np.lib.shape_base)
warnings.warn("Some warning")
warn_other_module()
# Check that the suppression did test the file correctly (this module
# got filtered)
assert_(len(sup.log) == 1)
assert_(sup.log[0].message.args[0] == "Some warning")

assert_warn_len_equal(my_mod, 0)
sup = suppress_warnings()
# Will have to be changed if apply_along_axis is moved:
sup.filter(module=my_mod)
with sup:
warnings.warn('Some warning')
assert_warn_len_equal(my_mod, 0)
# And test repeat works:
sup.filter(module=my_mod)
with sup:
warnings.warn('Some warning')
assert_warn_len_equal(my_mod, 0)

# Without specified modules, don't clear warnings during context
with suppress_warnings():
warnings.simplefilter('ignore')
warnings.warn('Some warning')
assert_warn_len_equal(my_mod, 1)


def test_suppress_warnings_type():
# Initial state of module, no warnings
my_mod = _get_fresh_mod()
assert_equal(getattr(my_mod, '__warningregistry__', {}), {})

# Test module based warning suppression:
with suppress_warnings() as sup:
sup.filter(UserWarning)
warnings.warn('Some warning')
assert_warn_len_equal(my_mod, 0)
sup = suppress_warnings()
sup.filter(UserWarning)
with sup:
warnings.warn('Some warning')
assert_warn_len_equal(my_mod, 0)
# And test repeat works:
sup.filter(module=my_mod)
with sup:
warnings.warn('Some warning')
assert_warn_len_equal(my_mod, 0)

# Without specified modules, don't clear warnings during context
with suppress_warnings():
warnings.simplefilter('ignore')
warnings.warn('Some warning')
assert_warn_len_equal(my_mod, 1)


def test_suppress_warnings_decorate_no_record():
sup = suppress_warnings()
sup.filter(UserWarning)

@sup
def warn(category):
warnings.warn('Some warning', category)

with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
warn(UserWarning) # should be supppressed
warn(RuntimeWarning)
assert_(len(w) == 1)


def test_suppress_warnings_record():
sup = suppress_warnings()
log1 = sup.record()

with sup:
log2 = sup.record(message='Some other warning 2')
sup.filter(message='Some warning')
warnings.warn('Some warning')
warnings.warn('Some other warning')
warnings.warn('Some other warning 2')

assert_(len(sup.log) == 2)
assert_(len(log1) == 1)
assert_(len(log2) == 1)
assert_(log2[0].message.args[0] == 'Some other warning 2')

# Do it again, with the same context to see if some warnings survived:
with sup:
log2 = sup.record(message='Some other warning 2')
sup.filter(message='Some warning')
warnings.warn('Some warning')
warnings.warn('Some other warning')
warnings.warn('Some other warning 2')

assert_(len(sup.log) == 2)
assert_(len(log1) == 1)
assert_(len(log2) == 1)
assert_(log2[0].message.args[0] == 'Some other warning 2')

# Test nested:
with suppress_warnings() as sup:
sup.record()
with suppress_warnings() as sup2:
sup2.record(message='Some warning')
warnings.warn('Some warning')
warnings.warn('Some other warning')
assert_(len(sup2.log) == 1)
assert_(len(sup.log) == 1)


def test_suppress_warnings_forwarding():
def warn_other_module():
# Apply along axis is implemented in python; stacklevel=2 means
# we end up inside its module, not ours.
def warn(arr):
warnings.warn("Some warning", stacklevel=2)
return arr
np.apply_along_axis(warn, 0, [0])

with suppress_warnings() as sup:
sup.record()
with suppress_warnings("always"):
for i in range(2):
warnings.warn("Some warning")

assert_(len(sup.log) == 2)

with suppress_warnings() as sup:
sup.record()
with suppress_warnings("location"):
for i in range(2):
warnings.warn("Some warning")
warnings.warn("Some warning")

assert_(len(sup.log) == 2)

with suppress_warnings() as sup:
sup.record()
with suppress_warnings("module"):
for i in range(2):
warnings.warn("Some warning")
warnings.warn("Some warning")
warn_other_module()

assert_(len(sup.log) == 2)

with suppress_warnings() as sup:
sup.record()
with suppress_warnings("once"):
for i in range(2):
warnings.warn("Some warning")
warnings.warn("Some other warning")
warn_other_module()

assert_(len(sup.log) == 2)


def test_tempdir():
Expand Down
Loading
0