Skip to content

Commit 4056ded

Browse files
jbrockmendeljreback
authored andcommitted
BUG: Avoid try/except in blocks, fix setitem bug in datetimelike EA (#27704)
1 parent 2263982 commit 4056ded

File tree

4 files changed

+52
-14
lines changed

4 files changed

+52
-14
lines changed

pandas/core/arrays/datetimelike.py

+2
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,8 @@ def __setitem__(
473473
# to a period in from_sequence). For DatetimeArray, it's Timestamp...
474474
# I don't know if mypy can do that, possibly with Generics.
475475
# https://mypy.readthedocs.io/en/latest/generics.html
476+
if lib.is_scalar(value) and not isna(value):
477+
value = com.maybe_box_datetimelike(value)
476478

477479
if is_list_like(value):
478480
is_slice = isinstance(key, slice)

pandas/core/internals/blocks.py

+18-14
Original file line numberDiff line numberDiff line change
@@ -2230,7 +2230,9 @@ def _can_hold_element(self, element):
22302230
if tipo is not None:
22312231
if self.is_datetimetz:
22322232
# require exact match, since non-nano does not exist
2233-
return is_dtype_equal(tipo, self.dtype)
2233+
return is_dtype_equal(tipo, self.dtype) or is_valid_nat_for_dtype(
2234+
element, self.dtype
2235+
)
22342236

22352237
# GH#27419 if we get a non-nano datetime64 object
22362238
return is_datetime64_dtype(tipo)
@@ -2500,26 +2502,28 @@ def concat_same_type(self, to_concat, placement=None):
25002502
def fillna(self, value, limit=None, inplace=False, downcast=None):
25012503
# We support filling a DatetimeTZ with a `value` whose timezone
25022504
# is different by coercing to object.
2503-
try:
2505+
if self._can_hold_element(value):
25042506
return super().fillna(value, limit, inplace, downcast)
2505-
except (ValueError, TypeError):
2506-
# different timezones, or a non-tz
2507-
return self.astype(object).fillna(
2508-
value, limit=limit, inplace=inplace, downcast=downcast
2509-
)
2507+
2508+
# different timezones, or a non-tz
2509+
return self.astype(object).fillna(
2510+
value, limit=limit, inplace=inplace, downcast=downcast
2511+
)
25102512

25112513
def setitem(self, indexer, value):
25122514
# https://github.com/pandas-dev/pandas/issues/24020
25132515
# Need a dedicated setitem until #24020 (type promotion in setitem
25142516
# for extension arrays) is designed and implemented.
2515-
try:
2517+
if self._can_hold_element(value) or (
2518+
isinstance(indexer, np.ndarray) and indexer.size == 0
2519+
):
25162520
return super().setitem(indexer, value)
2517-
except (ValueError, TypeError):
2518-
obj_vals = self.values.astype(object)
2519-
newb = make_block(
2520-
obj_vals, placement=self.mgr_locs, klass=ObjectBlock, ndim=self.ndim
2521-
)
2522-
return newb.setitem(indexer, value)
2521+
2522+
obj_vals = self.values.astype(object)
2523+
newb = make_block(
2524+
obj_vals, placement=self.mgr_locs, klass=ObjectBlock, ndim=self.ndim
2525+
)
2526+
return newb.setitem(indexer, value)
25232527

25242528
def equals(self, other):
25252529
# override for significant performance improvement

pandas/tests/arrays/test_datetimes.py

+16
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,22 @@ def test_setitem_clears_freq(self):
179179
a[0] = pd.Timestamp("2000", tz="US/Central")
180180
assert a.freq is None
181181

182+
@pytest.mark.parametrize(
183+
"obj",
184+
[
185+
pd.Timestamp.now(),
186+
pd.Timestamp.now().to_datetime64(),
187+
pd.Timestamp.now().to_pydatetime(),
188+
],
189+
)
190+
def test_setitem_objects(self, obj):
191+
# make sure we accept datetime64 and datetime in addition to Timestamp
192+
dti = pd.date_range("2000", periods=2, freq="D")
193+
arr = dti._data
194+
195+
arr[0] = obj
196+
assert arr[0] == obj
197+
182198
def test_repeat_preserves_tz(self):
183199
dti = pd.date_range("2000", periods=2, freq="D", tz="US/Central")
184200
arr = DatetimeArray(dti)

pandas/tests/arrays/test_timedeltas.py

+16
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,22 @@ def test_setitem_clears_freq(self):
125125
a[0] = pd.Timedelta("1H")
126126
assert a.freq is None
127127

128+
@pytest.mark.parametrize(
129+
"obj",
130+
[
131+
pd.Timedelta(seconds=1),
132+
pd.Timedelta(seconds=1).to_timedelta64(),
133+
pd.Timedelta(seconds=1).to_pytimedelta(),
134+
],
135+
)
136+
def test_setitem_objects(self, obj):
137+
# make sure we accept timedelta64 and timedelta in addition to Timedelta
138+
tdi = pd.timedelta_range("2 Days", periods=4, freq="H")
139+
arr = TimedeltaArray(tdi, freq=tdi.freq)
140+
141+
arr[0] = obj
142+
assert arr[0] == pd.Timedelta(seconds=1)
143+
128144

129145
class TestReductions:
130146
def test_min_max(self):

0 commit comments

Comments
 (0)