Skip to content

Commit d68a6bb

Browse files
authored
Merge pull request #477 from grlee77/pytest
MAINT/TST: update tests to use pytest
2 parents 0136844 + 1bc1420 commit d68a6bb

32 files changed

Lines changed: 393 additions & 259 deletions

.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ include = */pywt/*
55
omit =
66
*/version.py
77
*/pywt/tests/*
8+
*/pywt/_doc_utils.py*
89
*/pywt/data/create_dat.py
910
*.pxd
1011
stringsource

.travis.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ before_install:
5656
- pip install $NUMPYSPEC
5757
- pip install $MATPLOTLIBSPEC
5858
- pip install $CYTHONSPEC
59-
- pip install nose coverage codecov futures
59+
- pip install pytest pytest-cov coverage codecov futures
6060
- set -o pipefail
6161
- if [ "${USE_WHEEL}" == "1" ]; then pip install wheel; fi
6262
- |
@@ -72,23 +72,26 @@ script:
7272
pip wheel . -v
7373
pip install PyWavelets*.whl -v
7474
pushd demo
75-
nosetests pywt
75+
pytest --pyargs pywt
7676
python ../pywt/tests/test_doc.py
7777
popd
7878
elif [ "${USE_SDIST}" == "1" ]; then
7979
python setup.py sdist
8080
# Move out of source directory to avoid finding local pywt
8181
pushd dist
8282
pip install PyWavelets* -v
83-
nosetests pywt
83+
pytest --pyargs pywt
8484
python ../pywt/tests/test_doc.py
8585
popd
8686
elif [ "${REFGUIDE_CHECK}" == "1" ]; then
8787
pip install -e . -v
8888
python util/refguide_check.py --doctests
8989
else
9090
CFLAGS="--coverage" python setup.py build --build-lib build/lib/ --build-temp build/tmp/
91-
nosetests build/lib/ --tests pywt/tests
91+
CFLAGS="--coverage" pip install -e . -v
92+
pushd demo
93+
pytest --pyargs pywt --cov=pywt
94+
popd
9295
fi
9396
9497
after_success:

LICENSE

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Copyright (c) 2006-2012 Filip Wasilewski <http://en.ig.ma/>
2-
Copyright (c) 2012-2017 The PyWavelets Developers <https://github.com/PyWavelets/pywt>
2+
Copyright (c) 2012-2019 The PyWavelets Developers <https://github.com/PyWavelets/pywt>
33

