From 70c5052a9f07025c236033cf629506bb38eb6d97 Mon Sep 17 00:00:00 2001 From: Evgeni Burovski Date: Sun, 15 Nov 2015 07:48:20 +0000 Subject: [PATCH] ENH: testing: add SkipTest and KnownFailureException * use SkipTest in numpy tests instead of importing it from nose * add a KnownFailureException as an alias for KnownFailureTest (the former is preferred, but the latter is kept for backcompat) * rename the KnownFailure nose plugin into KnownFailurePlugin, and keep the old name for backcompat --- doc/release/1.11.0-notes.rst | 4 ++++ numpy/core/tests/test_multiarray.py | 3 +-- numpy/core/tests/test_print.py | 8 ++++---- numpy/f2py/tests/test_array_from_pyobj.py | 6 ++---- numpy/f2py/tests/util.py | 9 ++++----- numpy/lib/tests/test__datasource.py | 8 +++----- numpy/lib/tests/test_format.py | 3 +-- numpy/linalg/tests/test_linalg.py | 3 +-- numpy/testing/decorators.py | 19 ++++++++++--------- numpy/testing/noseclasses.py | 19 +++++++++---------- numpy/testing/nosetester.py | 8 ++++---- numpy/testing/tests/test_decorators.py | 20 ++++++++++---------- numpy/testing/utils.py | 21 +++++++++++++++++++-- 13 files changed, 72 insertions(+), 59 deletions(-) diff --git a/doc/release/1.11.0-notes.rst b/doc/release/1.11.0-notes.rst index f8d3d4dbfe31..580c0c952b74 100644 --- a/doc/release/1.11.0-notes.rst +++ b/doc/release/1.11.0-notes.rst @@ -71,6 +71,10 @@ via ``python runtests.py --bench``. For more details, see ``benchmarks/README.rs arrays have memory overlap is added. ``np.may_share_memory`` also now has an option to spend more effort to reduce false positives. +* ``SkipTest`` and ``KnownFailureException`` exception classes are exposed in the +``numpy.testing`` namespace. Raise them in a test function to mark the test to +be skipped or mark it as a known failure, respectively. + Improvements ============ diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 15dd9302cc8d..693847273ff5 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -17,7 +17,6 @@ import numpy as np -from nose import SkipTest from numpy.compat import asbytes, getexception, strchar, unicode, sixu from test_print import in_foreign_locale from numpy.core.multiarray_tests import ( @@ -29,7 +28,7 @@ TestCase, run_module_suite, assert_, assert_raises, assert_equal, assert_almost_equal, assert_array_equal, assert_array_almost_equal, assert_allclose, - assert_array_less, runstring, dec + assert_array_less, runstring, dec, SkipTest ) # Need to test an object that does not fully implement math interface diff --git a/numpy/core/tests/test_print.py b/numpy/core/tests/test_print.py index f595cbe44bfd..6234b641ea8c 100644 --- a/numpy/core/tests/test_print.py +++ b/numpy/core/tests/test_print.py @@ -6,7 +6,7 @@ import numpy as np from numpy.testing import ( - run_module_suite, assert_, assert_equal + run_module_suite, assert_, assert_equal, SkipTest ) @@ -207,7 +207,7 @@ def test_scalar_format(): def in_foreign_locale(func): """ Swap LC_NUMERIC locale to one in which the decimal point is ',' and not '.' - If not possible, raise nose.SkipTest + If not possible, raise SkipTest """ if sys.platform == 'win32': @@ -225,8 +225,8 @@ def wrapper(*args, **kwargs): except locale.Error: pass else: - raise nose.SkipTest("Skipping locale test, because " - "French locale not found") + raise SkipTest("Skipping locale test, because " + "French locale not found") return func(*args, **kwargs) finally: locale.setlocale(locale.LC_NUMERIC, locale=curloc) diff --git a/numpy/f2py/tests/test_array_from_pyobj.py b/numpy/f2py/tests/test_array_from_pyobj.py index 9551c099ed3c..48bb7c0f4d93 100644 --- a/numpy/f2py/tests/test_array_from_pyobj.py +++ b/numpy/f2py/tests/test_array_from_pyobj.py @@ -5,13 +5,11 @@ import sys import copy -import nose - from numpy import ( array, alltrue, ndarray, zeros, dtype, intp, clongdouble ) from numpy.testing import ( - run_module_suite, assert_, assert_equal + run_module_suite, assert_, assert_equal, SkipTest ) from numpy.core.multiarray import typeinfo import util @@ -28,7 +26,7 @@ def setup(): # Check compiler availability first if not util.has_c_compiler(): - raise nose.SkipTest("No C compiler available") + raise SkipTest("No C compiler available") if wrap is None: config_code = """ diff --git a/numpy/f2py/tests/util.py b/numpy/f2py/tests/util.py index 5b4e072e7ff9..8d06d96800ae 100644 --- a/numpy/f2py/tests/util.py +++ b/numpy/f2py/tests/util.py @@ -17,10 +17,9 @@ import re import random -import nose - from numpy.compat import asbytes, asstr import numpy.f2py +from numpy.testing import SkipTest try: from hashlib import md5 @@ -334,7 +333,7 @@ def setUp(self): # Check compiler availability first if not has_c_compiler(): - raise nose.SkipTest("No C compiler available") + raise SkipTest("No C compiler available") codes = [] if self.sources: @@ -350,9 +349,9 @@ def setUp(self): elif fn.endswith('.f90'): needs_f90 = True if needs_f77 and not has_f77_compiler(): - raise nose.SkipTest("No Fortran 77 compiler available") + raise SkipTest("No Fortran 77 compiler available") if needs_f90 and not has_f90_compiler(): - raise nose.SkipTest("No Fortran 90 compiler available") + raise SkipTest("No Fortran 90 compiler available") # Build the module if self.code is not None: diff --git a/numpy/lib/tests/test__datasource.py b/numpy/lib/tests/test__datasource.py index 090f71f670c9..f4bece352b76 100644 --- a/numpy/lib/tests/test__datasource.py +++ b/numpy/lib/tests/test__datasource.py @@ -7,7 +7,7 @@ from numpy.compat import asbytes from numpy.testing import ( - run_module_suite, TestCase, assert_ + run_module_suite, TestCase, assert_, SkipTest ) import numpy.lib._datasource as datasource @@ -137,8 +137,7 @@ def test_ValidGzipFile(self): import gzip except ImportError: # We don't have the gzip capabilities to test. - import nose - raise nose.SkipTest + raise SkipTest # Test datasource's internal file_opener for Gzip files. filepath = os.path.join(self.tmpdir, 'foobar.txt.gz') fp = gzip.open(filepath, 'w') @@ -154,8 +153,7 @@ def test_ValidBz2File(self): import bz2 except ImportError: # We don't have the bz2 capabilities to test. - import nose - raise nose.SkipTest + raise SkipTest # Test datasource's internal file_opener for BZip2 files. filepath = os.path.join(self.tmpdir, 'foobar.txt.bz2') fp = bz2.BZ2File(filepath, 'w') diff --git a/numpy/lib/tests/test_format.py b/numpy/lib/tests/test_format.py index 4f8a651489da..1bf65fa61d4f 100644 --- a/numpy/lib/tests/test_format.py +++ b/numpy/lib/tests/test_format.py @@ -287,7 +287,7 @@ from numpy.compat import asbytes, asbytes_nested, sixu from numpy.testing import ( run_module_suite, assert_, assert_array_equal, assert_raises, raises, - dec + dec, SkipTest ) from numpy.lib import format @@ -812,7 +812,6 @@ def test_bad_header(): def test_large_file_support(): - from nose import SkipTest if (sys.platform == 'win32' or sys.platform == 'cygwin'): raise SkipTest("Unknown if Windows has sparse filesystems") # try creating a large sparse file diff --git a/numpy/linalg/tests/test_linalg.py b/numpy/linalg/tests/test_linalg.py index 7c577d86fed2..afa098f12250 100644 --- a/numpy/linalg/tests/test_linalg.py +++ b/numpy/linalg/tests/test_linalg.py @@ -17,7 +17,7 @@ from numpy.testing import ( assert_, assert_equal, assert_raises, assert_array_equal, assert_almost_equal, assert_allclose, run_module_suite, - dec + dec, SkipTest ) @@ -1215,7 +1215,6 @@ def test_xerbla_override(): # Check that our xerbla has been successfully linked in. If it is not, # the default xerbla routine is called, which prints a message to stdout # and may, or may not, abort the process depending on the LAPACK package. - from nose import SkipTest XERBLA_OK = 255 diff --git a/numpy/testing/decorators.py b/numpy/testing/decorators.py index 56962b93c322..df3d297ff2fe 100644 --- a/numpy/testing/decorators.py +++ b/numpy/testing/decorators.py @@ -18,6 +18,7 @@ import warnings import collections +from .utils import SkipTest def slow(t): """ @@ -141,14 +142,14 @@ def get_msg(func,msg=None): def skipper_func(*args, **kwargs): """Skipper for normal test functions.""" if skip_val(): - raise nose.SkipTest(get_msg(f, msg)) + raise SkipTest(get_msg(f, msg)) else: return f(*args, **kwargs) def skipper_gen(*args, **kwargs): """Skipper for test generators.""" if skip_val(): - raise nose.SkipTest(get_msg(f, msg)) + raise SkipTest(get_msg(f, msg)) else: for x in f(*args, **kwargs): yield x @@ -166,7 +167,7 @@ def skipper_gen(*args, **kwargs): def knownfailureif(fail_condition, msg=None): """ - Make function raise KnownFailureTest exception if given condition is true. + Make function raise KnownFailureException exception if given condition is true. If the condition is a callable, it is used at runtime to dynamically make the decision. This is useful for tests that may require costly @@ -178,15 +179,15 @@ def knownfailureif(fail_condition, msg=None): Flag to determine whether to mark the decorated test as a known failure (if True) or not (if False). msg : str, optional - Message to give on raising a KnownFailureTest exception. + Message to give on raising a KnownFailureException exception. Default is None. Returns ------- decorator : function - Decorator, which, when applied to a function, causes SkipTest - to be raised when `skip_condition` is True, and the function - to be called normally otherwise. + Decorator, which, when applied to a function, causes + KnownFailureException to be raised when `fail_condition` is True, + and the function to be called normally otherwise. Notes ----- @@ -207,11 +208,11 @@ def knownfail_decorator(f): # Local import to avoid a hard nose dependency and only incur the # import time overhead at actual test-time. import nose - from .noseclasses import KnownFailureTest + from .noseclasses import KnownFailureException def knownfailer(*args, **kwargs): if fail_val(): - raise KnownFailureTest(msg) + raise KnownFailureException(msg) else: return f(*args, **kwargs) return nose.tools.make_decorator(f)(knownfailer) diff --git a/numpy/testing/noseclasses.py b/numpy/testing/noseclasses.py index e6cc10179125..197e20bacb50 100644 --- a/numpy/testing/noseclasses.py +++ b/numpy/testing/noseclasses.py @@ -8,6 +8,7 @@ import os import doctest +import inspect import nose from nose.plugins import doctests as npd @@ -16,7 +17,8 @@ from nose.util import src import numpy from .nosetester import get_package_name -import inspect +from .utils import KnownFailureException, KnownFailureTest + # Some of the classes in this module begin with 'Numpy' to clearly distinguish # them from the plethora of very similar names from nose/unittest/doctest @@ -298,19 +300,14 @@ def configure(self, options, config): if p.name != self.to_unplug] -class KnownFailureTest(Exception): - '''Raise this exception to mark a test as a known failing test.''' - pass - - -class KnownFailure(ErrorClassPlugin): +class KnownFailurePlugin(ErrorClassPlugin): '''Plugin that installs a KNOWNFAIL error class for the - KnownFailureClass exception. When KnownFailureTest is raised, + KnownFailureClass exception. When KnownFailure is raised, the exception will be logged in the knownfail attribute of the result, 'K' or 'KNOWNFAIL' (verbose) will be output, and the exception will not be counted as an error or failure.''' enabled = True - knownfail = ErrorClass(KnownFailureTest, + knownfail = ErrorClass(KnownFailureException, label='KNOWNFAIL', isfailure=False) @@ -318,7 +315,7 @@ def options(self, parser, env=os.environ): env_opt = 'NOSE_WITHOUT_KNOWNFAIL' parser.add_option('--no-knownfail', action='store_true', dest='noKnownFail', default=env.get(env_opt, False), - help='Disable special handling of KnownFailureTest ' + help='Disable special handling of KnownFailure ' 'exceptions') def configure(self, options, conf): @@ -329,6 +326,8 @@ def configure(self, options, conf): if disable: self.enabled = False +KnownFailure = KnownFailurePlugin # backwards compat + # Class allows us to save the results of the tests in runTests - see runTests # method docstring for details diff --git a/numpy/testing/nosetester.py b/numpy/testing/nosetester.py index c9c6d10f02b6..551e630ec2cd 100644 --- a/numpy/testing/nosetester.py +++ b/numpy/testing/nosetester.py @@ -121,8 +121,8 @@ def run_module_suite(file_to_run=None, argv=None): argv = argv + [file_to_run] nose = import_nose() - from .noseclasses import KnownFailure - nose.run(argv=argv, addplugins=[KnownFailure()]) + from .noseclasses import KnownFailurePlugin + nose.run(argv=argv, addplugins=[KnownFailurePlugin()]) class NoseTester(object): @@ -301,8 +301,8 @@ def prepare_test_args(self, label='fast', verbose=1, extra_argv=None, '--cover-tests', '--cover-erase'] # construct list of plugins import nose.plugins.builtin - from .noseclasses import KnownFailure, Unplugger - plugins = [KnownFailure()] + from .noseclasses import KnownFailurePlugin, Unplugger + plugins = [KnownFailurePlugin()] plugins += [p() for p in nose.plugins.builtin.plugins] # add doctesting if required doctest_argv = '--with-doctest' in argv diff --git a/numpy/testing/tests/test_decorators.py b/numpy/testing/tests/test_decorators.py index f8a5be672c07..7dbb5a8286e9 100644 --- a/numpy/testing/tests/test_decorators.py +++ b/numpy/testing/tests/test_decorators.py @@ -1,7 +1,7 @@ from __future__ import division, absolute_import, print_function -from numpy.testing import dec, assert_, assert_raises, run_module_suite -from numpy.testing.noseclasses import KnownFailureTest +from numpy.testing import (dec, assert_, assert_raises, run_module_suite, + SkipTest, KnownFailureException) import nose def test_slow(): @@ -40,7 +40,7 @@ def f1(x): f1('a') except DidntSkipException: raise Exception('Failed to skip') - except nose.SkipTest: + except SkipTest: pass @dec.skipif(False) @@ -51,7 +51,7 @@ def f2(x): f2('a') except DidntSkipException: pass - except nose.SkipTest: + except SkipTest: raise Exception('Skipped when not expected to') @@ -68,7 +68,7 @@ def f1(x): f1('a') except DidntSkipException: raise Exception('Failed to skip') - except nose.SkipTest: + except SkipTest: pass @dec.skipif(skip_tester) @@ -80,7 +80,7 @@ def f2(x): f2('a') except DidntSkipException: pass - except nose.SkipTest: + except SkipTest: raise Exception('Skipped when not expected to') @@ -93,7 +93,7 @@ def g1(x): try: for j in g1(10): pass - except KnownFailureTest: + except KnownFailureException: pass else: raise Exception('Failed to mark as known failure') @@ -107,7 +107,7 @@ def g2(x): try: for j in g2(10): pass - except KnownFailureTest: + except KnownFailureException: raise Exception('Marked incorretly as known failure') except DidntSkipException: pass @@ -126,7 +126,7 @@ def g1(x): skip_flag = 'skip me!' for j in g1(10): pass - except KnownFailureTest: + except KnownFailureException: pass else: raise Exception('Failed to mark as known failure') @@ -141,7 +141,7 @@ def g2(x): skip_flag = 'do not skip' for j in g2(10): pass - except KnownFailureTest: + except KnownFailureException: raise Exception('Marked incorretly as known failure') except DidntSkipException: pass diff --git a/numpy/testing/utils.py b/numpy/testing/utils.py index 8a282ff3c4f5..00f7ce4d1976 100644 --- a/numpy/testing/utils.py +++ b/numpy/testing/utils.py @@ -13,6 +13,7 @@ import shutil import contextlib from tempfile import mkdtemp + from .nosetester import import_nose from numpy.core import float32, empty, arange, array_repr, ndarray @@ -28,11 +29,27 @@ 'raises', 'rand', 'rundocs', 'runstring', 'verbose', 'measure', 'assert_', 'assert_array_almost_equal_nulp', 'assert_raises_regex', 'assert_array_max_ulp', 'assert_warns', 'assert_no_warnings', - 'assert_allclose', 'IgnoreException', 'clear_and_catch_warnings'] + 'assert_allclose', 'IgnoreException', 'clear_and_catch_warnings', + 'SkipTest', 'KnownFailureException'] -verbose = 0 +class KnownFailureException(Exception): + '''Raise this exception to mark a test as a known failing test.''' + pass + +KnownFailureTest = KnownFailureException # backwards compat + +# nose.SkipTest is unittest.case.SkipTest +# import it into the namespace, so that it's available as np.testing.SkipTest +try: + from unittest.case import SkipTest +except ImportError: + # on py2.6 unittest.case is not available. Ask nose for a replacement. + SkipTest = import_nose().SkipTest + + +verbose = 0 def assert_(val, msg=''): """