diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 9bc0dd7cc02e6..e902c4f7eb14f 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -773,7 +773,7 @@ def _validate_fill_value(self, fill_value): return fill_value def _validate_shift_value(self, fill_value): - # TODO(2.0): once this deprecation is enforced, used _validate_fill_value + # TODO(2.0): once this deprecation is enforced, use _validate_fill_value if is_valid_nat_for_dtype(fill_value, self.dtype): fill_value = NaT elif not isinstance(fill_value, self._recognized_scalars): @@ -813,6 +813,9 @@ def _validate_searchsorted_value(self, value): elif isinstance(value, self._recognized_scalars): value = self._scalar_type(value) + elif isinstance(value, type(self)): + pass + elif is_list_like(value) and not isinstance(value, type(self)): value = array(value) @@ -822,7 +825,7 @@ def _validate_searchsorted_value(self, value): f"not {type(value).__name__}" ) - if not (isinstance(value, (self._scalar_type, type(self))) or (value is NaT)): + else: raise TypeError(f"Unexpected type for 'value': {type(value)}") if isinstance(value, type(self)): @@ -834,18 +837,28 @@ def _validate_searchsorted_value(self, value): return value def _validate_setitem_value(self, value): - if lib.is_scalar(value) and not isna(value): - value = com.maybe_box_datetimelike(value) if is_list_like(value): - value = type(self)._from_sequence(value, dtype=self.dtype) - self._check_compatible_with(value, setitem=True) - value = value.asi8 - elif isinstance(value, self._scalar_type): - self._check_compatible_with(value, setitem=True) - value = self._unbox_scalar(value) + value = array(value) + if is_dtype_equal(value.dtype, "string"): + # We got a StringArray + try: + # TODO: Could use from_sequence_of_strings if implemented + # Note: passing dtype is necessary for PeriodArray tests + value = type(self)._from_sequence(value, dtype=self.dtype) + except ValueError: + pass + + if not type(self)._is_recognized_dtype(value): + raise TypeError( + "setitem requires compatible dtype or scalar, " + f"not {type(value).__name__}" + ) + + elif isinstance(value, self._recognized_scalars): + value = self._scalar_type(value) elif is_valid_nat_for_dtype(value, self.dtype): - value = iNaT + value = NaT else: msg = ( f"'value' should be a '{self._scalar_type.__name__}', 'NaT', " @@ -853,6 +866,12 @@ def _validate_setitem_value(self, value): ) raise TypeError(msg) + self._check_compatible_with(value, setitem=True) + if isinstance(value, type(self)): + value = value.asi8 + else: + value = self._unbox_scalar(value) + return value def _validate_insert_value(self, value): diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index 5b703cfe8fae5..e84ebf3e479f1 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -241,6 +241,16 @@ def test_setitem(self): expected[:2] = expected[-2:] tm.assert_numpy_array_equal(arr.asi8, expected) + def test_setitem_str_array(self, arr1d): + if isinstance(arr1d, DatetimeArray) and arr1d.tz is not None: + pytest.xfail(reason="timezone comparisons inconsistent") + expected = arr1d.copy() + expected[[0, 1]] = arr1d[-2:] + + arr1d[:2] = [str(x) for x in arr1d[-2:]] + + tm.assert_equal(arr1d, expected) + def test_setitem_raises(self): data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9 arr = self.array_cls(data, freq="D") @@ -252,6 +262,17 @@ def test_setitem_raises(self): with pytest.raises(TypeError, match="'value' should be a.* 'object'"): arr[0] = object() + @pytest.mark.parametrize("box", [list, np.array, pd.Index, pd.Series]) + def test_setitem_numeric_raises(self, arr1d, box): + # We dont case e.g. int64 to our own dtype for setitem + + msg = "requires compatible dtype" + with pytest.raises(TypeError, match=msg): + arr1d[:2] = box([0, 1]) + + with pytest.raises(TypeError, match=msg): + arr1d[:2] = box([0.0, 1.0]) + def test_inplace_arithmetic(self): # GH#24115 check that iadd and isub are actually in-place data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9