@@ -121,8 +121,7 @@ def _check_loop(self, new_parent: Tree | None) -> None:
121
121
)
122
122
123
123
def _is_descendant_of (self , node : Tree ) -> bool :
124
- _self , * lineage = list (node .lineage )
125
- return any (n is self for n in lineage )
124
+ return any (n is self for n in node .parents )
126
125
127
126
def _detach (self , parent : Tree | None ) -> None :
128
127
if parent is not None :
@@ -236,26 +235,53 @@ def _post_attach_children(self: Tree, children: Mapping[str, Tree]) -> None:
236
235
"""Method call after attaching `children`."""
237
236
pass
238
237
239
- def iter_lineage (self : Tree ) -> Iterator [Tree ]:
238
+ def _iter_parents (self : Tree ) -> Iterator [Tree ]:
240
239
"""Iterate up the tree, starting from the current node."""
241
- node : Tree | None = self
240
+ node : Tree | None = self . parent
242
241
while node is not None :
243
242
yield node
244
243
node = node .parent
245
244
245
+ def iter_lineage (self : Tree ) -> Tuple [Tree , ...]:
246
+ """Iterate up the tree, starting from the current node."""
247
+ from warnings import warn
248
+
249
+ warn (
250
+ "`iter_lineage` has been deprecated, and in the future will raise an error."
251
+ "Please use `parents` from now on." ,
252
+ DeprecationWarning ,
253
+ )
254
+ return tuple ((self , * self .parents ))
255
+
246
256
@property
247
257
def lineage (self : Tree ) -> Tuple [Tree , ...]:
248
258
"""All parent nodes and their parent nodes, starting with the closest."""
249
- return tuple (self .iter_lineage ())
259
+ from warnings import warn
260
+
261
+ warn (
262
+ "`lineage` has been deprecated, and in the future will raise an error."
263
+ "Please use `parents` from now on." ,
264
+ DeprecationWarning ,
265
+ )
266
+ return self .iter_lineage ()
267
+
268
+ @property
269
+ def parents (self : Tree ) -> Tuple [Tree , ...]:
270
+ """All parent nodes and their parent nodes, starting with the closest."""
271
+ return tuple (self ._iter_parents ())
250
272
251
273
@property
252
274
def ancestors (self : Tree ) -> Tuple [Tree , ...]:
253
275
"""All parent nodes and their parent nodes, starting with the most distant."""
254
- if self .parent is None :
255
- return (self ,)
256
- else :
257
- ancestors = tuple (reversed (list (self .lineage )))
258
- return ancestors
276
+
277
+ from warnings import warn
278
+
279
+ warn (
280
+ "`ancestors` has been deprecated, and in the future will raise an error."
281
+ "Please use `parents`. Example: `tuple(reversed(node.parents))`" ,
282
+ DeprecationWarning ,
283
+ )
284
+ return tuple ((* reversed (self .parents ), self ))
259
285
260
286
@property
261
287
def root (self : Tree ) -> Tree :
@@ -351,7 +377,7 @@ def level(self: Tree) -> int:
351
377
depth
352
378
width
353
379
"""
354
- return len (self .ancestors ) - 1
380
+ return len (self .parents )
355
381
356
382
@property
357
383
def depth (self : Tree ) -> int :
@@ -591,9 +617,9 @@ def path(self) -> str:
591
617
if self .is_root :
592
618
return "/"
593
619
else :
594
- root , * ancestors = self .ancestors
620
+ root , * ancestors = tuple ( reversed ( self .parents ))
595
621
# don't include name of root because (a) root might not have a name & (b) we want path relative to root.
596
- names = [node .name for node in ancestors ]
622
+ names = [* ( node .name for node in ancestors ), self . name ]
597
623
return "/" + "/" .join (names )
598
624
599
625
def relative_to (self : NamedNode , other : NamedNode ) -> str :
@@ -608,7 +634,7 @@ def relative_to(self: NamedNode, other: NamedNode) -> str:
608
634
)
609
635
610
636
this_path = NodePath (self .path )
611
- if other .path in list (ancestor .path for ancestor in self . lineage ):
637
+ if other .path in list (parent .path for parent in ( self , * self . parents ) ):
612
638
return str (this_path .relative_to (other .path ))
613
639
else :
614
640
common_ancestor = self .find_common_ancestor (other )
@@ -623,18 +649,17 @@ def find_common_ancestor(self, other: NamedNode) -> NamedNode:
623
649
624
650
Raise ValueError if they are not in the same tree.
625
651
"""
626
- common_ancestor = None
627
- for node in other .iter_lineage ():
628
- if node .path in [ancestor .path for ancestor in self .ancestors ]:
629
- common_ancestor = node
630
- break
652
+ if self is other :
653
+ return self
631
654
632
- if not common_ancestor :
633
- raise NotFoundInTreeError (
634
- "Cannot find common ancestor because nodes do not lie within the same tree"
635
- )
655
+ other_paths = [ op . path for op in other . parents ]
656
+ for parent in ( self , * self . parents ):
657
+ if parent . path in other_paths :
658
+ return parent
636
659
637
- return common_ancestor
660
+ raise NotFoundInTreeError (
661
+ "Cannot find common ancestor because nodes do not lie within the same tree"
662
+ )
638
663
639
664
def _path_to_ancestor (self , ancestor : NamedNode ) -> NodePath :
640
665
"""Return the relative path from this node to the given ancestor node"""
@@ -643,12 +668,12 @@ def _path_to_ancestor(self, ancestor: NamedNode) -> NodePath:
643
668
raise NotFoundInTreeError (
644
669
"Cannot find relative path to ancestor because nodes do not lie within the same tree"
645
670
)
646
- if ancestor .path not in list (a .path for a in self . ancestors ):
671
+ if ancestor .path not in list (a .path for a in ( self , * self . parents ) ):
647
672
raise NotFoundInTreeError (
648
673
"Cannot find relative path to ancestor because given node is not an ancestor of this node"
649
674
)
650
675
651
- lineage_paths = list (ancestor .path for ancestor in self . lineage )
652
- generation_gap = list (lineage_paths ).index (ancestor .path )
653
- path_upwards = "../" * generation_gap if generation_gap > 0 else "/ "
676
+ parents_paths = list (parent .path for parent in ( self , * self . parents ) )
677
+ generation_gap = list (parents_paths ).index (ancestor .path )
678
+ path_upwards = "../" * generation_gap if generation_gap > 0 else ". "
654
679
return NodePath (path_upwards )
0 commit comments