From c7df27b9920017b5549595e53fe59471200eb119 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Sat, 13 Apr 2024 12:55:20 +0200 Subject: [PATCH 01/10] use `numpy.finfo` instead of `numpy.core.finfo` --- xarray/core/dataset.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 4c80a47209e..a360fd89660 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -8862,9 +8862,7 @@ def polyfit( lhs = np.vander(x, order) if rcond is None: - rcond = ( - x.shape[0] * np.core.finfo(x.dtype).eps # type: ignore[attr-defined] - ) + rcond = x.shape[0] * np.finfo(x.dtype).eps # type: ignore[attr-defined] # Weights: if w is not None: From 88c3670b525d737890dee84fd9500f2301918980 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Sat, 13 Apr 2024 13:07:05 +0200 Subject: [PATCH 02/10] add `copy` to the signature of `__array__` --- xarray/core/common.py | 4 +++- xarray/core/dataset.py | 2 +- xarray/core/datatree.py | 2 +- xarray/core/groupby.py | 4 ++-- xarray/core/indexing.py | 38 ++++++++++++++++++++++----------- xarray/namedarray/_typing.py | 8 ++++--- xarray/tests/__init__.py | 4 ++-- xarray/tests/test_formatting.py | 2 +- xarray/tests/test_namedarray.py | 4 ++-- 9 files changed, 42 insertions(+), 26 deletions(-) diff --git a/xarray/core/common.py b/xarray/core/common.py index 7b9a049c662..9f2b9a54469 100644 --- a/xarray/core/common.py +++ b/xarray/core/common.py @@ -161,7 +161,9 @@ def __int__(self: Any) -> int: def __complex__(self: Any) -> complex: return complex(self.values) - def __array__(self: Any, dtype: DTypeLike | None = None) -> np.ndarray: + def __array__( + self: Any, dtype: DTypeLike | None = None, copy: bool | None = None + ) -> np.ndarray: return np.asarray(self.values, dtype=dtype) def __repr__(self) -> str: diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index a360fd89660..c6de0c37c69 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -1507,7 +1507,7 @@ def __iter__(self) -> Iterator[Hashable]: else: - def __array__(self, dtype=None): + def __array__(self, dtype=None, copy=None): raise TypeError( "cannot directly convert an xarray.Dataset into a " "numpy array. Instead, create an xarray.DataArray " diff --git a/xarray/core/datatree.py b/xarray/core/datatree.py index 1b06d87c9fb..456eecd8c14 100644 --- a/xarray/core/datatree.py +++ b/xarray/core/datatree.py @@ -624,7 +624,7 @@ def __bool__(self) -> bool: def __iter__(self) -> Iterator[Hashable]: return itertools.chain(self.ds.data_vars, self.children) - def __array__(self, dtype=None): + def __array__(self, dtype=None, copy=None): raise TypeError( "cannot directly convert a DataTree into a " "numpy array. Instead, create an xarray.DataArray " diff --git a/xarray/core/groupby.py b/xarray/core/groupby.py index 5966c32df92..76a0e97b760 100644 --- a/xarray/core/groupby.py +++ b/xarray/core/groupby.py @@ -243,8 +243,8 @@ def values(self) -> range: def data(self) -> range: return range(self.size) - def __array__(self) -> np.ndarray: - return np.arange(self.size) + def __array__(self, dtype=None, copy=None) -> np.ndarray: + return np.arange(self.size, dtype=dtype) @property def shape(self) -> tuple[int]: diff --git a/xarray/core/indexing.py b/xarray/core/indexing.py index 0926da6fd80..2a1e876b429 100644 --- a/xarray/core/indexing.py +++ b/xarray/core/indexing.py @@ -503,9 +503,11 @@ class ExplicitlyIndexed: __slots__ = () - def __array__(self, dtype: np.typing.DTypeLike = None) -> np.ndarray: + def __array__( + self, dtype: np.typing.DTypeLike = None, copy: bool | None = None + ) -> np.ndarray: # Leave casting to an array up to the underlying array type. - return np.asarray(self.get_duck_array(), dtype=dtype) + return np.asarray(self.get_duck_array(), dtype=dtype, copy=copy) def get_duck_array(self): return self.array @@ -518,10 +520,12 @@ def get_duck_array(self): key = BasicIndexer((slice(None),) * self.ndim) return self[key] - def __array__(self, dtype: np.typing.DTypeLike = None) -> np.ndarray: + def __array__( + self, dtype: np.typing.DTypeLike = None, copy: bool | None = None + ) -> np.ndarray: # This is necessary because we apply the indexing key in self.get_duck_array() # Note this is the base class for all lazy indexing classes - return np.asarray(self.get_duck_array(), dtype=dtype) + return np.asarray(self.get_duck_array(), dtype=dtype, copy=copy) def _oindex_get(self, indexer: OuterIndexer): raise NotImplementedError( @@ -568,8 +572,10 @@ def __init__(self, array, indexer_cls: type[ExplicitIndexer] = BasicIndexer): self.array = as_indexable(array) self.indexer_cls = indexer_cls - def __array__(self, dtype: np.typing.DTypeLike = None) -> np.ndarray: - return np.asarray(self.get_duck_array(), dtype=dtype) + def __array__( + self, dtype: np.typing.DTypeLike = None, copy: bool | None = None + ) -> np.ndarray: + return np.asarray(self.get_duck_array(), dtype=dtype, copy=copy) def get_duck_array(self): return self.array.get_duck_array() @@ -826,8 +832,10 @@ def __init__(self, array): def _ensure_cached(self): self.array = as_indexable(self.array.get_duck_array()) - def __array__(self, dtype: np.typing.DTypeLike = None) -> np.ndarray: - return np.asarray(self.get_duck_array(), dtype=dtype) + def __array__( + self, dtype: np.typing.DTypeLike = None, copy: bool | None = None + ) -> np.ndarray: + return np.asarray(self.get_duck_array(), dtype=dtype, copy=copy) def get_duck_array(self): self._ensure_cached() @@ -1667,7 +1675,9 @@ def __init__(self, array: pd.Index, dtype: DTypeLike = None): def dtype(self) -> np.dtype: return self._dtype - def __array__(self, dtype: DTypeLike = None) -> np.ndarray: + def __array__( + self, dtype: DTypeLike = None, copy: bool | None = None + ) -> np.ndarray: if dtype is None: dtype = self.dtype array = self.array @@ -1675,7 +1685,7 @@ def __array__(self, dtype: DTypeLike = None) -> np.ndarray: with suppress(AttributeError): # this might not be public API array = array.astype("object") - return np.asarray(array.values, dtype=dtype) + return np.asarray(array.values, dtype=dtype, copy=copy) def get_duck_array(self) -> np.ndarray: return np.asarray(self) @@ -1821,15 +1831,17 @@ def __init__( super().__init__(array, dtype) self.level = level - def __array__(self, dtype: DTypeLike = None) -> np.ndarray: + def __array__( + self, dtype: DTypeLike = None, copy: bool | None = None + ) -> np.ndarray: if dtype is None: dtype = self.dtype if self.level is not None: return np.asarray( - self.array.get_level_values(self.level).values, dtype=dtype + self.array.get_level_values(self.level).values, dtype=dtype, copy=copy ) else: - return super().__array__(dtype) + return super().__array__(dtype, copy=copy) def _convert_scalar(self, item): if isinstance(item, tuple) and self.level is not None: diff --git a/xarray/namedarray/_typing.py b/xarray/namedarray/_typing.py index b715973814f..fa7e5f30cc9 100644 --- a/xarray/namedarray/_typing.py +++ b/xarray/namedarray/_typing.py @@ -153,13 +153,15 @@ def __getitem__( ) -> _arrayfunction[Any, _DType_co] | Any: ... @overload - def __array__(self, dtype: None = ..., /) -> np.ndarray[Any, _DType_co]: ... + def __array__( + self, dtype: None = ..., copy: None = ..., / + ) -> np.ndarray[Any, _DType_co]: ... @overload - def __array__(self, dtype: _DType, /) -> np.ndarray[Any, _DType]: ... + def __array__(self, dtype: _DType, copy: bool, /) -> np.ndarray[Any, _DType]: ... def __array__( - self, dtype: _DType | None = ..., / + self, dtype: _DType | None = ..., copy: bool | None = None, / ) -> np.ndarray[Any, _DType] | np.ndarray[Any, _DType_co]: ... # TODO: Should return the same subclass but with a new dtype generic. diff --git a/xarray/tests/__init__.py b/xarray/tests/__init__.py index 5007db9eeb2..b0386f2e719 100644 --- a/xarray/tests/__init__.py +++ b/xarray/tests/__init__.py @@ -201,7 +201,7 @@ def __init__(self, array): def get_duck_array(self): raise UnexpectedDataAccess("Tried accessing data") - def __array__(self, dtype: np.typing.DTypeLike = None): + def __array__(self, dtype: np.typing.DTypeLike = None, copy: bool | None = None): raise UnexpectedDataAccess("Tried accessing data") def __getitem__(self, key): @@ -226,7 +226,7 @@ def __init__(self, array: np.ndarray): def __getitem__(self, key): return type(self)(self.array[key]) - def __array__(self, dtype: np.typing.DTypeLike = None): + def __array__(self, dtype: np.typing.DTypeLike = None, copy: bool | None = None): raise UnexpectedDataAccess("Tried accessing data") def __array_namespace__(self): diff --git a/xarray/tests/test_formatting.py b/xarray/tests/test_formatting.py index 6923d26b79a..ff1542f6a10 100644 --- a/xarray/tests/test_formatting.py +++ b/xarray/tests/test_formatting.py @@ -775,7 +775,7 @@ def test_lazy_array_wont_compute() -> None: from xarray.core.indexing import LazilyIndexedArray class LazilyIndexedArrayNotComputable(LazilyIndexedArray): - def __array__(self, dtype=None): + def __array__(self, dtype=None, copy=None): raise NotImplementedError("Computing this array is not possible.") arr = LazilyIndexedArrayNotComputable(np.array([1, 2])) diff --git a/xarray/tests/test_namedarray.py b/xarray/tests/test_namedarray.py index 2a3faf32b85..ca18841584e 100644 --- a/xarray/tests/test_namedarray.py +++ b/xarray/tests/test_namedarray.py @@ -53,8 +53,8 @@ def shape(self) -> _Shape: class CustomArray( CustomArrayBase[_ShapeType_co, _DType_co], Generic[_ShapeType_co, _DType_co] ): - def __array__(self) -> np.ndarray[Any, np.dtype[np.generic]]: - return np.array(self.array) + def __array__(self, dtype=None, copy=None) -> np.ndarray[Any, np.dtype[np.generic]]: + return np.array(self.array, dtype=dtype, copy=copy) class CustomArrayIndexable( From e77463d708850ecf086fda02484d565f8f904f57 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Sun, 14 Apr 2024 11:35:40 +0200 Subject: [PATCH 03/10] don't pass `copy` to `asarray` The `copy` kwarg appears to only exist in `numpy>=2`. --- xarray/core/indexing.py | 14 +++++++------- xarray/tests/test_assertions.py | 2 +- xarray/tests/test_namedarray.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/xarray/core/indexing.py b/xarray/core/indexing.py index 2a1e876b429..f1631bc01c8 100644 --- a/xarray/core/indexing.py +++ b/xarray/core/indexing.py @@ -507,7 +507,7 @@ def __array__( self, dtype: np.typing.DTypeLike = None, copy: bool | None = None ) -> np.ndarray: # Leave casting to an array up to the underlying array type. - return np.asarray(self.get_duck_array(), dtype=dtype, copy=copy) + return np.asarray(self.get_duck_array(), dtype=dtype) def get_duck_array(self): return self.array @@ -525,7 +525,7 @@ def __array__( ) -> np.ndarray: # This is necessary because we apply the indexing key in self.get_duck_array() # Note this is the base class for all lazy indexing classes - return np.asarray(self.get_duck_array(), dtype=dtype, copy=copy) + return np.asarray(self.get_duck_array(), dtype=dtype) def _oindex_get(self, indexer: OuterIndexer): raise NotImplementedError( @@ -575,7 +575,7 @@ def __init__(self, array, indexer_cls: type[ExplicitIndexer] = BasicIndexer): def __array__( self, dtype: np.typing.DTypeLike = None, copy: bool | None = None ) -> np.ndarray: - return np.asarray(self.get_duck_array(), dtype=dtype, copy=copy) + return np.asarray(self.get_duck_array(), dtype=dtype) def get_duck_array(self): return self.array.get_duck_array() @@ -835,7 +835,7 @@ def _ensure_cached(self): def __array__( self, dtype: np.typing.DTypeLike = None, copy: bool | None = None ) -> np.ndarray: - return np.asarray(self.get_duck_array(), dtype=dtype, copy=copy) + return np.asarray(self.get_duck_array(), dtype=dtype) def get_duck_array(self): self._ensure_cached() @@ -1685,7 +1685,7 @@ def __array__( with suppress(AttributeError): # this might not be public API array = array.astype("object") - return np.asarray(array.values, dtype=dtype, copy=copy) + return np.asarray(array.values, dtype=dtype) def get_duck_array(self) -> np.ndarray: return np.asarray(self) @@ -1838,10 +1838,10 @@ def __array__( dtype = self.dtype if self.level is not None: return np.asarray( - self.array.get_level_values(self.level).values, dtype=dtype, copy=copy + self.array.get_level_values(self.level).values, dtype=dtype ) else: - return super().__array__(dtype, copy=copy) + return super().__array__(dtype) def _convert_scalar(self, item): if isinstance(item, tuple) and self.level is not None: diff --git a/xarray/tests/test_assertions.py b/xarray/tests/test_assertions.py index 59861ef7981..f7e49a0f3de 100644 --- a/xarray/tests/test_assertions.py +++ b/xarray/tests/test_assertions.py @@ -149,7 +149,7 @@ def dims(self): warnings.warn("warning in test") return super().dims - def __array__(self): + def __array__(self, dtype=None, copy=None): warnings.warn("warning in test") return super().__array__() diff --git a/xarray/tests/test_namedarray.py b/xarray/tests/test_namedarray.py index ca18841584e..83565679eba 100644 --- a/xarray/tests/test_namedarray.py +++ b/xarray/tests/test_namedarray.py @@ -54,7 +54,7 @@ class CustomArray( CustomArrayBase[_ShapeType_co, _DType_co], Generic[_ShapeType_co, _DType_co] ): def __array__(self, dtype=None, copy=None) -> np.ndarray[Any, np.dtype[np.generic]]: - return np.array(self.array, dtype=dtype, copy=copy) + return np.array(self.array, dtype=dtype) class CustomArrayIndexable( From 441482c3f9b6401720d1e5cffb58b0b9ac639b0f Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Sun, 14 Apr 2024 11:45:00 +0200 Subject: [PATCH 04/10] try making the typing the same for all overloads --- xarray/namedarray/_typing.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xarray/namedarray/_typing.py b/xarray/namedarray/_typing.py index fa7e5f30cc9..4f6049478b8 100644 --- a/xarray/namedarray/_typing.py +++ b/xarray/namedarray/_typing.py @@ -154,11 +154,13 @@ def __getitem__( @overload def __array__( - self, dtype: None = ..., copy: None = ..., / + self, dtype: None = ..., copy: bool | None = None, / ) -> np.ndarray[Any, _DType_co]: ... @overload - def __array__(self, dtype: _DType, copy: bool, /) -> np.ndarray[Any, _DType]: ... + def __array__( + self, dtype: _DType, copy: bool | None = None, / + ) -> np.ndarray[Any, _DType]: ... def __array__( self, dtype: _DType | None = ..., copy: bool | None = None, / From 4ad40002152cecde163d3ee10feeee229562eabb Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Sun, 14 Apr 2024 19:02:14 +0200 Subject: [PATCH 05/10] change the typing for `NamedArray.__array__` --- xarray/namedarray/_typing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xarray/namedarray/_typing.py b/xarray/namedarray/_typing.py index 4f6049478b8..58fe47e5683 100644 --- a/xarray/namedarray/_typing.py +++ b/xarray/namedarray/_typing.py @@ -154,16 +154,16 @@ def __getitem__( @overload def __array__( - self, dtype: None = ..., copy: bool | None = None, / + self, dtype: None = ..., copy: None | bool = ..., / ) -> np.ndarray[Any, _DType_co]: ... @overload def __array__( - self, dtype: _DType, copy: bool | None = None, / + self, dtype: _DType, copy: None | bool = ..., / ) -> np.ndarray[Any, _DType]: ... def __array__( - self, dtype: _DType | None = ..., copy: bool | None = None, / + self, dtype: _DType | None = ..., /, *, copy: None | bool = ... ) -> np.ndarray[Any, _DType] | np.ndarray[Any, _DType_co]: ... # TODO: Should return the same subclass but with a new dtype generic. From d830cef922ecd2b6432469512a6ba038a11a896c Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Sun, 14 Apr 2024 19:08:01 +0200 Subject: [PATCH 06/10] fix the signature of `__array__` --- xarray/namedarray/_typing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xarray/namedarray/_typing.py b/xarray/namedarray/_typing.py index 58fe47e5683..bec23b53b6a 100644 --- a/xarray/namedarray/_typing.py +++ b/xarray/namedarray/_typing.py @@ -154,12 +154,12 @@ def __getitem__( @overload def __array__( - self, dtype: None = ..., copy: None | bool = ..., / + self, dtype: None = ..., /, *, copy: None | bool = ... ) -> np.ndarray[Any, _DType_co]: ... @overload def __array__( - self, dtype: _DType, copy: None | bool = ..., / + self, dtype: _DType, /, *, copy: None | bool = ... ) -> np.ndarray[Any, _DType]: ... def __array__( From bb73a31c9112178144d4c979c2054f4e92bfd589 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Tue, 30 Apr 2024 14:01:08 +0200 Subject: [PATCH 07/10] revert the change to the signature of `__array__` --- xarray/core/common.py | 4 +--- xarray/core/dataset.py | 2 +- xarray/core/datatree.py | 2 +- xarray/core/groupby.py | 4 ++-- xarray/core/indexing.py | 24 ++++++------------------ xarray/namedarray/_typing.py | 10 +++------- xarray/tests/__init__.py | 4 ++-- xarray/tests/test_formatting.py | 2 +- xarray/tests/test_namedarray.py | 4 ++-- 9 files changed, 19 insertions(+), 37 deletions(-) diff --git a/xarray/core/common.py b/xarray/core/common.py index 9f2b9a54469..7b9a049c662 100644 --- a/xarray/core/common.py +++ b/xarray/core/common.py @@ -161,9 +161,7 @@ def __int__(self: Any) -> int: def __complex__(self: Any) -> complex: return complex(self.values) - def __array__( - self: Any, dtype: DTypeLike | None = None, copy: bool | None = None - ) -> np.ndarray: + def __array__(self: Any, dtype: DTypeLike | None = None) -> np.ndarray: return np.asarray(self.values, dtype=dtype) def __repr__(self) -> str: diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index b7e2af77920..05a9a534431 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -1509,7 +1509,7 @@ def __iter__(self) -> Iterator[Hashable]: else: - def __array__(self, dtype=None, copy=None): + def __array__(self, dtype=None): raise TypeError( "cannot directly convert an xarray.Dataset into a " "numpy array. Instead, create an xarray.DataArray " diff --git a/xarray/core/datatree.py b/xarray/core/datatree.py index 9c4462e3aff..48c714b697c 100644 --- a/xarray/core/datatree.py +++ b/xarray/core/datatree.py @@ -624,7 +624,7 @@ def __bool__(self) -> bool: def __iter__(self) -> Iterator[Hashable]: return itertools.chain(self.ds.data_vars, self.children) - def __array__(self, dtype=None, copy=None): + def __array__(self, dtype=None): raise TypeError( "cannot directly convert a DataTree into a " "numpy array. Instead, create an xarray.DataArray " diff --git a/xarray/core/groupby.py b/xarray/core/groupby.py index 76a0e97b760..5966c32df92 100644 --- a/xarray/core/groupby.py +++ b/xarray/core/groupby.py @@ -243,8 +243,8 @@ def values(self) -> range: def data(self) -> range: return range(self.size) - def __array__(self, dtype=None, copy=None) -> np.ndarray: - return np.arange(self.size, dtype=dtype) + def __array__(self) -> np.ndarray: + return np.arange(self.size) @property def shape(self) -> tuple[int]: diff --git a/xarray/core/indexing.py b/xarray/core/indexing.py index f1631bc01c8..0926da6fd80 100644 --- a/xarray/core/indexing.py +++ b/xarray/core/indexing.py @@ -503,9 +503,7 @@ class ExplicitlyIndexed: __slots__ = () - def __array__( - self, dtype: np.typing.DTypeLike = None, copy: bool | None = None - ) -> np.ndarray: + def __array__(self, dtype: np.typing.DTypeLike = None) -> np.ndarray: # Leave casting to an array up to the underlying array type. return np.asarray(self.get_duck_array(), dtype=dtype) @@ -520,9 +518,7 @@ def get_duck_array(self): key = BasicIndexer((slice(None),) * self.ndim) return self[key] - def __array__( - self, dtype: np.typing.DTypeLike = None, copy: bool | None = None - ) -> np.ndarray: + def __array__(self, dtype: np.typing.DTypeLike = None) -> np.ndarray: # This is necessary because we apply the indexing key in self.get_duck_array() # Note this is the base class for all lazy indexing classes return np.asarray(self.get_duck_array(), dtype=dtype) @@ -572,9 +568,7 @@ def __init__(self, array, indexer_cls: type[ExplicitIndexer] = BasicIndexer): self.array = as_indexable(array) self.indexer_cls = indexer_cls - def __array__( - self, dtype: np.typing.DTypeLike = None, copy: bool | None = None - ) -> np.ndarray: + def __array__(self, dtype: np.typing.DTypeLike = None) -> np.ndarray: return np.asarray(self.get_duck_array(), dtype=dtype) def get_duck_array(self): @@ -832,9 +826,7 @@ def __init__(self, array): def _ensure_cached(self): self.array = as_indexable(self.array.get_duck_array()) - def __array__( - self, dtype: np.typing.DTypeLike = None, copy: bool | None = None - ) -> np.ndarray: + def __array__(self, dtype: np.typing.DTypeLike = None) -> np.ndarray: return np.asarray(self.get_duck_array(), dtype=dtype) def get_duck_array(self): @@ -1675,9 +1667,7 @@ def __init__(self, array: pd.Index, dtype: DTypeLike = None): def dtype(self) -> np.dtype: return self._dtype - def __array__( - self, dtype: DTypeLike = None, copy: bool | None = None - ) -> np.ndarray: + def __array__(self, dtype: DTypeLike = None) -> np.ndarray: if dtype is None: dtype = self.dtype array = self.array @@ -1831,9 +1821,7 @@ def __init__( super().__init__(array, dtype) self.level = level - def __array__( - self, dtype: DTypeLike = None, copy: bool | None = None - ) -> np.ndarray: + def __array__(self, dtype: DTypeLike = None) -> np.ndarray: if dtype is None: dtype = self.dtype if self.level is not None: diff --git a/xarray/namedarray/_typing.py b/xarray/namedarray/_typing.py index bec23b53b6a..b715973814f 100644 --- a/xarray/namedarray/_typing.py +++ b/xarray/namedarray/_typing.py @@ -153,17 +153,13 @@ def __getitem__( ) -> _arrayfunction[Any, _DType_co] | Any: ... @overload - def __array__( - self, dtype: None = ..., /, *, copy: None | bool = ... - ) -> np.ndarray[Any, _DType_co]: ... + def __array__(self, dtype: None = ..., /) -> np.ndarray[Any, _DType_co]: ... @overload - def __array__( - self, dtype: _DType, /, *, copy: None | bool = ... - ) -> np.ndarray[Any, _DType]: ... + def __array__(self, dtype: _DType, /) -> np.ndarray[Any, _DType]: ... def __array__( - self, dtype: _DType | None = ..., /, *, copy: None | bool = ... + self, dtype: _DType | None = ..., / ) -> np.ndarray[Any, _DType] | np.ndarray[Any, _DType_co]: ... # TODO: Should return the same subclass but with a new dtype generic. diff --git a/xarray/tests/__init__.py b/xarray/tests/__init__.py index 9346cfa8300..c202e191293 100644 --- a/xarray/tests/__init__.py +++ b/xarray/tests/__init__.py @@ -227,7 +227,7 @@ def __init__(self, array): def get_duck_array(self): raise UnexpectedDataAccess("Tried accessing data") - def __array__(self, dtype: np.typing.DTypeLike = None, copy: bool | None = None): + def __array__(self, dtype: np.typing.DTypeLike = None): raise UnexpectedDataAccess("Tried accessing data") def __getitem__(self, key): @@ -252,7 +252,7 @@ def __init__(self, array: np.ndarray): def __getitem__(self, key): return type(self)(self.array[key]) - def __array__(self, dtype: np.typing.DTypeLike = None, copy: bool | None = None): + def __array__(self, dtype: np.typing.DTypeLike = None): raise UnexpectedDataAccess("Tried accessing data") def __array_namespace__(self): diff --git a/xarray/tests/test_formatting.py b/xarray/tests/test_formatting.py index 2c40ac88f98..cb765ce91f8 100644 --- a/xarray/tests/test_formatting.py +++ b/xarray/tests/test_formatting.py @@ -877,7 +877,7 @@ def test_lazy_array_wont_compute() -> None: from xarray.core.indexing import LazilyIndexedArray class LazilyIndexedArrayNotComputable(LazilyIndexedArray): - def __array__(self, dtype=None, copy=None): + def __array__(self, dtype=None): raise NotImplementedError("Computing this array is not possible.") arr = LazilyIndexedArrayNotComputable(np.array([1, 2])) diff --git a/xarray/tests/test_namedarray.py b/xarray/tests/test_namedarray.py index 83565679eba..2a3faf32b85 100644 --- a/xarray/tests/test_namedarray.py +++ b/xarray/tests/test_namedarray.py @@ -53,8 +53,8 @@ def shape(self) -> _Shape: class CustomArray( CustomArrayBase[_ShapeType_co, _DType_co], Generic[_ShapeType_co, _DType_co] ): - def __array__(self, dtype=None, copy=None) -> np.ndarray[Any, np.dtype[np.generic]]: - return np.array(self.array, dtype=dtype) + def __array__(self) -> np.ndarray[Any, np.dtype[np.generic]]: + return np.array(self.array) class CustomArrayIndexable( From d65c1e5e8d176a8e485446aedbe01fdba81eb8ce Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Tue, 30 Apr 2024 14:03:26 +0200 Subject: [PATCH 08/10] ignore all deprecation warnings about `__array__` gaining a new kwarg I don't know enough about typing to ignore typing issues within a protocol, so that was the only way I could figure out how to get this PR unstuck. Once we know what to do here, we can remove the ignore in a new PR. --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index d2b4b57e0bf..c11a077f01a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -327,6 +327,8 @@ filterwarnings = [ "default:Using a non-tuple sequence for multidimensional indexing is deprecated:FutureWarning", "default:Duplicate dimension names present:UserWarning:xarray.namedarray.core", "default:::xarray.tests.test_strategies", + # TODO: remove once we know how to deal with a changed signature in protocols + "ignore:__array__ implementation doesn't accept a copy keyword, so passing copy=False failed.", ] log_cli_level = "INFO" From 7b0b077293a43e07653dcc4b5ca9e67b342ac8b5 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Tue, 30 Apr 2024 14:14:28 +0200 Subject: [PATCH 09/10] add back the `copy` kwarg if the protocol does not have type hints --- xarray/core/dataset.py | 2 +- xarray/core/datatree.py | 2 +- xarray/tests/test_formatting.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 05a9a534431..b7e2af77920 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -1509,7 +1509,7 @@ def __iter__(self) -> Iterator[Hashable]: else: - def __array__(self, dtype=None): + def __array__(self, dtype=None, copy=None): raise TypeError( "cannot directly convert an xarray.Dataset into a " "numpy array. Instead, create an xarray.DataArray " diff --git a/xarray/core/datatree.py b/xarray/core/datatree.py index 48c714b697c..9c4462e3aff 100644 --- a/xarray/core/datatree.py +++ b/xarray/core/datatree.py @@ -624,7 +624,7 @@ def __bool__(self) -> bool: def __iter__(self) -> Iterator[Hashable]: return itertools.chain(self.ds.data_vars, self.children) - def __array__(self, dtype=None): + def __array__(self, dtype=None, copy=None): raise TypeError( "cannot directly convert a DataTree into a " "numpy array. Instead, create an xarray.DataArray " diff --git a/xarray/tests/test_formatting.py b/xarray/tests/test_formatting.py index cb765ce91f8..2c40ac88f98 100644 --- a/xarray/tests/test_formatting.py +++ b/xarray/tests/test_formatting.py @@ -877,7 +877,7 @@ def test_lazy_array_wont_compute() -> None: from xarray.core.indexing import LazilyIndexedArray class LazilyIndexedArrayNotComputable(LazilyIndexedArray): - def __array__(self, dtype=None): + def __array__(self, dtype=None, copy=None): raise NotImplementedError("Computing this array is not possible.") arr = LazilyIndexedArrayNotComputable(np.array([1, 2])) From 709f338752dbb75c9e5ed0c4702bdb6b028065eb Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Tue, 30 Apr 2024 14:58:20 -0600 Subject: [PATCH 10/10] Update xarray/core/dataset.py --- xarray/core/dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index b7e2af77920..91810787321 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -8922,7 +8922,7 @@ def polyfit( lhs = np.vander(x, order) if rcond is None: - rcond = x.shape[0] * np.finfo(x.dtype).eps # type: ignore[attr-defined] + rcond = x.shape[0] * np.finfo(x.dtype).eps # Weights: if w is not None: