diff options
Diffstat (limited to 'numpy/testing')
-rw-r--r-- | numpy/testing/__init__.py | 10 | ||||
-rw-r--r-- | numpy/testing/__init__.pyi | 6 | ||||
-rw-r--r-- | numpy/testing/_private/decorators.py | 331 | ||||
-rw-r--r-- | numpy/testing/_private/noseclasses.py | 364 | ||||
-rw-r--r-- | numpy/testing/_private/nosetester.py | 545 | ||||
-rw-r--r-- | numpy/testing/_private/parameterized.py | 432 | ||||
-rw-r--r-- | numpy/testing/_private/utils.py | 210 | ||||
-rw-r--r-- | numpy/testing/overrides.py | 11 | ||||
-rw-r--r-- | numpy/testing/tests/test_doctesting.py | 57 | ||||
-rw-r--r-- | numpy/testing/tests/test_utils.py | 40 | ||||
-rw-r--r-- | numpy/testing/utils.py | 29 |
11 files changed, 77 insertions, 1958 deletions
diff --git a/numpy/testing/__init__.py b/numpy/testing/__init__.py index 087527e43..8a34221e4 100644 --- a/numpy/testing/__init__.py +++ b/numpy/testing/__init__.py @@ -10,12 +10,12 @@ from unittest import TestCase from . import _private from ._private.utils import * from ._private.utils import (_assert_valid_refcount, _gen_alignment_data) -from ._private import extbuild, decorators as dec -from ._private.nosetester import ( - run_module_suite, NoseTester as Tester - ) +from ._private import extbuild +from . import overrides -__all__ = _private.utils.__all__ + ['TestCase', 'run_module_suite'] +__all__ = ( + _private.utils.__all__ + ['TestCase', 'overrides'] +) from numpy._pytesttester import PytestTester test = PytestTester(__name__) diff --git a/numpy/testing/__init__.pyi b/numpy/testing/__init__.pyi index a981d6113..d65860ccb 100644 --- a/numpy/testing/__init__.pyi +++ b/numpy/testing/__init__.pyi @@ -18,7 +18,6 @@ from numpy.testing._private.utils import ( jiffies as jiffies, memusage as memusage, print_assert_equal as print_assert_equal, - raises as raises, rundocs as rundocs, runstring as runstring, verbose as verbose, @@ -49,8 +48,3 @@ from numpy.testing._private.utils import ( __all__: list[str] __path__: list[str] test: PytestTester - -def run_module_suite( - file_to_run: None | str = ..., - argv: None | list[str] = ..., -) -> None: ... diff --git a/numpy/testing/_private/decorators.py b/numpy/testing/_private/decorators.py deleted file mode 100644 index cb49d9a73..000000000 --- a/numpy/testing/_private/decorators.py +++ /dev/null @@ -1,331 +0,0 @@ -""" -Decorators for labeling and modifying behavior of test objects. - -Decorators that merely return a modified version of the original -function object are straightforward. Decorators that return a new -function object need to use -:: - - nose.tools.make_decorator(original_function)(decorator) - -in returning the decorator, in order to preserve meta-data such as -function name, setup and teardown functions and so on - see -``nose.tools`` for more information. - -""" -import collections.abc -import warnings - -from .utils import SkipTest, assert_warns, HAS_REFCOUNT - -__all__ = ['slow', 'setastest', 'skipif', 'knownfailureif', 'deprecated', - 'parametrize', '_needs_refcount',] - - -def slow(t): - """ - .. deprecated:: 1.21 - This decorator is retained for compatibility with the nose testing framework, which is being phased out. - Please use the nose2 or pytest frameworks instead. - - Label a test as 'slow'. - - The exact definition of a slow test is obviously both subjective and - hardware-dependent, but in general any individual test that requires more - than a second or two should be labeled as slow (the whole suite consists of - thousands of tests, so even a second is significant). - - Parameters - ---------- - t : callable - The test to label as slow. - - Returns - ------- - t : callable - The decorated test `t`. - - Examples - -------- - The `numpy.testing` module includes ``import decorators as dec``. - A test can be decorated as slow like this:: - - from numpy.testing import * - - @dec.slow - def test_big(self): - print('Big, slow test') - - """ - # Numpy 1.21, 2020-12-20 - warnings.warn('the np.testing.dec decorators are included for nose support, and are ' - 'deprecated since NumPy v1.21. Use the nose2 or pytest frameworks instead.', DeprecationWarning, stacklevel=2) - - t.slow = True - return t - -def setastest(tf=True): - """ - .. deprecated:: 1.21 - This decorator is retained for compatibility with the nose testing framework, which is being phased out. - Please use the nose2 or pytest frameworks instead. - - Signals to nose that this function is or is not a test. - - Parameters - ---------- - tf : bool - If True, specifies that the decorated callable is a test. - If False, specifies that the decorated callable is not a test. - Default is True. - - Notes - ----- - This decorator can't use the nose namespace, because it can be - called from a non-test module. See also ``istest`` and ``nottest`` in - ``nose.tools``. - - Examples - -------- - `setastest` can be used in the following way:: - - from numpy.testing import dec - - @dec.setastest(False) - def func_with_test_in_name(arg1, arg2): - pass - - """ - # Numpy 1.21, 2020-12-20 - warnings.warn('the np.testing.dec decorators are included for nose support, and are ' - 'deprecated since NumPy v1.21. Use the nose2 or pytest frameworks instead.', DeprecationWarning, stacklevel=2) - def set_test(t): - t.__test__ = tf - return t - return set_test - -def skipif(skip_condition, msg=None): - """ - .. deprecated:: 1.21 - This decorator is retained for compatibility with the nose testing framework, which is being phased out. - Please use the nose2 or pytest frameworks instead. - - Make function raise SkipTest exception if a 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 - imports, to delay the cost until the test suite is actually executed. - - Parameters - ---------- - skip_condition : bool or callable - Flag to determine whether to skip the decorated test. - msg : str, optional - Message to give on raising a SkipTest 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. - - Notes - ----- - The decorator itself is decorated with the ``nose.tools.make_decorator`` - function in order to transmit function name, and various other metadata. - - """ - - 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 - - # Numpy 1.21, 2020-12-20 - warnings.warn('the np.testing.dec decorators are included for nose support, and are ' - 'deprecated since NumPy v1.21. Use the nose2 or pytest frameworks instead.', DeprecationWarning, stacklevel=2) - - # Allow for both boolean or callable skip conditions. - if isinstance(skip_condition, collections.abc.Callable): - skip_val = lambda: skip_condition() - else: - skip_val = lambda: skip_condition - - def get_msg(func,msg=None): - """Skip message with information about function being skipped.""" - if msg is None: - out = 'Test skipped due to test condition' - else: - out = msg - - return f'Skipping test: {func.__name__}: {out}' - - # We need to define *two* skippers because Python doesn't allow both - # return with value and yield inside the same function. - def skipper_func(*args, **kwargs): - """Skipper for normal test functions.""" - if skip_val(): - raise SkipTest(get_msg(f, msg)) - else: - return f(*args, **kwargs) - - def skipper_gen(*args, **kwargs): - """Skipper for test generators.""" - if skip_val(): - raise SkipTest(get_msg(f, msg)) - else: - yield from f(*args, **kwargs) - - # Choose the right skipper to use when building the actual decorator. - if nose.util.isgenerator(f): - skipper = skipper_gen - else: - skipper = skipper_func - - return nose.tools.make_decorator(f)(skipper) - - return skip_decorator - - -def knownfailureif(fail_condition, msg=None): - """ - .. deprecated:: 1.21 - This decorator is retained for compatibility with the nose testing framework, which is being phased out. - Please use the nose2 or pytest frameworks instead. - - 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 - imports, to delay the cost until the test suite is actually executed. - - Parameters - ---------- - fail_condition : bool or callable - 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 KnownFailureException exception. - Default is None. - - Returns - ------- - decorator : function - 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 - ----- - The decorator itself is decorated with the ``nose.tools.make_decorator`` - function in order to transmit function name, and various other metadata. - - """ - # Numpy 1.21, 2020-12-20 - warnings.warn('the np.testing.dec decorators are included for nose support, and are ' - 'deprecated since NumPy v1.21. Use the nose2 or pytest frameworks instead.', DeprecationWarning, stacklevel=2) - - if msg is None: - msg = 'Test skipped due to known failure' - - # Allow for both boolean or callable known failure conditions. - if isinstance(fail_condition, collections.abc.Callable): - fail_val = lambda: fail_condition() - else: - fail_val = lambda: fail_condition - - 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 KnownFailureException - - def knownfailer(*args, **kwargs): - if fail_val(): - raise KnownFailureException(msg) - else: - return f(*args, **kwargs) - return nose.tools.make_decorator(f)(knownfailer) - - return knownfail_decorator - -def deprecated(conditional=True): - """ - .. deprecated:: 1.21 - This decorator is retained for compatibility with the nose testing framework, which is being phased out. - Please use the nose2 or pytest frameworks instead. - - Filter deprecation warnings while running the test suite. - - This decorator can be used to filter DeprecationWarning's, to avoid - printing them during the test suite run, while checking that the test - actually raises a DeprecationWarning. - - Parameters - ---------- - conditional : bool or callable, optional - Flag to determine whether to mark test as deprecated or not. If the - condition is a callable, it is used at runtime to dynamically make the - decision. Default is True. - - Returns - ------- - decorator : function - The `deprecated` decorator itself. - - Notes - ----- - .. versionadded:: 1.4.0 - - """ - def deprecate_decorator(f): - # Local import to avoid a hard nose dependency and only incur the - # import time overhead at actual test-time. - import nose - - # Numpy 1.21, 2020-12-20 - warnings.warn('the np.testing.dec decorators are included for nose support, and are ' - 'deprecated since NumPy v1.21. Use the nose2 or pytest frameworks instead.', DeprecationWarning, stacklevel=2) - - def _deprecated_imp(*args, **kwargs): - # Poor man's replacement for the with statement - with assert_warns(DeprecationWarning): - f(*args, **kwargs) - - if isinstance(conditional, collections.abc.Callable): - cond = conditional() - else: - cond = conditional - if cond: - return nose.tools.make_decorator(f)(_deprecated_imp) - else: - return f - return deprecate_decorator - - -def parametrize(vars, input): - """ - .. deprecated:: 1.21 - This decorator is retained for compatibility with the nose testing framework, which is being phased out. - Please use the nose2 or pytest frameworks instead. - - Pytest compatibility class. This implements the simplest level of - pytest.mark.parametrize for use in nose as an aid in making the transition - to pytest. It achieves that by adding a dummy var parameter and ignoring - the doc_func parameter of the base class. It does not support variable - substitution by name, nor does it support nesting or classes. See the - pytest documentation for usage. - - .. versionadded:: 1.14.0 - - """ - from .parameterized import parameterized - - # Numpy 1.21, 2020-12-20 - warnings.warn('the np.testing.dec decorators are included for nose support, and are ' - 'deprecated since NumPy v1.21. Use the nose2 or pytest frameworks instead.', DeprecationWarning, stacklevel=2) - - return parameterized(input) - -_needs_refcount = skipif(not HAS_REFCOUNT, "python has no sys.getrefcount") diff --git a/numpy/testing/_private/noseclasses.py b/numpy/testing/_private/noseclasses.py deleted file mode 100644 index 48fa4dc1f..000000000 --- a/numpy/testing/_private/noseclasses.py +++ /dev/null @@ -1,364 +0,0 @@ -# These classes implement a doctest runner plugin for nose, a "known failure" -# error class, and a customized TestProgram for NumPy. - -# Because this module imports nose directly, it should not -# be used except by nosetester.py to avoid a general NumPy -# dependency on nose. -import os -import sys -import doctest -import inspect - -import numpy -import nose -from nose.plugins import doctests as npd -from nose.plugins.errorclass import ErrorClass, ErrorClassPlugin -from nose.plugins.base import Plugin -from nose.util import src -from .nosetester import get_package_name -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 - -#----------------------------------------------------------------------------- -# Modified version of the one in the stdlib, that fixes a python bug (doctests -# not found in extension modules, https://bugs.python.org/issue3158) -class NumpyDocTestFinder(doctest.DocTestFinder): - - def _from_module(self, module, object): - """ - Return true if the given object is defined in the given - module. - """ - if module is None: - return True - elif inspect.isfunction(object): - return module.__dict__ is object.__globals__ - elif inspect.isbuiltin(object): - return module.__name__ == object.__module__ - elif inspect.isclass(object): - return module.__name__ == object.__module__ - elif inspect.ismethod(object): - # This one may be a bug in cython that fails to correctly set the - # __module__ attribute of methods, but since the same error is easy - # to make by extension code writers, having this safety in place - # isn't such a bad idea - return module.__name__ == object.__self__.__class__.__module__ - elif inspect.getmodule(object) is not None: - return module is inspect.getmodule(object) - elif hasattr(object, '__module__'): - return module.__name__ == object.__module__ - elif isinstance(object, property): - return True # [XX] no way not be sure. - else: - raise ValueError("object must be a class or function") - - def _find(self, tests, obj, name, module, source_lines, globs, seen): - """ - Find tests for the given object and any contained objects, and - add them to `tests`. - """ - - doctest.DocTestFinder._find(self, tests, obj, name, module, - source_lines, globs, seen) - - # Below we re-run pieces of the above method with manual modifications, - # because the original code is buggy and fails to correctly identify - # doctests in extension modules. - - # Local shorthands - from inspect import ( - isroutine, isclass, ismodule, isfunction, ismethod - ) - - # Look for tests in a module's contained objects. - if ismodule(obj) and self._recurse: - for valname, val in obj.__dict__.items(): - valname1 = f'{name}.{valname}' - if ( (isroutine(val) or isclass(val)) - and self._from_module(module, val)): - - self._find(tests, val, valname1, module, source_lines, - globs, seen) - - # Look for tests in a class's contained objects. - if isclass(obj) and self._recurse: - for valname, val in obj.__dict__.items(): - # Special handling for staticmethod/classmethod. - if isinstance(val, staticmethod): - val = getattr(obj, valname) - if isinstance(val, classmethod): - val = getattr(obj, valname).__func__ - - # Recurse to methods, properties, and nested classes. - if ((isfunction(val) or isclass(val) or - ismethod(val) or isinstance(val, property)) and - self._from_module(module, val)): - valname = f'{name}.{valname}' - self._find(tests, val, valname, module, source_lines, - globs, seen) - - -# second-chance checker; if the default comparison doesn't -# pass, then see if the expected output string contains flags that -# tell us to ignore the output -class NumpyOutputChecker(doctest.OutputChecker): - def check_output(self, want, got, optionflags): - ret = doctest.OutputChecker.check_output(self, want, got, - optionflags) - if not ret: - if "#random" in want: - return True - - # it would be useful to normalize endianness so that - # bigendian machines don't fail all the tests (and there are - # actually some bigendian examples in the doctests). Let's try - # making them all little endian - got = got.replace("'>", "'<") - want = want.replace("'>", "'<") - - # try to normalize out 32 and 64 bit default int sizes - for sz in [4, 8]: - got = got.replace("'<i%d'" % sz, "int") - want = want.replace("'<i%d'" % sz, "int") - - ret = doctest.OutputChecker.check_output(self, want, - got, optionflags) - - return ret - - -# Subclass nose.plugins.doctests.DocTestCase to work around a bug in -# its constructor that blocks non-default arguments from being passed -# down into doctest.DocTestCase -class NumpyDocTestCase(npd.DocTestCase): - def __init__(self, test, optionflags=0, setUp=None, tearDown=None, - checker=None, obj=None, result_var='_'): - self._result_var = result_var - self._nose_obj = obj - doctest.DocTestCase.__init__(self, test, - optionflags=optionflags, - setUp=setUp, tearDown=tearDown, - checker=checker) - - -print_state = numpy.get_printoptions() - -class NumpyDoctest(npd.Doctest): - name = 'numpydoctest' # call nosetests with --with-numpydoctest - score = 1000 # load late, after doctest builtin - - # always use whitespace and ellipsis options for doctests - doctest_optflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS - - # files that should be ignored for doctests - doctest_ignore = ['generate_numpy_api.py', - 'setup.py'] - - # Custom classes; class variables to allow subclassing - doctest_case_class = NumpyDocTestCase - out_check_class = NumpyOutputChecker - test_finder_class = NumpyDocTestFinder - - # Don't use the standard doctest option handler; hard-code the option values - def options(self, parser, env=os.environ): - Plugin.options(self, parser, env) - # Test doctests in 'test' files / directories. Standard plugin default - # is False - self.doctest_tests = True - # Variable name; if defined, doctest results stored in this variable in - # the top-level namespace. None is the standard default - self.doctest_result_var = None - - def configure(self, options, config): - # parent method sets enabled flag from command line --with-numpydoctest - Plugin.configure(self, options, config) - self.finder = self.test_finder_class() - self.parser = doctest.DocTestParser() - if self.enabled: - # Pull standard doctest out of plugin list; there's no reason to run - # both. In practice the Unplugger plugin above would cover us when - # run from a standard numpy.test() call; this is just in case - # someone wants to run our plugin outside the numpy.test() machinery - config.plugins.plugins = [p for p in config.plugins.plugins - if p.name != 'doctest'] - - def set_test_context(self, test): - """ Configure `test` object to set test context - - We set the numpy / scipy standard doctest namespace - - Parameters - ---------- - test : test object - with ``globs`` dictionary defining namespace - - Returns - ------- - None - - Notes - ----- - `test` object modified in place - """ - # set the namespace for tests - pkg_name = get_package_name(os.path.dirname(test.filename)) - - # Each doctest should execute in an environment equivalent to - # starting Python and executing "import numpy as np", and, - # for SciPy packages, an additional import of the local - # package (so that scipy.linalg.basic.py's doctests have an - # implicit "from scipy import linalg" as well). - # - # Note: __file__ allows the doctest in NoseTester to run - # without producing an error - test.globs = {'__builtins__':__builtins__, - '__file__':'__main__', - '__name__':'__main__', - 'np':numpy} - # add appropriate scipy import for SciPy tests - if 'scipy' in pkg_name: - p = pkg_name.split('.') - p2 = p[-1] - test.globs[p2] = __import__(pkg_name, test.globs, {}, [p2]) - - # Override test loading to customize test context (with set_test_context - # method), set standard docstring options, and install our own test output - # checker - def loadTestsFromModule(self, module): - if not self.matches(module.__name__): - npd.log.debug("Doctest doesn't want module %s", module) - return - try: - tests = self.finder.find(module) - except AttributeError: - # nose allows module.__test__ = False; doctest does not and - # throws AttributeError - return - if not tests: - return - tests.sort() - module_file = src(module.__file__) - for test in tests: - if not test.examples: - continue - if not test.filename: - test.filename = module_file - # Set test namespace; test altered in place - self.set_test_context(test) - yield self.doctest_case_class(test, - optionflags=self.doctest_optflags, - checker=self.out_check_class(), - result_var=self.doctest_result_var) - - # Add an afterContext method to nose.plugins.doctests.Doctest in order - # to restore print options to the original state after each doctest - def afterContext(self): - numpy.set_printoptions(**print_state) - - # Ignore NumPy-specific build files that shouldn't be searched for tests - def wantFile(self, file): - bn = os.path.basename(file) - if bn in self.doctest_ignore: - return False - return npd.Doctest.wantFile(self, file) - - -class Unplugger: - """ Nose plugin to remove named plugin late in loading - - By default it removes the "doctest" plugin. - """ - name = 'unplugger' - enabled = True # always enabled - score = 4000 # load late in order to be after builtins - - def __init__(self, to_unplug='doctest'): - self.to_unplug = to_unplug - - def options(self, parser, env): - pass - - def configure(self, options, config): - # Pull named plugin out of plugins list - config.plugins.plugins = [p for p in config.plugins.plugins - if p.name != self.to_unplug] - - -class KnownFailurePlugin(ErrorClassPlugin): - '''Plugin that installs a KNOWNFAIL error class for the - 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(KnownFailureException, - label='KNOWNFAIL', - isfailure=False) - - 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 KnownFailure ' - 'exceptions') - - def configure(self, options, conf): - if not self.can_configure: - return - self.conf = conf - disable = getattr(options, 'noKnownFail', False) - if disable: - self.enabled = False - -KnownFailure = KnownFailurePlugin # backwards compat - - -class FPUModeCheckPlugin(Plugin): - """ - Plugin that checks the FPU mode before and after each test, - raising failures if the test changed the mode. - """ - - def prepareTestCase(self, test): - from numpy.core._multiarray_tests import get_fpu_mode - - def run(result): - old_mode = get_fpu_mode() - test.test(result) - new_mode = get_fpu_mode() - - if old_mode != new_mode: - try: - raise AssertionError( - "FPU mode changed from {0:#x} to {1:#x} during the " - "test".format(old_mode, new_mode)) - except AssertionError: - result.addFailure(test, sys.exc_info()) - - return run - - -# Class allows us to save the results of the tests in runTests - see runTests -# method docstring for details -class NumpyTestProgram(nose.core.TestProgram): - def runTests(self): - """Run Tests. Returns true on success, false on failure, and - sets self.success to the same value. - - Because nose currently discards the test result object, but we need - to return it to the user, override TestProgram.runTests to retain - the result - """ - if self.testRunner is None: - self.testRunner = nose.core.TextTestRunner(stream=self.config.stream, - verbosity=self.config.verbosity, - config=self.config) - plug_runner = self.config.plugins.prepareTestRunner(self.testRunner) - if plug_runner is not None: - self.testRunner = plug_runner - self.result = self.testRunner.run(self.test) - self.success = self.result.wasSuccessful() - return self.success diff --git a/numpy/testing/_private/nosetester.py b/numpy/testing/_private/nosetester.py deleted file mode 100644 index bccec8236..000000000 --- a/numpy/testing/_private/nosetester.py +++ /dev/null @@ -1,545 +0,0 @@ -""" -Nose test running. - -This module implements ``test()`` and ``bench()`` functions for NumPy modules. - -""" -import os -import sys -import warnings -import numpy as np - -from .utils import import_nose, suppress_warnings - - -__all__ = ['get_package_name', 'run_module_suite', 'NoseTester', - '_numpy_tester', 'get_package_name', 'import_nose', - 'suppress_warnings'] - - -def get_package_name(filepath): - """ - Given a path where a package is installed, determine its name. - - Parameters - ---------- - filepath : str - Path to a file. If the determination fails, "numpy" is returned. - - Examples - -------- - >>> np.testing.nosetester.get_package_name('nonsense') - 'numpy' - - """ - - fullpath = filepath[:] - pkg_name = [] - while 'site-packages' in filepath or 'dist-packages' in filepath: - filepath, p2 = os.path.split(filepath) - if p2 in ('site-packages', 'dist-packages'): - break - pkg_name.append(p2) - - # if package name determination failed, just default to numpy/scipy - if not pkg_name: - if 'scipy' in fullpath: - return 'scipy' - else: - return 'numpy' - - # otherwise, reverse to get correct order and return - pkg_name.reverse() - - # don't include the outer egg directory - if pkg_name[0].endswith('.egg'): - pkg_name.pop(0) - - return '.'.join(pkg_name) - - -def run_module_suite(file_to_run=None, argv=None): - """ - Run a test module. - - Equivalent to calling ``$ nosetests <argv> <file_to_run>`` from - the command line - - Parameters - ---------- - file_to_run : str, optional - Path to test module, or None. - By default, run the module from which this function is called. - argv : list of strings - Arguments to be passed to the nose test runner. ``argv[0]`` is - ignored. All command line arguments accepted by ``nosetests`` - will work. If it is the default value None, sys.argv is used. - - .. versionadded:: 1.9.0 - - Examples - -------- - Adding the following:: - - if __name__ == "__main__" : - run_module_suite(argv=sys.argv) - - at the end of a test module will run the tests when that module is - called in the python interpreter. - - Alternatively, calling:: - - >>> run_module_suite(file_to_run="numpy/tests/test_matlib.py") # doctest: +SKIP - - from an interpreter will run all the test routine in 'test_matlib.py'. - """ - if file_to_run is None: - f = sys._getframe(1) - file_to_run = f.f_locals.get('__file__', None) - if file_to_run is None: - raise AssertionError - - if argv is None: - argv = sys.argv + [file_to_run] - else: - argv = argv + [file_to_run] - - nose = import_nose() - from .noseclasses import KnownFailurePlugin - nose.run(argv=argv, addplugins=[KnownFailurePlugin()]) - - -class NoseTester: - """ - Nose test runner. - - This class is made available as numpy.testing.Tester, and a test function - is typically added to a package's __init__.py like so:: - - from numpy.testing import Tester - test = Tester().test - - Calling this test function finds and runs all tests associated with the - package and all its sub-packages. - - Attributes - ---------- - package_path : str - Full path to the package to test. - package_name : str - Name of the package to test. - - Parameters - ---------- - package : module, str or None, optional - The package to test. If a string, this should be the full path to - the package. If None (default), `package` is set to the module from - which `NoseTester` is initialized. - raise_warnings : None, str or sequence of warnings, optional - This specifies which warnings to configure as 'raise' instead - of being shown once during the test execution. Valid strings are: - - - "develop" : equals ``(Warning,)`` - - "release" : equals ``()``, don't raise on any warnings. - - Default is "release". - depth : int, optional - If `package` is None, then this can be used to initialize from the - module of the caller of (the caller of (...)) the code that - initializes `NoseTester`. Default of 0 means the module of the - immediate caller; higher values are useful for utility routines that - want to initialize `NoseTester` objects on behalf of other code. - - """ - def __init__(self, package=None, raise_warnings="release", depth=0, - check_fpu_mode=False): - # Back-compat: 'None' used to mean either "release" or "develop" - # depending on whether this was a release or develop version of - # numpy. Those semantics were fine for testing numpy, but not so - # helpful for downstream projects like scipy that use - # numpy.testing. (They want to set this based on whether *they* are a - # release or develop version, not whether numpy is.) So we continue to - # accept 'None' for back-compat, but it's now just an alias for the - # default "release". - if raise_warnings is None: - raise_warnings = "release" - - package_name = None - if package is None: - f = sys._getframe(1 + depth) - package_path = f.f_locals.get('__file__', None) - if package_path is None: - raise AssertionError - package_path = os.path.dirname(package_path) - package_name = f.f_locals.get('__name__', None) - elif isinstance(package, type(os)): - package_path = os.path.dirname(package.__file__) - package_name = getattr(package, '__name__', None) - else: - package_path = str(package) - - self.package_path = package_path - - # Find the package name under test; this name is used to limit coverage - # reporting (if enabled). - if package_name is None: - package_name = get_package_name(package_path) - self.package_name = package_name - - # Set to "release" in constructor in maintenance branches. - self.raise_warnings = raise_warnings - - # Whether to check for FPU mode changes - self.check_fpu_mode = check_fpu_mode - - def _test_argv(self, label, verbose, extra_argv): - ''' Generate argv for nosetest command - - Parameters - ---------- - label : {'fast', 'full', '', attribute identifier}, optional - see ``test`` docstring - verbose : int, optional - Verbosity value for test outputs, in the range 1-10. Default is 1. - extra_argv : list, optional - List with any extra arguments to pass to nosetests. - - Returns - ------- - argv : list - command line arguments that will be passed to nose - ''' - argv = [__file__, self.package_path, '-s'] - if label and label != 'full': - if not isinstance(label, str): - raise TypeError('Selection label should be a string') - if label == 'fast': - label = 'not slow' - argv += ['-A', label] - argv += ['--verbosity', str(verbose)] - - # When installing with setuptools, and also in some other cases, the - # test_*.py files end up marked +x executable. Nose, by default, does - # not run files marked with +x as they might be scripts. However, in - # our case nose only looks for test_*.py files under the package - # directory, which should be safe. - argv += ['--exe'] - - if extra_argv: - argv += extra_argv - return argv - - def _show_system_info(self): - nose = import_nose() - - import numpy - print(f'NumPy version {numpy.__version__}') - relaxed_strides = numpy.ones((10, 1), order="C").flags.f_contiguous - print("NumPy relaxed strides checking option:", relaxed_strides) - npdir = os.path.dirname(numpy.__file__) - print(f'NumPy is installed in {npdir}') - - if 'scipy' in self.package_name: - import scipy - print(f'SciPy version {scipy.__version__}') - spdir = os.path.dirname(scipy.__file__) - print(f'SciPy is installed in {spdir}') - - pyversion = sys.version.replace('\n', '') - print(f'Python version {pyversion}') - print("nose version %d.%d.%d" % nose.__versioninfo__) - - def _get_custom_doctester(self): - """ Return instantiated plugin for doctests - - Allows subclassing of this class to override doctester - - A return value of None means use the nose builtin doctest plugin - """ - from .noseclasses import NumpyDoctest - return NumpyDoctest() - - def prepare_test_args(self, label='fast', verbose=1, extra_argv=None, - doctests=False, coverage=False, timer=False): - """ - Run tests for module using nose. - - This method does the heavy lifting for the `test` method. It takes all - the same arguments, for details see `test`. - - See Also - -------- - test - - """ - # fail with nice error message if nose is not present - import_nose() - # compile argv - argv = self._test_argv(label, verbose, extra_argv) - # our way of doing coverage - if coverage: - argv += [f'--cover-package={self.package_name}', '--with-coverage', - '--cover-tests', '--cover-erase'] - - if timer: - if timer is True: - argv += ['--with-timer'] - elif isinstance(timer, int): - argv += ['--with-timer', '--timer-top-n', str(timer)] - - # construct list of plugins - import nose.plugins.builtin - from nose.plugins import EntryPointPluginManager - from .noseclasses import (KnownFailurePlugin, Unplugger, - FPUModeCheckPlugin) - plugins = [KnownFailurePlugin()] - plugins += [p() for p in nose.plugins.builtin.plugins] - if self.check_fpu_mode: - plugins += [FPUModeCheckPlugin()] - argv += ["--with-fpumodecheckplugin"] - try: - # External plugins (like nose-timer) - entrypoint_manager = EntryPointPluginManager() - entrypoint_manager.loadPlugins() - plugins += [p for p in entrypoint_manager.plugins] - except ImportError: - # Relies on pkg_resources, not a hard dependency - pass - - # add doctesting if required - doctest_argv = '--with-doctest' in argv - if doctests == False and doctest_argv: - doctests = True - plug = self._get_custom_doctester() - if plug is None: - # use standard doctesting - if doctests and not doctest_argv: - argv += ['--with-doctest'] - else: # custom doctesting - if doctest_argv: # in fact the unplugger would take care of this - argv.remove('--with-doctest') - plugins += [Unplugger('doctest'), plug] - if doctests: - argv += ['--with-' + plug.name] - return argv, plugins - - def test(self, label='fast', verbose=1, extra_argv=None, - doctests=False, coverage=False, raise_warnings=None, - timer=False): - """ - Run tests for module using nose. - - Parameters - ---------- - label : {'fast', 'full', '', attribute identifier}, optional - Identifies the tests to run. This can be a string to pass to - the nosetests executable with the '-A' option, or one of several - special values. Special values are: - - * 'fast' - the default - which corresponds to the ``nosetests -A`` - option of 'not slow'. - * 'full' - fast (as above) and slow tests as in the - 'no -A' option to nosetests - this is the same as ''. - * None or '' - run all tests. - * attribute_identifier - string passed directly to nosetests as '-A'. - - verbose : int, optional - Verbosity value for test outputs, in the range 1-10. Default is 1. - extra_argv : list, optional - List with any extra arguments to pass to nosetests. - doctests : bool, optional - If True, run doctests in module. Default is False. - coverage : bool, optional - If True, report coverage of NumPy code. Default is False. - (This requires the - `coverage module <https://pypi.org/project/coverage/>`_). - raise_warnings : None, str or sequence of warnings, optional - This specifies which warnings to configure as 'raise' instead - of being shown once during the test execution. Valid strings are: - - * "develop" : equals ``(Warning,)`` - * "release" : equals ``()``, do not raise on any warnings. - timer : bool or int, optional - Timing of individual tests with ``nose-timer`` (which needs to be - installed). If True, time tests and report on all of them. - If an integer (say ``N``), report timing results for ``N`` slowest - tests. - - Returns - ------- - result : object - Returns the result of running the tests as a - ``nose.result.TextTestResult`` object. - - Notes - ----- - Each NumPy module exposes `test` in its namespace to run all tests for it. - For example, to run all tests for numpy.lib: - - >>> np.lib.test() #doctest: +SKIP - - Examples - -------- - >>> result = np.lib.test() #doctest: +SKIP - Running unit tests for numpy.lib - ... - Ran 976 tests in 3.933s - - OK - - >>> result.errors #doctest: +SKIP - [] - >>> result.knownfail #doctest: +SKIP - [] - """ - - # cap verbosity at 3 because nose becomes *very* verbose beyond that - verbose = min(verbose, 3) - - from . import utils - utils.verbose = verbose - - argv, plugins = self.prepare_test_args( - label, verbose, extra_argv, doctests, coverage, timer) - - if doctests: - print(f'Running unit tests and doctests for {self.package_name}') - else: - print(f'Running unit tests for {self.package_name}') - - self._show_system_info() - - # reset doctest state on every run - import doctest - doctest.master = None - - if raise_warnings is None: - raise_warnings = self.raise_warnings - - _warn_opts = dict(develop=(Warning,), - release=()) - if isinstance(raise_warnings, str): - raise_warnings = _warn_opts[raise_warnings] - - with suppress_warnings("location") as sup: - # Reset the warning filters to the default state, - # so that running the tests is more repeatable. - warnings.resetwarnings() - # Set all warnings to 'warn', this is because the default 'once' - # has the bad property of possibly shadowing later warnings. - warnings.filterwarnings('always') - # Force the requested warnings to raise - for warningtype in raise_warnings: - warnings.filterwarnings('error', category=warningtype) - # Filter out annoying import messages. - sup.filter(message='Not importing directory') - sup.filter(message="numpy.dtype size changed") - sup.filter(message="numpy.ufunc size changed") - sup.filter(category=np.ModuleDeprecationWarning) - # Filter out boolean '-' deprecation messages. This allows - # older versions of scipy to test without a flood of messages. - sup.filter(message=".*boolean negative.*") - sup.filter(message=".*boolean subtract.*") - # Filter out distutils cpu warnings (could be localized to - # distutils tests). ASV has problems with top level import, - # so fetch module for suppression here. - with warnings.catch_warnings(): - warnings.simplefilter("always") - from ...distutils import cpuinfo - sup.filter(category=UserWarning, module=cpuinfo) - # Filter out some deprecation warnings inside nose 1.3.7 when run - # on python 3.5b2. See - # https://github.com/nose-devs/nose/issues/929 - # Note: it is hard to filter based on module for sup (lineno could - # be implemented). - warnings.filterwarnings("ignore", message=".*getargspec.*", - category=DeprecationWarning, - module=r"nose\.") - - from .noseclasses import NumpyTestProgram - - t = NumpyTestProgram(argv=argv, exit=False, plugins=plugins) - - return t.result - - def bench(self, label='fast', verbose=1, extra_argv=None): - """ - Run benchmarks for module using nose. - - Parameters - ---------- - label : {'fast', 'full', '', attribute identifier}, optional - Identifies the benchmarks to run. This can be a string to pass to - the nosetests executable with the '-A' option, or one of several - special values. Special values are: - - * 'fast' - the default - which corresponds to the ``nosetests -A`` - option of 'not slow'. - * 'full' - fast (as above) and slow benchmarks as in the - 'no -A' option to nosetests - this is the same as ''. - * None or '' - run all tests. - * attribute_identifier - string passed directly to nosetests as '-A'. - - verbose : int, optional - Verbosity value for benchmark outputs, in the range 1-10. Default is 1. - extra_argv : list, optional - List with any extra arguments to pass to nosetests. - - Returns - ------- - success : bool - Returns True if running the benchmarks works, False if an error - occurred. - - Notes - ----- - Benchmarks are like tests, but have names starting with "bench" instead - of "test", and can be found under the "benchmarks" sub-directory of the - module. - - Each NumPy module exposes `bench` in its namespace to run all benchmarks - for it. - - Examples - -------- - >>> success = np.lib.bench() #doctest: +SKIP - Running benchmarks for numpy.lib - ... - using 562341 items: - unique: - 0.11 - unique1d: - 0.11 - ratio: 1.0 - nUnique: 56230 == 56230 - ... - OK - - >>> success #doctest: +SKIP - True - - """ - - print(f'Running benchmarks for {self.package_name}') - self._show_system_info() - - argv = self._test_argv(label, verbose, extra_argv) - argv += ['--match', r'(?:^|[\\b_\\.%s-])[Bb]ench' % os.sep] - - # import nose or make informative error - nose = import_nose() - - # get plugin to disable doctests - from .noseclasses import Unplugger - add_plugins = [Unplugger('doctest')] - - return nose.run(argv=argv, addplugins=add_plugins) - - -def _numpy_tester(): - if hasattr(np, "__version__") and ".dev0" in np.__version__: - mode = "develop" - else: - mode = "release" - return NoseTester(raise_warnings=mode, depth=1, - check_fpu_mode=True) diff --git a/numpy/testing/_private/parameterized.py b/numpy/testing/_private/parameterized.py deleted file mode 100644 index 3a29a1811..000000000 --- a/numpy/testing/_private/parameterized.py +++ /dev/null @@ -1,432 +0,0 @@ -""" -tl;dr: all code is licensed under simplified BSD, unless stated otherwise. - -Unless stated otherwise in the source files, all code is copyright 2010 David -Wolever <david@wolever.net>. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation are those -of the authors and should not be interpreted as representing official policies, -either expressed or implied, of David Wolever. - -""" -import re -import inspect -import warnings -from functools import wraps -from types import MethodType -from collections import namedtuple - -from unittest import TestCase - -_param = namedtuple("param", "args kwargs") - -class param(_param): - """ Represents a single parameter to a test case. - - For example:: - - >>> p = param("foo", bar=16) - >>> p - param("foo", bar=16) - >>> p.args - ('foo', ) - >>> p.kwargs - {'bar': 16} - - Intended to be used as an argument to ``@parameterized``:: - - @parameterized([ - param("foo", bar=16), - ]) - def test_stuff(foo, bar=16): - pass - """ - - def __new__(cls, *args , **kwargs): - return _param.__new__(cls, args, kwargs) - - @classmethod - def explicit(cls, args=None, kwargs=None): - """ Creates a ``param`` by explicitly specifying ``args`` and - ``kwargs``:: - - >>> param.explicit([1,2,3]) - param(*(1, 2, 3)) - >>> param.explicit(kwargs={"foo": 42}) - param(*(), **{"foo": "42"}) - """ - args = args or () - kwargs = kwargs or {} - return cls(*args, **kwargs) - - @classmethod - def from_decorator(cls, args): - """ Returns an instance of ``param()`` for ``@parameterized`` argument - ``args``:: - - >>> param.from_decorator((42, )) - param(args=(42, ), kwargs={}) - >>> param.from_decorator("foo") - param(args=("foo", ), kwargs={}) - """ - if isinstance(args, param): - return args - elif isinstance(args, (str,)): - args = (args, ) - try: - return cls(*args) - except TypeError as e: - if "after * must be" not in str(e): - raise - raise TypeError( - "Parameters must be tuples, but %r is not (hint: use '(%r, )')" - %(args, args), - ) - - def __repr__(self): - return "param(*%r, **%r)" %self - - -def parameterized_argument_value_pairs(func, p): - """Return tuples of parameterized arguments and their values. - - This is useful if you are writing your own doc_func - function and need to know the values for each parameter name:: - - >>> def func(a, foo=None, bar=42, **kwargs): pass - >>> p = param(1, foo=7, extra=99) - >>> parameterized_argument_value_pairs(func, p) - [("a", 1), ("foo", 7), ("bar", 42), ("**kwargs", {"extra": 99})] - - If the function's first argument is named ``self`` then it will be - ignored:: - - >>> def func(self, a): pass - >>> p = param(1) - >>> parameterized_argument_value_pairs(func, p) - [("a", 1)] - - Additionally, empty ``*args`` or ``**kwargs`` will be ignored:: - - >>> def func(foo, *args): pass - >>> p = param(1) - >>> parameterized_argument_value_pairs(func, p) - [("foo", 1)] - >>> p = param(1, 16) - >>> parameterized_argument_value_pairs(func, p) - [("foo", 1), ("*args", (16, ))] - """ - argspec = inspect.getargspec(func) - arg_offset = 1 if argspec.args[:1] == ["self"] else 0 - - named_args = argspec.args[arg_offset:] - - result = list(zip(named_args, p.args)) - named_args = argspec.args[len(result) + arg_offset:] - varargs = p.args[len(result):] - - result.extend([ - (name, p.kwargs.get(name, default)) - for (name, default) - in zip(named_args, argspec.defaults or []) - ]) - - seen_arg_names = {n for (n, _) in result} - keywords = dict(sorted([ - (name, p.kwargs[name]) - for name in p.kwargs - if name not in seen_arg_names - ])) - - if varargs: - result.append(("*%s" %(argspec.varargs, ), tuple(varargs))) - - if keywords: - result.append(("**%s" %(argspec.keywords, ), keywords)) - - return result - -def short_repr(x, n=64): - """ A shortened repr of ``x`` which is guaranteed to be ``unicode``:: - - >>> short_repr("foo") - u"foo" - >>> short_repr("123456789", n=4) - u"12...89" - """ - - x_repr = repr(x) - if isinstance(x_repr, bytes): - try: - x_repr = str(x_repr, "utf-8") - except UnicodeDecodeError: - x_repr = str(x_repr, "latin1") - if len(x_repr) > n: - x_repr = x_repr[:n//2] + "..." + x_repr[len(x_repr) - n//2:] - return x_repr - -def default_doc_func(func, num, p): - if func.__doc__ is None: - return None - - all_args_with_values = parameterized_argument_value_pairs(func, p) - - # Assumes that the function passed is a bound method. - descs = [f'{n}={short_repr(v)}' for n, v in all_args_with_values] - - # The documentation might be a multiline string, so split it - # and just work with the first string, ignoring the period - # at the end if there is one. - first, nl, rest = func.__doc__.lstrip().partition("\n") - suffix = "" - if first.endswith("."): - suffix = "." - first = first[:-1] - args = "%s[with %s]" %(len(first) and " " or "", ", ".join(descs)) - return "".join([first.rstrip(), args, suffix, nl, rest]) - -def default_name_func(func, num, p): - base_name = func.__name__ - name_suffix = "_%s" %(num, ) - if len(p.args) > 0 and isinstance(p.args[0], (str,)): - name_suffix += "_" + parameterized.to_safe_name(p.args[0]) - return base_name + name_suffix - - -# force nose for numpy purposes. -_test_runner_override = 'nose' -_test_runner_guess = False -_test_runners = set(["unittest", "unittest2", "nose", "nose2", "pytest"]) -_test_runner_aliases = { - "_pytest": "pytest", -} - -def set_test_runner(name): - global _test_runner_override - if name not in _test_runners: - raise TypeError( - "Invalid test runner: %r (must be one of: %s)" - %(name, ", ".join(_test_runners)), - ) - _test_runner_override = name - -def detect_runner(): - """ Guess which test runner we're using by traversing the stack and looking - for the first matching module. This *should* be reasonably safe, as - it's done during test discovery where the test runner should be the - stack frame immediately outside. """ - if _test_runner_override is not None: - return _test_runner_override - global _test_runner_guess - if _test_runner_guess is False: - stack = inspect.stack() - for record in reversed(stack): - frame = record[0] - module = frame.f_globals.get("__name__").partition(".")[0] - if module in _test_runner_aliases: - module = _test_runner_aliases[module] - if module in _test_runners: - _test_runner_guess = module - break - else: - _test_runner_guess = None - return _test_runner_guess - -class parameterized: - """ Parameterize a test case:: - - class TestInt: - @parameterized([ - ("A", 10), - ("F", 15), - param("10", 42, base=42) - ]) - def test_int(self, input, expected, base=16): - actual = int(input, base=base) - assert_equal(actual, expected) - - @parameterized([ - (2, 3, 5) - (3, 5, 8), - ]) - def test_add(a, b, expected): - assert_equal(a + b, expected) - """ - - def __init__(self, input, doc_func=None): - self.get_input = self.input_as_callable(input) - self.doc_func = doc_func or default_doc_func - - def __call__(self, test_func): - self.assert_not_in_testcase_subclass() - - @wraps(test_func) - def wrapper(test_self=None): - test_cls = test_self and type(test_self) - - original_doc = wrapper.__doc__ - for num, args in enumerate(wrapper.parameterized_input): - p = param.from_decorator(args) - unbound_func, nose_tuple = self.param_as_nose_tuple(test_self, test_func, num, p) - try: - wrapper.__doc__ = nose_tuple[0].__doc__ - # Nose uses `getattr(instance, test_func.__name__)` to get - # a method bound to the test instance (as opposed to a - # method bound to the instance of the class created when - # tests were being enumerated). Set a value here to make - # sure nose can get the correct test method. - if test_self is not None: - setattr(test_cls, test_func.__name__, unbound_func) - yield nose_tuple - finally: - if test_self is not None: - delattr(test_cls, test_func.__name__) - wrapper.__doc__ = original_doc - wrapper.parameterized_input = self.get_input() - wrapper.parameterized_func = test_func - test_func.__name__ = "_parameterized_original_%s" %(test_func.__name__, ) - return wrapper - - def param_as_nose_tuple(self, test_self, func, num, p): - nose_func = wraps(func)(lambda *args: func(*args[:-1], **args[-1])) - nose_func.__doc__ = self.doc_func(func, num, p) - # Track the unbound function because we need to setattr the unbound - # function onto the class for nose to work (see comments above), and - # Python 3 doesn't let us pull the function out of a bound method. - unbound_func = nose_func - if test_self is not None: - nose_func = MethodType(nose_func, test_self) - return unbound_func, (nose_func, ) + p.args + (p.kwargs or {}, ) - - def assert_not_in_testcase_subclass(self): - parent_classes = self._terrible_magic_get_defining_classes() - if any(issubclass(cls, TestCase) for cls in parent_classes): - raise Exception("Warning: '@parameterized' tests won't work " - "inside subclasses of 'TestCase' - use " - "'@parameterized.expand' instead.") - - def _terrible_magic_get_defining_classes(self): - """ Returns the list of parent classes of the class currently being defined. - Will likely only work if called from the ``parameterized`` decorator. - This function is entirely @brandon_rhodes's fault, as he suggested - the implementation: http://stackoverflow.com/a/8793684/71522 - """ - stack = inspect.stack() - if len(stack) <= 4: - return [] - frame = stack[4] - code_context = frame[4] and frame[4][0].strip() - if not (code_context and code_context.startswith("class ")): - return [] - _, _, parents = code_context.partition("(") - parents, _, _ = parents.partition(")") - return eval("[" + parents + "]", frame[0].f_globals, frame[0].f_locals) - - @classmethod - def input_as_callable(cls, input): - if callable(input): - return lambda: cls.check_input_values(input()) - input_values = cls.check_input_values(input) - return lambda: input_values - - @classmethod - def check_input_values(cls, input_values): - # Explicitly convert non-list inputs to a list so that: - # 1. A helpful exception will be raised if they aren't iterable, and - # 2. Generators are unwrapped exactly once (otherwise `nosetests - # --processes=n` has issues; see: - # https://github.com/wolever/nose-parameterized/pull/31) - if not isinstance(input_values, list): - input_values = list(input_values) - return [ param.from_decorator(p) for p in input_values ] - - @classmethod - def expand(cls, input, name_func=None, doc_func=None, **legacy): - """ A "brute force" method of parameterizing test cases. Creates new - test cases and injects them into the namespace that the wrapped - function is being defined in. Useful for parameterizing tests in - subclasses of 'UnitTest', where Nose test generators don't work. - - >>> @parameterized.expand([("foo", 1, 2)]) - ... def test_add1(name, input, expected): - ... actual = add1(input) - ... assert_equal(actual, expected) - ... - >>> locals() - ... 'test_add1_foo_0': <function ...> ... - >>> - """ - - if "testcase_func_name" in legacy: - warnings.warn("testcase_func_name= is deprecated; use name_func=", - DeprecationWarning, stacklevel=2) - if not name_func: - name_func = legacy["testcase_func_name"] - - if "testcase_func_doc" in legacy: - warnings.warn("testcase_func_doc= is deprecated; use doc_func=", - DeprecationWarning, stacklevel=2) - if not doc_func: - doc_func = legacy["testcase_func_doc"] - - doc_func = doc_func or default_doc_func - name_func = name_func or default_name_func - - def parameterized_expand_wrapper(f, instance=None): - stack = inspect.stack() - frame = stack[1] - frame_locals = frame[0].f_locals - - parameters = cls.input_as_callable(input)() - for num, p in enumerate(parameters): - name = name_func(f, num, p) - frame_locals[name] = cls.param_as_standalone_func(p, f, name) - frame_locals[name].__doc__ = doc_func(f, num, p) - - f.__test__ = False - return parameterized_expand_wrapper - - @classmethod - def param_as_standalone_func(cls, p, func, name): - @wraps(func) - def standalone_func(*a): - return func(*(a + p.args), **p.kwargs) - standalone_func.__name__ = name - - # place_as is used by py.test to determine what source file should be - # used for this test. - standalone_func.place_as = func - - # Remove __wrapped__ because py.test will try to look at __wrapped__ - # to determine which parameters should be used with this test case, - # and obviously we don't need it to do any parameterization. - try: - del standalone_func.__wrapped__ - except AttributeError: - pass - return standalone_func - - @classmethod - def to_safe_name(cls, s): - return str(re.sub("[^a-zA-Z0-9_]+", "_", s)) diff --git a/numpy/testing/_private/utils.py b/numpy/testing/_private/utils.py index 45400856b..cca7b8063 100644 --- a/numpy/testing/_private/utils.py +++ b/numpy/testing/_private/utils.py @@ -16,10 +16,12 @@ from tempfile import mkdtemp, mkstemp from unittest.case import SkipTest from warnings import WarningMessage import pprint +import sysconfig import numpy as np -from numpy.core import( +from numpy.core import ( intp, float32, empty, arange, array_repr, ndarray, isnat, array) +from numpy import isfinite, isnan, isinf import numpy.linalg.lapack_lite from io import StringIO @@ -29,14 +31,14 @@ __all__ = [ 'assert_array_equal', 'assert_array_less', 'assert_string_equal', 'assert_array_almost_equal', 'assert_raises', 'build_err_msg', 'decorate_methods', 'jiffies', 'memusage', 'print_assert_equal', - 'raises', 'rundocs', 'runstring', 'verbose', 'measure', + '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', 'SkipTest', 'KnownFailureException', 'temppath', 'tempdir', 'IS_PYPY', 'HAS_REFCOUNT', "IS_WASM", 'suppress_warnings', 'assert_array_compare', 'assert_no_gc_cycles', 'break_cycles', 'HAS_LAPACK64', 'IS_PYSTON', - '_OLD_PROMOTION' + '_OLD_PROMOTION', 'IS_MUSL', '_SUPPORTS_SVE' ] @@ -56,27 +58,18 @@ HAS_LAPACK64 = numpy.linalg.lapack_lite._ilp64 _OLD_PROMOTION = lambda: np._get_promotion_state() == 'legacy' - -def import_nose(): - """ Import nose only when needed. - """ - nose_is_good = True - minimum_nose_version = (1, 0, 0) - try: - import nose - except ImportError: - nose_is_good = False - else: - if nose.__versioninfo__ < minimum_nose_version: - nose_is_good = False - - if not nose_is_good: - msg = ('Need nose >= %d.%d.%d for tests - see ' - 'https://nose.readthedocs.io' % - minimum_nose_version) - raise ImportError(msg) - - return nose +IS_MUSL = False +try: + from packaging.tags import sys_tags + _tags = list(sys_tags()) + if 'musllinux' in _tags[0].platform: + IS_MUSL = True +except ImportError: + # fallback to sysconfig (might be flaky) + # value could be None. + v = sysconfig.get_config_var('HOST_GNU_TYPE') or '' + if 'musl' in v: + IS_MUSL = True def assert_(val, msg=''): @@ -99,62 +92,6 @@ def assert_(val, msg=''): raise AssertionError(smsg) -def gisnan(x): - """like isnan, but always raise an error if type not supported instead of - returning a TypeError object. - - Notes - ----- - isnan and other ufunc sometimes return a NotImplementedType object instead - of raising any exception. This function is a wrapper to make sure an - exception is always raised. - - This should be removed once this problem is solved at the Ufunc level.""" - from numpy.core import isnan - st = isnan(x) - if isinstance(st, type(NotImplemented)): - raise TypeError("isnan not supported for this type") - return st - - -def gisfinite(x): - """like isfinite, but always raise an error if type not supported instead - of returning a TypeError object. - - Notes - ----- - isfinite and other ufunc sometimes return a NotImplementedType object - instead of raising any exception. This function is a wrapper to make sure - an exception is always raised. - - This should be removed once this problem is solved at the Ufunc level.""" - from numpy.core import isfinite, errstate - with errstate(invalid='ignore'): - st = isfinite(x) - if isinstance(st, type(NotImplemented)): - raise TypeError("isfinite not supported for this type") - return st - - -def gisinf(x): - """like isinf, but always raise an error if type not supported instead of - returning a TypeError object. - - Notes - ----- - isinf and other ufunc sometimes return a NotImplementedType object instead - of raising any exception. This function is a wrapper to make sure an - exception is always raised. - - This should be removed once this problem is solved at the Ufunc level.""" - from numpy.core import isinf, errstate - with errstate(invalid='ignore'): - st = isinf(x) - if isinstance(st, type(NotImplemented)): - raise TypeError("isinf not supported for this type") - return st - - if os.name == 'nt': # Code "stolen" from enthought/debug/memusage.py def GetPerformanceAttributes(object, counter, instance=None, @@ -198,7 +135,7 @@ elif sys.platform[:5] == 'linux': """ try: - with open(_proc_pid_stat, 'r') as f: + with open(_proc_pid_stat) as f: l = f.readline().split(' ') return int(l[22]) except Exception: @@ -225,7 +162,7 @@ if sys.platform[:5] == 'linux': if not _load_time: _load_time.append(time.time()) try: - with open(_proc_pid_stat, 'r') as f: + with open(_proc_pid_stat) as f: l = f.readline().split(' ') return int(l[13]) except Exception: @@ -398,8 +335,8 @@ def assert_equal(actual, desired, err_msg='', verbose=True): # Inf/nan/negative zero handling try: - isdesnan = gisnan(desired) - isactnan = gisnan(actual) + isdesnan = isnan(desired) + isactnan = isnan(actual) if isdesnan and isactnan: return # both nan, so equal @@ -409,7 +346,7 @@ def assert_equal(actual, desired, err_msg='', verbose=True): if (array_actual.dtype.char in 'Mm' or array_desired.dtype.char in 'Mm'): # version 1.18 - # until this version, gisnan failed for datetime64 and timedelta64. + # until this version, isnan failed for datetime64 and timedelta64. # Now it succeeds but comparison to scalar with a different type # emits a DeprecationWarning. # Avoid that by skipping the next check @@ -478,7 +415,7 @@ def print_assert_equal(test_string, actual, desired): @np._no_nep50_warning() -def assert_almost_equal(actual,desired,decimal=7,err_msg='',verbose=True): +def assert_almost_equal(actual, desired, decimal=7, err_msg='', verbose=True): """ Raises an AssertionError if two items are not equal up to desired precision. @@ -590,9 +527,9 @@ def assert_almost_equal(actual,desired,decimal=7,err_msg='',verbose=True): # If one of desired/actual is not finite, handle it specially here: # check that both are nan if any is a nan, and test for equality # otherwise - if not (gisfinite(desired) and gisfinite(actual)): - if gisnan(desired) or gisnan(actual): - if not (gisnan(desired) and gisnan(actual)): + if not (isfinite(desired) and isfinite(actual)): + if isnan(desired) or isnan(actual): + if not (isnan(desired) and isnan(actual)): raise AssertionError(_build_err_msg()) else: if not desired == actual: @@ -605,7 +542,8 @@ def assert_almost_equal(actual,desired,decimal=7,err_msg='',verbose=True): @np._no_nep50_warning() -def assert_approx_equal(actual,desired,significant=7,err_msg='',verbose=True): +def assert_approx_equal(actual, desired, significant=7, err_msg='', + verbose=True): """ Raises an AssertionError if two items are not equal up to significant digits. @@ -690,9 +628,9 @@ def assert_approx_equal(actual,desired,significant=7,err_msg='',verbose=True): # If one of desired/actual is not finite, handle it specially here: # check that both are nan if any is a nan, and test for equality # otherwise - if not (gisfinite(desired) and gisfinite(actual)): - if gisnan(desired) or gisnan(actual): - if not (gisnan(desired) and gisnan(actual)): + if not (isfinite(desired) and isfinite(actual)): + if isnan(desired) or isnan(actual): + if not (isnan(desired) and isnan(actual)): raise AssertionError(msg) else: if not desired == actual: @@ -709,7 +647,8 @@ def assert_array_compare(comparison, x, y, err_msg='', verbose=True, header='', precision=6, equal_nan=True, equal_inf=True, *, strict=False): __tracebackhide__ = True # Hide traceback for py.test - from numpy.core import array, array2string, isnan, inf, bool_, errstate, all, max, object_ + from numpy.core import (array2string, isnan, inf, bool_, errstate, + all, max, object_) x = np.asanyarray(x) y = np.asanyarray(y) @@ -835,10 +774,10 @@ def assert_array_compare(comparison, x, y, err_msg='', verbose=True, header='', max_abs_error = max(error) if getattr(error, 'dtype', object_) == object_: remarks.append('Max absolute difference: ' - + str(max_abs_error)) + + str(max_abs_error)) else: remarks.append('Max absolute difference: ' - + array2string(max_abs_error)) + + array2string(max_abs_error)) # note: this definition of relative error matches that one # used by assert_allclose (found in np.isclose) @@ -850,10 +789,10 @@ def assert_array_compare(comparison, x, y, err_msg='', verbose=True, header='', max_rel_error = max(error[nonzero] / abs(y[nonzero])) if getattr(error, 'dtype', object_) == object_: remarks.append('Max relative difference: ' - + str(max_rel_error)) + + str(max_rel_error)) else: remarks.append('Max relative difference: ' - + array2string(max_rel_error)) + + array2string(max_rel_error)) err_msg += '\n' + '\n'.join(remarks) msg = build_err_msg([ox, oy], err_msg, @@ -899,6 +838,8 @@ def assert_array_equal(x, y, err_msg='', verbose=True, *, strict=False): type of the array_like objects does not match. The special handling for scalars mentioned in the Notes section is disabled. + .. versionadded:: 1.24.0 + Raises ------ AssertionError @@ -1064,15 +1005,15 @@ def assert_array_almost_equal(x, y, decimal=6, err_msg='', verbose=True): """ __tracebackhide__ = True # Hide traceback for py.test - from numpy.core import number, float_, result_type, array + from numpy.core import number, float_, result_type from numpy.core.numerictypes import issubdtype from numpy.core.fromnumeric import any as npany def compare(x, y): try: - if npany(gisinf(x)) or npany( gisinf(y)): - xinfid = gisinf(x) - yinfid = gisinf(y) + if npany(isinf(x)) or npany(isinf(y)): + xinfid = isinf(x) + yinfid = isinf(y) if not (xinfid == yinfid).all(): return False # if one item, x and y is +- inf @@ -1112,8 +1053,6 @@ def assert_array_less(x, y, err_msg='', verbose=True): compared, no assertion is raised if both objects have NaNs in the same positions. - - Parameters ---------- x : array_like @@ -1128,15 +1067,13 @@ def assert_array_less(x, y, err_msg='', verbose=True): Raises ------ AssertionError - If actual and desired objects are not equal. + If x is not strictly smaller than y, element-wise. See Also -------- assert_array_equal: tests objects for equality assert_array_almost_equal: test objects for equality up to precision - - Examples -------- >>> np.testing.assert_array_less([1.0, 1.0, np.nan], [1.1, 2.0, np.nan]) @@ -1303,42 +1240,21 @@ def rundocs(filename=None, raise_on_error=True): raise AssertionError("Some doctests failed:\n%s" % "\n".join(msg)) -def raises(*args): - """Decorator to check for raised exceptions. - - The decorated test function must raise one of the passed exceptions to - pass. If you want to test many assertions about exceptions in a single - test, you may want to use `assert_raises` instead. - - .. warning:: - This decorator is nose specific, do not use it if you are using a - different test framework. - - Parameters - ---------- - args : exceptions - The test passes if any of the passed exceptions is raised. - - Raises - ------ - AssertionError - - Examples - -------- - - Usage:: +def check_support_sve(): + """ + gh-22982 + """ + + import subprocess + cmd = 'lscpu' + try: + return "sve" in (subprocess.Popen(cmd, stdout=subprocess.PIPE, + shell=True).communicate()[0]).decode('utf-8') + except OSError: + return False - @raises(TypeError, ValueError) - def test_raises_type_error(): - raise TypeError("This test passes") - @raises(Exception) - def test_that_fails_by_passing(): - pass - - """ - nose = import_nose() - return nose.tools.raises(*args) +_SUPPORTS_SVE = check_support_sve() # # assert_raises and assert_raises_regex are taken from unittest. @@ -1350,8 +1266,10 @@ class _Dummy(unittest.TestCase): def nop(self): pass + _d = _Dummy('nop') + def assert_raises(*args, **kwargs): """ assert_raises(exception_class, callable, *args, **kwargs) @@ -1378,7 +1296,7 @@ def assert_raises(*args, **kwargs): """ __tracebackhide__ = True # Hide traceback for py.test - return _d.assertRaises(*args,**kwargs) + return _d.assertRaises(*args, **kwargs) def assert_raises_regex(exception_class, expected_regexp, *args, **kwargs): @@ -1529,7 +1447,7 @@ def assert_allclose(actual, desired, rtol=1e-7, atol=0, equal_nan=True, Given two array_like objects, check that their shapes and all elements are equal (but see the Notes for the special handling of a scalar). An - exception is raised if the shapes mismatch or any values conflict. In + exception is raised if the shapes mismatch or any values conflict. In contrast to the standard usage in numpy, NaNs are compared like numbers, no assertion is raised if both objects have NaNs in the same positions. @@ -2451,6 +2369,7 @@ def assert_no_gc_cycles(*args, **kwargs): with _assert_no_gc_cycles_context(name=func.__name__): func(*args, **kwargs) + def break_cycles(): """ Break reference cycles by calling gc.collect @@ -2546,7 +2465,7 @@ def _get_mem_available(): if sys.platform.startswith('linux'): info = {} - with open('/proc/meminfo', 'r') as f: + with open('/proc/meminfo') as f: for line in f: p = line.split() info[p[0].strip(':').lower()] = int(p[1]) * 1024 @@ -2583,7 +2502,7 @@ def _no_tracing(func): def _get_glibc_version(): try: ver = os.confstr('CS_GNU_LIBC_VERSION').rsplit(' ')[1] - except Exception as inst: + except Exception: ver = '0.0' return ver @@ -2591,3 +2510,4 @@ def _get_glibc_version(): _glibcver = _get_glibc_version() _glibc_older_than = lambda x: (_glibcver != '0.0' and _glibcver < x) + diff --git a/numpy/testing/overrides.py b/numpy/testing/overrides.py index d20ed60e5..edc7132c2 100644 --- a/numpy/testing/overrides.py +++ b/numpy/testing/overrides.py @@ -21,10 +21,11 @@ def get_overridable_numpy_ufuncs(): """ ufuncs = {obj for obj in _umath.__dict__.values() if isinstance(obj, _ufunc)} + return ufuncs def allows_array_ufunc_override(func): - """Determine if a function can be overriden via `__array_ufunc__` + """Determine if a function can be overridden via `__array_ufunc__` Parameters ---------- @@ -37,9 +38,9 @@ def allows_array_ufunc_override(func): `True` if `func` is overridable via `__array_ufunc__` and `False` otherwise. - Note - ---- - This function is equivalent to `isinstance(func, np.ufunc)` and + Notes + ----- + This function is equivalent to ``isinstance(func, np.ufunc)`` and will work correctly for ufuncs defined outside of Numpy. """ @@ -66,7 +67,7 @@ def get_overridable_numpy_array_functions(): return _array_functions.copy() def allows_array_function_override(func): - """Determine if a Numpy function can be overriden via `__array_function__` + """Determine if a Numpy function can be overridden via `__array_function__` Parameters ---------- diff --git a/numpy/testing/tests/test_doctesting.py b/numpy/testing/tests/test_doctesting.py deleted file mode 100644 index 92c2156d8..000000000 --- a/numpy/testing/tests/test_doctesting.py +++ /dev/null @@ -1,57 +0,0 @@ -""" Doctests for NumPy-specific nose/doctest modifications - -""" -#FIXME: None of these tests is run, because 'check' is not a recognized -# testing prefix. - -# try the #random directive on the output line -def check_random_directive(): - ''' - >>> 2+2 - <BadExample object at 0x084D05AC> #random: may vary on your system - ''' - -# check the implicit "import numpy as np" -def check_implicit_np(): - ''' - >>> np.array([1,2,3]) - array([1, 2, 3]) - ''' - -# there's some extraneous whitespace around the correct responses -def check_whitespace_enabled(): - ''' - # whitespace after the 3 - >>> 1+2 - 3 - - # whitespace before the 7 - >>> 3+4 - 7 - ''' - -def check_empty_output(): - """ Check that no output does not cause an error. - - This is related to nose bug 445; the numpy plugin changed the - doctest-result-variable default and therefore hit this bug: - http://code.google.com/p/python-nose/issues/detail?id=445 - - >>> a = 10 - """ - -def check_skip(): - """ Check skip directive - - The test below should not run - - >>> 1/0 #doctest: +SKIP - """ - - -if __name__ == '__main__': - # Run tests outside numpy test rig - import nose - from numpy.testing.noseclasses import NumpyDoctest - argv = ['', __file__, '--with-numpydoctest'] - nose.core.TestProgram(argv=argv, addplugins=[NumpyDoctest()]) diff --git a/numpy/testing/tests/test_utils.py b/numpy/testing/tests/test_utils.py index 052cc3aa1..0aaa508ee 100644 --- a/numpy/testing/tests/test_utils.py +++ b/numpy/testing/tests/test_utils.py @@ -8,13 +8,12 @@ import weakref import numpy as np from numpy.testing import ( assert_equal, assert_array_equal, assert_almost_equal, - assert_array_almost_equal, assert_array_less, build_err_msg, raises, + assert_array_almost_equal, assert_array_less, build_err_msg, 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, suppress_warnings, assert_string_equal, assert_, tempdir, temppath, assert_no_gc_cycles, HAS_REFCOUNT ) -from numpy.core.overrides import ARRAY_FUNCTION_ENABLED class _GenericTest: @@ -191,8 +190,6 @@ class TestArrayEqual(_GenericTest): self._test_not_equal(a, b) self._test_not_equal(b, a) - @pytest.mark.skipif( - not ARRAY_FUNCTION_ENABLED, reason='requires __array_function__') def test_subclass_that_does_not_implement_npall(self): class MyArray(np.ndarray): def __array_function__(self, *args, **kwargs): @@ -793,41 +790,6 @@ class TestArrayAssertLess: self._assert_func(-ainf, x) -@pytest.mark.skip(reason="The raises decorator depends on Nose") -class TestRaises: - - def setup_method(self): - class MyException(Exception): - pass - - self.e = MyException - - def raises_exception(self, e): - raise e - - def does_not_raise_exception(self): - pass - - def test_correct_catch(self): - raises(self.e)(self.raises_exception)(self.e) # raises? - - def test_wrong_exception(self): - try: - raises(self.e)(self.raises_exception)(RuntimeError) # raises? - except RuntimeError: - return - else: - raise AssertionError("should have caught RuntimeError") - - def test_catch_no_raise(self): - try: - raises(self.e)(self.does_not_raise_exception)() # raises? - except AssertionError: - return - else: - raise AssertionError("should have raised an AssertionError") - - class TestWarns: def test_warn(self): diff --git a/numpy/testing/utils.py b/numpy/testing/utils.py deleted file mode 100644 index 20a883304..000000000 --- a/numpy/testing/utils.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Back compatibility utils module. It will import the appropriate -set of tools - -""" -import warnings - -# 2018-04-04, numpy 1.15.0 ImportWarning -# 2019-09-18, numpy 1.18.0 DeprecatonWarning (changed) -warnings.warn("Importing from numpy.testing.utils is deprecated " - "since 1.15.0, import from numpy.testing instead.", - DeprecationWarning, stacklevel=2) - -from ._private.utils import * -from ._private.utils import _assert_valid_refcount, _gen_alignment_data - -__all__ = [ - 'assert_equal', 'assert_almost_equal', 'assert_approx_equal', - 'assert_array_equal', 'assert_array_less', 'assert_string_equal', - 'assert_array_almost_equal', 'assert_raises', 'build_err_msg', - 'decorate_methods', 'jiffies', 'memusage', 'print_assert_equal', - 'raises', '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', - 'SkipTest', 'KnownFailureException', 'temppath', 'tempdir', 'IS_PYPY', - 'HAS_REFCOUNT', 'suppress_warnings', 'assert_array_compare', - 'assert_no_gc_cycles' - ] |