diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 33e70daa55e66..8b2b9c14046af 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -80,6 +80,7 @@ Datetimelike ^^^^^^^^^^^^ - Bug in :attr:`DatetimeArray.date` where a ``ValueError`` would be raised with a read-only backing array (:issue:`33530`) - Bug in ``NaT`` comparisons failing to raise ``TypeError`` on invalid inequality comparisons (:issue:`35046`) +- Bug in :class:`DateOffset` where attributes reconstructed from pickle files differ from original objects when input values exceed normal ranges (e.g months=12) (:issue:`34511`) - Timedelta diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index ac2725fc58aee..7f0314d737619 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -989,13 +989,6 @@ cdef class RelativeDeltaOffset(BaseOffset): state["_offset"] = state.pop("offset") state["kwds"]["offset"] = state["_offset"] - if "_offset" in state and not isinstance(state["_offset"], timedelta): - # relativedelta, we need to populate using its kwds - offset = state["_offset"] - odict = offset.__dict__ - kwds = {key: odict[key] for key in odict if odict[key]} - state.update(kwds) - self.n = state.pop("n") self.normalize = state.pop("normalize") self._cache = state.pop("_cache", {}) diff --git a/pandas/tests/io/data/legacy_pickle/1.1.0/1.1.0_x86_64_darwin_3.8.5.pickle b/pandas/tests/io/data/legacy_pickle/1.1.0/1.1.0_x86_64_darwin_3.8.5.pickle new file mode 100644 index 0000000000000..f8df9afff6565 Binary files /dev/null and b/pandas/tests/io/data/legacy_pickle/1.1.0/1.1.0_x86_64_darwin_3.8.5.pickle differ diff --git a/pandas/tests/io/generate_legacy_storage_files.py b/pandas/tests/io/generate_legacy_storage_files.py index e64103bd2cde8..61e1fc019faac 100644 --- a/pandas/tests/io/generate_legacy_storage_files.py +++ b/pandas/tests/io/generate_legacy_storage_files.py @@ -6,10 +6,10 @@ in ~/pandas . activate pandas_0.20.3 -cd ~/ +cd ~/pandas/pandas -$ python pandas/pandas/tests/io/generate_legacy_storage_files.py \ - pandas/pandas/tests/io/data/legacy_pickle/0.20.3/ pickle +$ python -m tests.io.generate_legacy_storage_files \ + tests/io/data/legacy_pickle/0.20.3/ pickle This script generates a storage file for the current arch, system, and python version @@ -328,7 +328,7 @@ def write_legacy_pickles(output_dir): pth = f"{platform_name()}.pickle" fh = open(os.path.join(output_dir, pth), "wb") - pickle.dump(create_pickle_data(), fh, pickle.HIGHEST_PROTOCOL) + pickle.dump(create_pickle_data(), fh, pickle.DEFAULT_PROTOCOL) fh.close() print(f"created pickle file: {pth}") diff --git a/pandas/tests/tseries/offsets/data/dateoffset_0_15_2.pickle b/pandas/tests/tseries/offsets/data/dateoffset_0_15_2.pickle deleted file mode 100644 index ce561526a5e12..0000000000000 --- a/pandas/tests/tseries/offsets/data/dateoffset_0_15_2.pickle +++ /dev/null @@ -1,183 +0,0 @@ -(dp0 -S'YearBegin' -p1 -ccopy_reg -_reconstructor -p2 -(cpandas.tseries.offsets -YearBegin -p3 -c__builtin__ -object -p4 -Ntp5 -Rp6 -(dp7 -S'normalize' -p8 -I00 -sS'kwds' -p9 -(dp10 -sS'n' -p11 -I1 -sS'_offset' -p12 -cdatetime -timedelta -p13 -(I1 -I0 -I0 -tp14 -Rp15 -sS'month' -p16 -I1 -sS'_use_relativedelta' -p17 -I00 -sbsS'Week' -p18 -g2 -(cpandas.tseries.offsets -Week -p19 -g4 -Ntp20 -Rp21 -(dp22 -g8 -I00 -sS'_inc' -p23 -g13 -(I7 -I0 -I0 -tp24 -Rp25 -sg9 -(dp26 -sS'weekday' -p27 -Nsg11 -I1 -sbsS'MonthBegin' -p28 -g2 -(cpandas.tseries.offsets -MonthBegin -p29 -g4 -Ntp30 -Rp31 -(dp32 -g8 -I00 -sg12 -g13 -(I1 -I0 -I0 -tp33 -Rp34 -sg17 -I00 -sg9 -(dp35 -sg11 -I1 -sbsS'Day' -p36 -g2 -(cpandas.tseries.offsets -Day -p37 -g4 -Ntp38 -Rp39 -(dp40 -g8 -I00 -sg12 -g13 -(I1 -I0 -I0 -tp41 -Rp42 -sg17 -I00 -sg9 -(dp43 -sg11 -I1 -sbsS'DateOffset' -p44 -g2 -(cpandas.tseries.offsets -DateOffset -p45 -g4 -Ntp46 -Rp47 -(dp48 -g8 -I00 -sg12 -(idateutil.relativedelta -relativedelta -p49 -(dp50 -S'_has_time' -p51 -I0 -sS'hour' -p52 -NsS'seconds' -p53 -I0 -sS'months' -p54 -I0 -sS'year' -p55 -NsS'days' -p56 -I0 -sS'years' -p57 -I1 -sS'hours' -p58 -I0 -sS'second' -p59 -NsS'microsecond' -p60 -Nsg16 -NsS'microseconds' -p61 -I0 -sS'leapdays' -p62 -I0 -sS'minutes' -p63 -I0 -sS'day' -p64 -NsS'minute' -p65 -Nsg27 -Nsbsg17 -I01 -sg9 -(dp66 -g57 -I1 -ssg11 -I1 -sbs. \ No newline at end of file diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index 8c51908c547f4..d994ce35d682b 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -635,22 +635,6 @@ def test_add_empty_datetimeindex(self, offset_types, tz_naive_fixture): result = offset_s + dta tm.assert_equal(result, dta) - def test_pickle_v0_15_2(self, datapath): - offsets = { - "DateOffset": DateOffset(years=1), - "MonthBegin": MonthBegin(1), - "Day": Day(1), - "YearBegin": YearBegin(1), - "Week": Week(1), - } - - pickle_path = datapath("tseries", "offsets", "data", "dateoffset_0_15_2.pickle") - # This code was executed once on v0.15.2 to generate the pickle: - # with open(pickle_path, 'wb') as f: pickle.dump(offsets, f) - # - result = read_pickle(pickle_path) - tm.assert_dict_equal(offsets, result) - def test_pickle_roundtrip(self, offset_types): off = self._get_offset(offset_types) res = tm.round_trip_pickle(off) @@ -664,6 +648,15 @@ def test_pickle_roundtrip(self, offset_types): # Make sure nothings got lost from _params (which __eq__) is based on assert getattr(off, attr) == getattr(res, attr) + def test_pickle_dateoffset_odd_inputs(self): + # GH#34511 + off = DateOffset(months=12) + res = tm.round_trip_pickle(off) + assert off == res + + base_dt = datetime(2020, 1, 1) + assert base_dt + off == base_dt + res + def test_onOffset_deprecated(self, offset_types): # GH#30340 use idiomatic naming off = self._get_offset(offset_types)