""" Nose test running.
This module implements ``test()`` and ``bench()`` functions for NumPy modules.
"""
'_numpy_tester', 'get_package_name', 'import_nose', 'suppress_warnings']
""" 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)
""" 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()])
""" 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
''' 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, basestring): 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
nose = import_nose()
import numpy print("NumPy version %s" % 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("NumPy is installed in %s" % npdir)
if 'scipy' in self.package_name: import scipy print("SciPy version %s" % scipy.__version__) spdir = os.path.dirname(scipy.__file__) print("SciPy is installed in %s" % spdir)
pyversion = sys.version.replace('\n', '') print("Python version %s" % pyversion) print("nose version %d.%d.%d" % nose.__versioninfo__)
""" 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()
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 += ['--cover-package=%s' % 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://nedbatchelder.com/code/modules/coveragehtml>`_). 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("Running unit tests and doctests for %s" % self.package_name) else: print("Running unit tests for %s" % 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, basestring): 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) # See #7949: Filter out deprecation warnings due to the -3 flag to # python 2 if sys.version_info.major == 2 and sys.py3kwarning: # This is very specific, so using the fragile module filter # is fine import threading sup.filter(DeprecationWarning, r"sys\.exc_clear\(\) not supported in 3\.x", module=threading) sup.filter(DeprecationWarning, message=r"in 3\.x, __setslice__") sup.filter(DeprecationWarning, message=r"in 3\.x, __getslice__") sup.filter(DeprecationWarning, message=r"buffer\(\) not supported in 3\.x") sup.filter(DeprecationWarning, message=r"CObject type is not supported in 3\.x") sup.filter(DeprecationWarning, message=r"comparing unequal types not supported in 3\.x") # 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
""" 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("Running benchmarks for %s" % 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)
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) |