diff --git a/ChangeLog b/ChangeLog index 4d2ea83a8..c22e6940d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -19,6 +19,8 @@ Release date: TBA Closes PyCQA/pylint#3887 +* Add ``node_ancestors`` method to ``NodeNG`` for obtaining the ancestors of nodes. + What's New in astroid 2.7.4? ============================ Release date: TBA diff --git a/astroid/nodes/node_classes.py b/astroid/nodes/node_classes.py index 254e479e5..951c5d419 100644 --- a/astroid/nodes/node_classes.py +++ b/astroid/nodes/node_classes.py @@ -109,17 +109,14 @@ def are_exclusive(stmt1, stmt2, exceptions: Optional[typing.List[str]] = None) - # index stmt1's parents stmt1_parents = {} children = {} - node = stmt1.parent previous = stmt1 - while node: + for node in stmt1.node_ancestors(): stmt1_parents[node] = 1 children[node] = previous previous = node - node = node.parent # climb among stmt2's parents until we find a common parent - node = stmt2.parent previous = stmt2 - while node: + for node in stmt2.node_ancestors(): if node in stmt1_parents: # if the common parent is a If or TryExcept statement, look if # nodes are in exclusive branches @@ -162,7 +159,6 @@ def are_exclusive(stmt1, stmt2, exceptions: Optional[typing.List[str]] = None) - return previous is not children[node] return False previous = node - node = node.parent return False @@ -4719,9 +4715,7 @@ def const_factory(value): def is_from_decorator(node): """Return True if the given node is the child of a decorator""" - parent = node.parent - while parent is not None: + for parent in node.node_ancestors(): if isinstance(parent, Decorators): return True - parent = parent.parent return False diff --git a/astroid/nodes/node_ng.py b/astroid/nodes/node_ng.py index 26f1bb27c..68a6837e6 100644 --- a/astroid/nodes/node_ng.py +++ b/astroid/nodes/node_ng.py @@ -208,6 +208,13 @@ def last_child(self) -> Optional["NodeNG"]: return attr return None + def node_ancestors(self) -> Iterator["NodeNG"]: + """Yield parent, grandparent, etc until there are no more.""" + parent = self.parent + while parent is not None: + yield parent + parent = parent.parent + def parent_of(self, node): """Check if this node is the parent of the given node. @@ -218,11 +225,9 @@ def parent_of(self, node): False otherwise. :rtype: bool """ - parent = node.parent - while parent is not None: + for parent in node.node_ancestors(): if self is parent: return True - parent = parent.parent return False def statement(self):