diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index efc8bc695df85..f8e0ec05bd5c0 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -236,6 +236,7 @@ Deprecations - Deprecated :func:`is_interval_dtype`, check ``isinstance(dtype, pd.IntervalDtype)`` instead (:issue:`52607`) - Deprecated :meth:`DataFrame.applymap`. Use the new :meth:`DataFrame.map` method instead (:issue:`52353`) - Deprecated :meth:`DataFrame.swapaxes` and :meth:`Series.swapaxes`, use :meth:`DataFrame.transpose` or :meth:`Series.transpose` instead (:issue:`51946`) +- Deprecated ``_metadata`` propagation (:issue:`51280`) - Deprecated ``freq`` parameter in :class:`PeriodArray` constructor, pass ``dtype`` instead (:issue:`52462`) - Deprecated behavior of :func:`concat` when :class:`DataFrame` has columns that are all-NA, in a future version these will not be discarded when determining the resulting dtype (:issue:`40893`) - Deprecated behavior of :meth:`Series.dt.to_pydatetime`, in a future version this will return a :class:`Series` containing python ``datetime`` objects instead of an ``ndarray`` of datetimes; this matches the behavior of other :meth:`Series.dt` properties (:issue:`20306`) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index f3de296841510..03cdb2d47be26 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -6057,6 +6057,14 @@ def __finalize__(self, other, method: str | None = None, **kwargs) -> Self: # For subclasses using _metadata. for name in set(self._metadata) & set(other._metadata): assert isinstance(name, str) + if name != "_name": + warnings.warn( + "_metadata propagation is deprecated and will be removed " + "in a future version. To retain the old behavior, " + "override __finalize__ in a subclass.", + FutureWarning, + stacklevel=find_stack_level(), + ) object.__setattr__(self, name, getattr(other, name, None)) if method == "concat": @@ -6111,6 +6119,12 @@ def __setattr__(self, name: str, value) -> None: if name in self._internal_names_set: object.__setattr__(self, name, value) elif name in self._metadata: + warnings.warn( + "_metadata handling is deprecated and will be removed in " + "a future version.", + FutureWarning, + stacklevel=find_stack_level(), + ) object.__setattr__(self, name, value) else: try: diff --git a/pandas/tests/frame/test_arithmetic.py b/pandas/tests/frame/test_arithmetic.py index 090b3d64e7c41..d77ff8ddbdd84 100644 --- a/pandas/tests/frame/test_arithmetic.py +++ b/pandas/tests/frame/test_arithmetic.py @@ -2069,12 +2069,18 @@ def _constructor(self): def _constructor_sliced(self): return SubclassedSeries - sdf = SubclassedDataFrame("some_data", {"A": [1, 2, 3], "B": [4, 5, 6]}) - result = sdf * 2 - expected = SubclassedDataFrame("some_data", {"A": [2, 4, 6], "B": [8, 10, 12]}) + msg = "_metadata handling is deprecated" + + with tm.assert_produces_warning(FutureWarning, match=msg): + sdf = SubclassedDataFrame("some_data", {"A": [1, 2, 3], "B": [4, 5, 6]}) + with tm.assert_produces_warning(FutureWarning, match=msg): + result = sdf * 2 + with tm.assert_produces_warning(FutureWarning, match=msg): + expected = SubclassedDataFrame("some_data", {"A": [2, 4, 6], "B": [8, 10, 12]}) tm.assert_frame_equal(result, expected) - result = sdf + sdf + with tm.assert_produces_warning(FutureWarning, match=msg): + result = sdf + sdf tm.assert_frame_equal(result, expected) diff --git a/pandas/tests/frame/test_subclass.py b/pandas/tests/frame/test_subclass.py index 5c44a957b9373..e4eec8d1ddbb9 100644 --- a/pandas/tests/frame/test_subclass.py +++ b/pandas/tests/frame/test_subclass.py @@ -1,3 +1,5 @@ +import warnings + import numpy as np import pytest @@ -12,6 +14,8 @@ ) import pandas._testing as tm +warn_msg = "_metadata propagation is deprecated" + @pytest.fixture() def gpd_style_subclass_df(): @@ -80,22 +84,30 @@ def custom_frame_function(self): assert isinstance(cdf_multi2["A"], CustomSeries) def test_dataframe_metadata(self): + setattr_msg = "_metadata handling is deprecated" + df = tm.SubclassedDataFrame( {"X": [1, 2, 3], "Y": [1, 2, 3]}, index=["a", "b", "c"] ) - df.testattr = "XXX" + with tm.assert_produces_warning(FutureWarning, match=setattr_msg): + df.testattr = "XXX" assert df.testattr == "XXX" - assert df[["X"]].testattr == "XXX" - assert df.loc[["a", "b"], :].testattr == "XXX" - assert df.iloc[[0, 1], :].testattr == "XXX" + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + assert df[["X"]].testattr == "XXX" + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + assert df.loc[["a", "b"], :].testattr == "XXX" + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + assert df.iloc[[0, 1], :].testattr == "XXX" # see gh-9776 - assert df.iloc[0:1, :].testattr == "XXX" + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + assert df.iloc[0:1, :].testattr == "XXX" # see gh-10553 unpickled = tm.round_trip_pickle(df) - tm.assert_frame_equal(df, unpickled) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(df, unpickled) assert df._metadata == unpickled._metadata assert df.testattr == unpickled.testattr @@ -104,32 +116,38 @@ def test_indexing_sliced(self): df = tm.SubclassedDataFrame( {"X": [1, 2, 3], "Y": [4, 5, 6], "Z": [7, 8, 9]}, index=["a", "b", "c"] ) - res = df.loc[:, "X"] + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.loc[:, "X"] exp = tm.SubclassedSeries([1, 2, 3], index=list("abc"), name="X") tm.assert_series_equal(res, exp) assert isinstance(res, tm.SubclassedSeries) - res = df.iloc[:, 1] + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.iloc[:, 1] exp = tm.SubclassedSeries([4, 5, 6], index=list("abc"), name="Y") tm.assert_series_equal(res, exp) assert isinstance(res, tm.SubclassedSeries) - res = df.loc[:, "Z"] + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.loc[:, "Z"] exp = tm.SubclassedSeries([7, 8, 9], index=list("abc"), name="Z") tm.assert_series_equal(res, exp) assert isinstance(res, tm.SubclassedSeries) - res = df.loc["a", :] + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.loc["a", :] exp = tm.SubclassedSeries([1, 4, 7], index=list("XYZ"), name="a") tm.assert_series_equal(res, exp) assert isinstance(res, tm.SubclassedSeries) - res = df.iloc[1, :] + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.iloc[1, :] exp = tm.SubclassedSeries([2, 5, 8], index=list("XYZ"), name="b") tm.assert_series_equal(res, exp) assert isinstance(res, tm.SubclassedSeries) - res = df.loc["c", :] + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.loc["c", :] exp = tm.SubclassedSeries([3, 6, 9], index=list("XYZ"), name="c") tm.assert_series_equal(res, exp) assert isinstance(res, tm.SubclassedSeries) @@ -153,7 +171,8 @@ def test_subclass_align(self): {"c": [1, 2, 4], "d": [1, 2, 4]}, index=list("ABD") ) - res1, res2 = df1.align(df2, axis=0) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res1, res2 = df1.align(df2, axis=0) exp1 = tm.SubclassedDataFrame( {"a": [1, np.nan, 3, np.nan, 5], "b": [1, np.nan, 3, np.nan, 5]}, index=list("ABCDE"), @@ -163,15 +182,20 @@ def test_subclass_align(self): index=list("ABCDE"), ) assert isinstance(res1, tm.SubclassedDataFrame) - tm.assert_frame_equal(res1, exp1) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res1, exp1) assert isinstance(res2, tm.SubclassedDataFrame) - tm.assert_frame_equal(res2, exp2) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res2, exp2) - res1, res2 = df1.a.align(df2.c) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res1, res2 = df1.a.align(df2.c) assert isinstance(res1, tm.SubclassedSeries) - tm.assert_series_equal(res1, exp1.a) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_series_equal(res1, exp1.a) assert isinstance(res2, tm.SubclassedSeries) - tm.assert_series_equal(res2, exp2.c) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_series_equal(res2, exp2.c) def test_subclass_align_combinations(self): # GH 12983 @@ -179,7 +203,8 @@ def test_subclass_align_combinations(self): s = tm.SubclassedSeries([1, 2, 4], index=list("ABD"), name="x") # frame + series - res1, res2 = df.align(s, axis=0) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res1, res2 = df.align(s, axis=0) exp1 = tm.SubclassedDataFrame( {"a": [1, np.nan, 3, np.nan, 5], "b": [1, np.nan, 3, np.nan, 5]}, index=list("ABCDE"), @@ -190,23 +215,27 @@ def test_subclass_align_combinations(self): ) assert isinstance(res1, tm.SubclassedDataFrame) - tm.assert_frame_equal(res1, exp1) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res1, exp1) assert isinstance(res2, tm.SubclassedSeries) tm.assert_series_equal(res2, exp2) # series + frame - res1, res2 = s.align(df) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res1, res2 = s.align(df) assert isinstance(res1, tm.SubclassedSeries) tm.assert_series_equal(res1, exp2) assert isinstance(res2, tm.SubclassedDataFrame) - tm.assert_frame_equal(res2, exp1) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res2, exp1) def test_subclass_iterrows(self): # GH 13977 df = tm.SubclassedDataFrame({"a": [1]}) - for i, row in df.iterrows(): - assert isinstance(row, tm.SubclassedSeries) - tm.assert_series_equal(row, df.loc[i]) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + for i, row in df.iterrows(): + assert isinstance(row, tm.SubclassedSeries) + tm.assert_series_equal(row, df.loc[i]) def test_subclass_stack(self): # GH 15564 @@ -216,7 +245,8 @@ def test_subclass_stack(self): columns=["X", "Y", "Z"], ) - res = df.stack() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.stack() exp = tm.SubclassedSeries( [1, 2, 3, 4, 5, 6, 7, 8, 9], index=[list("aaabbbccc"), list("XYZXYZXYZ")] ) @@ -252,12 +282,15 @@ def test_subclass_stack_multi(self): ), columns=Index(["W", "X"], name="www"), ) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.stack() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) - res = df.stack() - tm.assert_frame_equal(res, exp) - - res = df.stack("yyy") - tm.assert_frame_equal(res, exp) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.stack("yyy") + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) exp = tm.SubclassedDataFrame( [ @@ -277,8 +310,10 @@ def test_subclass_stack_multi(self): columns=Index(["y", "z"], name="yyy"), ) - res = df.stack("www") - tm.assert_frame_equal(res, exp) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.stack("www") + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) def test_subclass_stack_multi_mixed(self): # GH 15564 @@ -314,12 +349,15 @@ def test_subclass_stack_multi_mixed(self): ), columns=Index(["W", "X"], name="www"), ) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.stack() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) - res = df.stack() - tm.assert_frame_equal(res, exp) - - res = df.stack("yyy") - tm.assert_frame_equal(res, exp) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.stack("yyy") + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) exp = tm.SubclassedDataFrame( [ @@ -339,8 +377,10 @@ def test_subclass_stack_multi_mixed(self): columns=Index(["y", "z"], name="yyy"), ) - res = df.stack("www") - tm.assert_frame_equal(res, exp) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.stack("www") + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) def test_subclass_unstack(self): # GH 15564 @@ -350,7 +390,8 @@ def test_subclass_unstack(self): columns=["X", "Y", "Z"], ) - res = df.unstack() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.unstack() exp = tm.SubclassedSeries( [1, 4, 7, 2, 5, 8, 3, 6, 9], index=[list("XXXYYYZZZ"), list("abcabcabc")] ) @@ -378,11 +419,15 @@ def test_subclass_unstack_multi(self): ), ) - res = df.unstack() - tm.assert_frame_equal(res, exp) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.unstack() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) - res = df.unstack("ccc") - tm.assert_frame_equal(res, exp) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.unstack("ccc") + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) exp = tm.SubclassedDataFrame( [[10, 30, 11, 31, 12, 32, 13, 33], [20, 40, 21, 41, 22, 42, 23, 43]], @@ -393,8 +438,10 @@ def test_subclass_unstack_multi(self): ), ) - res = df.unstack("aaa") - tm.assert_frame_equal(res, exp) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.unstack("aaa") + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) def test_subclass_unstack_multi_mixed(self): # GH 15564 @@ -425,11 +472,15 @@ def test_subclass_unstack_multi_mixed(self): ), ) - res = df.unstack() - tm.assert_frame_equal(res, exp) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.unstack() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) - res = df.unstack("ccc") - tm.assert_frame_equal(res, exp) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.unstack("ccc") + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) exp = tm.SubclassedDataFrame( [ @@ -443,8 +494,10 @@ def test_subclass_unstack_multi_mixed(self): ), ) - res = df.unstack("aaa") - tm.assert_frame_equal(res, exp) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = df.unstack("aaa") + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) def test_subclass_pivot(self): # GH 15564 @@ -456,7 +509,8 @@ def test_subclass_pivot(self): } ) - pivoted = df.pivot(index="index", columns="columns", values="values") + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + pivoted = df.pivot(index="index", columns="columns", values="values") expected = tm.SubclassedDataFrame( { @@ -467,7 +521,8 @@ def test_subclass_pivot(self): expected.index.name, expected.columns.name = "index", "columns" - tm.assert_frame_equal(pivoted, expected) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(pivoted, expected) def test_subclassed_melt(self): # GH 15564 @@ -480,7 +535,8 @@ def test_subclassed_melt(self): } ) - melted = pd.melt(cheese, id_vars=["first", "last"]) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + melted = pd.melt(cheese, id_vars=["first", "last"]) expected = tm.SubclassedDataFrame( [ @@ -492,7 +548,8 @@ def test_subclassed_melt(self): columns=["first", "last", "variable", "value"], ) - tm.assert_frame_equal(melted, expected) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(melted, expected) def test_subclassed_wide_to_long(self): # GH 9762 @@ -518,10 +575,13 @@ def test_subclassed_wide_to_long(self): "id": [0, 1, 2, 0, 1, 2], } expected = tm.SubclassedDataFrame(exp_data) - expected = expected.set_index(["id", "year"])[["X", "A", "B"]] - long_frame = pd.wide_to_long(df, ["A", "B"], i="id", j="year") + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + expected = expected.set_index(["id", "year"])[["X", "A", "B"]] + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + long_frame = pd.wide_to_long(df, ["A", "B"], i="id", j="year") - tm.assert_frame_equal(long_frame, expected) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(long_frame, expected) def test_subclassed_apply(self): # GH 19822 @@ -544,8 +604,10 @@ def stretch(row): columns=["first", "last", "variable", "value"], ) - df.apply(lambda x: check_row_subclass(x)) - df.apply(lambda x: check_row_subclass(x), axis=1) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + df.apply(lambda x: check_row_subclass(x)) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + df.apply(lambda x: check_row_subclass(x), axis=1) expected = tm.SubclassedDataFrame( [ @@ -557,23 +619,30 @@ def stretch(row): columns=["first", "last", "variable", "value"], ) - result = df.apply(lambda x: stretch(x), axis=1) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.apply(lambda x: stretch(x), axis=1) assert isinstance(result, tm.SubclassedDataFrame) - tm.assert_frame_equal(result, expected) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(result, expected) expected = tm.SubclassedDataFrame([[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]) - result = df.apply(lambda x: tm.SubclassedSeries([1, 2, 3]), axis=1) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.apply(lambda x: tm.SubclassedSeries([1, 2, 3]), axis=1) assert isinstance(result, tm.SubclassedDataFrame) - tm.assert_frame_equal(result, expected) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(result, expected) - result = df.apply(lambda x: [1, 2, 3], axis=1, result_type="expand") + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.apply(lambda x: [1, 2, 3], axis=1, result_type="expand") assert isinstance(result, tm.SubclassedDataFrame) - tm.assert_frame_equal(result, expected) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(result, expected) expected = tm.SubclassedSeries([[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]) - result = df.apply(lambda x: [1, 2, 3], axis=1) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.apply(lambda x: [1, 2, 3], axis=1) assert not isinstance(result, tm.SubclassedDataFrame) tm.assert_series_equal(result, expected) @@ -581,7 +650,8 @@ def test_subclassed_reductions(self, all_reductions): # GH 25596 df = tm.SubclassedDataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]}) - result = getattr(df, all_reductions)() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = getattr(df, all_reductions)() assert isinstance(result, tm.SubclassedSeries) def test_subclassed_count(self): @@ -592,11 +662,13 @@ def test_subclassed_count(self): "Single": [False, True, True, True, False], } ) - result = df.count() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.count() assert isinstance(result, tm.SubclassedSeries) df = tm.SubclassedDataFrame({"A": [1, 0, 3], "B": [0, 5, 6], "C": [7, 8, 0]}) - result = df.count() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.count() assert isinstance(result, tm.SubclassedSeries) df = tm.SubclassedDataFrame( @@ -608,23 +680,27 @@ def test_subclassed_count(self): list(zip(list("WWXX"), list("yzyz"))), names=["www", "yyy"] ), ) - result = df.count() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.count() assert isinstance(result, tm.SubclassedSeries) df = tm.SubclassedDataFrame() - result = df.count() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.count() assert isinstance(result, tm.SubclassedSeries) def test_isin(self): df = tm.SubclassedDataFrame( {"num_legs": [2, 4], "num_wings": [2, 0]}, index=["falcon", "dog"] ) - result = df.isin([0, 2]) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.isin([0, 2]) assert isinstance(result, tm.SubclassedDataFrame) def test_duplicated(self): df = tm.SubclassedDataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]}) - result = df.duplicated() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.duplicated() assert isinstance(result, tm.SubclassedSeries) df = tm.SubclassedDataFrame() @@ -634,26 +710,33 @@ def test_duplicated(self): @pytest.mark.parametrize("idx_method", ["idxmax", "idxmin"]) def test_idx(self, idx_method): df = tm.SubclassedDataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]}) - result = getattr(df, idx_method)() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = getattr(df, idx_method)() assert isinstance(result, tm.SubclassedSeries) def test_dot(self): df = tm.SubclassedDataFrame([[0, 1, -2, -1], [1, 1, 1, 1]]) s = tm.SubclassedSeries([1, 1, 2, 1]) - result = df.dot(s) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.dot(s) assert isinstance(result, tm.SubclassedSeries) df = tm.SubclassedDataFrame([[0, 1, -2, -1], [1, 1, 1, 1]]) s = tm.SubclassedDataFrame([1, 1, 2, 1]) - result = df.dot(s) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.dot(s) assert isinstance(result, tm.SubclassedDataFrame) def test_memory_usage(self): df = tm.SubclassedDataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]}) - result = df.memory_usage() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.memory_usage() assert isinstance(result, tm.SubclassedSeries) - result = df.memory_usage(index=False) + with warnings.catch_warnings(): + # this only warns in the CoW build + warnings.filterwarnings("ignore") + result = df.memory_usage(index=False) assert isinstance(result, tm.SubclassedSeries) @td.skip_if_no_scipy @@ -666,7 +749,8 @@ def test_corrwith(self): df2 = tm.SubclassedDataFrame( np.random.randn(4, 4), index=index[:4], columns=columns ) - correls = df1.corrwith(df2, axis=1, drop=True, method="kendall") + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + correls = df1.corrwith(df2, axis=1, drop=True, method="kendall") assert isinstance(correls, (tm.SubclassedSeries)) @@ -682,10 +766,12 @@ def test_asof(self): index=rng, ) - result = df.asof(rng[-2:]) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.asof(rng[-2:]) assert isinstance(result, tm.SubclassedDataFrame) - result = df.asof(rng[-2]) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.asof(rng[-2]) assert isinstance(result, tm.SubclassedSeries) result = df.asof("1989-12-31") @@ -695,20 +781,23 @@ def test_idxmin_preserves_subclass(self): # GH 28330 df = tm.SubclassedDataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]}) - result = df.idxmin() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.idxmin() assert isinstance(result, tm.SubclassedSeries) def test_idxmax_preserves_subclass(self): # GH 28330 df = tm.SubclassedDataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]}) - result = df.idxmax() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.idxmax() assert isinstance(result, tm.SubclassedSeries) def test_convert_dtypes_preserves_subclass(self, gpd_style_subclass_df): # GH 43668 df = tm.SubclassedDataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]}) - result = df.convert_dtypes() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.convert_dtypes() assert isinstance(result, tm.SubclassedDataFrame) result = gpd_style_subclass_df.convert_dtypes() @@ -718,7 +807,8 @@ def test_astype_preserves_subclass(self): # GH#40810 df = tm.SubclassedDataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]}) - result = df.astype({"A": np.int64, "B": np.int32, "C": np.float64}) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.astype({"A": np.int64, "B": np.int32, "C": np.float64}) assert isinstance(result, tm.SubclassedDataFrame) def test_equals_subclass(self): @@ -732,7 +822,9 @@ def test_equals_subclass(self): def test_replace_list_method(self): # https://github.com/pandas-dev/pandas/pull/46018 df = tm.SubclassedDataFrame({"A": [0, 1, 2]}) - result = df.replace([1, 2], method="ffill") + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = df.replace([1, 2], method="ffill") expected = tm.SubclassedDataFrame({"A": [0, 0, 0]}) assert isinstance(result, tm.SubclassedDataFrame) - tm.assert_frame_equal(result, expected) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(result, expected) diff --git a/pandas/tests/generic/test_frame.py b/pandas/tests/generic/test_frame.py index 79f055909fdea..70cf44558f9fe 100644 --- a/pandas/tests/generic/test_frame.py +++ b/pandas/tests/generic/test_frame.py @@ -118,8 +118,11 @@ def finalize(self, other, method=None, **kwargs): df1 = DataFrame(np.random.randint(0, 4, (3, 2)), columns=["a", "b"]) df2 = DataFrame(np.random.randint(0, 4, (3, 2)), columns=["c", "d"]) DataFrame._metadata = ["filename"] - df1.filename = "fname1.csv" - df2.filename = "fname2.csv" + msg = "_metadata handling is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + df1.filename = "fname1.csv" + with tm.assert_produces_warning(FutureWarning, match=msg): + df2.filename = "fname2.csv" result = df1.merge(df2, left_on=["a"], right_on=["c"], how="inner") assert result.filename == "fname1.csv|fname2.csv" @@ -127,7 +130,8 @@ def finalize(self, other, method=None, **kwargs): # concat # GH#6927 df1 = DataFrame(np.random.randint(0, 4, (3, 2)), columns=list("ab")) - df1.filename = "foo" + with tm.assert_produces_warning(FutureWarning, match=msg): + df1.filename = "foo" result = pd.concat([df1, df1]) assert result.filename == "foo+foo" diff --git a/pandas/tests/generic/test_series.py b/pandas/tests/generic/test_series.py index ee0a7fb77f336..c2a6f1e65a39c 100644 --- a/pandas/tests/generic/test_series.py +++ b/pandas/tests/generic/test_series.py @@ -122,6 +122,7 @@ def test_metadata_propagation_indiv_resample(self): def test_metadata_propagation_indiv(self, monkeypatch): # check that the metadata matches up on the resulting ops + msg = "_metadata handling is deprecated" ser = Series(range(3), range(3)) ser.name = "foo" @@ -151,8 +152,10 @@ def finalize(self, other, method=None, **kwargs): m.setattr(Series, "_metadata", ["name", "filename"]) m.setattr(Series, "__finalize__", finalize) - ser.filename = "foo" - ser2.filename = "bar" + with tm.assert_produces_warning(FutureWarning, match=msg): + ser.filename = "foo" + with tm.assert_produces_warning(FutureWarning, match=msg): + ser2.filename = "bar" result = pd.concat([ser, ser2]) assert result.filename == "foo+bar" diff --git a/pandas/tests/groupby/test_groupby_subclass.py b/pandas/tests/groupby/test_groupby_subclass.py index 601e67bbca5e3..bff6ada3947da 100644 --- a/pandas/tests/groupby/test_groupby_subclass.py +++ b/pandas/tests/groupby/test_groupby_subclass.py @@ -28,12 +28,38 @@ def test_groupby_preserves_subclass(obj, groupby_func): grouped = obj.groupby(np.arange(0, 10)) # Groups should preserve subclass type - assert isinstance(grouped.get_group(0), type(obj)) + msg = "_metadata propagation is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + grp = grouped.get_group(0) + assert isinstance(grp, type(obj)) args = get_groupby_method_args(groupby_func, obj) - result1 = getattr(grouped, groupby_func)(*args) - result2 = grouped.agg(groupby_func, *args) + warn = None + if groupby_func in [ + "fillna", + "diff", + "sum", + "pct_change", + "shift", + "prod", + "mean", + "median", + "first", + "last", + "max", + "min", + "idxmax", + "idxmin", + "corrwith", + ]: + warn = FutureWarning + if groupby_func == "nunique" and obj.ndim == 2: + warn = FutureWarning + with tm.assert_produces_warning(warn, match=msg): + result1 = getattr(grouped, groupby_func)(*args) + with tm.assert_produces_warning(warn, match=msg): + result2 = grouped.agg(groupby_func, *args) # Reduction or transformation kernels should preserve type slices = {"ngroup", "cumcount", "size"} @@ -44,18 +70,24 @@ def test_groupby_preserves_subclass(obj, groupby_func): # Confirm .agg() groupby operations return same results if isinstance(result1, DataFrame): - tm.assert_frame_equal(result1, result2) + with tm.assert_produces_warning(FutureWarning, match=msg): + tm.assert_frame_equal(result1, result2) else: tm.assert_series_equal(result1, result2) def test_groupby_preserves_metadata(): # GH-37343 + set_msg = "_metadata handling is deprecated" + warn_msg = "_metadata propagation is deprecated" + custom_df = tm.SubclassedDataFrame({"a": [1, 2, 3], "b": [1, 1, 2], "c": [7, 8, 9]}) assert "testattr" in custom_df._metadata - custom_df.testattr = "hello" - for _, group_df in custom_df.groupby("c"): - assert group_df.testattr == "hello" + with tm.assert_produces_warning(FutureWarning, match=set_msg): + custom_df.testattr = "hello" + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + for _, group_df in custom_df.groupby("c"): + assert group_df.testattr == "hello" # GH-45314 def func(group): @@ -63,8 +95,7 @@ def func(group): assert hasattr(group, "testattr") return group.testattr - msg = "DataFrameGroupBy.apply operated on the grouping columns" - with tm.assert_produces_warning(FutureWarning, match=msg): + with tm.assert_produces_warning(FutureWarning, match=warn_msg): result = custom_df.groupby("c").apply(func) expected = tm.SubclassedSeries(["hello"] * 3, index=Index([7, 8, 9], name="c")) tm.assert_series_equal(result, expected) @@ -75,10 +106,13 @@ def func2(group): return group.testattr custom_series = tm.SubclassedSeries([1, 2, 3]) - custom_series.testattr = "hello" - result = custom_series.groupby(custom_df["c"]).apply(func2) + with tm.assert_produces_warning(FutureWarning, match=set_msg): + custom_series.testattr = "hello" + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = custom_series.groupby(custom_df["c"]).apply(func2) tm.assert_series_equal(result, expected) - result = custom_series.groupby(custom_df["c"]).agg(func2) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = custom_series.groupby(custom_df["c"]).agg(func2) tm.assert_series_equal(result, expected) @@ -100,7 +134,10 @@ def test_groupby_resample_preserves_subclass(obj): ], } ) - df = df.set_index("Date") + msg = "_metadata propagation is deprecated" + warn = None if obj is DataFrame else FutureWarning + with tm.assert_produces_warning(warn, match=msg): + df = df.set_index("Date") # Confirm groupby.resample() preserves dataframe type msg = "DataFrameGroupBy.resample operated on the grouping columns" diff --git a/pandas/tests/io/pytables/test_subclass.py b/pandas/tests/io/pytables/test_subclass.py index 823d2875c5417..958bb981507b0 100644 --- a/pandas/tests/io/pytables/test_subclass.py +++ b/pandas/tests/io/pytables/test_subclass.py @@ -17,6 +17,9 @@ class TestHDFStoreSubclass: # GH 33748 + + # _metadata warning only shows up on ArrayManager build + @pytest.mark.filterwarnings("ignore") def test_supported_for_subclass_dataframe(self, tmp_path): data = {"a": [1, 2], "b": [3, 4]} sdf = tm.SubclassedDataFrame(data, dtype=np.intp) diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index be53209d889ee..bd8bee5c84fbd 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -86,8 +86,12 @@ def _constructor(self): opname = all_arithmetic_operators op = getattr(Series, opname) m = MySeries([1, 2, 3], name="test") - m.x = 42 - result = op(m, 1) + msg = "_metadata handling is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + m.x = 42 + msg = "_metadata propagation is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + result = op(m, 1) assert result.x == 42 def test_flex_add_scalar_fill_value(self): diff --git a/pandas/tests/series/test_subclass.py b/pandas/tests/series/test_subclass.py index a3550c6de6780..16d29f67f9a77 100644 --- a/pandas/tests/series/test_subclass.py +++ b/pandas/tests/series/test_subclass.py @@ -4,6 +4,8 @@ import pandas as pd import pandas._testing as tm +warn_msg = "_metadata propagation is deprecated" + class TestSeriesSubclassing: @pytest.mark.parametrize( @@ -15,15 +17,18 @@ class TestSeriesSubclassing: ) def test_indexing_sliced(self, idx_method, indexer, exp_data, exp_idx): s = tm.SubclassedSeries([1, 2, 3, 4], index=list("abcd")) - res = getattr(s, idx_method)[indexer] + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = getattr(s, idx_method)[indexer] exp = tm.SubclassedSeries(exp_data, index=list(exp_idx)) tm.assert_series_equal(res, exp) def test_to_frame(self): s = tm.SubclassedSeries([1, 2, 3, 4], index=list("abcd"), name="xxx") - res = s.to_frame() + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = s.to_frame() exp = tm.SubclassedDataFrame({"xxx": [1, 2, 3, 4]}, index=list("abcd")) - tm.assert_frame_equal(res, exp) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) def test_subclass_unstack(self): # GH 15564 @@ -32,7 +37,8 @@ def test_subclass_unstack(self): res = s.unstack() exp = tm.SubclassedDataFrame({"x": [1, 3], "y": [2, 4]}, index=["a", "b"]) - tm.assert_frame_equal(res, exp) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + tm.assert_frame_equal(res, exp) def test_subclass_empty_repr(self): sub_series = tm.SubclassedSeries() @@ -41,9 +47,11 @@ def test_subclass_empty_repr(self): def test_asof(self): N = 3 rng = pd.date_range("1/1/1990", periods=N, freq="53s") + s = tm.SubclassedSeries({"A": [np.nan, np.nan, np.nan]}, index=rng) - result = s.asof(rng[-2:]) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = s.asof(rng[-2:]) assert isinstance(result, tm.SubclassedSeries) def test_explode(self):