Skip to content

Commit b047e85

Browse files
authored
Merge pull request #1922 from braingram/copy_lazy_tree
fix deepcopy of lazy tree
2 parents 59c33c8 + ca6ed10 commit b047e85

4 files changed

Lines changed: 41 additions & 8 deletions

File tree

asdf/_tests/test_lazy_nodes.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,20 @@ def test_node_empty_init(NodeClass, base):
123123

124124

125125
@pytest.mark.parametrize(
126-
"node",
126+
"node,base_type",
127127
[
128-
AsdfDictNode({"a": 1, "b": 2}),
129-
AsdfListNode([1, 2, 3]),
130-
AsdfOrderedDictNode({"a": 1, "b": 2}),
128+
(AsdfDictNode({"a": 1, "b": 2}), dict),
129+
(AsdfListNode([1, 2, 3]), list),
130+
(AsdfOrderedDictNode({"a": 1, "b": 2}), collections.OrderedDict),
131131
],
132132
)
133133
@pytest.mark.parametrize("copy_operation", [copy.copy, copy.deepcopy])
134-
def test_copy(node, copy_operation):
134+
def test_copy(node, base_type, copy_operation):
135135
copied_node = copy_operation(node)
136-
assert isinstance(copied_node, type(node))
136+
if copy_operation is copy.copy:
137+
assert type(copied_node) is type(node)
138+
else:
139+
assert type(copied_node) is base_type
137140
assert copied_node == node
138141

139142

@@ -408,3 +411,26 @@ def test_lazy_generator_converter(tmp_path, lazy_generator_class):
408411

409412
with asdf.open(fn, lazy_tree=True) as af:
410413
assert isinstance(af["obj"].data, dict)
414+
415+
416+
def test_lazy_copy(tmp_path):
417+
"""
418+
Test that copying an AsdfFile instance with a lazy
419+
tree doesn't result in the copy retaining references
420+
to the instance.
421+
"""
422+
fn = tmp_path / "test.asdf"
423+
obj = asdf.tags.core.IntegerType(1)
424+
tree = {"a": {"b": obj}}
425+
# make a recursive structure
426+
tree["a"]["c"] = tree["a"]
427+
428+
asdf.AsdfFile(tree).write_to(fn)
429+
430+
with asdf.open(fn, lazy_tree=True) as af:
431+
af2 = af.copy()
432+
433+
del af
434+
gc.collect(2)
435+
assert af2["a"]["b"] == obj
436+
assert af2["a"]["c"]["b"] is af2["a"]["b"]

asdf/lazy_nodes.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import warnings
99
import weakref
1010

11-
from . import tagged, yamlutil
11+
from . import tagged, treeutil, yamlutil
1212
from .exceptions import AsdfConversionWarning, AsdfLazyReferenceError
1313
from .extension._serialization_context import BlockAccess
1414

@@ -137,6 +137,9 @@ def tagged(self):
137137
"""
138138
return self.data
139139

140+
def __deepcopy__(self, memo):
141+
return treeutil.walk_and_modify(self, lambda n: n)
142+
140143
def _convert_and_cache(self, value, key):
141144
"""
142145
Convert ``value`` to either:

asdf/treeutil.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Utility functions for managing tree-like data structures.
33
"""
44

5+
import collections
56
import types
67
from contextlib import contextmanager
78

@@ -278,7 +279,9 @@ def _handle_callback(node, json_id):
278279
return _handle_generator(result)
279280

280281
def _handle_mapping(node, json_id):
281-
if isinstance(node, lazy_nodes.AsdfDictNode):
282+
if isinstance(node, lazy_nodes.AsdfOrderedDictNode):
283+
result = collections.OrderedDict()
284+
elif isinstance(node, lazy_nodes.AsdfDictNode):
282285
result = {}
283286
else:
284287
result = node.__class__()

changes/1922.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix deepcopy of lazy tree.

0 commit comments

Comments
 (0)