Skip to content

Commit

Permalink
Add node_ancestors method
Browse files Browse the repository at this point in the history
I've been thinking about ways to get rid of unbounded `while` loops in
the Pylint codebase. A common use is to loop over a node's ancestors.
The `node_ancestors` method centralizes this logic in one place so
that those `while` loops can be rewritten as `for`.

A few uses are made of this new method. It only comes up a few places
in Astroid, but there are many more in Pylint.
  • Loading branch information
nickdrozd committed Sep 12, 2021
1 parent 741cd61 commit f4ccfda
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 18 deletions.
15 changes: 3 additions & 12 deletions astroid/nodes/node_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -4719,9 +4715,4 @@ 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:
if isinstance(parent, Decorators):
return True
parent = parent.parent
return False
return any(isinstance(ancestor, Decorators) for ancestor in node.node_ancestors())
14 changes: 8 additions & 6 deletions astroid/nodes/node_ng.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,13 @@ def last_child(self) -> Optional["NodeNG"]:
return attr
return None

def node_ancestors(self):
"""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.
Expand All @@ -216,12 +223,7 @@ def parent_of(self, node):
False otherwise.
:rtype: bool
"""
parent = node.parent
while parent is not None:
if self is parent:
return True
parent = parent.parent
return False
return any(self is ancestor for ancestor in node.node_ancestors())

def statement(self):
"""The first parent node, including self, marked as statement node.
Expand Down

0 comments on commit f4ccfda

Please sign in to comment.