Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: fix for _validate_setitem_value fails to raise for PandasArray and fix tests #59326

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ ExtensionArray
- Bug in :meth:`.arrays.ArrowExtensionArray.__setitem__` which caused wrong behavior when using an integer array with repeated values as a key (:issue:`58530`)
- Bug in :meth:`api.types.is_datetime64_any_dtype` where a custom :class:`ExtensionDtype` would return ``False`` for array-likes (:issue:`57055`)
- Bug in various :class:`DataFrame` reductions for pyarrow temporal dtypes returning incorrect dtype when result was null (:issue:`59234`)
- Bug in :class:`NumpyExtensionArray` where it did not raise any error if validated value to be inserted did not have the same dtype (:issue:`51044`).

Styler
^^^^^^
Expand Down
79 changes: 79 additions & 0 deletions pandas/core/arrays/numpy_.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,85 @@ def _validate_scalar(self, fill_value):
fill_value = self.dtype.na_value
return fill_value

def _validate_setitem_value(self, value) -> type(value) | None:
"""
Check if we have a scalar that we can cast losslessly.

Raises
------
TypeError
"""

kind = self.dtype.kind

if kind == "b":
if lib.is_bool(value) or np.can_cast(type(value), self.dtype.type):
return value
if isinstance(value, NumpyExtensionArray) and (
lib.is_bool_array(value.to_numpy())
or lib.is_bool_list(value.to_numpy())
):
return value

elif kind == "i":
if lib.is_integer(value) or np.can_cast(type(value), self.dtype.type):
return value
if isinstance(value, NumpyExtensionArray) and lib.is_integer_array(
value.to_numpy()
):
return value

elif kind == "u":
if (lib.is_integer(value) and value > -1) or np.can_cast(
type(value), self.dtype.type
):
return value

elif kind == "c":
if lib.is_complex(value) or np.can_cast(type(value), self.dtype.type):
return value

elif kind == "S":
if isinstance(value, str) or np.can_cast(type(value), self.dtype.type):
return value
if isinstance(value, NumpyExtensionArray) and lib.is_string_array(
value.to_numpy()
):
return value

elif kind == "M":
if isinstance(value, np.datetime64):
return value
if isinstance(value, NumpyExtensionArray) and (
lib.is_date_array(value.to_numpy())
or lib.is_datetime_array(value.to_numpy())
or lib.is_datetime64_array(value.to_numpy())
or lib.is_datetime_with_singletz_array(value.to_numpy())
):
return value

elif kind == "m":
if isinstance(value, np.timedelta64):
return value
if isinstance(value, NumpyExtensionArray) and (
lib.is_timedelta_or_timedelta64_array(value.to_numpy())
or lib.is_time_array(value.to_numpy())
):
return value

elif kind == "f":
if lib.is_float(value) or np.can_cast(type(value), self.dtype.type):
return value
if isinstance(value, NumpyExtensionArray) and lib.is_float_array(
value.to_numpy()
):
return value

elif np.can_cast(type(value), self.dtype.type):
return value

raise TypeError(f"Invalid value '{value!s}' for dtype {self.dtype}")

def _values_for_factorize(self) -> tuple[np.ndarray, float | None]:
if self.dtype.kind in "iub":
fv = None
Expand Down
12 changes: 9 additions & 3 deletions pandas/tests/arrays/numpy_/test_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,12 +275,15 @@ def test_setitem_object_typecode(dtype):
def test_setitem_no_coercion():
# https://github.com/pandas-dev/pandas/issues/28150
arr = NumpyExtensionArray(np.array([1, 2, 3]))
with pytest.raises(ValueError, match="int"):
with pytest.raises(TypeError):
arr[0] = "a"

# With a value that we do coerce, check that we coerce the value
# and not the underlying array.
arr[0] = 2.5
with pytest.raises(TypeError):
arr[0] = 2.5

arr[0] = 9
assert isinstance(arr[0], (int, np.integer)), type(arr[0])


Expand All @@ -296,7 +299,10 @@ def test_setitem_preserves_views():
assert view2[0] == 9
assert view3[0] == 9

arr[-1] = 2.5
with pytest.raises(TypeError):
arr[-1] = 2.5

arr[-1] = 4
view1[-1] = 5
assert arr[-1] == 5

Expand Down
Loading