Skip to content
This repository has been archived by the owner on Oct 24, 2024. It is now read-only.

Setting node name keeps tree linkage #310

Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions datatree/tests/test_datatree.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,23 @@ def test_child_gets_named_on_attach(self):
mary = DataTree(children={"Sue": sue}) # noqa
assert sue.name == "Sue"

def test_setting_node_name_keeps_tree_linkage(self):
root = DataTree(name="root")
child = DataTree(name="child", parent=root)
grandchild = DataTree(name="grandchild", parent=child)

assert root.name == "root"
assert child.name == "child"
assert grandchild.name == "grandchild"

# changing the name of a child node should correctly update the dict key in
# its parent's children
child.name = "childish"

assert child.name == "childish"
assert "childish" in root
assert list(root.children) == ["childish"]


class TestPaths:
def test_path_property(self):
Expand Down
7 changes: 5 additions & 2 deletions datatree/treenode.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def parent(self) -> Tree | None:
return self._parent

def _set_parent(
self, new_parent: Tree | None, child_name: Optional[str] = None
self, new_parent: Tree | None, child_name: str | None = None
) -> None:
# TODO is it possible to refactor in a way that removes this private method?

Expand Down Expand Up @@ -601,15 +601,18 @@ def name(self, name: str | None) -> None:
raise TypeError("node name must be a string or None")
if "/" in name:
raise ValueError("node names cannot contain forward slashes")
parent = self._parent
self.orphan()
self._name = name
self._set_parent(parent, name)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this orphaning then un-orphaning logic, couldn't we just change the key in .children directly via some logic more like in _post_attach

Copy link
Contributor Author

@etienneschalk etienneschalk Feb 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does seem immediate to me right now (I tried some things but not successfully until now)
I still need to remove then add again the node in the parent's children at some point 🤔

In all cases I get more messy code than the current solution

__delitem__ itself uses orphan under the hood for instance

Copy link
Contributor Author

@etienneschalk etienneschalk Feb 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally, I went for a .children = assignment. I ensured the order of children is preserved by re-creating an OrderedDict from a generator on the existing parent's children. Code is more verbose, but I think this is unavoidable to ensure the order is preserved. Indeed, orphaning then re-attaching would have lost the ordering information.

Note: _post_attach solely won't rename the node _name, so two renaming still occur:

  • One on the parent's children (preserving order)
  • One on the node _name itself

Edit: if the node had a parent, there is no need to manually set the ._name. Reassigning the updated list of children to its parents renames the node along the way


def __str__(self) -> str:
return f"NamedNode({self.name})" if self.name else "NamedNode()"

def _post_attach(self: NamedNode, parent: NamedNode) -> None:
"""Ensures child has name attribute corresponding to key under which it has been stored."""
key = next(k for k, v in parent.children.items() if v is self)
self.name = key
self._name = key

@property
def path(self) -> str:
Expand Down
4 changes: 4 additions & 0 deletions docs/source/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ Deprecations

Bug fixes
~~~~~~~~~

- Renaming a node also update the key of its parent's children
(:issue:`309`, :pull:`310`)
By `Etienne Schalk <https://github.com/etienneschalk>`_.
- Keep attributes on nodes containing no data in :py:func:`map_over_subtree`. (:issue:`278`, :pull:`279`)
By `Sam Levang <https://github.com/slevang>`_.

Expand Down
Loading