Skip to content

REGR: fix return class in _constructor_from_mgr for simple subclasses #55764

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2 changes: 1 addition & 1 deletion doc/source/whatsnew/v2.1.3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ including other versions of pandas.

Fixed regressions
~~~~~~~~~~~~~~~~~
-
- Fixed infinite recursion from operations that return a new object on some DataFrame subclasses (:issue:`55763`)
-

.. ---------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,7 @@ def _constructor(self) -> Callable[..., DataFrame]:
def _constructor_from_mgr(self, mgr, axes):
if self._constructor is DataFrame:
# we are pandas.DataFrame (or a subclass that doesn't override _constructor)
return self._from_mgr(mgr, axes=axes)
return DataFrame._from_mgr(mgr, axes=axes)
else:
assert axes is mgr.axes
return self._constructor(mgr)
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ def _constructor(self) -> Callable[..., Series]:
def _constructor_from_mgr(self, mgr, axes):
if self._constructor is Series:
# we are pandas.Series (or a subclass that doesn't override _constructor)
ser = self._from_mgr(mgr, axes=axes)
ser = Series._from_mgr(mgr, axes=axes)
ser._name = None # caller is responsible for setting real name
return ser
else:
Expand Down
31 changes: 31 additions & 0 deletions pandas/tests/frame/test_subclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -773,3 +773,34 @@ def test_constructor_with_metadata():
)
subset = df[["A", "B"]]
assert isinstance(subset, MySubclassWithMetadata)


class SimpleDataFrameSubClass(DataFrame):
"""A subclass of DataFrame that does not define a constructor."""


class SimpleSeriesSubClass(Series):
"""A subclass of Series that does not define a constructor."""


class TestSubclassWithoutConstructor:
def test_copy_df(self):
expected = DataFrame({"a": [1, 2, 3]})
result = SimpleDataFrameSubClass(expected).copy()

assert (
type(result) is DataFrame
) # assert_frame_equal only checks isinstance(lhs, type(rhs))
tm.assert_frame_equal(result, expected)

def test_copy_series(self):
expected = Series([1, 2, 3])
result = SimpleSeriesSubClass(expected).copy()

tm.assert_series_equal(result, expected)

def test_groupby(self):
df = SimpleDataFrameSubClass(DataFrame({"a": [1, 2, 3]}))

for _, v in df.groupby("a"):
assert type(v) is DataFrame