From 9721041ec4f558c73d5884160e4bc4444dcd1229 Mon Sep 17 00:00:00 2001 From: Brian McFee Date: Fri, 17 Feb 2017 15:03:03 -0500 Subject: [PATCH 1/7] added copy/deepcopy to ndframe, fixes #15370 --- pandas/core/generic.py | 7 +++++++ pandas/tests/frame/test_misc_api.py | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 26b9a880dd2c7..f2d7930e3eea6 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -3161,6 +3161,13 @@ def copy(self, deep=True): data = self._data.copy(deep=deep) return self._constructor(data).__finalize__(self) + __copy__ = copy + + def __deepcopy__(self, memo=None): + if memo is None: + memo = {} + return self.copy(deep=True) + def _convert(self, datetime=False, numeric=False, timedelta=False, coerce=False, copy=True): """ diff --git a/pandas/tests/frame/test_misc_api.py b/pandas/tests/frame/test_misc_api.py index 674202980807a..ab3e5d5fbbb5f 100644 --- a/pandas/tests/frame/test_misc_api.py +++ b/pandas/tests/frame/test_misc_api.py @@ -332,6 +332,15 @@ def test_deepcopy(self): for idx, value in compat.iteritems(series): self.assertNotEqual(self.frame['A'][idx], value) + def test_deepcopy_empty(self): + # This test covers empty frame copying with non-empty column sets + # as reported in issue #15370 + # https://github.com/pandas-dev/pandas/issues/15370 + empty_frame = DataFrame(data=[], index=[], columns=['A']) + empty_frame_copy = deepcopy(empty_frame) + + self.assertEqual(empty_frame, empty_frame_copy) + # --------------------------------------------------------------------- # Transposing From 820664c99d753e4781b59734d7f1e423a5384b69 Mon Sep 17 00:00:00 2001 From: Brian McFee Date: Fri, 17 Feb 2017 17:49:19 -0500 Subject: [PATCH 2/7] moved deepcopy test to generic.py --- pandas/tests/frame/test_misc_api.py | 9 --------- pandas/tests/test_generic.py | 10 ++++++++++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pandas/tests/frame/test_misc_api.py b/pandas/tests/frame/test_misc_api.py index ab3e5d5fbbb5f..674202980807a 100644 --- a/pandas/tests/frame/test_misc_api.py +++ b/pandas/tests/frame/test_misc_api.py @@ -332,15 +332,6 @@ def test_deepcopy(self): for idx, value in compat.iteritems(series): self.assertNotEqual(self.frame['A'][idx], value) - def test_deepcopy_empty(self): - # This test covers empty frame copying with non-empty column sets - # as reported in issue #15370 - # https://github.com/pandas-dev/pandas/issues/15370 - empty_frame = DataFrame(data=[], index=[], columns=['A']) - empty_frame_copy = deepcopy(empty_frame) - - self.assertEqual(empty_frame, empty_frame_copy) - # --------------------------------------------------------------------- # Transposing diff --git a/pandas/tests/test_generic.py b/pandas/tests/test_generic.py index b087ca21d3c25..8f4b2d81f1fca 100644 --- a/pandas/tests/test_generic.py +++ b/pandas/tests/test_generic.py @@ -2,6 +2,7 @@ # pylint: disable-msg=E1101,W0612 from operator import methodcaller +from copy import deepcopy import pytest import numpy as np from numpy import nan @@ -1539,6 +1540,15 @@ def test_to_xarray(self): expected, check_index_type=False) + def test_deepcopy_empty(self): + # This test covers empty frame copying with non-empty column sets + # as reported in issue #15370 + # https://github.com/pandas-dev/pandas/issues/15370 + empty_frame = DataFrame(data=[], index=[], columns=['A']) + empty_frame_copy = deepcopy(empty_frame) + + self.assertEqual(empty_frame, empty_frame_copy) + class TestPanel(tm.TestCase, Generic): _typ = Panel From 7e67e7de726f994340f6f00fa84bd90b3b7258ba Mon Sep 17 00:00:00 2001 From: Brian McFee Date: Fri, 17 Feb 2017 17:53:37 -0500 Subject: [PATCH 3/7] ndframe and index __copy__ are now proper methods --- pandas/core/generic.py | 3 ++- pandas/indexes/base.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index f2d7930e3eea6..76fbb9884753d 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -3161,7 +3161,8 @@ def copy(self, deep=True): data = self._data.copy(deep=deep) return self._constructor(data).__finalize__(self) - __copy__ = copy + def __copy__(self, deep=True): + return self.copy(deep=deep) def __deepcopy__(self, memo=None): if memo is None: diff --git a/pandas/indexes/base.py b/pandas/indexes/base.py index e51824e72a2a0..5226b71efa091 100644 --- a/pandas/indexes/base.py +++ b/pandas/indexes/base.py @@ -724,7 +724,8 @@ def copy(self, name=None, deep=False, dtype=None, **kwargs): new_index = new_index.astype(dtype) return new_index - __copy__ = copy + def __copy__(self, **kwargs): + return self.copy(**kwargs) def _validate_names(self, name=None, names=None, deep=False): """ From 1aea940ce54041b39761bb65389ba7c0f34559eb Mon Sep 17 00:00:00 2001 From: Brian McFee Date: Sat, 18 Feb 2017 10:49:54 -0500 Subject: [PATCH 4/7] switched deepcopy test to using generic comparator --- pandas/tests/test_generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/test_generic.py b/pandas/tests/test_generic.py index 8f4b2d81f1fca..96ef735d1b30c 100644 --- a/pandas/tests/test_generic.py +++ b/pandas/tests/test_generic.py @@ -1547,7 +1547,7 @@ def test_deepcopy_empty(self): empty_frame = DataFrame(data=[], index=[], columns=['A']) empty_frame_copy = deepcopy(empty_frame) - self.assertEqual(empty_frame, empty_frame_copy) + self._compare(empty_frame_copy, empty_frame) class TestPanel(tm.TestCase, Generic): From 35f3e0f6820060bcf580001b9f1c3339289f1f9c Mon Sep 17 00:00:00 2001 From: Brian McFee Date: Sat, 18 Feb 2017 10:51:35 -0500 Subject: [PATCH 5/7] relocated Index.__deepcopy__ to live near __copy__ --- pandas/indexes/base.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pandas/indexes/base.py b/pandas/indexes/base.py index 5226b71efa091..f1f37622b2a74 100644 --- a/pandas/indexes/base.py +++ b/pandas/indexes/base.py @@ -727,6 +727,11 @@ def copy(self, name=None, deep=False, dtype=None, **kwargs): def __copy__(self, **kwargs): return self.copy(**kwargs) + def __deepcopy__(self, memo=None): + if memo is None: + memo = {} + return self.copy(deep=True) + def _validate_names(self, name=None, names=None, deep=False): """ Handles the quirks of having a singular 'name' parameter for general @@ -1481,11 +1486,6 @@ def __setstate__(self, state): _unpickle_compat = __setstate__ - def __deepcopy__(self, memo=None): - if memo is None: - memo = {} - return self.copy(deep=True) - def __nonzero__(self): raise ValueError("The truth value of a {0} is ambiguous. " "Use a.empty, a.bool(), a.item(), a.any() or a.all()." From d58b1f6911e327db6165421cf7a17439f9e5333e Mon Sep 17 00:00:00 2001 From: Brian McFee Date: Sat, 18 Feb 2017 11:22:59 -0500 Subject: [PATCH 6/7] added tests for copy and deepcopy --- pandas/tests/test_generic.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pandas/tests/test_generic.py b/pandas/tests/test_generic.py index 96ef735d1b30c..63376698420de 100644 --- a/pandas/tests/test_generic.py +++ b/pandas/tests/test_generic.py @@ -2,7 +2,7 @@ # pylint: disable-msg=E1101,W0612 from operator import methodcaller -from copy import deepcopy +from copy import copy, deepcopy import pytest import numpy as np from numpy import nan @@ -676,6 +676,16 @@ def test_validate_bool_args(self): with self.assertRaises(ValueError): super(DataFrame, df).mask(cond=df.a > 2, inplace=value) + def test_copy_and_deepcopy(self): + + for shape in [0, 1, 2]: + obj = self._construct(shape) + + for func in (copy, deepcopy): + obj_copy = func(obj) + self.assertIsNot(obj_copy, obj) + self._compare(obj_copy, obj) + class TestSeries(tm.TestCase, Generic): _typ = Series @@ -1542,8 +1552,7 @@ def test_to_xarray(self): def test_deepcopy_empty(self): # This test covers empty frame copying with non-empty column sets - # as reported in issue #15370 - # https://github.com/pandas-dev/pandas/issues/15370 + # as reported in issue GH15370 empty_frame = DataFrame(data=[], index=[], columns=['A']) empty_frame_copy = deepcopy(empty_frame) From bf36f352bdc3dd83dab86b1d55607a68321ea0d1 Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Mon, 20 Feb 2017 10:29:21 -0500 Subject: [PATCH 7/7] TST: skip the panel4d deepcopy tests --- pandas/tests/test_generic.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pandas/tests/test_generic.py b/pandas/tests/test_generic.py index 63376698420de..9ad8b5da7eea8 100644 --- a/pandas/tests/test_generic.py +++ b/pandas/tests/test_generic.py @@ -677,11 +677,12 @@ def test_validate_bool_args(self): super(DataFrame, df).mask(cond=df.a > 2, inplace=value) def test_copy_and_deepcopy(self): - for shape in [0, 1, 2]: obj = self._construct(shape) - - for func in (copy, deepcopy): + for func in [copy, + deepcopy, + lambda x: x.copy(deep=False), + lambda x: x.copy(deep=True)]: obj_copy = func(obj) self.assertIsNot(obj_copy, obj) self._compare(obj_copy, obj) @@ -1588,6 +1589,9 @@ class TestPanel4D(tm.TestCase, Generic): def test_sample(self): pytest.skip("sample on Panel4D") + def test_copy_and_deepcopy(self): + pytest.skip("copy_and_deepcopy on Panel4D") + def test_to_xarray(self): tm._skip_if_no_xarray()