Skip to content

Commit

Permalink
Fixed Tree.from_dict failing to recreate with a mapper. The recursive…
Browse files Browse the repository at this point in the history
… call to from_dict in class Node needed the mapper passed through.
  • Loading branch information
MyCreativityOutlet committed May 22, 2024
1 parent 64ad4a3 commit ffee351
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 1 deletion.
2 changes: 1 addition & 1 deletion nutree/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,7 @@ def from_dict(
)
child_items = item.get("children")
if child_items:
child.from_dict(child_items)
child.from_dict(child_items, mapper=mapper)
return

def _visit_pre(self, callback, memo) -> None:
Expand Down
65 changes: 65 additions & 0 deletions tests/test_serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,71 @@ def test_serialize_to_dict_list(self):
assert tree_2._self_check()


class TestFromDict:
def test_from_dict(self):
"""Save/load a tree with to_dict_list and from_dict with objects."""
tree = fixture.create_tree()
tree_dict_list = tree.to_dict_list()
tree_2 = Tree.from_dict(tree_dict_list)

assert fixture.trees_equal(tree, tree_2)
assert type(tree_2.first_child().first_child().data) is type(tree.first_child().first_child().data)
assert tree.first_child() is not tree_2.first_child()
assert tree.first_child() == tree_2.first_child()
assert tree.count == tree_2.count

assert tree._self_check()
assert tree_2._self_check()

def test_from_dict_objects(self):
"""Save/load an object tree with to_dict_list and from_dict"""

def _calc_id(tree, data):
# print("calc_id", data)
if isinstance(data, (fixture.Person, fixture.Department)):
return data.guid
return hash(data)

def serialize_mapper(node: Node, data: dict) -> dict:
if isinstance(node.data, fixture.Department):
# _calc_id() already makes sure that the 'data_id' is set to `guid`
# data["guid"] = node.data.guid
data["type"] = "dept"
data["name"] = node.data.name
elif isinstance(node.data, fixture.Person):
data["type"] = "person"
data["name"] = node.data.name
data["age"] = node.data.age
return data

def deserialize_mapper(parent, data):
node_type = data["type"]
# print("deserialize_mapper", data)
if node_type == "person":
data = fixture.Person(
name=data["name"], age=data["age"], guid=data["data_id"]
)
elif node_type == "dept":
data = fixture.Department(name=data["name"], guid=data["data_id"])
# print(f"deserialize_mapper -> {data}")
return data

# Use a tree
tree = Tree(calc_data_id=_calc_id)
fixture.create_tree(style="objects", tree=tree)
tree_dict_list = tree.to_dict_list(mapper=serialize_mapper)
tree_2 = Tree.from_dict(tree_dict_list, mapper=deserialize_mapper)

assert fixture.trees_equal(tree, tree_2)
assert type(tree_2.first_child().first_child().data) is type(tree.first_child().first_child().data)
assert tree.first_child() is not tree_2.first_child()
assert tree.first_child().data.name == tree_2.first_child().data.name
assert tree.count == tree_2.count

assert tree._self_check()
assert tree_2._self_check()


class TestDot:
def test_serialize_dot(self):
"""Save/load as object tree with clones."""
Expand Down

0 comments on commit ffee351

Please sign in to comment.