Skip to content

Commit bb495f8

Browse files
authored
(fix): numeric arrow dtype deep copies (#10315)
1 parent c8affb3 commit bb495f8

File tree

2 files changed

+22
-0
lines changed

2 files changed

+22
-0
lines changed

xarray/core/extension_array.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import copy
34
from collections.abc import Callable, Sequence
45
from dataclasses import dataclass
56
from typing import Any, Generic, cast
@@ -148,4 +149,14 @@ def __getattr__(self, attr: str) -> Any:
148149
# Thus, if we didn't have `super().__getattribute__("array")` this method would call `self.array` (i.e., `getattr(self, "array")`) again while looking for `__setstate__`
149150
# (which is apparently the first thing sought in copy.copy from the under-construction copied object),
150151
# which would cause a recursion error since `array` is not present on the object when it is being constructed during `__{deep}copy__`.
152+
# Even though we have defined these two methods now below due to `test_extension_array_copy_arrow_type` (cause unknown)
153+
# we leave this here as it more robust than self.array
151154
return getattr(super().__getattribute__("array"), attr)
155+
156+
def __copy__(self) -> PandasExtensionArray[T_ExtensionArray]:
157+
return PandasExtensionArray(copy.copy(self.array))
158+
159+
def __deepcopy__(
160+
self, memo: dict[int, Any] | None = None
161+
) -> PandasExtensionArray[T_ExtensionArray]:
162+
return PandasExtensionArray(copy.deepcopy(self.array, memo=memo))

xarray/tests/test_duck_array_ops.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import copy
34
import datetime as dt
45
import pickle
56
import warnings
@@ -200,6 +201,16 @@ def test_extension_array_pyarrow_concatenate(self, arrow1, arrow2):
200201
assert concatenated[2].array[0]["x"] == 3
201202
assert concatenated[3].array[0]["y"]
202203

204+
@requires_pyarrow
205+
def test_extension_array_copy_arrow_type(self):
206+
arr = pd.array([pd.NA, 1, 2], dtype="int64[pyarrow]")
207+
# Relying on the `__getattr__` of `PandasExtensionArray` to do the deep copy
208+
# recursively only fails for `int64[pyarrow]` and similar types so this
209+
# test ensures that copying still works there.
210+
assert isinstance(
211+
copy.deepcopy(PandasExtensionArray(arr), memo=None).array, type(arr)
212+
)
213+
203214
def test___getitem__extension_duck_array(self, categorical1):
204215
extension_duck_array = PandasExtensionArray(categorical1)
205216
assert (extension_duck_array[0:2] == categorical1[0:2]).all()

0 commit comments

Comments
 (0)