From cc4f5bb25c3af3fcec60d016f340cd8499ae9f4e Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Sun, 22 Sep 2024 14:43:54 +0200 Subject: [PATCH 1/5] don't pass along `out` for `nanprod` We don't do this anywhere elsewhere, so it doesn't make sense to do this only for `nanprod`. --- xarray/core/nanops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/core/nanops.py b/xarray/core/nanops.py index fc7240139aa..7fbb63068c0 100644 --- a/xarray/core/nanops.py +++ b/xarray/core/nanops.py @@ -162,7 +162,7 @@ def nanstd(a, axis=None, dtype=None, out=None, ddof=0): def nanprod(a, axis=None, dtype=None, out=None, min_count=None): mask = isnull(a) - result = nputils.nanprod(a, axis=axis, dtype=dtype, out=out) + result = nputils.nanprod(a, axis=axis, dtype=dtype) if min_count is not None: return _maybe_null_out(result, axis, mask, min_count) else: From a10e97a22734b4533d568a70de62ff6791d8da33 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Sun, 22 Sep 2024 15:01:25 +0200 Subject: [PATCH 2/5] add tests for `as_indexable` --- xarray/tests/test_indexing.py | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/xarray/tests/test_indexing.py b/xarray/tests/test_indexing.py index 985fb02a87e..39ac09bf802 100644 --- a/xarray/tests/test_indexing.py +++ b/xarray/tests/test_indexing.py @@ -894,6 +894,46 @@ def test_posify_mask_subindexer(indices, expected) -> None: np.testing.assert_array_equal(expected, actual) +class ArrayWithNamespace: + def __array_namespace__(self, version=None): + pass + + +class ArrayWithArrayFunction: + def __array_function__(self, func, types, args, kwargs): + pass + + +@pytest.mark.parametrize( + ["array", "expected_type"], + ( + pytest.param( + indexing.CopyOnWriteArray(np.array([1, 2])), + indexing.CopyOnWriteArray, + id="ExplicitlyIndexed", + ), + pytest.param( + np.array([1, 2]), indexing.NumpyIndexingAdapter, id="numpy.ndarray" + ), + pytest.param( + pd.Index([1, 2]), indexing.PandasIndexingAdapter, id="pandas.Index" + ), + pytest.param( + ArrayWithNamespace(), indexing.ArrayApiIndexingAdapter, id="array_api" + ), + pytest.param( + ArrayWithArrayFunction(), + indexing.NdArrayLikeIndexingAdapter, + id="array_like", + ), + ), +) +def test_as_indexable(array, expected_type): + actual = indexing.as_indexable(array) + + assert isinstance(actual, expected_type) + + def test_indexing_1d_object_array() -> None: items = (np.arange(3), np.arange(6)) arr = DataArray(np.array(items, dtype=object)) From ecb47d6005da928a19af4225c15b52bd01d48d9d Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Sun, 22 Sep 2024 15:01:46 +0200 Subject: [PATCH 3/5] allow using `__array_function__` as a fallback for missing array API funcs --- xarray/core/indexing.py | 4 ++-- xarray/tests/test_indexing.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/xarray/core/indexing.py b/xarray/core/indexing.py index 67912908a2b..6985d6681a5 100644 --- a/xarray/core/indexing.py +++ b/xarray/core/indexing.py @@ -875,10 +875,10 @@ def as_indexable(array): return PandasIndexingAdapter(array) if is_duck_dask_array(array): return DaskIndexingAdapter(array) - if hasattr(array, "__array_function__"): - return NdArrayLikeIndexingAdapter(array) if hasattr(array, "__array_namespace__"): return ArrayApiIndexingAdapter(array) + if hasattr(array, "__array_function__"): + return NdArrayLikeIndexingAdapter(array) raise TypeError(f"Invalid array type: {type(array)}") diff --git a/xarray/tests/test_indexing.py b/xarray/tests/test_indexing.py index 39ac09bf802..4bf05471d71 100644 --- a/xarray/tests/test_indexing.py +++ b/xarray/tests/test_indexing.py @@ -904,6 +904,14 @@ def __array_function__(self, func, types, args, kwargs): pass +class ArrayWithNamespaceAndArrayFunction: + def __array_namespace__(self, version=None): + pass + + def __array_function__(self, func, types, args, kwargs): + pass + + @pytest.mark.parametrize( ["array", "expected_type"], ( @@ -926,6 +934,11 @@ def __array_function__(self, func, types, args, kwargs): indexing.NdArrayLikeIndexingAdapter, id="array_like", ), + pytest.param( + ArrayWithNamespaceAndArrayFunction(), + indexing.ArrayApiIndexingAdapter, + id="array_api_with_fallback", + ), ), ) def test_as_indexable(array, expected_type): From 445a887ee08586bb06cc774c023ac5dd6b1230c0 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Sun, 22 Sep 2024 15:06:43 +0200 Subject: [PATCH 4/5] also check dask --- xarray/tests/test_indexing.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/xarray/tests/test_indexing.py b/xarray/tests/test_indexing.py index 4bf05471d71..78a63cb380c 100644 --- a/xarray/tests/test_indexing.py +++ b/xarray/tests/test_indexing.py @@ -912,6 +912,12 @@ def __array_function__(self, func, types, args, kwargs): pass +def as_dask_array(arr, chunks): + import dask.array as da + + return da.from_array(arr, chunks=chunks) + + @pytest.mark.parametrize( ["array", "expected_type"], ( @@ -926,6 +932,12 @@ def __array_function__(self, func, types, args, kwargs): pytest.param( pd.Index([1, 2]), indexing.PandasIndexingAdapter, id="pandas.Index" ), + pytest.param( + as_dask_array(np.array([1, 2]), chunks=(1,)), + indexing.DaskIndexingAdapter, + id="dask.array", + marks=requires_dask, + ), pytest.param( ArrayWithNamespace(), indexing.ArrayApiIndexingAdapter, id="array_api" ), From 581f4652a295c66a11fe287dae5b0e2589ecde11 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Sun, 22 Sep 2024 20:00:37 +0200 Subject: [PATCH 5/5] don't try to create a `dask` array if `dask` is not installed --- xarray/tests/test_indexing.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xarray/tests/test_indexing.py b/xarray/tests/test_indexing.py index 78a63cb380c..92c21cc32fb 100644 --- a/xarray/tests/test_indexing.py +++ b/xarray/tests/test_indexing.py @@ -913,7 +913,10 @@ def __array_function__(self, func, types, args, kwargs): def as_dask_array(arr, chunks): - import dask.array as da + try: + import dask.array as da + except ImportError: + return None return da.from_array(arr, chunks=chunks)