Skip to content

Commit b1d3897

Browse files
authored
REF: pass setitem to unbox_scalar to de-duplicate validation (#36234)
1 parent 9e3b8df commit b1d3897

File tree

5 files changed

+20
-16
lines changed

5 files changed

+20
-16
lines changed

pandas/core/arrays/datetimelike.py

+11-9
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,16 @@ def _rebox_native(cls, value: int) -> Union[int, np.datetime64, np.timedelta64]:
183183
"""
184184
raise AbstractMethodError(cls)
185185

186-
def _unbox_scalar(self, value: DTScalarOrNaT) -> int:
186+
def _unbox_scalar(self, value: DTScalarOrNaT, setitem: bool = False) -> int:
187187
"""
188188
Unbox the integer value of a scalar `value`.
189189
190190
Parameters
191191
----------
192192
value : Period, Timestamp, Timedelta, or NaT
193193
Depending on subclass.
194+
setitem : bool, default False
195+
Whether to check compatiblity with setitem strictness.
194196
195197
Returns
196198
-------
@@ -841,6 +843,7 @@ def _validate_listlike(
841843
if is_dtype_equal(value.categories.dtype, self.dtype):
842844
# TODO: do we need equal dtype or just comparable?
843845
value = value._internal_get_values()
846+
value = extract_array(value, extract_numpy=True)
844847

845848
if allow_object and is_object_dtype(value.dtype):
846849
pass
@@ -875,8 +878,7 @@ def _validate_setitem_value(self, value):
875878
# TODO: cast_str for consistency?
876879
value = self._validate_scalar(value, msg, cast_str=False)
877880

878-
self._check_compatible_with(value, setitem=True)
879-
return self._unbox(value)
881+
return self._unbox(value, setitem=True)
880882

881883
def _validate_insert_value(self, value):
882884
msg = f"cannot insert {type(self).__name__} with incompatible label"
@@ -886,27 +888,27 @@ def _validate_insert_value(self, value):
886888
# TODO: if we dont have compat, should we raise or astype(object)?
887889
# PeriodIndex does astype(object)
888890
return value
891+
# Note: we do not unbox here because the caller needs boxed value
892+
# to check for freq.
889893

890894
def _validate_where_value(self, other):
891895
msg = f"Where requires matching dtype, not {type(other)}"
892896
if not is_list_like(other):
893897
other = self._validate_scalar(other, msg)
894898
else:
895899
other = self._validate_listlike(other, "where")
896-
self._check_compatible_with(other, setitem=True)
897900

898-
self._check_compatible_with(other, setitem=True)
899-
return self._unbox(other)
901+
return self._unbox(other, setitem=True)
900902

901-
def _unbox(self, other) -> Union[np.int64, np.ndarray]:
903+
def _unbox(self, other, setitem: bool = False) -> Union[np.int64, np.ndarray]:
902904
"""
903905
Unbox either a scalar with _unbox_scalar or an instance of our own type.
904906
"""
905907
if lib.is_scalar(other):
906-
other = self._unbox_scalar(other)
908+
other = self._unbox_scalar(other, setitem=setitem)
907909
else:
908910
# same type as self
909-
self._check_compatible_with(other)
911+
self._check_compatible_with(other, setitem=setitem)
910912
other = other.view("i8")
911913
return other
912914

pandas/core/arrays/datetimes.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -451,11 +451,11 @@ def _generate_range(
451451
def _rebox_native(cls, value: int) -> np.datetime64:
452452
return np.int64(value).view("M8[ns]")
453453

454-
def _unbox_scalar(self, value):
454+
def _unbox_scalar(self, value, setitem: bool = False):
455455
if not isinstance(value, self._scalar_type) and value is not NaT:
456456
raise ValueError("'value' should be a Timestamp.")
457457
if not isna(value):
458-
self._check_compatible_with(value)
458+
self._check_compatible_with(value, setitem=setitem)
459459
return value.value
460460

461461
def _scalar_from_string(self, value):

pandas/core/arrays/period.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,13 @@ def _generate_range(cls, start, end, periods, freq, fields):
257257
def _rebox_native(cls, value: int) -> np.int64:
258258
return np.int64(value)
259259

260-
def _unbox_scalar(self, value: Union[Period, NaTType]) -> int:
260+
def _unbox_scalar(
261+
self, value: Union[Period, NaTType], setitem: bool = False
262+
) -> int:
261263
if value is NaT:
262264
return value.value
263265
elif isinstance(value, self._scalar_type):
264-
self._check_compatible_with(value)
266+
self._check_compatible_with(value, setitem=setitem)
265267
return value.ordinal
266268
else:
267269
raise ValueError(f"'value' should be a Period. Got '{value}' instead.")

pandas/core/arrays/timedeltas.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -283,10 +283,10 @@ def _generate_range(cls, start, end, periods, freq, closed=None):
283283
def _rebox_native(cls, value: int) -> np.timedelta64:
284284
return np.int64(value).view("m8[ns]")
285285

286-
def _unbox_scalar(self, value):
286+
def _unbox_scalar(self, value, setitem: bool = False):
287287
if not isinstance(value, self._scalar_type) and value is not NaT:
288288
raise ValueError("'value' should be a Timedelta.")
289-
self._check_compatible_with(value)
289+
self._check_compatible_with(value, setitem=setitem)
290290
return value.value
291291

292292
def _scalar_from_string(self, value):

pandas/tests/arrays/test_datetimelike.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ def test_searchsorted(self):
244244
# GH#29884 match numpy convention on whether NaT goes
245245
# at the end or the beginning
246246
result = arr.searchsorted(pd.NaT)
247-
if _np_version_under1p18 or self.array_cls is PeriodArray:
247+
if np_version_under1p18 or self.array_cls is PeriodArray:
248248
# Following numpy convention, NaT goes at the beginning
249249
# (unlike NaN which goes at the end)
250250
assert result == 0

0 commit comments

Comments
 (0)