Skip to content
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

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

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
41 changes: 41 additions & 0 deletions pandas/tests/frame/test_subclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -773,3 +773,44 @@ 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)
ivirshup marked this conversation as resolved.
Show resolved Hide resolved

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

tm.assert_series_equal(result, expected)

ivirshup marked this conversation as resolved.
Show resolved Hide resolved
def test_series_to_frame(self):
orig = Series([1, 2, 3])
expected = orig.to_frame()
result = SimpleSeriesSubClass(orig).to_frame()

assert (
type(result) is DataFrame
) # assert_frame_equal only checks isinstance(lhs, type(rhs))
tm.assert_frame_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