1"""
2Pytest test running.
3
4This module implements the ``test()`` function for NumPy modules. The usual
5boiler plate for doing that is to put the following in the module
6``__init__.py`` file::
7
8 from numpy._pytesttester import PytestTester
9 test = PytestTester(__name__)
10 del PytestTester
11
12
13Warnings filtering and other runtime settings should be dealt with in the
14``pytest.ini`` file in the numpy repo root. The behavior of the test depends on
15whether or not that file is found as follows:
16
17* ``pytest.ini`` is present (develop mode)
18 All warnings except those explicitly filtered out are raised as error.
19* ``pytest.ini`` is absent (release mode)
20 DeprecationWarnings and PendingDeprecationWarnings are ignored, other
21 warnings are passed through.
22
23In practice, tests run from the numpy repo are run in develop mode. That
24includes the standard ``python runtests.py`` invocation.
25
26This module is imported by every numpy subpackage, so lies at the top level to
27simplify circular import issues. For the same reason, it contains no numpy
28imports at module scope, instead importing numpy within function calls.
29"""
30import sys
31import os
32
33__all__ = ['PytestTester']
34
35
36def _show_numpy_info():
37 import numpy as np
38
39 print("NumPy version %s" % np.__version__)
40 relaxed_strides = np.ones((10, 1), order="C").flags.f_contiguous
41 print("NumPy relaxed strides checking option:", relaxed_strides)
42 info = np.lib.utils._opt_info()
43 print("NumPy CPU features: ", (info if info else 'nothing enabled'))
44
45
46class PytestTester:
47 """
48 Pytest test runner.
49
50 A test function is typically added to a package's __init__.py like so::
51
52 from numpy._pytesttester import PytestTester
53 test = PytestTester(__name__).test
54 del PytestTester
55
56 Calling this test function finds and runs all tests associated with the
57 module and all its sub-modules.
58
59 Attributes
60 ----------
61 module_name : str
62 Full path to the package to test.
63
64 Parameters
65 ----------
66 module_name : module name
67 The name of the module to test.
68
69 Notes
70 -----
71 Unlike the previous ``nose``-based implementation, this class is not
72 publicly exposed as it performs some ``numpy``-specific warning
73 suppression.
74
75 """
76 def __init__(self, module_name):
77 self.module_name = module_name
78
79 def __call__(self, label='fast', verbose=1, extra_argv=None,
80 doctests=False, coverage=False, durations=-1, tests=None):
81 """
82 Run tests for module using pytest.
83
84 Parameters
85 ----------
86 label : {'fast', 'full'}, optional
87 Identifies the tests to run. When set to 'fast', tests decorated
88 with `pytest.mark.slow` are skipped, when 'full', the slow marker
89 is ignored.
90 verbose : int, optional
91 Verbosity value for test outputs, in the range 1-3. Default is 1.
92 extra_argv : list, optional
93 List with any extra arguments to pass to pytests.
94 doctests : bool, optional
95 .. note:: Not supported
96 coverage : bool, optional
97 If True, report coverage of NumPy code. Default is False.
98 Requires installation of (pip) pytest-cov.
99 durations : int, optional
100 If < 0, do nothing, If 0, report time of all tests, if > 0,
101 report the time of the slowest `timer` tests. Default is -1.
102 tests : test or list of tests
103 Tests to be executed with pytest '--pyargs'
104
105 Returns
106 -------
107 result : bool
108 Return True on success, false otherwise.
109
110 Notes
111 -----
112 Each NumPy module exposes `test` in its namespace to run all tests for
113 it. For example, to run all tests for numpy.lib:
114
115 >>> np.lib.test() #doctest: +SKIP
116
117 Examples
118 --------
119 >>> result = np.lib.test() #doctest: +SKIP
120 ...
121 1023 passed, 2 skipped, 6 deselected, 1 xfailed in 10.39 seconds
122 >>> result
123 True
124
125 """
126 import pytest
127 import warnings
128
129 module = sys.modules[self.module_name]
130 module_path = os.path.abspath(module.__path__[0])
131
132 # setup the pytest arguments
133 pytest_args = ["-l"]
134
135 # offset verbosity. The "-q" cancels a "-v".
136 pytest_args += ["-q"]
137
138 with warnings.catch_warnings():
139 warnings.simplefilter("always")
140 # Filter out distutils cpu warnings (could be localized to
141 # distutils tests). ASV has problems with top level import,
142 # so fetch module for suppression here.
143 from numpy.distutils import cpuinfo
144
145 with warnings.catch_warnings(record=True):
146 # Ignore the warning from importing the array_api submodule. This
147 # warning is done on import, so it would break pytest collection,
148 # but importing it early here prevents the warning from being
149 # issued when it imported again.
150 import numpy.array_api
151
152 # Filter out annoying import messages. Want these in both develop and
153 # release mode.
154 pytest_args += [
155 "-W ignore:Not importing directory",
156 "-W ignore:numpy.dtype size changed",
157 "-W ignore:numpy.ufunc size changed",
158 "-W ignore::UserWarning:cpuinfo",
159 ]
160
161 # When testing matrices, ignore their PendingDeprecationWarnings
162 pytest_args += [
163 "-W ignore:the matrix subclass is not",
164 "-W ignore:Importing from numpy.matlib is",
165 ]
166
167 if doctests:
168 pytest_args += ["--doctest-modules"]
169
170 if extra_argv:
171 pytest_args += list(extra_argv)
172
173 if verbose > 1:
174 pytest_args += ["-" + "v"*(verbose - 1)]
175
176 if coverage:
177 pytest_args += ["--cov=" + module_path]
178
179 if label == "fast":
180 # not importing at the top level to avoid circular import of module
181 from numpy.testing import IS_PYPY
182 if IS_PYPY:
183 pytest_args += ["-m", "not slow and not slow_pypy"]
184 else:
185 pytest_args += ["-m", "not slow"]
186
187 elif label != "full":
188 pytest_args += ["-m", label]
189
190 if durations >= 0:
191 pytest_args += ["--durations=%s" % durations]
192
193 if tests is None:
194 tests = [self.module_name]
195
196 pytest_args += ["--pyargs"] + list(tests)
197
198 # run tests.
199 _show_numpy_info()
200
201 try:
202 code = pytest.main(pytest_args)
203 except SystemExit as exc:
204 code = exc.code
205
206 return code == 0