diff --git a/.gitignore b/.gitignore
index a6e11ca..71067df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@ dist
*.tmp*
.coverage
MANIFEST
+.cache/
diff --git a/.travis.yml b/.travis.yml
index 95d5bfe..638dc50 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,7 @@ sudo: false
env:
global:
- - PACKAGES_STANDARD="setuptools numpy nose pep8 coverage"
+ - PACKAGES_STANDARD="setuptools numpy pytest pycodestyle"
matrix:
- NAME="Python 2.7 (standard)"
PYTHON_VERSION=2.7
@@ -59,7 +59,7 @@ script:
# installed package rather than the source:
- mkdir ../test_directory
- cd ../test_directory
- - nosetests eofs --verbosity=2 --with-coverage --cover-package=eofs
+ - pytest -vrsx --pyargs eofs
notifications:
email: false
diff --git a/doc/devguide/testing.rst b/doc/devguide/testing.rst
index e02dd40..6416b05 100644
--- a/doc/devguide/testing.rst
+++ b/doc/devguide/testing.rst
@@ -5,7 +5,7 @@ The package comes with a comprehensive set of tests to make sure it is working c
The tests can be run against an installed version of `eofs` or against the current source tree.
Testing against the source tree is handy during development when quick iteration is required, but for most other cases testing against the installed version is more suitable.
-Running the test suite requires nose_ and pep8_ to be installed.
+Running the test suite requires pytest_ and pycodestyle_ to be installed.
The test suite will function as long as the minimum dependencies for the package are installed, but some tests will be skipped if they require optional dependencies that are not present.
To run the full test suite you need to have the optional dependencies `cdms2` (from UV-CDAT_), iris_, and xarray_ installed.
@@ -14,7 +14,7 @@ Testing against the current source tree
Testing the current source is straightforward, from the source directory run::
- nosetests -sv
+ pytest
This will perform verbose testing of the current source tree and print a summary at the end.
@@ -27,16 +27,16 @@ First you need to install `eofs` into your current Python environment::
cd eofs/
python setup.py install
-Then create a directory somewhere else without any Python code in it and run ``nosetests`` from there giving the package name ``eofs`` as a positional argument::
+Then create a directory somewhere else without any Python code in it and run ``pytest`` from there::
mkdir $HOME/eofs-test-dir && cd $HOME/eofs-test-dir
- nosetests -sv eofs
+ pytest --pyargs eofs
This will run the tests on the version of `eofs` you just installed.
-.. _nose: https://nose.readthedocs.org/en/latest/
+.. _pytest: https://docs.pytest.org/en/latest/
-.. _pep8: https://pypi.python.org/pypi/pep8
+.. _pycodestyle: https://pypi.python.org/pypi/pycodestyle
.. _UV-CDAT: http://uv-cdat.llnl.gov
diff --git a/lib/eofs/tests/__init__.py b/lib/eofs/tests/__init__.py
index e7c2b54..24f1830 100644
--- a/lib/eofs/tests/__init__.py
+++ b/lib/eofs/tests/__init__.py
@@ -17,9 +17,8 @@
# along with eofs. If not, see .
from __future__ import (absolute_import, division, print_function) # noqa
-from nose.tools import assert_almost_equal, assert_true, assert_equal
import numpy as np
-from numpy.testing.utils import assert_array_almost_equal
+import numpy.ma as ma
np.seterr(all='ignore')
@@ -51,15 +50,18 @@ def assert_array_almost_equal(self, a, b):
comparison is made.
"""
- assert_array_almost_equal(self._tomasked(a), self._tomasked(b))
+ assert ma.allclose(self._tomasked(a), self._tomasked(b))
def assert_almost_equal(self, a, b):
"""Assertion that two values compare almost equal."""
- assert_almost_equal(self._tomasked(a), self._tomasked(b))
+ assert ma.allclose(self._tomasked(a), self._tomasked(b))
def assert_true(self, cond):
"""Assertion that a condition is True."""
- assert_true(cond)
+ assert cond
def assert_equal(self, a, b, message=None):
- assert_equal(a, b, message)
+ if message is not None:
+ assert a == b, message
+ else:
+ assert a == b
diff --git a/lib/eofs/tests/test_coding_standards.py b/lib/eofs/tests/test_coding_standards.py
index f988fcb..fe2d5ad 100644
--- a/lib/eofs/tests/test_coding_standards.py
+++ b/lib/eofs/tests/test_coding_standards.py
@@ -18,7 +18,7 @@
import os
-import pep8
+import pycodestyle
import eofs
from eofs.tests import EofsTest
@@ -27,7 +27,7 @@
class TestCodingStandards(EofsTest):
def test_pep8(self):
- pep8style = pep8.StyleGuide(quiet=False)
+ pep8style = pycodestyle.StyleGuide(quiet=False)
base_paths = [os.path.dirname(eofs.__file__)]
result = pep8style.check_files(base_paths)
self.assert_equal(result.total_errors, 0, "Found PEP8 style issues.")
diff --git a/lib/eofs/tests/test_error_handling.py b/lib/eofs/tests/test_error_handling.py
index 05e8580..ca9d7c0 100644
--- a/lib/eofs/tests/test_error_handling.py
+++ b/lib/eofs/tests/test_error_handling.py
@@ -17,13 +17,12 @@
# along with eofs. If not, see .
from __future__ import (absolute_import, division, print_function) # noqa
-from nose import SkipTest
-from nose.tools import raises
import numpy as np
try:
import cdms2
except ImportError:
pass
+import pytest
import eofs
from eofs.tests import EofsTest
@@ -56,46 +55,46 @@ def setup_class(cls):
try:
cls.solution = reference_solution(cls.interface, cls.weights)
except ValueError:
- raise SkipTest('library component not available '
- 'for {!s} interface'.format(cls.interface))
+ pytest.skip('missing dependencies required to test '
+ 'the {!s} interface'.format(cls.interface))
cls.neofs = cls.solution['eigenvalues'].shape[0]
try:
cls.solver = solvers[cls.interface](
cls.solution['sst'], weights=cls.solution['weights'])
except KeyError:
- raise SkipTest('library component not available '
- 'for {!s} interface'.format(cls.interface))
+ pytest.skip('missing dependencies required to test '
+ 'the {!s} interface'.format(cls.interface))
- @raises(ValueError)
def test_invalid_pcscaling(self):
# PCs with invalid scaling
- pcs = self.solver.pcs(pcscaling=-1)
+ with pytest.raises(ValueError):
+ pcs = self.solver.pcs(pcscaling=-1)
- @raises(ValueError)
def test_invalid_eofscaling(self):
# EOFs with invalid scaling
- eofs = self.solver.eofs(eofscaling=-1)
+ with pytest.raises(ValueError):
+ eofs = self.solver.eofs(eofscaling=-1)
- @raises(ValueError)
def test_projectField_invalid_dimensions(self):
# projecting a field with too few dimensions
data = self.solution['sst'][:, 0, 0]
- pcs = self.solver.projectField(data)
+ with pytest.raises(ValueError):
+ pcs = self.solver.projectField(data)
- @raises(ValueError)
def test_projectField_invalid_shape(self):
# projecting a field with the wrong shape
data = self.solution['sst'][..., 0:1]
- pcs = self.solver.projectField(data)
+ with pytest.raises(ValueError):
+ pcs = self.solver.projectField(data)
- @raises(ValueError)
def test_projectField_different_missing_values(self):
# projecting a field with different missing values
solution = reference_solution(self.interface, self.weights)
data = solution['sst']
mask = data.mask
mask[0] = True
- pcs = self.solver.projectField(data)
+ with pytest.raises(ValueError):
+ pcs = self.solver.projectField(data)
# ----------------------------------------------------------------------------
@@ -116,11 +115,11 @@ class TestErrorHandlersCDMS(ErrorHandlersTest):
interface = 'cdms'
weights = 'equal'
- @raises(TypeError)
def test_projectField_invalid_type(self):
# projecting a field of the wrong type
solution = reference_solution('standard', 'equal')
- pcs = self.solver.projectField(solution['sst'])
+ with pytest.raises(TypeError):
+ pcs = self.solver.projectField(solution['sst'])
# ----------------------------------------------------------------------------
@@ -132,28 +131,28 @@ class TestErrorHandlersIris(ErrorHandlersTest):
interface = 'iris'
weights = 'equal'
- @raises(TypeError)
def test_projectField_invalid_type(self):
# projecting a field of the wrong type
solution = reference_solution('standard', 'equal')
- pcs = self.solver.projectField(solution['sst'])
+ with pytest.raises(TypeError):
+ pcs = self.solver.projectField(solution['sst'])
- @raises(ValueError)
def test_projectField_different_missing_values(self):
# projecting a field with different missing values
solution = reference_solution(self.interface, self.weights)
data = solution['sst']
mask = data.data.mask
mask[0] = True
- pcs = self.solver.projectField(data)
+ with pytest.raises(ValueError):
+ pcs = self.solver.projectField(data)
- @raises(ValueError)
def test_projectField_invalid_dimension_order(self):
# projecting a field with the time dimension not at the front
solution = reference_solution(self.interface, self.weights)
data = solution['sst']
data.transpose((2, 1, 0))
- pcs = self.solver.projectField(data)
+ with pytest.raises(ValueError):
+ pcs = self.solver.projectField(data)
# ----------------------------------------------------------------------------
@@ -165,27 +164,27 @@ class TestErrorHandlersXarray(ErrorHandlersTest):
interface = 'xarray'
weights = 'equal'
- @raises(TypeError)
def test_projectField_invalid_type(self):
# projecting a field of the wrong type
solution = reference_solution('standard', 'equal')
- pcs = self.solver.projectField(solution['sst'])
+ with pytest.raises(TypeError):
+ pcs = self.solver.projectField(solution['sst'])
- @raises(ValueError)
def test_projectField_different_missing_values(self):
# projecting a field with different missing values
solution = reference_solution(self.interface, self.weights)
data = solution['sst']
data.values[0] = np.nan
- pcs = self.solver.projectField(data)
+ with pytest.raises(ValueError):
+ pcs = self.solver.projectField(data)
- @raises(ValueError)
def test_projectField_invalid_dimension_order(self):
# projecting a field with the time dimension not at the front
solution = reference_solution(self.interface, self.weights)
data = solution['sst']
- data.transpose((2, 1, 0))
- pcs = self.solver.projectField(data)
+ data = data.transpose('longitude', 'latitude', 'time')
+ with pytest.raises(ValueError):
+ pcs = self.solver.projectField(data)
# ----------------------------------------------------------------------------
@@ -199,30 +198,29 @@ class TestConstructorStandard(EofsTest):
def setup_class(cls):
cls.solver_class = solvers['standard']
- @raises(ValueError)
def test_invalid_input_dimensions(self):
# too few input dimensions
solution = reference_solution('standard', 'equal')
data = solution['sst'][:, 0, 0]
- solver = self.solver_class(data)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_invalid_weights_dimensions(self):
# weights with incompatible dimensions
solution = reference_solution('standard', 'area')
data = solution['sst']
weights = solution['weights'][:, 0]
- solver = self.solver_class(data, weights=weights)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data, weights=weights)
- @raises(TypeError)
def test_invalid_weights_type(self):
# weights with an incompatible type
solution = reference_solution('standard', 'area')
data = solution['sst']
weights = 'area'
- solver = self.solver_class(data, weights=weights)
+ with pytest.raises(TypeError):
+ solver = self.solver_class(data, weights=weights)
- @raises(ValueError)
def test_input_with_all_missing_values(self):
# input with only missing values, missing values are propagated
# because the default for the center argument is True
@@ -230,9 +228,9 @@ def test_input_with_all_missing_values(self):
data = solution['sst']
mask = data.mask
mask[-1] = True
- solver = self.solver_class(data)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_input_with_non_uniform_missing_values(self):
# missing values in different places at different times leads to
# errors in computing the SVD
@@ -240,7 +238,8 @@ def test_input_with_non_uniform_missing_values(self):
data = solution['sst']
mask = data.mask
mask[-1] = True
- solver = self.solver_class(data, center=False)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data, center=False)
# ----------------------------------------------------------------------------
@@ -255,60 +254,59 @@ def setup_class(cls):
try:
cls.solver_class = solvers['cdms']
except KeyError:
- raise SkipTest('library component not available '
- 'for cdms interface')
+ pytest.skip('missing dependencies required to test the '
+ 'cdms interface')
- @raises(TypeError)
def test_wrong_input_type(self):
# input of the wrong type
solution = reference_solution('standard', 'equal')
data = solution['sst']
- solver = self.solver_class(data)
+ with pytest.raises(TypeError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_input_without_time_dimension(self):
# no time dimension in the input
solution = reference_solution('cdms', 'equal')
data = solution['sst'](time=slice(0, 1), squeeze=True)
- solver = self.solver_class(data)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_input_time_dimension_not_first(self):
# time not the first dimension in the input
solution = reference_solution('cdms', 'equal')
data = solution['sst']
data = data.reorder('xyt')
- solver = self.solver_class(data)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_input_no_spatial_dimensions(self):
# not enough dimensions in the input
solution = reference_solution('cdms', 'equal')
data = solution['sst'][:, 0, 0]
- solver = self.solver_class(data)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_invalid_builtin_weights_value(self):
# invalid weighting scheme name
solution = reference_solution('cdms', 'equal')
data = solution['sst']
- solver = self.solver_class(data, weights='invalid')
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data, weights='invalid')
- @raises(ValueError)
def test_builtin_latitude_weights_with_missing_dimension(self):
# latitude weights without latitude dimension
solution = reference_solution('cdms', 'equal')
data = solution['sst'](latitude=slice(0, 1), squeeze=True)
- solver = self.solver_class(data, weights='coslat')
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data, weights='coslat')
- @raises(ValueError)
def test_builtin_area_weights_with_missing_dimension(self):
# area weights without longitude dimension
solution = reference_solution('cdms', 'equal')
data = solution['sst'](longitude=slice(0, 1), squeeze=True)
- solver = self.solver_class(data, weights='area')
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data, weights='area')
- @raises(ValueError)
def test_builtin_area_weights_with_non_adjacent_dimensions(self):
# area weights with latitude and longitude not adjacent in input
solution = reference_solution('cdms', 'equal')
@@ -319,7 +317,8 @@ def test_builtin_area_weights_with_non_adjacent_dimensions(self):
axes=data.getAxisList() + [newdim],
id=data.id)
data = data.reorder('txzy')
- solver = self.solver_class(data, weights='area')
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data, weights='area')
# ----------------------------------------------------------------------------
@@ -334,62 +333,61 @@ def setup_class(cls):
try:
cls.solver_class = solvers['iris']
except KeyError:
- raise SkipTest('library component not available '
- 'for iris interface')
+ pytest.skip('missing dependencies required to test the '
+ 'iris interface')
- @raises(TypeError)
def test_wrong_input_type(self):
# input of the wrong type
solution = reference_solution('standard', 'equal')
data = solution['sst']
- solver = self.solver_class(data)
+ with pytest.raises(TypeError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_input_without_time_dimension(self):
# no time dimension in the input
solution = reference_solution('iris', 'equal')
data = solution['sst'][0]
- solver = self.solver_class(data)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_input_time_dimension_not_first(self):
# time not the first dimension in the input
solution = reference_solution('iris', 'equal')
data = solution['sst']
data.transpose((2, 1, 0))
- solver = self.solver_class(data)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_input_no_spatial_dimensions(self):
# not enough dimensions in the input
solution = reference_solution('iris', 'equal')
data = solution['sst'][:, 0, 0]
- solver = self.solver_class(data)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_invalid_builtin_weights_value(self):
# invalid weighting scheme name
solution = reference_solution('iris', 'equal')
data = solution['sst']
- solver = self.solver_class(data, weights='invalid')
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data, weights='invalid')
- @raises(ValueError)
def test_builtin_latitude_weights_with_missing_dimension(self):
# latitude weights without latitude dimension
solution = reference_solution('iris', 'latitude')
data = solution['sst'][:, 0, :]
data.remove_coord('latitude')
- solver = self.solver_class(data, weights='coslat')
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data, weights='coslat')
- @raises(ValueError)
def test_builtin_area_weights_with_missing_dimension(self):
# latitude weights without latitude dimension
solution = reference_solution('iris', 'area')
data = solution['sst'][:, :, 0]
data.remove_coord('longitude')
- solver = self.solver_class(data, weights='area')
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data, weights='area')
- @raises(ValueError)
def test_multiple_time_dimensions(self):
# multiple dimensions representing time
solution = reference_solution('iris', 'equal')
@@ -397,7 +395,8 @@ def test_multiple_time_dimensions(self):
lon = data.coord('longitude')
lon.rename('time')
lon.units = 'days since 1999-01-01 00:00:0.0'
- solver = self.solver_class(data)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data)
# ----------------------------------------------------------------------------
@@ -412,49 +411,49 @@ def setup_class(cls):
try:
cls.solver_class = solvers['xarray']
except KeyError:
- raise SkipTest('library component not available '
- 'for iris interface')
+ pytest.skip('missing dependencies required to test the '
+ 'xarray interface')
- @raises(TypeError)
def test_wrong_input_type(self):
# input of the wrong type
solution = reference_solution('standard', 'equal')
data = solution['sst']
- solver = self.solver_class(data)
+ with pytest.raises(TypeError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_input_without_time_dimension(self):
# no time dimension in the input
solution = reference_solution('xarray', 'equal')
data = solution['sst'][0]
- solver = self.solver_class(data)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_input_time_dimension_not_first(self):
# time not the first dimension in the input
solution = reference_solution('xarray', 'equal')
data = solution['sst']
- data.transpose((2, 1, 0))
- solver = self.solver_class(data)
+ data = data.transpose('longitude', 'latitude', 'time')
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_input_no_spatial_dimensions(self):
# not enough dimensions in the input
solution = reference_solution('xarray', 'equal')
data = solution['sst'][:, 0, 0]
- solver = self.solver_class(data)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data)
- @raises(ValueError)
def test_invalid_builtin_weights_value(self):
# invalid weighting scheme name
solution = reference_solution('xarray', 'equal')
data = solution['sst']
- solver = self.solver_class(data, weights='invalid')
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data, weights='invalid')
- @raises(ValueError)
def test_multiple_time_dimensions(self):
# multiple dimensions representing time
solution = reference_solution('xarray', 'equal')
data = solution['sst']
data.coords['longitude'].attrs['axis'] = 'T'
- solver = self.solver_class(data)
+ with pytest.raises(ValueError):
+ solver = self.solver_class(data)
diff --git a/lib/eofs/tests/test_multivariate_error_handling.py b/lib/eofs/tests/test_multivariate_error_handling.py
index 0111fd2..190c848 100644
--- a/lib/eofs/tests/test_multivariate_error_handling.py
+++ b/lib/eofs/tests/test_multivariate_error_handling.py
@@ -17,13 +17,12 @@
# along with eofs. If not, see .
from __future__ import (absolute_import, division, print_function) # noqa
-from nose import SkipTest
-from nose.tools import raises
import numpy as np
try:
import cdms2
except ImportError:
pass
+import pytest
import eofs.multivariate as multivariate
from eofs.tests import EofsTest
@@ -53,26 +52,26 @@ def setup_class(cls):
cls.solution = reference_multivariate_solution(cls.interface,
cls.weights)
except ValueError:
- raise SkipTest('library component not available '
- 'for {!s} interface'.format(cls.interface))
+ pytest.skip('missing dependencies required to test '
+ 'the {!s} interface'.format(cls.interface))
cls.neofs = cls.solution['eigenvalues'].shape[0]
try:
cls.solver = solvers[cls.interface](
cls.solution['sst'], weights=cls.solution['weights'])
except KeyError:
- raise SkipTest('library component not available '
- 'for {!s} interface'.format(cls.interface))
+ pytest.skip('missing dependencies required to test '
+ 'the {!s} interface'.format(cls.interface))
- @raises(ValueError)
def test_projectField_wrong_number_fields(self):
- pcs = self.solver.projectField([self.solution['sst'][0]])
+ with pytest.raises(ValueError):
+ pcs = self.solver.projectField([self.solution['sst'][0]])
- @raises(ValueError)
def testProjectField_time_dimension_mixture(self):
sst1, sst2 = self.solution['sst']
sst1 = sst1[0]
sst2 = sst2[0:1]
- pcs = self.solver.projectField([sst1, sst2])
+ with pytest.raises(ValueError):
+ pcs = self.solver.projectField([sst1, sst2])
# ----------------------------------------------------------------------------
@@ -92,16 +91,16 @@ class TestErrorHandlersCDMS(MVErrorHandlersTest):
interface = 'cdms'
weights = 'equal'
- @raises(TypeError)
def test_projectField_wrong_input_type(self):
solution = reference_multivariate_solution('standard', self.weights)
- pcs = self.solver.projectField(solution['sst'])
+ with pytest.raises(TypeError):
+ pcs = self.solver.projectField(solution['sst'])
- @raises(ValueError)
def test_projectField_time_dimension_not_first(self):
sst1, sst2 = self.solution['sst']
sst1 = sst1.reorder('-t')
- pcs = self.solver.projectField([sst1, sst2])
+ with pytest.raises(ValueError):
+ pcs = self.solver.projectField([sst1, sst2])
# ----------------------------------------------------------------------------
@@ -112,16 +111,16 @@ class TestErrorHandlersIris(MVErrorHandlersTest):
interface = 'iris'
weights = 'equal'
- @raises(TypeError)
def test_projectField_wrong_input_type(self):
solution = reference_multivariate_solution('standard', self.weights)
- pcs = self.solver.projectField(solution['sst'])
+ with pytest.raises(TypeError):
+ pcs = self.solver.projectField(solution['sst'])
- @raises(ValueError)
def test_projectField_time_dimension_not_first(self):
sst1, sst2 = self.solution['sst']
sst1.transpose([1, 2, 0])
- pcs = self.solver.projectField([sst1, sst2])
+ with pytest.raises(ValueError):
+ pcs = self.solver.projectField([sst1, sst2])
# ----------------------------------------------------------------------------
@@ -135,27 +134,27 @@ class TestConstructorStandard(EofsTest):
def setup_class(cls):
cls.solver_class = solvers['standard']
- @raises(ValueError)
def test_input_first_dimension_different(self):
solution = reference_multivariate_solution('standard', 'equal')
sst1, sst2 = solution['sst']
sst1 = sst1[0:3]
sst2 = sst2[0:4]
- solver = self.solver_class([sst1, sst2])
+ with pytest.raises(ValueError):
+ solver = self.solver_class([sst1, sst2])
- @raises(ValueError)
def test_wrong_number_weights(self):
solution = reference_multivariate_solution('standard', 'area')
weights1, weights2 = solution['weights']
- solver = self.solver_class(solution['sst'], weights=[weights1])
+ with pytest.raises(ValueError):
+ solver = self.solver_class(solution['sst'], weights=[weights1])
- @raises(ValueError)
def test_incompatible_weights(self):
solution = reference_multivariate_solution('standard', 'area')
weights1, weights2 = solution['weights']
weights2 = weights2[..., :-1]
- solver = self.solver_class(solution['sst'],
- weights=[weights1, weights2])
+ with pytest.raises(ValueError):
+ solver = self.solver_class(solution['sst'],
+ weights=[weights1, weights2])
# ----------------------------------------------------------------------------
@@ -170,33 +169,33 @@ def setup_class(cls):
try:
cls.solver_class = solvers['cdms']
except KeyError:
- raise SkipTest('library component not available '
- 'for cdms interface')
+ pytest.skip('missing dependencies required to test '
+ 'the cdms interface')
- @raises(ValueError)
def test_wrong_number_weights(self):
solution = reference_multivariate_solution('cdms', 'area')
weights1, weights2 = solution['weights']
- solver = self.solver_class(solution['sst'], weights=[weights1])
+ with pytest.raises(ValueError):
+ solver = self.solver_class(solution['sst'], weights=[weights1])
- @raises(TypeError)
def test_wrong_input_type(self):
solution = reference_multivariate_solution('standard', 'equal')
- solver = self.solver_class(solution['sst'])
+ with pytest.raises(TypeError):
+ solver = self.solver_class(solution['sst'])
- @raises(ValueError)
def test_input_time_dimension_not_first(self):
solution = reference_multivariate_solution('cdms', 'equal')
sst1, sst2 = solution['sst']
sst1 = sst1.reorder('-t')
- solver = self.solver_class([sst1, sst2])
+ with pytest.raises(ValueError):
+ solver = self.solver_class([sst1, sst2])
- @raises(ValueError)
def test_input_no_spatial_dimensions(self):
solution = reference_multivariate_solution('cdms', 'equal')
sst1, sst2 = solution['sst']
sst1 = sst1[:, 0, 0]
- solver = self.solver_class([sst1, sst2])
+ with pytest.raises(ValueError):
+ solver = self.solver_class([sst1, sst2])
# ----------------------------------------------------------------------------
@@ -211,30 +210,30 @@ def setup_class(cls):
try:
cls.solver_class = solvers['iris']
except KeyError:
- raise SkipTest('library component not available '
- 'for iris interface')
+ pytest.skip('missing dependencies required to test '
+ 'the iris interface')
- @raises(ValueError)
def test_wrong_number_weights(self):
solution = reference_multivariate_solution('iris', 'area')
weights1, weights2 = solution['weights']
- solver = self.solver_class(solution['sst'], weights=[weights1])
+ with pytest.raises(ValueError):
+ solver = self.solver_class(solution['sst'], weights=[weights1])
- @raises(TypeError)
def test_wrong_input_type(self):
solution = reference_multivariate_solution('standard', 'equal')
- solver = self.solver_class(solution['sst'])
+ with pytest.raises(TypeError):
+ solver = self.solver_class(solution['sst'])
- @raises(ValueError)
def test_input_time_dimension_not_first(self):
solution = reference_multivariate_solution('iris', 'equal')
sst1, sst2 = solution['sst']
sst1.transpose([1, 2, 0])
- solver = self.solver_class([sst1, sst2])
+ with pytest.raises(ValueError):
+ solver = self.solver_class([sst1, sst2])
- @raises(ValueError)
def test_input_no_spatial_dimensions(self):
solution = reference_multivariate_solution('iris', 'equal')
sst1, sst2 = solution['sst']
sst1 = sst1[:, 0, 0]
- solver = self.solver_class([sst1, sst2])
+ with pytest.raises(ValueError):
+ solver = self.solver_class([sst1, sst2])
diff --git a/lib/eofs/tests/test_multivariate_solution.py b/lib/eofs/tests/test_multivariate_solution.py
index b583441..844ff87 100644
--- a/lib/eofs/tests/test_multivariate_solution.py
+++ b/lib/eofs/tests/test_multivariate_solution.py
@@ -17,12 +17,12 @@
# along with eofs. If not, see .
from __future__ import (absolute_import, division, print_function) # noqa
-from nose import SkipTest
import numpy as np
try:
from iris.cube import Cube
-except:
+except ImportError:
pass
+import pytest
import eofs.multivariate as multivariate
from eofs.tests import EofsTest
@@ -55,8 +55,8 @@ def setup_class(cls):
cls.solution = reference_multivariate_solution(cls.interface,
cls.weights)
except ValueError:
- raise SkipTest('library component not available '
- 'for {!s} interface'.format(cls.interface))
+ pytest.skip('missing dependencies required to test '
+ 'the {!s} interface'.format(cls.interface))
cls.neofs = cls.solution['eigenvalues'].shape[0]
if cls.alternate_weights_arg is not None:
weights = cls.alternate_weights_arg
@@ -66,19 +66,16 @@ def setup_class(cls):
cls.solver = solvers[cls.interface](cls.solution['sst'],
weights=weights)
except KeyError:
- raise SkipTest('library component not available '
- 'for {!s} interface'.format(cls.interface))
+ pytest.skip('missing dependencies required to test '
+ 'the {!s} interface'.format(cls.interface))
def test_eigenvalues(self):
self.assert_array_almost_equal(
self.solver.eigenvalues(neigs=self.neofs),
self.solution['eigenvalues'])
- def test_eofs(self):
- for eofscaling in (0, 1, 2):
- yield self.check_eofs, eofscaling
-
- def check_eofs(self, eofscaling):
+ @pytest.mark.parametrize('eofscaling', (0, 1, 2))
+ def test_eofs(self, eofscaling):
eofs = [self._tomasked(e)
for e in self.solver.eofs(neofs=self.neofs,
eofscaling=eofscaling)]
@@ -123,12 +120,8 @@ def test_eofsAsCorrelation_range(self):
for e in eofs:
self.assert_true(np.abs(e).max() < 1.000000001)
- def test_pcs(self):
- # generate PCs tests for each value of the scaling parameter
- for pcscaling in (0, 1, 2):
- yield self.check_pcs, pcscaling
-
- def check_pcs(self, pcscaling):
+ @pytest.mark.parametrize('pcscaling', (0, 1, 2))
+ def test_pcs(self, pcscaling):
# PCs should match the (possibly scaled) reference solution
pcs = self._tomasked(self.solver.pcs(npcs=self.neofs,
pcscaling=pcscaling))
@@ -141,12 +134,8 @@ def check_pcs(self, pcscaling):
rpcs *= np.sqrt(reigs)
self.assert_array_almost_equal(pcs, rpcs)
- def test_pcs_uncorrelated(self):
- # generate PC correlation tests for each value of the scaling parameter
- for pcscaling in (0, 1, 2):
- yield self.check_pcs_uncorrelated, pcscaling
-
- def check_pcs_uncorrelated(self, pcscaling):
+ @pytest.mark.parametrize('pcscaling', (0, 1, 2))
+ def test_pcs_uncorrelated(self, pcscaling):
# PCs should be uncorrelated in time
pcs = self._tomasked(self.solver.pcs(npcs=self.neofs,
pcscaling=pcscaling))
@@ -186,24 +175,15 @@ def test_getWeights(self):
if weight is not None:
self.assert_array_almost_equal(weight, ref)
- def test_northTest(self):
- # generate tests for typical errors in the scaled and non-scaled cases
- for vfscaled in (True, False):
- yield self.check_northTest, vfscaled
-
- def check_northTest(self, vfscaled):
+ @pytest.mark.parametrize('vfscaled', (True, False))
+ def test_northTest(self, vfscaled):
# typical errors should match the reference solution
errs = self.solver.northTest(neigs=self.neofs, vfscaled=vfscaled)
error_name = 'scaled_errors' if vfscaled else 'errors'
self.assert_array_almost_equal(errs, self.solution[error_name])
- def test_projectField(self):
- # generate tests for projecting a field onto the EOFs using each value
- # of the scaling parameter
- for eofscaling in (0, 1, 2):
- yield self.check_projectField, eofscaling
-
- def check_projectField(self, eofscaling):
+ @pytest.mark.parametrize('eofscaling', (0, 1, 2))
+ def test_projectField(self, eofscaling):
# original input projected onto the EOFs should match the reference
# solution PCs
pcs = self._tomasked(self.solver.projectField(self.solution['sst'],
diff --git a/lib/eofs/tests/test_solution.py b/lib/eofs/tests/test_solution.py
index 4edb627..6739c35 100644
--- a/lib/eofs/tests/test_solution.py
+++ b/lib/eofs/tests/test_solution.py
@@ -17,13 +17,13 @@
# along with eofs. If not, see .
from __future__ import (absolute_import, division, print_function) # noqa
-from nose import SkipTest
import numpy as np
import numpy.ma as ma
try:
from iris.cube import Cube
-except:
+except ImportError:
pass
+import pytest
import eofs
from eofs.tests import EofsTest
@@ -44,7 +44,7 @@
pass
try:
solvers['xarray'] = eofs.xarray.Eof
-except:
+except AttributeError:
pass
@@ -59,8 +59,8 @@ def setup_class(cls):
try:
cls.solution = reference_solution(cls.interface, cls.weights)
except ValueError:
- raise SkipTest('library component not available '
- 'for {!s} interface'.format(cls.interface))
+ pytest.skip('missing dependencies required to test '
+ 'the {!s} interface'.format(cls.interface))
cls.modify_solution()
cls.neofs = cls.solution['eigenvalues'].shape[0]
if cls.alternate_weights_arg is not None:
@@ -71,8 +71,8 @@ def setup_class(cls):
cls.solver = solvers[cls.interface](cls.solution['sst'],
weights=weights)
except KeyError:
- raise SkipTest('library component not available '
- 'for {!s} interface'.format(cls.interface))
+ pytest.skip('missing dependencies required to test '
+ 'the {!s} interface'.format(cls.interface))
@classmethod
def modify_solution(cls):
@@ -83,12 +83,8 @@ def test_eigenvalues(self):
self.solver.eigenvalues(neigs=self.neofs),
self.solution['eigenvalues'])
- def test_eofs(self):
- # generate EOF tests for each value of the scaling parameter
- for eofscaling in (0, 1, 2):
- yield self.check_eofs, eofscaling
-
- def check_eofs(self, eofscaling):
+ @pytest.mark.parametrize('eofscaling', (0, 1, 2))
+ def test_eofs(self, eofscaling):
# EOFs should match the (possibly scaled) reference solution
eofs = self._tomasked(self.solver.eofs(neofs=self.neofs,
eofscaling=eofscaling))
@@ -101,13 +97,8 @@ def check_eofs(self, eofscaling):
reofs *= np.sqrt(reigs)[:, np.newaxis, np.newaxis]
self.assert_array_almost_equal(eofs, reofs)
- def test_eofs_orthogonal(self):
- # generate EOF orthogonality tests for each value of the scaling
- # parameter
- for eofscaling in (0, 1, 2):
- yield self.check_eofs_orthogonal, eofscaling
-
- def check_eofs_orthogonal(self, eofscaling):
+ @pytest.mark.parametrize('eofscaling', (0, 1, 2))
+ def test_eofs_orthogonal(self, eofscaling):
# EOFs should be mutually orthogonal
eofs = self._tomasked(self.solver.eofs(neofs=self.neofs,
eofscaling=eofscaling))
@@ -141,12 +132,8 @@ def test_eofsAsCorrelation_range(self):
eofs = self._tomasked(self.solver.eofsAsCorrelation(neofs=self.neofs))
self.assert_true(np.abs(eofs).max() < 1.000000001)
- def test_pcs(self):
- # generate PCs tests for each value of the scaling parameter
- for pcscaling in (0, 1, 2):
- yield self.check_pcs, pcscaling
-
- def check_pcs(self, pcscaling):
+ @pytest.mark.parametrize('pcscaling', (0, 1, 2))
+ def test_pcs(self, pcscaling):
# PCs should match the (possibly scaled) reference solution
pcs = self._tomasked(self.solver.pcs(npcs=self.neofs,
pcscaling=pcscaling))
@@ -159,12 +146,8 @@ def check_pcs(self, pcscaling):
rpcs *= np.sqrt(reigs)
self.assert_array_almost_equal(pcs, rpcs)
- def test_pcs_uncorrelated(self):
- # generate PC correlation tests for each value of the scaling parameter
- for pcscaling in (0, 1, 2):
- yield self.check_pcs_uncorrelated, pcscaling
-
- def check_pcs_uncorrelated(self, pcscaling):
+ @pytest.mark.parametrize('pcscaling', (0, 1, 2))
+ def test_pcs_uncorrelated(self, pcscaling):
# PCs should be uncorrelated in time
pcs = self._tomasked(self.solver.pcs(npcs=self.neofs,
pcscaling=pcscaling))
@@ -192,12 +175,8 @@ def test_getWeights(self):
if weights is not None:
self.assert_array_almost_equal(weights, self.solution['weights'])
- def test_northTest(self):
- # generate tests for typical errors in the scaled and non-scaled cases
- for vfscaled in (True, False):
- yield self.check_northTest, vfscaled
-
- def check_northTest(self, vfscaled):
+ @pytest.mark.parametrize('vfscaled', (True, False))
+ def test_northTest(self, vfscaled):
# typical errors should match the reference solution
errs = self.solver.northTest(neigs=self.neofs, vfscaled=vfscaled)
error_name = 'scaled_errors' if vfscaled else 'errors'
@@ -212,13 +191,8 @@ def test_reconstructedField_arb(self):
sst = self.solver.reconstructedField([1, 2, 5])
self.assert_array_almost_equal(sst, self.solution['rcon'])
- def test_projectField(self):
- # generate tests for projecting a field onto the EOFs using each value
- # of the scaling parameter
- for eofscaling in (0, 1, 2):
- yield self.check_projectField, eofscaling
-
- def check_projectField(self, eofscaling):
+ @pytest.mark.parametrize('eofscaling', (0, 1, 2))
+ def test_projectField(self, eofscaling):
# original input projected onto the EOFs should match the reference
# solution PCs
pcs = self._tomasked(self.solver.projectField(self.solution['sst'],
@@ -312,7 +286,7 @@ class XarraySolutionTest(SolutionTest):
def _tomasked(self, value):
try:
return ma.masked_invalid(value.values)
- except:
+ except AttributeError:
return ma.masked_invalid(value)
diff --git a/lib/eofs/tests/test_tools.py b/lib/eofs/tests/test_tools.py
index 2fd3bec..0e152cf 100644
--- a/lib/eofs/tests/test_tools.py
+++ b/lib/eofs/tests/test_tools.py
@@ -17,14 +17,13 @@
# along with eofs. If not, see .
from __future__ import (absolute_import, division, print_function) # noqa
-from nose import SkipTest
-from nose.tools import raises
import numpy as np
import numpy.ma as ma
try:
from iris.cube import Cube
except ImportError:
pass
+import pytest
import eofs
from eofs.tests import EofsTest
@@ -63,8 +62,8 @@ def setup_class(cls):
try:
cls.solution = reference_solution(cls.interface, cls.weights)
except ValueError:
- raise SkipTest('library component not available '
- 'for {!s} interface'.format(cls.interface))
+ pytest.skip('missing dependencies required to test '
+ 'the {!s} interface'.format(cls.interface))
cls.neofs = cls.solution['eigenvalues'].shape[0]
try:
cls.solver = solvers[cls.interface](
@@ -72,8 +71,8 @@ def setup_class(cls):
cls.tools = {'covariance': tools[cls.interface].covariance_map,
'correlation': tools[cls.interface].correlation_map}
except KeyError:
- raise SkipTest('library component not available '
- 'for {!s} interface'.format(cls.interface))
+ pytest.skip('missing dependencies required to test '
+ 'the {!s} interface'.format(cls.interface))
def test_covariance_map(self):
# covariance maps should match reference EOFs as covariance
@@ -113,29 +112,19 @@ def test_correlation_map_point(self):
cor = self._tomasked(cor) * sign_adjustments(eofs, reofs)[0]
self.assert_array_almost_equal(cor, self.solution['eofscor'][0, 5, 5])
- def test_covcor_map_invalid_time_dimension(self):
- # generate tests for covariance/correlation maps with invalid time
- # dimensions
- for maptype in ('covariance', 'correlation'):
- yield self.check_covcor_map_invalid_time_dimension, maptype
-
- @raises(ValueError)
- def check_covcor_map_invalid_time_dimension(self, maptype):
+ @pytest.mark.parametrize('maptype', ('covariance', 'correlation'))
+ def test_covcor_map_invalid_time_dimension(self, maptype):
# compute a map with an invalid time dimension in the input
pcs = self.solver.pcs(npcs=self.neofs, pcscaling=1)[:-1]
- covcor = self.tools[maptype](pcs, self.solution['sst'])
-
- def test_covcor_map_invalid_pc_shape(self):
- # generate tests for covariance/correlation maps with input PCs with
- # invalid shape
- for maptype in ('covariance', 'correlation'):
- yield self.check_covcor_map_invalid_pc_shape, maptype
+ with pytest.raises(ValueError):
+ covcor = self.tools[maptype](pcs, self.solution['sst'])
- @raises(ValueError)
- def check_covcor_map_invalid_pc_shape(self, maptype):
+ @pytest.mark.parametrize('maptype', ('covariance', 'correlation'))
+ def test_covcor_map_invalid_pc_shape(self, maptype):
# compute a map for PCs with invalid shape
- covcor = self.tools[maptype](self.solution['sst'],
- self.solution['sst'])
+ with pytest.raises(ValueError):
+ covcor = self.tools[maptype](self.solution['sst'],
+ self.solution['sst'])
# ----------------------------------------------------------------------------
@@ -194,5 +183,5 @@ class TestToolsXarray(ToolsTest):
def _tomasked(self, value):
try:
return ma.masked_invalid(value.values)
- except:
+ except AttributeError:
return ma.masked_invalid(value)
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..0aeb9d2
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,3 @@
+[tool:pytest]
+addopts = -vrsx
+testpaths = lib