diff --git a/README.txt b/README.txt index 97856eb5cacb..820e355c83ab 100644 --- a/README.txt +++ b/README.txt @@ -13,8 +13,9 @@ directory) with: python -c 'import numpy; numpy.test()' -Please note that you must have the 'nose' test framework installed in order to -run the tests. More information about nose is available here: +Please note that you must have version 0.10 or later of the 'nose' test +framework installed in order to run the tests. More information about nose is +available here: http://somethingaboutorange.com/mrl/projects/nose/ diff --git a/numpy/testing/__init__.py b/numpy/testing/__init__.py index fa448f2988cc..17ca80c377bd 100644 --- a/numpy/testing/__init__.py +++ b/numpy/testing/__init__.py @@ -10,14 +10,6 @@ import decorators as dec from utils import * - -try: - import nose - from nose.tools import raises -except ImportError: - pass - from numpytest import * - from pkgtester import Tester test = Tester().test diff --git a/numpy/testing/decorators.py b/numpy/testing/decorators.py index 6573c2a438af..f74f0e57388a 100644 --- a/numpy/testing/decorators.py +++ b/numpy/testing/decorators.py @@ -10,11 +10,6 @@ """ -try: - import nose -except ImportError: - pass - def slow(t): """Labels a test as 'slow'. @@ -76,6 +71,9 @@ def skipif(skip_condition, msg=None): if msg is None: msg = 'Test skipped due to test condition' def skip_decorator(f): + # Local import to avoid a hard nose dependency and only incur the + # import time overhead at actual test-time. + import nose def skipper(*args, **kwargs): if skip_condition: raise nose.SkipTest, msg @@ -87,6 +85,9 @@ def skipper(*args, **kwargs): def skipknownfailure(f): ''' Decorator to raise SkipTest for test known to fail ''' + # Local import to avoid a hard nose dependency and only incur the + # import time overhead at actual test-time. + import nose def skipper(*args, **kwargs): raise nose.SkipTest, 'This test is known to fail' return nose.tools.make_decorator(f)(skipper) diff --git a/numpy/testing/nosetester.py b/numpy/testing/nosetester.py index e0a0aad3fb0e..d35890446786 100644 --- a/numpy/testing/nosetester.py +++ b/numpy/testing/nosetester.py @@ -7,7 +7,25 @@ import sys import re -import nose +def import_nose(): + """ Import nose only when needed. + """ + fine_nose = True + try: + import nose + from nose.tools import raises + except ImportError: + fine_nose = False + else: + nose_version = nose.__versioninfo__ + if nose_version[0] < 1 and nose_version[1] < 10: + fine_nose = False + + if not fine_nose: + raise ImportError('Need nose >=0.10 for tests - see ' + 'http://somethingaboutorange.com/mrl/projects/nose') + + return nose class NoseTester(object): """ Nose test runner. @@ -113,6 +131,7 @@ def test(self, label='fast', verbose=1, extra_argv=None, doctests=False, doctests : boolean If True, run doctests in module, default False ''' + nose = import_nose() argv = self._test_argv(label, verbose, extra_argv) if doctests: argv+=['--with-doctest','--doctest-tests'] @@ -135,6 +154,7 @@ def bench(self, label='fast', verbose=1, extra_argv=None): ''' Run benchmarks for module using nose %(test_header)s''' + nose = import_nose() argv = self._test_argv(label, verbose, extra_argv) argv += ['--match', r'(?:^|[\\b_\\.%s-])[Bb]ench' % os.sep] nose.run(argv=argv) diff --git a/numpy/testing/pkgtester.py b/numpy/testing/pkgtester.py index 8b22955faf17..51e8d62c4115 100644 --- a/numpy/testing/pkgtester.py +++ b/numpy/testing/pkgtester.py @@ -11,17 +11,4 @@ See nosetester module for test implementation ''' -fine_nose = True -try: - import nose -except ImportError: - fine_nose = False -else: - nose_version = nose.__versioninfo__ - if nose_version[0] < 1 and nose_version[1] < 10: - fine_nose = False - -if fine_nose: - from numpy.testing.nosetester import NoseTester as Tester -else: - from numpy.testing.nulltester import NullTester as Tester +from numpy.testing.nosetester import NoseTester as Tester diff --git a/numpy/testing/utils.py b/numpy/testing/utils.py index 538014f33e62..3e99fd386b73 100644 --- a/numpy/testing/utils.py +++ b/numpy/testing/utils.py @@ -11,7 +11,7 @@ __all__ = ['assert_equal', 'assert_almost_equal','assert_approx_equal', 'assert_array_equal', 'assert_array_less', 'assert_string_equal', 'assert_array_almost_equal', 'build_err_msg', 'jiffies', 'memusage', - 'rand', 'rundocs', 'runstring'] + 'raises', 'rand', 'rundocs', 'runstring'] def rand(*args): """Returns an array of random numbers with the given shape. @@ -317,3 +317,34 @@ def rundocs(filename=None): for test in tests: runner.run(test) return + + +def raises(*exceptions): + """ Assert that a test function raises one of the specified exceptions to + pass. + """ + # FIXME: when we transition to nose, just use its implementation. It's + # better. + def deco(function): + def f2(*args, **kwds): + try: + function(*args, **kwds) + except exceptions: + pass + except: + # Anything else. + raise + else: + raise AssertionError('%s() did not raise one of (%s)' % + (function.__name__, ', '.join([e.__name__ for e in exceptions]))) + try: + f2.__name__ = function.__name__ + except TypeError: + # Python 2.3 does not permit this. + pass + f2.__dict__ = function.__dict__ + f2.__doc__ = function.__doc__ + f2.__module__ = function.__module__ + return f2 + + return deco