-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Duckarray tests for constructors and properties #6903
Open
TomNicholas
wants to merge
157
commits into
pydata:main
Choose a base branch
from
TomNicholas:duckarray-tests-constructors
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
157 commits
Select commit
Hold shift + click to select a range
21696bf
add a initial, tiny draft of the automatic duckarray test machinery
keewis f14ba29
add missing comma
keewis 90f9c41
fix the global marks
keewis aa4a457
don't try to apply marks if marks is None
keewis 9fa2eca
only set pytestmark if the value is not None
keewis 7994bad
skip the module if pint is not installed
keewis c4a35f0
filter UnitStrippedWarnings
keewis 0efbbbb
also test sparse
keewis 73499b5
add a test for the test extractor
keewis 532f213
move the selector parsing code to a new function
keewis f44aafa
also skip the sparse tests
keewis d651438
move the utils tests into a different file
keewis f84894a
don't keep the utils tests in a test group
keewis 0090db5
split apply_marks into two separate functions
keewis ef05c7d
add a mark which attaches marks to test variants
keewis 20334d9
move the duckarray testing module to tests
keewis f7acc0f
move the utils to a separate module
keewis e41a15b
fix the existing tests
keewis 1f095a1
completely isolate the apply_marks tests
keewis 2503af7
add a test for applying marks to test variants
keewis b229645
skip failing test variants
keewis 0723418
fix the import path
keewis 6c4ccb0
rename the duckarray testing module
keewis c4aa05a
use Variable as example
keewis fc97e90
fix the skips
keewis 31e577a
only use dimensionless for cumprod
keewis 8d80212
also test dask wrapped by pint
keewis 7c43e91
add a function to concatenate mappings
keewis b6a90df
add tests for preprocess_marks
keewis a95b5c4
fix the tests
keewis aa7caaa
show the duplicates in the error message
keewis 6415be8
add back support for test marks
keewis de25594
allow passing a list of addition assert functions
keewis 1b0f372
add some notes about the test suite
keewis 706ee54
simplify the extra_assert function
keewis caf6308
Merge branch 'master' into duckarray-tests
keewis cd5aa70
convert to hypothesis
keewis 08d72ed
add a marker to convert label-space parameters
keewis 0649d59
add a dummy expect_error function
keewis c32cb5a
compute actual before expected
keewis 440e0bd
pass a strategy instead of a single dtype
keewis f74a29c
set a default for expect_error
keewis d9346f8
add a test for clip
keewis 75f584a
allow passing a separate "create_label" function
keewis b94c84d
draft the base class hierarchy tailored after pandas' extension array…
keewis 7a150f8
make sure multiple dims are passed as a list
keewis a6eecb8
sort the dtypes differently
keewis dcb9fc0
add a strategy to generate a single axis
keewis 0ee096d
add a function to compute the axes from the dims
keewis 53debb2
move the call of the operation to a hook
keewis 9b2c0a3
remove the arg* methods since they are not reducing anything
keewis a6efbe1
add a context manager to suppress specific warnings
keewis 5d679bf
don't try to reduce along multiple dimensions
keewis 50db3c3
demonstrate the new pattern using pint
keewis 8114d2a
fix the sparse tests
keewis 6afc7c3
Merge branch 'duckarray-testing-baseclasses' into duckarray-tests
keewis 2f084d0
Merge branch 'master' into duckarray-tests
keewis 14349f2
back to only raising for UnitStrippedWarning
keewis 6a658ef
remove the old duckarray testing module
keewis d595cd6
rename the tests
keewis 2b0dcba
add a mark to skip individual test nodes
keewis 6b61900
skip the prod and std tests
keewis b9535a1
skip all sparse tests for now
keewis c675f8d
also skip var
keewis 6e7c538
add a duckarray base class
keewis e0ee7a6
move the strategies to a separate file and add a variable strategy
keewis 3feef1c
add a simple DataArray strategy and use it in the DataArray tests
keewis 2a70c38
use the DataArray reduce tests with pint
keewis 6a18acf
add a simple strategy to create Dataset objects
keewis 835930c
fix the variable strategy
keewis 0a5c487
adjust the dataset strategy
keewis d1184a4
parametrize the dataset strategy
keewis 12b5527
fix some of the pint testing utils
keewis 1f95318
use flatmap to define the data_vars strategy
keewis 9800db5
add tests for dataset reduce
keewis c43f35e
demonstrate the use of the dataset reduce tests using pint
keewis d1b541e
simplify check_reduce
keewis 19d9d96
remove apparently unnecessary skips
keewis 69e0624
skip the tests if hypothesis is missing
keewis c7f6677
update the sparse tests
keewis 396c2ba
add DataArray and Dataset tests for sparse
keewis ead706e
fix attach_units
keewis 3cf9523
rename the test classes
keewis cd132c6
update a few strategies
keewis 1c310b0
fix the strategies and utils
keewis 7f879b0
use allclose instead of identical to compare
keewis ff91be8
don't provide a default for shape
keewis cb286ef
remove the function to generate dimension names
keewis 438f8a5
simplify the generation of the dimension sizes
keewis 01814ff
immediately draw the computed dimension sizes
keewis 0f1222e
convert the sizes to a dict when making sure data vars are not dims
keewis a38a307
align the default maximum number of dimensions
keewis ea3d015
draw the data before passing it to DataArray
keewis afa33ac
directly generate the reduce dimensions
keewis 566627a
Merge branch 'master' into duckarray-tests
keewis 2e0c6bf
disable dim=[] / axis=() because that's not supported by all duckarrays
keewis 01599b7
skip the sparse tests
keewis 259e1d5
typo
keewis 527b17c
use a single dtype for all variables of a dataset
keewis 3437c3d
specify tolerances per dtype
keewis 4866801
abandon the notion of single_dtype=True
keewis 8019a20
limit the values and add dtype specific tolerances
keewis cc75b46
Merge branch 'main' into duckarray-tests
keewis 566470b
Merge branch 'main' into duckarray-tests
keewis b0e94f1
disable bottleneck
keewis e57cd7b
Merge branch 'main' into duckarray-tests
keewis 33f63a7
reduce the maximum number of dims, dim sizes, and variables
keewis 11d41e3
disable bottleneck for the sparse tests
keewis 71a37ba
try activating the sparse tests
keewis 1d98fec
propagate the dtypes
keewis a826249
Merge remote-tracking branch 'upstream/main' into duckarray-tests
dcherian f2cd8a1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 21a5838
Merge branch 'main' into duckarray-tests
dcherian c747733
Turn off deadlines
dcherian 3f81995
Disable float16 tests.
dcherian a282686
Use as_numpy in as_dense
dcherian ede0045
Merge branch 'main' into duckarray-tests
dcherian cbf408c
Merge branch 'main' into duckarray-tests
keewis f5b9bdc
move the hypothesis importorskip to before the strategy definitions
keewis ed68dc2
properly filter out float16 dtypes for sparse
keewis 1e4f18e
also filter out complex64 because there seems to be a bug in sparse
keewis da2225f
Merge branch 'main' into duckarray-tests
dcherian 37622c5
Merge branch 'main' into duckarray-tests
dcherian 86377e6
Update xarray/tests/duckarrays/test_sparse.py
dcherian 5af49d8
use the proper base to check the dtypes
keewis 8d0a8c3
Merge branch 'main' into duckarray-tests
TomNicholas 50151a4
make sure the importorskip call is before any hypothesis imports
keewis 707aecb
remove the op parameter to create
keewis aa0f9c3
merge removal of ops arg
TomNicholas 4b51ce4
strategy for creating duck array objects
TomNicholas a984718
base class for testing creation of Variable object
TomNicholas 8711d46
proof-of-principle for testing creation of Variables wrapping sparse …
TomNicholas f40879f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 214084c
generalised generation of dimension names to nd
TomNicholas 4c54984
Merge branch 'duckarray-tests-constructors' of https://github.com/Tom…
TomNicholas e0dd10d
test DataArray constructor with sparse arrays
TomNicholas b8af1e6
Merge branch 'main' into duckarray-tests-constructors
TomNicholas 2aba7bc
move testing framework to testing module
TomNicholas 9c38519
absolute imports
TomNicholas 8b89911
test_units -> test_pint
TomNicholas ab64e5e
first variable constructor tests pass for numpy.array_api
TomNicholas f4dd250
constructor tests now don't use xarray strategies
TomNicholas 626efdf
constructor tests for sparse
TomNicholas ff08473
use new strategies in reduce test and remove old code
TomNicholas 5ab5a74
reinstate strategies module which I accidentally git rmed
TomNicholas ec7f726
reduce tests for numpy array api now pass
TomNicholas 843217e
use suppress_warning utility
TomNicholas 9d585ce
test sparse reductions
TomNicholas 7dc832d
remove old utilities
TomNicholas 32cbdc2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] a002d0b
remove pint tests for now
TomNicholas 52682bd
Merge branch 'duckarray-tests-constructors' of https://github.com/Tom…
TomNicholas bfc3fe7
remove conftest stuff
TomNicholas d23eaec
single class can test variable/dataarray/dataset
TomNicholas 8e3c655
test numpy outside of array API
TomNicholas c07c690
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] d2b35c5
narrow dtypes
TomNicholas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
from abc import abstractmethod | ||
from typing import TYPE_CHECKING | ||
|
||
import hypothesis.extra.numpy as npst | ||
import hypothesis.strategies as st | ||
import numpy as np | ||
import numpy.testing as npt | ||
import pytest | ||
from hypothesis import given, note | ||
|
||
import xarray as xr | ||
import xarray.testing.strategies as xrst | ||
from xarray.core.types import T_DuckArray | ||
from xarray.testing.assertions import assert_identical | ||
|
||
if TYPE_CHECKING: | ||
from xarray.core.types import _DTypeLikeNested, _ShapeLike | ||
|
||
|
||
__all__ = [ | ||
"ConstructorTests", | ||
"ReduceTests", | ||
] | ||
|
||
|
||
class ArrayConstructorChecksMixin: | ||
"""Mixin for checking results of Variable/DataArray constructors.""" | ||
|
||
def check(self, var, arr): | ||
self.check_types(var, arr) | ||
self.check_values(var, arr) | ||
self.check_attributes(var, arr) | ||
|
||
def check_types(self, var, arr): | ||
# test type of wrapped array | ||
assert isinstance( | ||
var.data, type(arr) | ||
), f"found {type(var.data)}, expected {type(arr)}" | ||
|
||
def check_attributes(self, var, arr): | ||
# test ndarray attributes are exposed correctly | ||
assert var.ndim == arr.ndim | ||
assert var.shape == arr.shape | ||
assert var.dtype == arr.dtype | ||
assert var.size == arr.size | ||
|
||
def check_values(self, var, arr): | ||
# test coercion to numpy | ||
npt.assert_equal(var.to_numpy(), np.asarray(arr)) | ||
|
||
|
||
class ConstructorTests(ArrayConstructorChecksMixin): | ||
shapes = npst.array_shapes() | ||
dtypes = xrst.supported_dtypes() | ||
|
||
@staticmethod | ||
@abstractmethod | ||
def array_strategy_fn( | ||
*, shape: "_ShapeLike", dtype: "_DTypeLikeNested" | ||
) -> st.SearchStrategy[T_DuckArray]: | ||
# TODO can we just make this an attribute? | ||
... | ||
|
||
@given(st.data()) | ||
def test_construct_variable(self, data) -> None: | ||
shape = data.draw(self.shapes) | ||
dtype = data.draw(self.dtypes) | ||
arr = data.draw(self.array_strategy_fn(shape=shape, dtype=dtype)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can't use |
||
|
||
dim_names = data.draw( | ||
xrst.dimension_names(min_dims=len(shape), max_dims=len(shape)) | ||
) | ||
var = xr.Variable(data=arr, dims=dim_names) | ||
|
||
self.check(var, arr) | ||
|
||
|
||
def is_real_floating(dtype): | ||
return np.issubdtype(dtype, np.number) and np.issubdtype(dtype, np.floating) | ||
|
||
|
||
class ReduceTests: | ||
dtypes = xrst.supported_dtypes() | ||
|
||
@staticmethod | ||
@abstractmethod | ||
def array_strategy_fn( | ||
*, shape: "_ShapeLike", dtype: "_DTypeLikeNested" | ||
) -> st.SearchStrategy[T_DuckArray]: | ||
# TODO can we just make this an attribute? | ||
... | ||
|
||
def check_reduce(self, var, op, dim, *args, **kwargs): | ||
actual = getattr(var, op)(dim=dim, *args, **kwargs) | ||
|
||
data = np.asarray(var.data) | ||
expected = getattr(var.copy(data=data), op)(*args, **kwargs) | ||
|
||
# create expected result (using nanmean because arrays with Nans will be generated) | ||
reduce_axes = tuple(var.get_axis_num(d) for d in dim) | ||
data = np.asarray(var.data) | ||
expected = getattr(var.copy(data=data), op)(*args, axis=reduce_axes, **kwargs) | ||
|
||
note(f"actual:\n{actual}") | ||
note(f"expected:\n{expected}") | ||
|
||
assert_identical(actual, expected) | ||
|
||
@pytest.mark.parametrize( | ||
"method, dtype_assumption", | ||
( | ||
("all", lambda x: True), # should work for any dtype | ||
("any", lambda x: True), # should work for any dtype | ||
# "cumprod", # not in array API | ||
# "cumsum", # not in array API | ||
("max", is_real_floating), # only in array API for real numeric dtypes | ||
# "median", # not in array API | ||
("min", is_real_floating), # only in array API for real numeric dtypes | ||
("prod", is_real_floating), # only in array API for real numeric dtypes | ||
# "std", # TypeError: std() got an unexpected keyword argument 'ddof' | ||
("sum", is_real_floating), # only in array API for real numeric dtypes | ||
# "var", # TypeError: std() got an unexpected keyword argument 'ddof' | ||
), | ||
) | ||
@given(st.data()) | ||
def test_reduce_variable(self, method, dtype_assumption, data): | ||
""" | ||
Test that the reduction applied to an xarray Variable is always equal | ||
to the same reduction applied to the underlying array. | ||
""" | ||
|
||
narrowed_dtypes = self.dtypes.filter(dtype_assumption) | ||
|
||
var = data.draw( | ||
xrst.variables( | ||
array_strategy_fn=self.array_strategy_fn, | ||
dims=xrst.dimension_names(min_dims=1), | ||
dtype=narrowed_dtypes, | ||
) | ||
) | ||
|
||
# specify arbitrary reduction along at least one dimension | ||
reduce_dims = data.draw(xrst.unique_subset_of(var.dims, min_size=1)) | ||
|
||
self.check_reduce(var, method, dim=reduce_dims) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import warnings | ||
from contextlib import contextmanager | ||
|
||
|
||
@contextmanager | ||
def suppress_warning(category, message=""): | ||
with warnings.catch_warnings(): | ||
warnings.filterwarnings("ignore", category=category, message=message) | ||
|
||
yield |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
from typing import TYPE_CHECKING | ||
|
||
import hypothesis.strategies as st | ||
from hypothesis.extra.array_api import make_strategies_namespace | ||
|
||
from xarray.core.types import T_DuckArray | ||
from xarray.testing import duckarrays | ||
from xarray.testing.utils import suppress_warning | ||
from xarray.tests import _importorskip | ||
|
||
if TYPE_CHECKING: | ||
from xarray.core.types import _DTypeLikeNested, _ShapeLike | ||
|
||
|
||
# ignore the warning that the array_api is experimental raised by numpy | ||
with suppress_warning( | ||
UserWarning, "The numpy.array_api submodule is still experimental. See NEP 47." | ||
): | ||
_importorskip("numpy", "1.26.0") | ||
import numpy.array_api as nxp | ||
|
||
|
||
nxps = make_strategies_namespace(nxp) | ||
|
||
|
||
class TestConstructors(duckarrays.ConstructorTests): | ||
dtypes = nxps.scalar_dtypes() | ||
|
||
@staticmethod | ||
def array_strategy_fn( | ||
shape: "_ShapeLike", | ||
dtype: "_DTypeLikeNested", | ||
) -> st.SearchStrategy[T_DuckArray]: | ||
return nxps.arrays(shape=shape, dtype=dtype) | ||
|
||
|
||
class TestReductions(duckarrays.ReduceTests): | ||
dtypes = nxps.scalar_dtypes() | ||
|
||
@staticmethod | ||
def array_strategy_fn( | ||
shape: "_ShapeLike", | ||
dtype: "_DTypeLikeNested", | ||
) -> st.SearchStrategy[T_DuckArray]: | ||
return nxps.arrays(shape=shape, dtype=dtype) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from typing import TYPE_CHECKING | ||
|
||
import hypothesis.extra.numpy as npst | ||
import hypothesis.strategies as st | ||
|
||
from xarray.core.types import T_DuckArray | ||
from xarray.testing import duckarrays | ||
from xarray.testing.strategies import supported_dtypes | ||
|
||
if TYPE_CHECKING: | ||
from xarray.core.types import _DTypeLikeNested, _ShapeLike | ||
|
||
|
||
class TestConstructors(duckarrays.ConstructorTests): | ||
dtypes = supported_dtypes() | ||
|
||
@staticmethod | ||
def array_strategy_fn( | ||
shape: "_ShapeLike", | ||
dtype: "_DTypeLikeNested", | ||
) -> st.SearchStrategy[T_DuckArray]: | ||
return npst.arrays(shape=shape, dtype=dtype) | ||
|
||
|
||
class TestReductions(duckarrays.ReduceTests): | ||
dtypes = supported_dtypes() | ||
|
||
@staticmethod | ||
def array_strategy_fn( | ||
shape: "_ShapeLike", | ||
dtype: "_DTypeLikeNested", | ||
) -> st.SearchStrategy[T_DuckArray]: | ||
return npst.arrays(shape=shape, dtype=dtype) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
from typing import TYPE_CHECKING | ||
|
||
import hypothesis.extra.numpy as npst | ||
import hypothesis.strategies as st | ||
import numpy as np | ||
import numpy.testing as npt | ||
import pytest | ||
|
||
import xarray.testing.strategies as xrst | ||
from xarray.testing import duckarrays | ||
from xarray.tests import _importorskip | ||
|
||
if TYPE_CHECKING: | ||
from xarray.core.types import _DTypeLikeNested, _ShapeLike | ||
|
||
|
||
_importorskip("sparse") | ||
import sparse | ||
|
||
|
||
@pytest.fixture(autouse=True) | ||
def disable_bottleneck(): | ||
from xarray import set_options | ||
|
||
with set_options(use_bottleneck=False): | ||
yield | ||
|
||
|
||
# sparse does not support float16 | ||
sparse_dtypes = xrst.supported_dtypes().filter( | ||
lambda dtype: (not np.issubdtype(dtype, np.float16)) | ||
) | ||
|
||
|
||
@st.composite | ||
def sparse_arrays_fn( | ||
draw: st.DrawFn, | ||
*, | ||
shape: "_ShapeLike", | ||
dtype: "_DTypeLikeNested", | ||
) -> sparse.COO: | ||
"""When called generates an arbitrary sparse.COO array of the given shape and dtype.""" | ||
np_arr = draw(npst.arrays(dtype, shape)) | ||
|
||
def to_sparse(arr: np.ndarray) -> sparse.COO: | ||
if arr.ndim == 0: | ||
return arr | ||
|
||
return sparse.COO.from_numpy(arr) | ||
|
||
return to_sparse(np_arr) | ||
|
||
|
||
class TestConstructors(duckarrays.ConstructorTests): | ||
dtypes = sparse_dtypes() | ||
|
||
@staticmethod | ||
def array_strategy_fn( | ||
shape: "_ShapeLike", | ||
dtype: "_DTypeLikeNested", | ||
) -> st.SearchStrategy[sparse.COO]: | ||
return sparse_arrays_fn | ||
|
||
def check_values(self, var, arr): | ||
npt.assert_equal(var.to_numpy(), arr.todense()) | ||
|
||
|
||
class TestReductions(duckarrays.ReduceTests): | ||
dtypes = nxps.scalar_dtypes() | ||
|
||
@staticmethod | ||
def array_strategy_fn( | ||
shape: "_ShapeLike", | ||
dtype: "_DTypeLikeNested", | ||
) -> st.SearchStrategy[T_DuckArray]: | ||
return nxps.arrays(shape=shape, dtype=dtype) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to just do this in the subclassed tests:
but then would I need to make
array_strategy_fn
an abstract property?