From 27c187d81cc1e259c14c2d312f3ae57c12e20f85 Mon Sep 17 00:00:00 2001 From: sinhrks Date: Wed, 16 Jul 2014 20:53:50 +0900 Subject: [PATCH] BUG/COMPAT: pickled dtindex with freq raises AttributeError in normalize related ops --- doc/source/v0.15.0.txt | 1 + pandas/io/tests/test_pickle.py | 30 ++++++++++++++++++++++++++---- pandas/tseries/offsets.py | 3 +++ setup.py | 1 + 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/doc/source/v0.15.0.txt b/doc/source/v0.15.0.txt index eb58f46f0f3fe..226ec28089b60 100644 --- a/doc/source/v0.15.0.txt +++ b/doc/source/v0.15.0.txt @@ -189,6 +189,7 @@ Bug Fixes - Bug in ``DataFrame.as_matrix()`` with mixed ``datetime64[ns]`` and ``timedelta64[ns]`` dtypes (:issue:`7778`) +- Bug in pickles contains ``DateOffset`` may raise ``AttributeError`` when ``normalize`` attribute is reffered internally (:issue:`7748`) diff --git a/pandas/io/tests/test_pickle.py b/pandas/io/tests/test_pickle.py index c52a405fe81ea..07d576ac1c8ae 100644 --- a/pandas/io/tests/test_pickle.py +++ b/pandas/io/tests/test_pickle.py @@ -48,9 +48,12 @@ def compare(self, vf): # py3 compat when reading py2 pickle try: data = pandas.read_pickle(vf) - except (ValueError) as detail: - # trying to read a py3 pickle in py2 - return + except (ValueError) as e: + if 'unsupported pickle protocol:' in str(e): + # trying to read a py3 pickle in py2 + return + else: + raise for typ, dv in data.items(): for dt, result in dv.items(): @@ -60,6 +63,7 @@ def compare(self, vf): continue self.compare_element(typ, result, expected) + return data def read_pickles(self, version): if not is_little_endian(): @@ -68,7 +72,14 @@ def read_pickles(self, version): pth = tm.get_data_path('legacy_pickle/{0}'.format(str(version))) for f in os.listdir(pth): vf = os.path.join(pth,f) - self.compare(vf) + data = self.compare(vf) + + if data is None: + continue + + if 'series' in data: + if 'ts' in data['series']: + self._validate_timeseries(data['series']['ts'], self.data['series']['ts']) def test_read_pickles_0_10_1(self): self.read_pickles('0.10.1') @@ -82,6 +93,9 @@ def test_read_pickles_0_12_0(self): def test_read_pickles_0_13_0(self): self.read_pickles('0.13.0') + def test_read_pickles_0_14_0(self): + self.read_pickles('0.14.0') + def test_round_trip_current(self): for typ, dv in self.data.items(): @@ -94,6 +108,14 @@ def test_round_trip_current(self): result = pd.read_pickle(path) self.compare_element(typ, result, expected) + def _validate_timeseries(self, pickled, current): + # GH 7748 + tm.assert_series_equal(pickled, current) + self.assertEqual(pickled.index.freq, current.index.freq) + self.assertEqual(pickled.index.freq.normalize, False) + self.assert_numpy_array_equal(pickled > 0, current > 0) + + if __name__ == '__main__': import nose nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'], diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index 57181b43df9f6..8f77f88910a3c 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -130,6 +130,9 @@ def __add__(date): _cacheable = False _normalize_cache = True + # default for prior pickles + normalize = False + def __init__(self, n=1, normalize=False, **kwds): self.n = int(n) self.normalize = normalize diff --git a/setup.py b/setup.py index 3ec992d91bb45..844f5742c0e69 100755 --- a/setup.py +++ b/setup.py @@ -578,6 +578,7 @@ def pxd(name): 'tests/data/legacy_pickle/0.11.0/*.pickle', 'tests/data/legacy_pickle/0.12.0/*.pickle', 'tests/data/legacy_pickle/0.13.0/*.pickle', + 'tests/data/legacy_pickle/0.14.0/*.pickle', 'tests/data/*.csv', 'tests/data/*.dta', 'tests/data/*.txt',