Skip to content

Commit f1f541b

Browse files
jbrockmendelrhshadrach
authored andcommitted
BUG: DTA/TDA/PA setitem incorrectly allowing i8 (pandas-dev#33717)
1 parent 19a1299 commit f1f541b

File tree

2 files changed

+51
-11
lines changed

2 files changed

+51
-11
lines changed

pandas/core/arrays/datetimelike.py

+30-11
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ def _validate_fill_value(self, fill_value):
740740
return fill_value
741741

742742
def _validate_shift_value(self, fill_value):
743-
# TODO(2.0): once this deprecation is enforced, used _validate_fill_value
743+
# TODO(2.0): once this deprecation is enforced, use _validate_fill_value
744744
if is_valid_nat_for_dtype(fill_value, self.dtype):
745745
fill_value = NaT
746746
elif isinstance(fill_value, self._recognized_scalars):
@@ -782,6 +782,9 @@ def _validate_searchsorted_value(self, value):
782782
elif isinstance(value, self._recognized_scalars):
783783
value = self._scalar_type(value)
784784

785+
elif isinstance(value, type(self)):
786+
pass
787+
785788
elif is_list_like(value) and not isinstance(value, type(self)):
786789
value = array(value)
787790

@@ -791,7 +794,7 @@ def _validate_searchsorted_value(self, value):
791794
f"not {type(value).__name__}"
792795
)
793796

794-
if not (isinstance(value, (self._scalar_type, type(self))) or (value is NaT)):
797+
else:
795798
raise TypeError(f"Unexpected type for 'value': {type(value)}")
796799

797800
if isinstance(value, type(self)):
@@ -803,25 +806,41 @@ def _validate_searchsorted_value(self, value):
803806
return value
804807

805808
def _validate_setitem_value(self, value):
806-
if lib.is_scalar(value) and not isna(value):
807-
value = com.maybe_box_datetimelike(value)
808809

809810
if is_list_like(value):
810-
value = type(self)._from_sequence(value, dtype=self.dtype)
811-
self._check_compatible_with(value, setitem=True)
812-
value = value.asi8
813-
elif isinstance(value, self._scalar_type):
814-
self._check_compatible_with(value, setitem=True)
815-
value = self._unbox_scalar(value)
811+
value = array(value)
812+
if is_dtype_equal(value.dtype, "string"):
813+
# We got a StringArray
814+
try:
815+
# TODO: Could use from_sequence_of_strings if implemented
816+
# Note: passing dtype is necessary for PeriodArray tests
817+
value = type(self)._from_sequence(value, dtype=self.dtype)
818+
except ValueError:
819+
pass
820+
821+
if not type(self)._is_recognized_dtype(value):
822+
raise TypeError(
823+
"setitem requires compatible dtype or scalar, "
824+
f"not {type(value).__name__}"
825+
)
826+
827+
elif isinstance(value, self._recognized_scalars):
828+
value = self._scalar_type(value)
816829
elif is_valid_nat_for_dtype(value, self.dtype):
817-
value = iNaT
830+
value = NaT
818831
else:
819832
msg = (
820833
f"'value' should be a '{self._scalar_type.__name__}', 'NaT', "
821834
f"or array of those. Got '{type(value).__name__}' instead."
822835
)
823836
raise TypeError(msg)
824837

838+
self._check_compatible_with(value, setitem=True)
839+
if isinstance(value, type(self)):
840+
value = value.asi8
841+
else:
842+
value = self._unbox_scalar(value)
843+
825844
return value
826845

827846
def _validate_insert_value(self, value):

pandas/tests/arrays/test_datetimelike.py

+21
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,16 @@ def test_setitem(self):
241241
expected[:2] = expected[-2:]
242242
tm.assert_numpy_array_equal(arr.asi8, expected)
243243

244+
def test_setitem_str_array(self, arr1d):
245+
if isinstance(arr1d, DatetimeArray) and arr1d.tz is not None:
246+
pytest.xfail(reason="timezone comparisons inconsistent")
247+
expected = arr1d.copy()
248+
expected[[0, 1]] = arr1d[-2:]
249+
250+
arr1d[:2] = [str(x) for x in arr1d[-2:]]
251+
252+
tm.assert_equal(arr1d, expected)
253+
244254
def test_setitem_raises(self):
245255
data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
246256
arr = self.array_cls(data, freq="D")
@@ -252,6 +262,17 @@ def test_setitem_raises(self):
252262
with pytest.raises(TypeError, match="'value' should be a.* 'object'"):
253263
arr[0] = object()
254264

265+
@pytest.mark.parametrize("box", [list, np.array, pd.Index, pd.Series])
266+
def test_setitem_numeric_raises(self, arr1d, box):
267+
# We dont case e.g. int64 to our own dtype for setitem
268+
269+
msg = "requires compatible dtype"
270+
with pytest.raises(TypeError, match=msg):
271+
arr1d[:2] = box([0, 1])
272+
273+
with pytest.raises(TypeError, match=msg):
274+
arr1d[:2] = box([0.0, 1.0])
275+
255276
def test_inplace_arithmetic(self):
256277
# GH#24115 check that iadd and isub are actually in-place
257278
data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9

0 commit comments

Comments
 (0)