From bf733861cfd9fb71973eaaab9a4e9d8c5c834536 Mon Sep 17 00:00:00 2001 From: Ryan Ly Date: Tue, 23 Jul 2019 16:01:11 -0700 Subject: [PATCH 1/6] Add ability to deepcopy container --- src/hdmf/container.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/hdmf/container.py b/src/hdmf/container.py index 9da5eb037..581eeed17 100644 --- a/src/hdmf/container.py +++ b/src/hdmf/container.py @@ -3,6 +3,7 @@ from six import with_metaclass from .utils import docval, getargs, ExtenderMeta from warnings import warn +from copy import deepcopy class Container(with_metaclass(ExtenderMeta, object)): @@ -126,6 +127,24 @@ def parent(self, parent_container): parent_container.__children.append(self) parent_container.set_modified() + def __deepcopy__(self, memo): + ''' Create a deep copy of this Container + Reset the root parent to None, set all container_source to None, and set new object_id value + ''' + deepcopy_method = self.__deepcopy__ + self.__deepcopy__ = None + cp = deepcopy(self, memo) + self.__deepcopy__ = deepcopy_method + + cp.__parent = None + cp.__container_source = None + cp.__object_id = str(uuid4()) + + for child in cp.children: + child.parent = cp + + return cp + class Data(Container): From f59f6c4a1b8618affd812e19dee492c2750be650 Mon Sep 17 00:00:00 2001 From: Ryan Ly Date: Tue, 23 Jul 2019 16:09:14 -0700 Subject: [PATCH 2/6] Change modified to True as well --- src/hdmf/container.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hdmf/container.py b/src/hdmf/container.py index 581eeed17..6bb0b37d7 100644 --- a/src/hdmf/container.py +++ b/src/hdmf/container.py @@ -139,6 +139,7 @@ def __deepcopy__(self, memo): cp.__parent = None cp.__container_source = None cp.__object_id = str(uuid4()) + cp.__modified = True for child in cp.children: child.parent = cp From deda8ae85fa6b0b15acff58a98b1e66982462fac Mon Sep 17 00:00:00 2001 From: Ryan Ly Date: Tue, 23 Jul 2019 16:20:22 -0700 Subject: [PATCH 3/6] Add unit test --- tests/unit/test_container.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/unit/test_container.py b/tests/unit/test_container.py index 8927e4b0f..00d92f23e 100644 --- a/tests/unit/test_container.py +++ b/tests/unit/test_container.py @@ -1,6 +1,7 @@ import unittest2 as unittest from hdmf.container import Container +from copy import deepcopy class Subcontainer(Container): @@ -118,6 +119,35 @@ def test_type_hierarchy(self): self.assertEqual(Container.type_hierarchy(), (Container, object)) self.assertEqual(Subcontainer.type_hierarchy(), (Subcontainer, Container, object)) + def test_deepcopy(self): + parent_obj = Container('obj1') + parent_obj.container_source = 'a file' + parent_obj.set_modified(False) + child_obj = Container('obj2') + child_obj.parent = parent_obj + child_obj.container_source = 'a file' + child_obj.set_modified(False) + child_child_obj = Container('obj3') + child_child_obj.parent = child_obj + child_child_obj.container_source = 'a file' + + parent_copy = deepcopy(parent_obj) + self.assertEqual(parent_copy.name, 'obj1') + self.assertEqual(parent_copy.children[0].name, 'obj2') + self.assertEqual(parent_copy.children[0].children[0].name, 'obj3') + self.assertNotEqual(parent_copy.object_id, parent_obj.object_id) + self.assertNotEqual(parent_copy.children[0].object_id, child_obj.object_id) + self.assertNotEqual(parent_copy.children[0].children[0].object_id, child_child_obj.object_id) + self.assertIsNone(parent_copy.container_source) + self.assertIsNone(parent_copy.children[0].container_source) + self.assertIsNone(parent_copy.children[0].children[0].container_source) + self.assertTrue(parent_copy.modified) + self.assertTrue(parent_copy.children[0].modified) + self.assertTrue(parent_copy.children[0].children[0].modified) + + child_copy = deepcopy(child_obj) + self.assertIsNone(child_copy.parent) + if __name__ == '__main__': unittest.main() From 00b2df6487e4dba8fece58b192c1303e986163d7 Mon Sep 17 00:00:00 2001 From: Ryan Ly Date: Tue, 23 Jul 2019 17:16:14 -0700 Subject: [PATCH 4/6] Unpack HDMFDataset and DataIO - note circular import currently --- src/hdmf/container.py | 11 +++++++++++ tests/unit/test_container.py | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/hdmf/container.py b/src/hdmf/container.py index 6bb0b37d7..f7c51515f 100644 --- a/src/hdmf/container.py +++ b/src/hdmf/container.py @@ -2,6 +2,8 @@ from uuid import uuid4 from six import with_metaclass from .utils import docval, getargs, ExtenderMeta +from .query import HDMFDataset +from .data_utils import DataIO from warnings import warn from copy import deepcopy @@ -144,6 +146,15 @@ def __deepcopy__(self, memo): for child in cp.children: child.parent = cp + for k, v in cp.__dict__.items(): + if isinstance(v, DataIO): + setattr(cp, k, v.data) + + # resolve HDMFDataset after and separately because DataIO can wrap an HDMFDataset + for k, v in cp.__dict__.items(): + if isinstance(v, HDMFDataset): + setattr(cp, k, v.dataset) + return cp diff --git a/tests/unit/test_container.py b/tests/unit/test_container.py index 00d92f23e..5e836de28 100644 --- a/tests/unit/test_container.py +++ b/tests/unit/test_container.py @@ -1,6 +1,9 @@ import unittest2 as unittest +from tests.unit.test_utils import Foo from hdmf.container import Container +from hdmf.query import HDMFDataset +from hdmf.data_utils import DataIO from copy import deepcopy @@ -148,6 +151,21 @@ def test_deepcopy(self): child_copy = deepcopy(child_obj) self.assertIsNone(child_copy.parent) + def test_deepcopy_data(self): + parent_obj = Foo('obj1', HDMFDataset([1, 2, 3, 4, 5]), 'a string', 10) + parent_obj.container_source = 'a file' + parent_obj.set_modified(False) + child_obj = Foo('obj2', DataIO(HDMFDataset([1, 2, 3, 4, 5])), 'a string2', 20) + child_obj.parent = parent_obj + child_obj.container_source = 'a file' + + parent_copy = deepcopy(parent_obj) + self.assertListEqual(parent_copy.my_data, [1, 2, 3, 4, 5]) + self.assertEqual(parent_copy.attr1, 'a string') + self.assertEqual(parent_copy.attr2, 10) + self.assertListEqual(parent_copy.children[0].my_data, [1, 2, 3, 4, 5]) + + # TODO test deepcopy with references and H5Dataset if __name__ == '__main__': unittest.main() From 7eada4c503a98b6ed0247ff724ee9eb0e36f8a52 Mon Sep 17 00:00:00 2001 From: Ryan Ly Date: Tue, 23 Jul 2019 17:27:33 -0700 Subject: [PATCH 5/6] Unpack and deepcopy DataIO --- src/hdmf/container.py | 5 ----- src/hdmf/data_utils.py | 5 ++++- tests/unit/test_utils.py | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/hdmf/container.py b/src/hdmf/container.py index f7c51515f..db5c38e93 100644 --- a/src/hdmf/container.py +++ b/src/hdmf/container.py @@ -3,7 +3,6 @@ from six import with_metaclass from .utils import docval, getargs, ExtenderMeta from .query import HDMFDataset -from .data_utils import DataIO from warnings import warn from copy import deepcopy @@ -146,10 +145,6 @@ def __deepcopy__(self, memo): for child in cp.children: child.parent = cp - for k, v in cp.__dict__.items(): - if isinstance(v, DataIO): - setattr(cp, k, v.data) - # resolve HDMFDataset after and separately because DataIO can wrap an HDMFDataset for k, v in cp.__dict__.items(): if isinstance(v, HDMFDataset): diff --git a/src/hdmf/data_utils.py b/src/hdmf/data_utils.py index f2fc0b9b5..10c8b7562 100644 --- a/src/hdmf/data_utils.py +++ b/src/hdmf/data_utils.py @@ -5,9 +5,9 @@ from collections import Iterable # Python 2.7 from operator import itemgetter - import numpy as np from six import with_metaclass, text_type, binary_type +from copy import deepcopy from .container import Data, DataRegion from .utils import docval, getargs, popargs, docval_macro, get_data_shape @@ -512,6 +512,9 @@ def __next__(self): def __iter__(self): return self.data.__iter__() + def __deepcopy__(self, memo): + return deepcopy(self.data) + class RegionSlicer(with_metaclass(ABCMeta, DataRegion)): ''' diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index e4cd1310c..1a4ef01ee 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -8,7 +8,7 @@ class Foo(Container): @docval({'name': 'name', 'type': str, 'doc': 'the name of this Foo'}, - {'name': 'my_data', 'type': 'array_data', 'doc': 'some data'}, + {'name': 'my_data', 'type': ('array_data', 'data'), 'doc': 'some data'}, {'name': 'attr1', 'type': str, 'doc': 'an attribute'}, {'name': 'attr2', 'type': int, 'doc': 'another attribute'}, {'name': 'attr3', 'type': float, 'doc': 'a third attribute', 'default': 3.14}) From dc0dcfb7367998539c891fb02e7af4f1b4275f59 Mon Sep 17 00:00:00 2001 From: Ryan Ly Date: Tue, 23 Jul 2019 17:31:47 -0700 Subject: [PATCH 6/6] Fix flake8 --- tests/unit/test_container.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/test_container.py b/tests/unit/test_container.py index 5e836de28..7e5e3800b 100644 --- a/tests/unit/test_container.py +++ b/tests/unit/test_container.py @@ -167,5 +167,6 @@ def test_deepcopy_data(self): # TODO test deepcopy with references and H5Dataset + if __name__ == '__main__': unittest.main()