@@ -516,16 +516,30 @@ def get_duck_array(self):
516
516
return self .array
517
517
518
518
519
- class ExplicitlyIndexedNDArrayMixin (NDArrayMixin , ExplicitlyIndexed ):
520
- __slots__ = ()
519
+ class IndexingAdapter :
520
+ """Marker class for indexing adapters.
521
+
522
+ These classes translate between Xarray's indexing semantics and the underlying array's
523
+ indexing semantics.
524
+ """
521
525
522
526
def get_duck_array (self ):
523
527
key = BasicIndexer ((slice (None ),) * self .ndim )
524
528
return self [key ]
525
529
526
530
async def async_get_duck_array (self ):
527
- key = BasicIndexer ((slice (None ),) * self .ndim )
528
- return await self .async_getitem (key )
531
+ """These classes are applied to in-memory arrays, so specific async support isn't needed."""
532
+ return self .get_duck_array ()
533
+
534
+
535
+ class ExplicitlyIndexedNDArrayMixin (NDArrayMixin , ExplicitlyIndexed ):
536
+ __slots__ = ()
537
+
538
+ def get_duck_array (self ):
539
+ raise NotImplementedError
540
+
541
+ async def async_get_duck_array (self ):
542
+ raise NotImplementedError
529
543
530
544
def _oindex_get (self , indexer : OuterIndexer ):
531
545
raise NotImplementedError (
@@ -650,37 +664,25 @@ def shape(self) -> _Shape:
650
664
return self ._shape
651
665
652
666
def get_duck_array (self ):
653
- if isinstance (self .array , ExplicitlyIndexedNDArrayMixin ):
654
- array = apply_indexer (self .array , self .key )
655
- else :
656
- # If the array is not an ExplicitlyIndexedNDArrayMixin,
657
- # it may wrap a BackendArray so use its __getitem__
658
- array = self .array [self .key ]
667
+ from xarray .backends .common import BackendArray
659
668
660
- # self.array[self.key] is now a numpy array when
661
- # self.array is a BackendArray subclass
662
- # and self.key is BasicIndexer((slice(None, None, None),))
663
- # so we need the explicit check for ExplicitlyIndexed
664
- if isinstance (array , ExplicitlyIndexed ):
665
- array = array .get_duck_array ()
669
+ if isinstance ( self .array , BackendArray ):
670
+ array = self . array [ self . key ]
671
+ else :
672
+ array = apply_indexer ( self . array , self . key )
673
+ if isinstance (array , ExplicitlyIndexed ):
674
+ array = array .get_duck_array ()
666
675
return _wrap_numpy_scalars (array )
667
676
668
677
async def async_get_duck_array (self ):
669
- if isinstance (self .array , ExplicitlyIndexedNDArrayMixin ):
670
- array = apply_indexer (self .array , self .key )
671
- else :
672
- # If the array is not an ExplicitlyIndexedNDArrayMixin,
673
- # it may wrap a BackendArray so use its (async) getitem
674
- array = await self .array .async_getitem (self .key )
678
+ from xarray .backends .common import BackendArray
675
679
676
- # self.array[self.key] is now a numpy array when
677
- # self.array is a BackendArray subclass
678
- # and self.key is BasicIndexer((slice(None, None, None),))
679
- # so we need the explicit check for ExplicitlyIndexed
680
- if isinstance (array , ExplicitlyIndexed ):
681
- # At this point, we have issued completed the possible async load from disk
682
- # and array is in-memory. So use the sync get
683
- array = array .get_duck_array ()
680
+ if isinstance (self .array , BackendArray ):
681
+ array = await self .array .async_getitem (self .key )
682
+ else :
683
+ array = apply_indexer (self .array , self .key )
684
+ if isinstance (array , ExplicitlyIndexed ):
685
+ array = await array .async_get_duck_array ()
684
686
return _wrap_numpy_scalars (array )
685
687
686
688
def transpose (self , order ):
@@ -744,36 +746,26 @@ def shape(self) -> _Shape:
744
746
return np .broadcast (* self .key .tuple ).shape
745
747
746
748
def get_duck_array (self ):
747
- if isinstance (self .array , ExplicitlyIndexedNDArrayMixin ):
748
- array = apply_indexer (self .array , self .key )
749
- else :
750
- # If the array is not an ExplicitlyIndexedNDArrayMixin,
751
- # it may wrap a BackendArray so use its __getitem__
749
+ from xarray .backends .common import BackendArray
750
+
751
+ if isinstance (self .array , BackendArray ):
752
752
array = self .array [self .key ]
753
- # self.array[self.key] is now a numpy array when
754
- # self.array is a BackendArray subclass
755
- # and self.key is BasicIndexer((slice(None, None, None),))
756
- # so we need the explicit check for ExplicitlyIndexed
757
- if isinstance (array , ExplicitlyIndexed ):
758
- array = array .get_duck_array ()
753
+ else :
754
+ array = apply_indexer (self .array , self .key )
755
+ if isinstance (array , ExplicitlyIndexed ):
756
+ array = array .get_duck_array ()
759
757
return _wrap_numpy_scalars (array )
760
758
761
759
async def async_get_duck_array (self ):
762
760
print ("inside LazilyVectorizedIndexedArray.async_get_duck_array" )
763
- if isinstance (self .array , ExplicitlyIndexedNDArrayMixin ):
764
- array = apply_indexer (self .array , self .key )
765
- else :
766
- # If the array is not an ExplicitlyIndexedNDArrayMixin,
767
- # it may wrap a BackendArray so use its __getitem__
761
+ from xarray .backends .common import BackendArray
762
+
763
+ if isinstance (self .array , BackendArray ):
768
764
array = await self .array .async_getitem (self .key )
769
- # self.array[self.key] is now a numpy array when
770
- # self.array is a BackendArray subclass
771
- # and self.key is BasicIndexer((slice(None, None, None),))
772
- # so we need the explicit check for ExplicitlyIndexed
773
- if isinstance (array , ExplicitlyIndexed ):
774
- # At this point, we have issued completed the possible async load from disk
775
- # and array is in-memory. So use the sync get
776
- array = array .get_duck_array ()
765
+ else :
766
+ array = apply_indexer (self .array , self .key )
767
+ if isinstance (array , ExplicitlyIndexed ):
768
+ array = await array .async_get_duck_array ()
777
769
return _wrap_numpy_scalars (array )
778
770
779
771
def _updated_key (self , new_key : ExplicitIndexer ):
@@ -1589,7 +1581,7 @@ def is_fancy_indexer(indexer: Any) -> bool:
1589
1581
return True
1590
1582
1591
1583
1592
- class NumpyIndexingAdapter (ExplicitlyIndexedNDArrayMixin ):
1584
+ class NumpyIndexingAdapter (IndexingAdapter , ExplicitlyIndexedNDArrayMixin ):
1593
1585
"""Wrap a NumPy array to use explicit indexing."""
1594
1586
1595
1587
__slots__ = ("array" ,)
@@ -1668,7 +1660,7 @@ def __init__(self, array):
1668
1660
self .array = array
1669
1661
1670
1662
1671
- class ArrayApiIndexingAdapter (ExplicitlyIndexedNDArrayMixin ):
1663
+ class ArrayApiIndexingAdapter (IndexingAdapter , ExplicitlyIndexedNDArrayMixin ):
1672
1664
"""Wrap an array API array to use explicit indexing."""
1673
1665
1674
1666
__slots__ = ("array" ,)
@@ -1733,7 +1725,7 @@ def _assert_not_chunked_indexer(idxr: tuple[Any, ...]) -> None:
1733
1725
)
1734
1726
1735
1727
1736
- class DaskIndexingAdapter (ExplicitlyIndexedNDArrayMixin ):
1728
+ class DaskIndexingAdapter (IndexingAdapter , ExplicitlyIndexedNDArrayMixin ):
1737
1729
"""Wrap a dask array to support explicit indexing."""
1738
1730
1739
1731
__slots__ = ("array" ,)
@@ -1809,7 +1801,7 @@ def transpose(self, order):
1809
1801
return self .array .transpose (order )
1810
1802
1811
1803
1812
- class PandasIndexingAdapter (ExplicitlyIndexedNDArrayMixin ):
1804
+ class PandasIndexingAdapter (IndexingAdapter , ExplicitlyIndexedNDArrayMixin ):
1813
1805
"""Wrap a pandas.Index to preserve dtypes and handle explicit indexing."""
1814
1806
1815
1807
__slots__ = ("_dtype" , "array" )
@@ -1872,15 +1864,6 @@ def get_duck_array(self) -> np.ndarray | PandasExtensionArray:
1872
1864
return PandasExtensionArray (self .array .array )
1873
1865
return np .asarray (self )
1874
1866
1875
- async def async_get_duck_array (self ) -> np .ndarray | PandasExtensionArray :
1876
- # TODO this must surely be wrong - it's not async yet
1877
- print ("in PandasIndexingAdapter" )
1878
- if pd .api .types .is_extension_array_dtype (self .array ):
1879
- from xarray .core .extension_array import PandasExtensionArray
1880
-
1881
- return PandasExtensionArray (self .array .array )
1882
- return np .asarray (self )
1883
-
1884
1867
@property
1885
1868
def shape (self ) -> _Shape :
1886
1869
return (len (self .array ),)
@@ -2135,7 +2118,9 @@ def copy(self, deep: bool = True) -> Self:
2135
2118
return type (self )(array , self ._dtype , self .level )
2136
2119
2137
2120
2138
- class CoordinateTransformIndexingAdapter (ExplicitlyIndexedNDArrayMixin ):
2121
+ class CoordinateTransformIndexingAdapter (
2122
+ IndexingAdapter , ExplicitlyIndexedNDArrayMixin
2123
+ ):
2139
2124
"""Wrap a CoordinateTransform as a lazy coordinate array.
2140
2125
2141
2126
Supports explicit indexing (both outer and vectorized).
0 commit comments