44
Permission is hereby granted, free of charge, to any person obtaining a copy of
55
this software and associated documentation files (the "Software"), to deal in
@@ -18,3 +18,15 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1818
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1919
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2020
SOFTWARE.
21+
22+
23+
The PyWavelets repository and source distributions bundle some code that is
24+
adapted from compatibly licensed projects. We list these here.
25+
26+
Name: NumPy
27+
Files: pywt/_pytesttester.py
28+
License: 3-clause BSD
29+
30+
Name: SciPy
31+
Files: setup.py, util/*
32+
License: 3-clause BSD

appveyor.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ install:
2323
- "util\\appveyor\\build.cmd %PYTHON%\\python.exe -m pip install
2424
numpy --cache-dir c:\\tmp\\pip-cache"
2525
- "util\\appveyor\\build.cmd %PYTHON%\\python.exe -m pip install
26-
Cython nose coverage matplotlib futures --cache-dir c:\\tmp\\pip-cache"
26+
Cython pytest coverage matplotlib futures --cache-dir c:\\tmp\\pip-cache"
2727

2828
test_script:
29-
- "util\\appveyor\\build.cmd %PYTHON%\\python.exe setup.py build --build-lib build\\lib\\"
30-
- "%PYTHON%\\Scripts\\nosetests build\\lib --tests pywt\\tests"
29+
- "util\\appveyor\\build.cmd %PYTHON%\\python.exe -m pip install -e . -v"
30+
- "cd demo"
31+
- "%PYTHON%\\Scripts\\pytest --pyargs pywt"
32+
- "cd .."
3133

3234
after_test:
3335
- "util\\appveyor\\build.cmd %PYTHON%\\python.exe setup.py bdist_wheel"

doc/source/dev/testing.rst

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,22 @@ does not break the build.
2323
Running tests locally
2424
---------------------
2525

26-
Tests are implemented with `nose`_, so use one of:
26+
Tests are implemented with `pytest`_, so use one of:
2727

28-
$ nosetests pywt
28+
$ pytest --pyargs pywt -v
2929

30-
>>> pywt.test() # doctest: +SKIP
30+
There are also older doctests that can be run by performing the following from
31+
the root of the project source.
3132

32-
Note doctests require `Matplotlib`_ in addition to the usual dependencies.
33+
$ python pywt/tests/test_doc.py
34+
$ cd doc
35+
$ make doctest
36+
37+
Additionally the examples in the demo subfolder can be checked by running:
38+
39+
$ python util/refguide_check.py
40+
41+
Note: doctests require `Matplotlib`_ in addition to the usual dependencies.
3342

3443

3544
Running tests with Tox
@@ -43,6 +52,6 @@ To for example run tests for Python 3.5 and 3.6 use::
4352
For more information see the `Tox`_ documentation.
4453

4554

46-
.. _nose: http://nose.readthedocs.org/en/latest/
47-
.. _Tox: http://tox.testrun.org/
48-
.. _Matplotlib: http://matplotlib.org
55+
.. _pytest: https://pytest.org
56+
.. _Tox: https://tox.readthedocs.io/en/latest/
57+
.. _Matplotlib: https://matplotlib.org

pytest.ini

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[pytest]
2+
addopts = -l
3+
norecursedirs = doc tools pywt/_extensions
4+
doctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS ALLOW_UNICODE ALLOW_BYTES
5+
6+
filterwarnings =
7+
error
8+
# Filter out annoying import messages.
9+
ignore:Not importing directory
10+
ignore:numpy.dtype size changed
11+
ignore:numpy.ufunc size changed
12+
ignore::UserWarning:cpuinfo,
13+
14+
env =
15+
PYTHONHASHSEED=0

pywt/__init__.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,6 @@
3535

3636
from pywt.version import version as __version__
3737

38-
import numpy as np
39-
if np.lib.NumpyVersion(np.__version__) >= '1.14.0':
40-
from ._utils import is_nose_running
41-
if is_nose_running():
42-
np.set_printoptions(legacy='1.13')
43-
44-
from numpy.testing import Tester
45-
test = Tester().test
38+
from ._pytesttester import PytestTester
39+
test = PytestTester(__name__)
40+
del PytestTester

pywt/_pytest.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""common test-related code."""
2+
import os
3+
import sys
4+
import multiprocessing
5+
import numpy as np
6+
import pytest
7+
8+
9+
__all__ = ['uses_matlab', # skip if pymatbridge and Matlab unavailable
10+
'uses_futures', # skip if futures unavailable
11+
'uses_pymatbridge', # skip if no PYWT_XSLOW environment variable
12+
'uses_precomputed', # skip if PYWT_XSLOW environment variable found
13+
'matlab_result_dict_cwt', # dict with precomputed Matlab dwt data
14+
'matlab_result_dict_dwt', # dict with precomputed Matlab cwt data
15+
'futures', # the futures module or None
16+
'max_workers', # the number of workers available to futures
17+
'size_set', # the set of Matlab tests to run
18+
]
19+
20+
try:
21+
if sys.version_info[0] == 2:
22+
import futures
23+
else:
24+
from concurrent import futures
25+
max_workers = multiprocessing.cpu_count()
26+
futures_available = True
27+
except ImportError:
28+
futures_available = False
29+
futures = None
30+
31+
# check if pymatbridge + MATLAB tests should be run
32+
matlab_result_dict_dwt = None
33+
matlab_result_dict_cwt = None
34+
matlab_missing = True
35+
use_precomputed = True
36+
size_set = 'reduced'
37+
if 'PYWT_XSLOW' in os.environ:
38+
try:
39+
from pymatbridge import Matlab
40+
mlab = Matlab()
41+
matlab_missing = False
42+
use_precomputed = False
43+
size_set = 'full'
44+
except ImportError:
45+
print("To run Matlab compatibility tests you need to have MathWorks "
46+
"MATLAB, MathWorks Wavelet Toolbox and the pymatbridge Python "
47+
"package installed.")
48+
if use_precomputed:
49+
# load dictionaries of precomputed results
50+
data_dir = os.path.join(os.path.dirname(__file__), 'tests', 'data')
51+
matlab_data_file_cwt = os.path.join(
52+
data_dir, 'cwt_matlabR2015b_result.npz')
53+
matlab_result_dict_cwt = np.load(matlab_data_file_cwt)
54+
55+
matlab_data_file_dwt = os.path.join(
56+
data_dir, 'dwt_matlabR2012a_result.npz')
57+
matlab_result_dict_dwt = np.load(matlab_data_file_dwt)
58+
59+
uses_futures = pytest.mark.skipif(
60+
not futures_available, reason='futures not available')
61+
uses_matlab = pytest.mark.skipif(
62+
matlab_missing, reason='pymatbridge and/or Matlab not available')
63+
uses_pymatbridge = pytest.mark.skipif(
64+
use_precomputed,
65+
reason='PYWT_XSLOW set: skipping tests against precomputed Matlab results')
66+
uses_precomputed = pytest.mark.skipif(
67+
not use_precomputed,
68+
reason='PYWT_XSLOW not set: test against precomputed matlab tests')

pywt/_pytesttester.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
"""
2+
Pytest test running.
3+
4+
This module implements the ``test()`` function for NumPy modules. The usual
5+
boiler plate for doing that is to put the following in the module
6+
``__init__.py`` file::
7+
8+
from pywt._pytesttester import PytestTester
9+
test = PytestTester(__name__).test
10+
del PytestTester
11+
12+
13+
Warnings filtering and other runtime settings should be dealt with in the
14+
``pytest.ini`` file in the pywt repo root. The behavior of the test depends on
15+
whether or not that file is found as follows:
16+
17+
* ``pytest.ini`` is present (develop mode)
18+
All warnings except those explicily 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+
23+
In practice, tests run from the PyWavelets repo are run in develop mode. That
24+
includes the standard ``python runtests.py`` invocation.
25+
26+
"""
27+
from __future__ import division, absolute_import, print_function
28+
29+
import sys
30+
import os
31+
32+
__all__ = ['PytestTester']
33+
34+
35+
def _show_pywt_info():
36+
import pywt
37+
from pywt._c99_config import _have_c99_complex
38+
print("PyWavelets version %s" % pywt.__version__)
39+
if _have_c99_complex:
40+
print("Compiled with C99 complex support.")
41+
else:
42+
print("Compiled without C99 complex support.")
43+
44+
45+
class PytestTester(object):
46+
"""
47+
Pytest test runner.
48+
49+
This class is made available in ``pywt.testing``, and a test function
50+
is typically added to a package's __init__.py like so::
51+
52+
from pywt.testing 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+
"""
70+
def __init__(self, module_name):
71+
self.module_name = module_name
72+
73+
def __call__(self, label='fast', verbose=1, extra_argv=None,
74+
doctests=False, coverage=False, durations=-1, tests=None):
75+
"""
76+
Run tests for module using pytest.
77+
78+
Parameters
79+
----------
80+
label : {'fast', 'full'}, optional
81+
Identifies the tests to run. When set to 'fast', tests decorated
82+
with `pytest.mark.slow` are skipped, when 'full', the slow marker
83+
is ignored.
84+
verbose : int, optional
85+
Verbosity value for test outputs, in the range 1-3. Default is 1.
86+
extra_argv : list, optional
87+
List with any extra arguments to pass to pytests.
88+
doctests : bool, optional
89+
.. note:: Not supported
90+
coverage : bool, optional
91+
If True, report coverage of NumPy code. Default is False.
92+
Requires installation of (pip) pytest-cov.
93+
durations : int, optional
94+
If < 0, do nothing, If 0, report time of all tests, if > 0,
95+
report the time of the slowest `timer` tests. Default is -1.
96+
tests : test or list of tests
97+
Tests to be executed with pytest '--pyargs'
98+
99+
Returns
100+
-------
101+
result : bool
102+
Return True on success, false otherwise.
103+
104+
Examples
105+
--------
106+
>>> result = np.lib.test() #doctest: +SKIP
107+
...
108+
1023 passed, 2 skipped, 6 deselected, 1 xfailed in 10.39 seconds
109+
>>> result
110+
True
111+
112+
"""
113+
import pytest
114+
115+
module = sys.modules[self.module_name]
116+
module_path = os.path.abspath(module.__path__[0])
117+
118+
# setup the pytest arguments
119+
pytest_args = ["-l"]
120+
121+
# offset verbosity. The "-q" cancels a "-v".
122+
pytest_args += ["-q"]
123+
124+
# Filter out annoying import messages. Want these in both develop and
125+
# release mode.
126+
pytest_args += [
127+
"-W ignore:Not importing directory",
128+
"-W ignore:numpy.dtype size changed",
129+
"-W ignore:numpy.ufunc size changed", ]
130+
131+
if doctests:
132+
raise ValueError("Doctests not supported")
133+
134+
if extra_argv:
135+
pytest_args += list(extra_argv)
136+
137+
if verbose > 1:
138+
pytest_args += ["-" + "v"*(verbose - 1)]
139+
140+
if coverage:
141+
pytest_args += ["--cov=" + module_path]
142+
143+
if label == "fast":
144+
pytest_args += ["-m", "not slow"]
145+
elif label != "full":
146+
pytest_args += ["-m", label]
147+
148+
if durations >= 0:
149+
pytest_args += ["--durations=%s" % durations]
150+
151+
if tests is None:
152+
tests = [self.module_name]
153+
154+
pytest_args += ["--pyargs"] + list(tests)
155+
156+
# run tests.
157+
_show_pywt_info()
158+
159+
try:
160+
code = pytest.main(pytest_args)
161+
except SystemExit as exc:
162+
code = exc.code
163+
164+
return code == 0

0 commit comments

Comments
 (0)