From d17c78a3d91c1c3fa98fe83745849ca4f2eb16bc Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Thu, 29 May 2025 21:58:52 -0600 Subject: [PATCH 01/14] Making decoding arrays lazy too --- xarray/coding/strings.py | 9 ++++++--- xarray/coding/variables.py | 17 +++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/xarray/coding/strings.py b/xarray/coding/strings.py index 4ca6a3f0a46..a2295c218a6 100644 --- a/xarray/coding/strings.py +++ b/xarray/coding/strings.py @@ -250,14 +250,17 @@ def __repr__(self): return f"{type(self).__name__}({self.array!r})" def _vindex_get(self, key): - return _numpy_char_to_bytes(self.array.vindex[key]) + return type(self)(self.array.vindex[key]) def _oindex_get(self, key): - return _numpy_char_to_bytes(self.array.oindex[key]) + return type(self)(self.array.oindex[key]) def __getitem__(self, key): # require slicing the last dimension completely key = type(key)(indexing.expanded_indexer(key.tuple, self.array.ndim)) if key.tuple[-1] != slice(None): raise IndexError("too many indices") - return _numpy_char_to_bytes(self.array[key]) + return type(self)(self.array[key]) + + def get_duck_array(self): + return _numpy_char_to_bytes(self.array.get_duck_array()) diff --git a/xarray/coding/variables.py b/xarray/coding/variables.py index 662fec4b2c4..1a91115d9c5 100644 --- a/xarray/coding/variables.py +++ b/xarray/coding/variables.py @@ -58,13 +58,16 @@ def dtype(self) -> np.dtype: return np.dtype(self.array.dtype.kind + str(self.array.dtype.itemsize)) def _oindex_get(self, key): - return np.asarray(self.array.oindex[key], dtype=self.dtype) + return type(self)(self.array.oindex[key]) def _vindex_get(self, key): - return np.asarray(self.array.vindex[key], dtype=self.dtype) + return type(self)(self.array.vindex[key]) def __getitem__(self, key) -> np.ndarray: - return np.asarray(self.array[key], dtype=self.dtype) + return type(self)(self.array[key]) + + def get_duck_array(self): + return duck_array_ops.astype(self.array.get_duck_array(), dtype=self.dtype) class BoolTypeArray(indexing.ExplicitlyIndexedNDArrayMixin): @@ -96,14 +99,16 @@ def dtype(self) -> np.dtype: return np.dtype("bool") def _oindex_get(self, key): - return np.asarray(self.array.oindex[key], dtype=self.dtype) + return type(self)(self.array.oindex[key]) def _vindex_get(self, key): - return np.asarray(self.array.vindex[key], dtype=self.dtype) + return type(self)(self.array.vindex[key]) def __getitem__(self, key) -> np.ndarray: - return np.asarray(self.array[key], dtype=self.dtype) + return type(self)(self.array[key]) + def get_duck_array(self): + return duck_array_ops.astype(self.array.get_duck_array(), dtype=self.dtype) def _apply_mask( data: np.ndarray, From 930f24d2e8b9cc7f3e7efa5b493c30ada16c5c7c Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Thu, 29 May 2025 22:19:32 -0600 Subject: [PATCH 02/14] Add IndexingAdapter mixin class --- xarray/core/indexing.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/xarray/core/indexing.py b/xarray/core/indexing.py index e14543e646f..8b52f96ad84 100644 --- a/xarray/core/indexing.py +++ b/xarray/core/indexing.py @@ -520,8 +520,7 @@ class ExplicitlyIndexedNDArrayMixin(NDArrayMixin, ExplicitlyIndexed): __slots__ = () def get_duck_array(self): - key = BasicIndexer((slice(None),) * self.ndim) - return self[key] + raise NotImplementedError def _oindex_get(self, indexer: OuterIndexer): raise NotImplementedError( @@ -559,6 +558,17 @@ def vindex(self) -> IndexCallable: return IndexCallable(self._vindex_get, self._vindex_set) +class IndexingAdapter: + """Marker class for indexing adapters. + These classes translate between Xarray's indexing semantics and the underlying array's + indexing semantics. + """ + + def get_duck_array(self): + key = BasicIndexer((slice(None),) * self.ndim) + return self[key] + + class ImplicitToExplicitIndexingAdapter(NDArrayMixin): """Wrap an array, converting tuples into the indicated explicit indexer.""" @@ -1527,7 +1537,7 @@ def is_fancy_indexer(indexer: Any) -> bool: return True -class NumpyIndexingAdapter(ExplicitlyIndexedNDArrayMixin): +class NumpyIndexingAdapter(IndexingAdapter, ExplicitlyIndexedNDArrayMixin): """Wrap a NumPy array to use explicit indexing.""" __slots__ = ("array",) @@ -1606,7 +1616,7 @@ def __init__(self, array): self.array = array -class ArrayApiIndexingAdapter(ExplicitlyIndexedNDArrayMixin): +class ArrayApiIndexingAdapter(IndexingAdapter, ExplicitlyIndexedNDArrayMixin): """Wrap an array API array to use explicit indexing.""" __slots__ = ("array",) @@ -1671,7 +1681,7 @@ def _assert_not_chunked_indexer(idxr: tuple[Any, ...]) -> None: ) -class DaskIndexingAdapter(ExplicitlyIndexedNDArrayMixin): +class DaskIndexingAdapter(IndexingAdapter, ExplicitlyIndexedNDArrayMixin): """Wrap a dask array to support explicit indexing.""" __slots__ = ("array",) @@ -1747,7 +1757,7 @@ def transpose(self, order): return self.array.transpose(order) -class PandasIndexingAdapter(ExplicitlyIndexedNDArrayMixin): +class PandasIndexingAdapter(IndexingAdapter, ExplicitlyIndexedNDArrayMixin): """Wrap a pandas.Index to preserve dtypes and handle explicit indexing.""" __slots__ = ("_dtype", "array") @@ -2068,7 +2078,9 @@ def copy(self, deep: bool = True) -> Self: return type(self)(array, self._dtype, self.level) -class CoordinateTransformIndexingAdapter(ExplicitlyIndexedNDArrayMixin): +class CoordinateTransformIndexingAdapter( + IndexingAdapter, ExplicitlyIndexedNDArrayMixin +): """Wrap a CoordinateTransform as a lazy coordinate array. Supports explicit indexing (both outer and vectorized). From afa9fb312fb329d513e76eefc4d0216cf26ef54e Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Tue, 17 Jun 2025 16:26:46 -0600 Subject: [PATCH 03/14] Cleanup backends some more --- xarray/backends/memory.py | 8 +++++++- xarray/backends/scipy_.py | 2 ++ xarray/conventions.py | 17 +++++++++++++++-- xarray/core/indexes.py | 2 +- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/xarray/backends/memory.py b/xarray/backends/memory.py index aba767ab731..22cb47d85f2 100644 --- a/xarray/backends/memory.py +++ b/xarray/backends/memory.py @@ -5,6 +5,7 @@ import numpy as np from xarray.backends.common import AbstractWritableDataStore +from xarray.core import indexing from xarray.core.variable import Variable @@ -24,7 +25,12 @@ def get_attrs(self): return self._attributes def get_variables(self): - return self._variables + res = {} + for k, v in self._variables.items(): + v = v.copy(deep=True) + res[k] = v + v._data = indexing.LazilyIndexedArray(v._data) + return res def get_dimensions(self): return {d: s for v in self._variables.values() for d, s in v.dims.items()} diff --git a/xarray/backends/scipy_.py b/xarray/backends/scipy_.py index 93d0e40a6e1..ae00855ac7d 100644 --- a/xarray/backends/scipy_.py +++ b/xarray/backends/scipy_.py @@ -190,6 +190,8 @@ def ds(self): def open_store_variable(self, name, var): return Variable( var.dimensions, + # this backend always loads in to memory, so we don't wrap + # with LazilyIndexedArray ScipyArrayWrapper(name, self), _decode_attrs(var._attributes), ) diff --git a/xarray/conventions.py b/xarray/conventions.py index c9cd2a5dcdc..f64d9b175e0 100644 --- a/xarray/conventions.py +++ b/xarray/conventions.py @@ -18,7 +18,7 @@ ) from xarray.core.utils import emit_user_level_warning from xarray.core.variable import IndexVariable, Variable -from xarray.namedarray.utils import is_duck_dask_array +from xarray.namedarray.utils import is_duck_array CF_RELATED_DATA = ( "bounds", @@ -248,7 +248,20 @@ def decode_cf_variable( encoding.setdefault("dtype", original_dtype) - if not is_duck_dask_array(data): + if ( + # we don't need to lazily index duck arrays + not is_duck_array(data) + # These arrays already support lazy indexing + # OR for IndexingAdapters, it makes no sense to wrap them + and not isinstance(data, indexing.ExplicitlyIndexedNDArrayMixin) + ): + # this path applies to bare BackendArray objects. + # It is not hit for any internal Xarray backend + emit_user_level_warning( + "The backend you are using has not explicitly supported lazy indexing with Xarray. " + "Please ask the backend developers to support lazy loading by wrapping with LazilyIndexedArray. " + "See https://docs.xarray.dev/en/stable/internals/how-to-add-new-backend.html#how-to-support-lazy-loading for more." + ) data = indexing.LazilyIndexedArray(data) return Variable(dimensions, data, attributes, encoding=encoding, fastpath=True) diff --git a/xarray/core/indexes.py b/xarray/core/indexes.py index ed71865060a..26ba5f93103 100644 --- a/xarray/core/indexes.py +++ b/xarray/core/indexes.py @@ -717,7 +717,7 @@ def from_variables( # preserve wrapped pd.Index (if any) # accessing `.data` can load data from disk, so we only access if needed - data = var._data.array if hasattr(var._data, "array") else var.data + data = var._data if isinstance(var._data, PandasIndexingAdapter) else var.data # multi-index level variable: get level index if isinstance(var._data, PandasMultiIndexingAdapter): level = var._data.level From 11fec41fe16283fd94370d1b129695f1945f3aea Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Tue, 17 Jun 2025 16:26:54 -0600 Subject: [PATCH 04/14] Revert "Add IndexingAdapter mixin class" This reverts commit 930f24d2e8b9cc7f3e7efa5b493c30ada16c5c7c. --- xarray/core/indexing.py | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/xarray/core/indexing.py b/xarray/core/indexing.py index 8b52f96ad84..e14543e646f 100644 --- a/xarray/core/indexing.py +++ b/xarray/core/indexing.py @@ -520,7 +520,8 @@ class ExplicitlyIndexedNDArrayMixin(NDArrayMixin, ExplicitlyIndexed): __slots__ = () def get_duck_array(self): - raise NotImplementedError + key = BasicIndexer((slice(None),) * self.ndim) + return self[key] def _oindex_get(self, indexer: OuterIndexer): raise NotImplementedError( @@ -558,17 +559,6 @@ def vindex(self) -> IndexCallable: return IndexCallable(self._vindex_get, self._vindex_set) -class IndexingAdapter: - """Marker class for indexing adapters. - These classes translate between Xarray's indexing semantics and the underlying array's - indexing semantics. - """ - - def get_duck_array(self): - key = BasicIndexer((slice(None),) * self.ndim) - return self[key] - - class ImplicitToExplicitIndexingAdapter(NDArrayMixin): """Wrap an array, converting tuples into the indicated explicit indexer.""" @@ -1537,7 +1527,7 @@ def is_fancy_indexer(indexer: Any) -> bool: return True -class NumpyIndexingAdapter(IndexingAdapter, ExplicitlyIndexedNDArrayMixin): +class NumpyIndexingAdapter(ExplicitlyIndexedNDArrayMixin): """Wrap a NumPy array to use explicit indexing.""" __slots__ = ("array",) @@ -1616,7 +1606,7 @@ def __init__(self, array): self.array = array -class ArrayApiIndexingAdapter(IndexingAdapter, ExplicitlyIndexedNDArrayMixin): +class ArrayApiIndexingAdapter(ExplicitlyIndexedNDArrayMixin): """Wrap an array API array to use explicit indexing.""" __slots__ = ("array",) @@ -1681,7 +1671,7 @@ def _assert_not_chunked_indexer(idxr: tuple[Any, ...]) -> None: ) -class DaskIndexingAdapter(IndexingAdapter, ExplicitlyIndexedNDArrayMixin): +class DaskIndexingAdapter(ExplicitlyIndexedNDArrayMixin): """Wrap a dask array to support explicit indexing.""" __slots__ = ("array",) @@ -1757,7 +1747,7 @@ def transpose(self, order): return self.array.transpose(order) -class PandasIndexingAdapter(IndexingAdapter, ExplicitlyIndexedNDArrayMixin): +class PandasIndexingAdapter(ExplicitlyIndexedNDArrayMixin): """Wrap a pandas.Index to preserve dtypes and handle explicit indexing.""" __slots__ = ("_dtype", "array") @@ -2078,9 +2068,7 @@ def copy(self, deep: bool = True) -> Self: return type(self)(array, self._dtype, self.level) -class CoordinateTransformIndexingAdapter( - IndexingAdapter, ExplicitlyIndexedNDArrayMixin -): +class CoordinateTransformIndexingAdapter(ExplicitlyIndexedNDArrayMixin): """Wrap a CoordinateTransform as a lazy coordinate array. Supports explicit indexing (both outer and vectorized). From 1e4037678d6eb9a8811fc767577e28dca2adee66 Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Tue, 17 Jun 2025 16:41:25 -0600 Subject: [PATCH 05/14] Fix scipy backend --- xarray/backends/scipy_.py | 4 +--- xarray/coding/variables.py | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/xarray/backends/scipy_.py b/xarray/backends/scipy_.py index ae00855ac7d..16fb4528f55 100644 --- a/xarray/backends/scipy_.py +++ b/xarray/backends/scipy_.py @@ -190,9 +190,7 @@ def ds(self): def open_store_variable(self, name, var): return Variable( var.dimensions, - # this backend always loads in to memory, so we don't wrap - # with LazilyIndexedArray - ScipyArrayWrapper(name, self), + indexing.LazilyIndexedArray(ScipyArrayWrapper(name, self)), _decode_attrs(var._attributes), ) diff --git a/xarray/coding/variables.py b/xarray/coding/variables.py index 1a91115d9c5..f76e011ef1e 100644 --- a/xarray/coding/variables.py +++ b/xarray/coding/variables.py @@ -110,6 +110,7 @@ def __getitem__(self, key) -> np.ndarray: def get_duck_array(self): return duck_array_ops.astype(self.array.get_duck_array(), dtype=self.dtype) + def _apply_mask( data: np.ndarray, encoded_fill_values: list, From 13ff262ea63244ca8a77cb2d6f7d290589041de6 Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Tue, 17 Jun 2025 16:45:27 -0600 Subject: [PATCH 06/14] Add scipy-only CI job xref #8909 --- .github/workflows/ci.yaml | 3 +++ ci/requirements/bare-min-and-scipy.yml | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 ci/requirements/bare-min-and-scipy.yml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b884b246f47..76f4f0d5e7e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -54,6 +54,9 @@ jobs: - env: "bare-minimum" python-version: "3.10" os: ubuntu-latest + - env: "bare-min-and-scipy" + python-version: "3.10" + os: ubuntu-latest - env: "min-all-deps" python-version: "3.10" os: ubuntu-latest diff --git a/ci/requirements/bare-min-and-scipy.yml b/ci/requirements/bare-min-and-scipy.yml new file mode 100644 index 00000000000..5e5522faaea --- /dev/null +++ b/ci/requirements/bare-min-and-scipy.yml @@ -0,0 +1,18 @@ +name: xarray-tests +channels: + - conda-forge + - nodefaults +dependencies: + - python=3.10 + - coveralls + - pip + - pytest + - pytest-cov + - pytest-env + - pytest-mypy-plugins + - pytest-timeout + - pytest-xdist + - numpy=1.24 + - packaging=23.1 + - pandas=2.1 + - scipy=1.11 From 1af56054f4eb863c4e89396267ca494cba57d97b Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Tue, 17 Jun 2025 18:52:52 -0600 Subject: [PATCH 07/14] Pin array-api-strict --- ci/requirements/all-but-dask.yml | 2 +- ci/requirements/all-but-numba.yml | 2 +- ci/requirements/environment-3.14.yml | 2 +- ci/requirements/environment-windows-3.14.yml | 2 +- ci/requirements/environment-windows.yml | 2 +- ci/requirements/environment.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ci/requirements/all-but-dask.yml b/ci/requirements/all-but-dask.yml index ca4943bddb1..5f5db4a0f18 100644 --- a/ci/requirements/all-but-dask.yml +++ b/ci/requirements/all-but-dask.yml @@ -4,7 +4,7 @@ channels: - nodefaults dependencies: - aiobotocore - - array-api-strict + - array-api-strict<2.4 - boto3 - bottleneck - cartopy diff --git a/ci/requirements/all-but-numba.yml b/ci/requirements/all-but-numba.yml index fa7ad81f198..7c492aec704 100644 --- a/ci/requirements/all-but-numba.yml +++ b/ci/requirements/all-but-numba.yml @@ -6,7 +6,7 @@ dependencies: # Pin a "very new numpy" (updated Sept 24, 2024) - numpy>=2.1.1 - aiobotocore - - array-api-strict + - array-api-strict<2.4 - boto3 - bottleneck - cartopy diff --git a/ci/requirements/environment-3.14.yml b/ci/requirements/environment-3.14.yml index 1e6ee7ff5f9..06c4df82663 100644 --- a/ci/requirements/environment-3.14.yml +++ b/ci/requirements/environment-3.14.yml @@ -4,7 +4,7 @@ channels: - nodefaults dependencies: - aiobotocore - - array-api-strict + - array-api-strict<2.4 - boto3 - bottleneck - cartopy diff --git a/ci/requirements/environment-windows-3.14.yml b/ci/requirements/environment-windows-3.14.yml index 4eb2049f2e6..dd48add6b73 100644 --- a/ci/requirements/environment-windows-3.14.yml +++ b/ci/requirements/environment-windows-3.14.yml @@ -2,7 +2,7 @@ name: xarray-tests channels: - conda-forge dependencies: - - array-api-strict + - array-api-strict<2.4 - boto3 - bottleneck - cartopy diff --git a/ci/requirements/environment-windows.yml b/ci/requirements/environment-windows.yml index 45cbebd38db..3213ef687d3 100644 --- a/ci/requirements/environment-windows.yml +++ b/ci/requirements/environment-windows.yml @@ -2,7 +2,7 @@ name: xarray-tests channels: - conda-forge dependencies: - - array-api-strict + - array-api-strict<2.4 - boto3 - bottleneck - cartopy diff --git a/ci/requirements/environment.yml b/ci/requirements/environment.yml index a9499694e15..fc54b6600fe 100644 --- a/ci/requirements/environment.yml +++ b/ci/requirements/environment.yml @@ -4,7 +4,7 @@ channels: - nodefaults dependencies: - aiobotocore - - array-api-strict + - array-api-strict<2.4 - boto3 - bottleneck - cartopy From f7ac28ac91afdbb1b4515ece82d8f37e9708d9d5 Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Tue, 17 Jun 2025 18:55:59 -0600 Subject: [PATCH 08/14] Fix doctest --- xarray/coding/strings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/coding/strings.py b/xarray/coding/strings.py index a2295c218a6..ea2f58274b6 100644 --- a/xarray/coding/strings.py +++ b/xarray/coding/strings.py @@ -221,7 +221,7 @@ class StackedBytesArray(indexing.ExplicitlyIndexedNDArrayMixin): values, when accessed, are automatically stacked along the last dimension. >>> indexer = indexing.BasicIndexer((slice(None),)) - >>> StackedBytesArray(np.array(["a", "b", "c"], dtype="S1"))[indexer] + >>> np.array(StackedBytesArray(np.array(["a", "b", "c"], dtype="S1"))[indexer]) array(b'abc', dtype='|S3') """ From 3a277474573d084a56e5d7230b185226b8ec0d1d Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Tue, 17 Jun 2025 19:09:27 -0600 Subject: [PATCH 09/14] Add test for #8909 Closes #8909, #8921 --- xarray/tests/test_backends.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index 68ff9233080..6a42e135587 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -1427,6 +1427,25 @@ def test_string_object_warning(self) -> None: with self.roundtrip(original) as actual: assert_identical(original, actual) + @pytest.mark.parametrize( + "indexer", + ( + {"y": [1]}, + {"y": slice(2)}, + {"y": 1}, + {"x": [1], "y": [1]}, + {"x": ("x0", [0, 1]), "y": ("x0", [0, 1])}, + ), + ) + def test_indexing_roundtrip(self, indexer) -> None: + # regression test for GH8909 + ds = xr.Dataset() + ds["A"] = xr.DataArray([[1, "a"], [2, "b"]], dims=["x", "y"]) + with self.roundtrip(ds) as ds2: + expected = ds2.sel(indexer) + with self.roundtrip(expected) as actual: + assert_identical(actual, expected) + class NetCDFBase(CFEncodedBase): """Tests for all netCDF3 and netCDF4 backends.""" From 00e05ddf72389c39a9d7b20d3a0a0e4722a09b83 Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Tue, 17 Jun 2025 19:12:16 -0600 Subject: [PATCH 10/14] Remove user warning --- xarray/conventions.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/xarray/conventions.py b/xarray/conventions.py index f64d9b175e0..5ae40ea57d8 100644 --- a/xarray/conventions.py +++ b/xarray/conventions.py @@ -257,11 +257,6 @@ def decode_cf_variable( ): # this path applies to bare BackendArray objects. # It is not hit for any internal Xarray backend - emit_user_level_warning( - "The backend you are using has not explicitly supported lazy indexing with Xarray. " - "Please ask the backend developers to support lazy loading by wrapping with LazilyIndexedArray. " - "See https://docs.xarray.dev/en/stable/internals/how-to-add-new-backend.html#how-to-support-lazy-loading for more." - ) data = indexing.LazilyIndexedArray(data) return Variable(dimensions, data, attributes, encoding=encoding, fastpath=True) From f0d8668bf3b6d3dc5a923fa915b6806ca9885fd4 Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Tue, 17 Jun 2025 19:20:07 -0600 Subject: [PATCH 11/14] fix types --- xarray/coding/variables.py | 5 +++-- xarray/core/indexes.py | 2 +- xarray/tests/test_dtypes.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/xarray/coding/variables.py b/xarray/coding/variables.py index f76e011ef1e..3b7be898ccf 100644 --- a/xarray/coding/variables.py +++ b/xarray/coding/variables.py @@ -21,6 +21,7 @@ ) from xarray.coding.times import CFDatetimeCoder, CFTimedeltaCoder from xarray.core import dtypes, duck_array_ops, indexing +from xarray.core.types import Self from xarray.core.variable import Variable if TYPE_CHECKING: @@ -63,7 +64,7 @@ def _oindex_get(self, key): def _vindex_get(self, key): return type(self)(self.array.vindex[key]) - def __getitem__(self, key) -> np.ndarray: + def __getitem__(self, key) -> Self: return type(self)(self.array[key]) def get_duck_array(self): @@ -104,7 +105,7 @@ def _oindex_get(self, key): def _vindex_get(self, key): return type(self)(self.array.vindex[key]) - def __getitem__(self, key) -> np.ndarray: + def __getitem__(self, key) -> Self: return type(self)(self.array[key]) def get_duck_array(self): diff --git a/xarray/core/indexes.py b/xarray/core/indexes.py index 26ba5f93103..552e743e1f1 100644 --- a/xarray/core/indexes.py +++ b/xarray/core/indexes.py @@ -717,7 +717,7 @@ def from_variables( # preserve wrapped pd.Index (if any) # accessing `.data` can load data from disk, so we only access if needed - data = var._data if isinstance(var._data, PandasIndexingAdapter) else var.data + data = var._data if isinstance(var._data, PandasIndexingAdapter) else var.data # type: ignore[redundant-expr] # multi-index level variable: get level index if isinstance(var._data, PandasMultiIndexingAdapter): level = var._data.level diff --git a/xarray/tests/test_dtypes.py b/xarray/tests/test_dtypes.py index 0ccda1d8074..c2950a833c2 100644 --- a/xarray/tests/test_dtypes.py +++ b/xarray/tests/test_dtypes.py @@ -15,7 +15,7 @@ class DummyArrayAPINamespace: int32 = None # type: ignore[unused-ignore,var-annotated] float64 = None # type: ignore[unused-ignore,var-annotated] - array_api_strict = DummyArrayAPINamespace + array_api_strict = DummyArrayAPINamespace # type: ignore[assignment] @pytest.mark.parametrize( From 0ed11f5e965c4cc29b26edaeb5ca65ed9473957b Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Tue, 17 Jun 2025 19:22:15 -0600 Subject: [PATCH 12/14] Add whats-new --- doc/whats-new.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 618fc72763d..ceb79a3e173 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -26,6 +26,8 @@ Bug fixes ~~~~~~~~~ - Fix Pydap test_cmp_local_file for numpy 2.3.0 changes, 1. do always return arrays for all versions and 2. skip astype(str) for numpy >= 2.3.0 for expected data. (:pull:`10421`) By `Kai Mühlbauer `_. +- Fix the SciPy backend for netCDF3 files . (:issue:`8909`, :pull:`10376`) + By `Deepak Cherian `_. Documentation From 432a1e8ebacb4c22f900cb62c6b94a6757073a9e Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Tue, 17 Jun 2025 21:41:20 -0600 Subject: [PATCH 13/14] fix types --- xarray/tests/test_dtypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/tests/test_dtypes.py b/xarray/tests/test_dtypes.py index c2950a833c2..0ccda1d8074 100644 --- a/xarray/tests/test_dtypes.py +++ b/xarray/tests/test_dtypes.py @@ -15,7 +15,7 @@ class DummyArrayAPINamespace: int32 = None # type: ignore[unused-ignore,var-annotated] float64 = None # type: ignore[unused-ignore,var-annotated] - array_api_strict = DummyArrayAPINamespace # type: ignore[assignment] + array_api_strict = DummyArrayAPINamespace @pytest.mark.parametrize( From 73bd320b53df643564aaab86728b2062c2b76bd3 Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Wed, 18 Jun 2025 11:04:07 -0600 Subject: [PATCH 14/14] Fix docs build --- xarray/coding/common.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/xarray/coding/common.py b/xarray/coding/common.py index 1b455009668..0e8d7e1955e 100644 --- a/xarray/coding/common.py +++ b/xarray/coding/common.py @@ -63,6 +63,10 @@ def __init__(self, array, func: Callable, dtype: np.typing.DTypeLike): def dtype(self) -> np.dtype: return np.dtype(self._dtype) + def transpose(self, order): + # For elementwise functions, we can compose transpose and function application + return type(self)(self.array.transpose(order), self.func, self.dtype) + def _oindex_get(self, key): return type(self)(self.array.oindex[key], self.func, self.dtype) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy