From a2102215c62a53a18c2d7edd6ca9cfbf3947558d Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Thu, 22 Aug 2024 20:18:07 +0200 Subject: [PATCH 1/7] first draft of the variable creation tests --- xarray_array_testing/creation.py | 17 +++++++++++++++++ xarray_array_testing/tests/test_numpy.py | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 xarray_array_testing/creation.py create mode 100644 xarray_array_testing/tests/test_numpy.py diff --git a/xarray_array_testing/creation.py b/xarray_array_testing/creation.py new file mode 100644 index 0000000..16f72b1 --- /dev/null +++ b/xarray_array_testing/creation.py @@ -0,0 +1,17 @@ +import hypothesis.strategies as st +import xarray.testing.strategies as xrst +from hypothesis import given + + +class CreationTests: + array_type: type + + @staticmethod + def array_strategy_fn(*, shape, dtype): + raise NotImplementedError + + @given(st.data()) + def test_create_variable(self, data): + variable = data.draw(xrst.variables(array_strategy_fn=self.array_strategy_fn)) + + assert isinstance(variable.data, self.array_type) diff --git a/xarray_array_testing/tests/test_numpy.py b/xarray_array_testing/tests/test_numpy.py new file mode 100644 index 0000000..69aea0a --- /dev/null +++ b/xarray_array_testing/tests/test_numpy.py @@ -0,0 +1,17 @@ +import hypothesis.strategies as st +import numpy as np + +from xarray_array_testing.creation import CreationTests + + +def create_numpy_array(*, shape, dtype): + return st.builds(np.ones, shape=st.just(shape), dtype=st.just(dtype)) + + +class TestCreationNumpy(CreationTests): + array_type = np.ndarray + array_module = np + + @staticmethod + def array_strategy_fn(*, shape, dtype): + return create_numpy_array(shape=shape, dtype=dtype) From fa78d47d3978bb915edd8f9fe29b0ce920dd3eeb Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Thu, 22 Aug 2024 20:21:44 +0200 Subject: [PATCH 2/7] reduction tests --- xarray_array_testing/reduction.py | 43 ++++++++++++++++++++++++ xarray_array_testing/tests/test_numpy.py | 9 +++++ 2 files changed, 52 insertions(+) create mode 100644 xarray_array_testing/reduction.py diff --git a/xarray_array_testing/reduction.py b/xarray_array_testing/reduction.py new file mode 100644 index 0000000..c72f6b8 --- /dev/null +++ b/xarray_array_testing/reduction.py @@ -0,0 +1,43 @@ +from contextlib import nullcontext +from types import ModuleType + +import hypothesis.strategies as st +import numpy as np +import xarray.testing.strategies as xrst +from hypothesis import given + + +class ReductionTests: + xp: ModuleType + + @staticmethod + def array_strategy_fn(*, shape, dtype): + raise NotImplementedError + + @staticmethod + def assert_equal(a, b): + np.testing.assert_allclose(a, b) + + @staticmethod + def expected_errors(op, **parameters): + return nullcontext() + + @given(st.data()) + def test_variable_mean(self, data): + variable = data.draw(xrst.variables(array_strategy_fn=self.array_strategy_fn)) + + with self.expected_errors("mean", variable=variable): + actual = variable.mean().data + expected = self.xp.mean(variable.data) + + self.assert_equal(actual, expected) + + @given(st.data()) + def test_variable_prod(self, data): + variable = data.draw(xrst.variables(array_strategy_fn=self.array_strategy_fn)) + + with self.expected_errors("prod", variable=variable): + actual = variable.prod().data + expected = self.xp.prod(variable.data) + + self.assert_equal(actual, expected) diff --git a/xarray_array_testing/tests/test_numpy.py b/xarray_array_testing/tests/test_numpy.py index 69aea0a..c88a56e 100644 --- a/xarray_array_testing/tests/test_numpy.py +++ b/xarray_array_testing/tests/test_numpy.py @@ -2,6 +2,7 @@ import numpy as np from xarray_array_testing.creation import CreationTests +from xarray_array_testing.reduction import ReductionTests def create_numpy_array(*, shape, dtype): @@ -15,3 +16,11 @@ class TestCreationNumpy(CreationTests): @staticmethod def array_strategy_fn(*, shape, dtype): return create_numpy_array(shape=shape, dtype=dtype) + + +class TestReductionNumpy(ReductionTests): + xp = np + + @staticmethod + def array_strategy_fn(*, shape, dtype): + return create_numpy_array(shape=shape, dtype=dtype) From 7cbca87b97b9f4216bd12f99f410f4aa3a0fcd72 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Thu, 22 Aug 2024 20:35:16 +0200 Subject: [PATCH 3/7] missing test dependency --- ci/requirements/environment.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/requirements/environment.yaml b/ci/requirements/environment.yaml index af4f6f8..c820588 100644 --- a/ci/requirements/environment.yaml +++ b/ci/requirements/environment.yaml @@ -7,6 +7,7 @@ dependencies: - pre-commit - pytest - pytest-reportlog + - pytest-cov - hypothesis - xarray - numpy From a4b62b95d23dc10aa2010ca23797de037482d370 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Thu, 22 Aug 2024 20:35:27 +0200 Subject: [PATCH 4/7] also allow python 3.11 --- ci/requirements/environment.yaml | 1 - pyproject.toml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ci/requirements/environment.yaml b/ci/requirements/environment.yaml index c820588..51bdd94 100644 --- a/ci/requirements/environment.yaml +++ b/ci/requirements/environment.yaml @@ -2,7 +2,6 @@ name: xarray-array-testing-tests channels: - conda-forge dependencies: - - python=3.12 - ipython - pre-commit - pytest diff --git a/pyproject.toml b/pyproject.toml index cb76175..ef441bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "xarray-array-testing" -requires-python = ">= 3.12" +requires-python = ">= 3.11" license = {text = "Apache-2.0"} dependencies = [ "hypothesis", From 4b506645356d930661ee6c8a2fa7d2811ced0695 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Thu, 22 Aug 2024 20:42:54 +0200 Subject: [PATCH 5/7] remove left-over class attribute --- xarray_array_testing/tests/test_numpy.py | 1 - 1 file changed, 1 deletion(-) diff --git a/xarray_array_testing/tests/test_numpy.py b/xarray_array_testing/tests/test_numpy.py index c88a56e..44c2345 100644 --- a/xarray_array_testing/tests/test_numpy.py +++ b/xarray_array_testing/tests/test_numpy.py @@ -11,7 +11,6 @@ def create_numpy_array(*, shape, dtype): class TestCreationNumpy(CreationTests): array_type = np.ndarray - array_module = np @staticmethod def array_strategy_fn(*, shape, dtype): From b2db0dc8526cb6a5c324eec45b063c957209c5d1 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Thu, 22 Aug 2024 21:30:33 +0200 Subject: [PATCH 6/7] move the configuration hooks to a abstract base class --- pyproject.toml | 2 +- xarray_array_testing/base.py | 26 ++++++++++++++++++++++++ xarray_array_testing/creation.py | 8 ++------ xarray_array_testing/reduction.py | 14 ++----------- xarray_array_testing/tests/test_numpy.py | 21 ++++++++++++------- 5 files changed, 45 insertions(+), 26 deletions(-) create mode 100644 xarray_array_testing/base.py diff --git a/pyproject.toml b/pyproject.toml index ef441bb..2d387a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,7 @@ ignore = [ "E501", # E501: line too long - let black worry about that "E731", # E731: do not assign a lambda expression, use a def ] -fixable = ["I"] +fixable = ["I", "TID"] extend-safe-fixes = [ "TID252", # absolute imports ] diff --git a/xarray_array_testing/base.py b/xarray_array_testing/base.py new file mode 100644 index 0000000..a052583 --- /dev/null +++ b/xarray_array_testing/base.py @@ -0,0 +1,26 @@ +import abc +from abc import ABC +from types import ModuleType + +import numpy.testing as npt + + +class DuckArrayTestMixin(ABC): + @property + @abc.abstractmethod + def xp() -> ModuleType: + pass + + @property + @abc.abstractmethod + def array_type(self) -> type: + pass + + @staticmethod + @abc.abstractmethod + def array_strategy_fn(*, shape, dtype): + raise NotImplementedError("has to be overridden") + + @staticmethod + def assert_equal(a, b): + npt.assert_equal(a, b) diff --git a/xarray_array_testing/creation.py b/xarray_array_testing/creation.py index 16f72b1..291e082 100644 --- a/xarray_array_testing/creation.py +++ b/xarray_array_testing/creation.py @@ -2,14 +2,10 @@ import xarray.testing.strategies as xrst from hypothesis import given +from xarray_array_testing.base import DuckArrayTestMixin -class CreationTests: - array_type: type - - @staticmethod - def array_strategy_fn(*, shape, dtype): - raise NotImplementedError +class CreationTests(DuckArrayTestMixin): @given(st.data()) def test_create_variable(self, data): variable = data.draw(xrst.variables(array_strategy_fn=self.array_strategy_fn)) diff --git a/xarray_array_testing/reduction.py b/xarray_array_testing/reduction.py index c72f6b8..8e58949 100644 --- a/xarray_array_testing/reduction.py +++ b/xarray_array_testing/reduction.py @@ -1,23 +1,13 @@ from contextlib import nullcontext -from types import ModuleType import hypothesis.strategies as st -import numpy as np import xarray.testing.strategies as xrst from hypothesis import given +from xarray_array_testing.base import DuckArrayTestMixin -class ReductionTests: - xp: ModuleType - - @staticmethod - def array_strategy_fn(*, shape, dtype): - raise NotImplementedError - - @staticmethod - def assert_equal(a, b): - np.testing.assert_allclose(a, b) +class ReductionTests(DuckArrayTestMixin): @staticmethod def expected_errors(op, **parameters): return nullcontext() diff --git a/xarray_array_testing/tests/test_numpy.py b/xarray_array_testing/tests/test_numpy.py index 44c2345..f6a26e9 100644 --- a/xarray_array_testing/tests/test_numpy.py +++ b/xarray_array_testing/tests/test_numpy.py @@ -1,6 +1,7 @@ import hypothesis.strategies as st import numpy as np +from xarray_array_testing.base import DuckArrayTestMixin from xarray_array_testing.creation import CreationTests from xarray_array_testing.reduction import ReductionTests @@ -9,17 +10,23 @@ def create_numpy_array(*, shape, dtype): return st.builds(np.ones, shape=st.just(shape), dtype=st.just(dtype)) -class TestCreationNumpy(CreationTests): - array_type = np.ndarray +class NumpyTestMixin(DuckArrayTestMixin): + @property + def xp(self): + return np + + @property + def array_type(self): + return np.ndarray @staticmethod def array_strategy_fn(*, shape, dtype): return create_numpy_array(shape=shape, dtype=dtype) -class TestReductionNumpy(ReductionTests): - xp = np +class TestCreationNumpy(CreationTests, NumpyTestMixin): + pass - @staticmethod - def array_strategy_fn(*, shape, dtype): - return create_numpy_array(shape=shape, dtype=dtype) + +class TestReductionNumpy(ReductionTests, NumpyTestMixin): + pass From 4b6fc19f2487bc5d96acaa1e7f8fb2e7a965da44 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Thu, 22 Aug 2024 21:42:32 +0200 Subject: [PATCH 7/7] typing --- xarray_array_testing/base.py | 3 ++- xarray_array_testing/tests/test_numpy.py | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/xarray_array_testing/base.py b/xarray_array_testing/base.py index a052583..2d04dbd 100644 --- a/xarray_array_testing/base.py +++ b/xarray_array_testing/base.py @@ -3,6 +3,7 @@ from types import ModuleType import numpy.testing as npt +from xarray.namedarray._typing import duckarray class DuckArrayTestMixin(ABC): @@ -13,7 +14,7 @@ def xp() -> ModuleType: @property @abc.abstractmethod - def array_type(self) -> type: + def array_type(self) -> type[duckarray]: pass @staticmethod diff --git a/xarray_array_testing/tests/test_numpy.py b/xarray_array_testing/tests/test_numpy.py index f6a26e9..2a9d95b 100644 --- a/xarray_array_testing/tests/test_numpy.py +++ b/xarray_array_testing/tests/test_numpy.py @@ -1,3 +1,5 @@ +from types import ModuleType + import hypothesis.strategies as st import numpy as np @@ -12,11 +14,11 @@ def create_numpy_array(*, shape, dtype): class NumpyTestMixin(DuckArrayTestMixin): @property - def xp(self): + def xp(self) -> ModuleType: return np @property - def array_type(self): + def array_type(self) -> type[np.ndarray]: return np.ndarray @staticmethod