Skip to content

Commit

Permalink
BUG: RecursionError in loc.setitem
Browse files Browse the repository at this point in the history
  • Loading branch information
jbrockmendel committed Sep 19, 2023
1 parent 8352331 commit 4f2216b
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 0 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v2.2.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ Interval
Indexing
^^^^^^^^
- Bug in :meth:`Index.difference` not returning a unique set of values when ``other`` is empty or ``other`` is considered non-comparable (:issue:`55113`)
- Bug in setting :class:`Categorical` values into a :class:`DataFrame` with numpy dtypes raising ``RecursionError`` (:issue:`52927`)
-

Missing
Expand Down
18 changes: 18 additions & 0 deletions pandas/core/dtypes/cast.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
PeriodDtype,
)
from pandas.core.dtypes.generic import (
ABCExtensionArray,
ABCIndex,
ABCSeries,
)
Expand Down Expand Up @@ -1772,6 +1773,23 @@ def np_can_hold_element(dtype: np.dtype, element: Any) -> Any:
return casted
raise LossySetitemError

elif isinstance(element, ABCExtensionArray) and isinstance(
element.dtype, CategoricalDtype
):
# GH#52927 setting Categorical value into non-EA frame
# TODO: general-case for EAs?
try:
casted = element.astype(dtype)
except (ValueError, TypeError):
raise LossySetitemError
# Check for cases of either
# a) lossy overflow/rounding or
# b) semantic changes like dt64->int64
comp = casted == element
if not comp.all():
raise LossySetitemError
return casted

# Anything other than integer we cannot hold
raise LossySetitemError
if (
Expand Down
6 changes: 6 additions & 0 deletions pandas/core/internals/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,12 @@ def coerce_to_target_dtype(self, other, warn_on_upcast: bool = False) -> Block:
and will receive the same block
"""
new_dtype = find_result_type(self.values.dtype, other)
if new_dtype == self.dtype:
# GH#52927 avoid RecursionError
raise AssertionError(
"Something has gone wrong, please report a bug at "
"https://github.com/pandas-dev/pandas/issues"
)

# In a future version of pandas, the default will be that
# setting `nan` into an integer series won't raise.
Expand Down
8 changes: 8 additions & 0 deletions pandas/tests/indexing/test_loc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1657,6 +1657,14 @@ def test_loc_setitem_range_key(self, frame_or_series):
expected = frame_or_series([0, 1, 10, 9, 11], index=obj.index)
tm.assert_equal(obj, expected)

def test_loc_setitem_numpy_frame_categorical_value(self):
# GH#52927
df = DataFrame({"a": [1, 1, 1, 1, 1], "b": ["a", "a", "a", "a", "a"]})
df.loc[1:2, "a"] = Categorical([2, 2], categories=[1, 2])

expected = DataFrame({"a": [1, 2, 2, 1, 1], "b": ["a", "a", "a", "a", "a"]})
tm.assert_frame_equal(df, expected)


class TestLocWithEllipsis:
@pytest.fixture(params=[tm.loc, tm.iloc])
Expand Down

0 comments on commit 4f2216b

Please sign in to comment.