Skip to content

Commit 9322fc3

Browse files
authored
Add node_ancestors method (#1169)
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.
1 parent 8fbef58 commit 9322fc3

File tree

3 files changed

+13
-12
lines changed

3 files changed

+13
-12
lines changed

ChangeLog

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Release date: TBA
1919

2020
Closes PyCQA/pylint#3887
2121

22+
* Add ``node_ancestors`` method to ``NodeNG`` for obtaining the ancestors of nodes.
23+
2224
What's New in astroid 2.7.4?
2325
============================
2426
Release date: TBA

astroid/nodes/node_classes.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,17 +109,14 @@ def are_exclusive(stmt1, stmt2, exceptions: Optional[typing.List[str]] = None) -
109109
# index stmt1's parents
110110
stmt1_parents = {}
111111
children = {}
112-
node = stmt1.parent
113112
previous = stmt1
114-
while node:
113+
for node in stmt1.node_ancestors():
115114
stmt1_parents[node] = 1
116115
children[node] = previous
117116
previous = node
118-
node = node.parent
119117
# climb among stmt2's parents until we find a common parent
120-
node = stmt2.parent
121118
previous = stmt2
122-
while node:
119+
for node in stmt2.node_ancestors():
123120
if node in stmt1_parents:
124121
# if the common parent is a If or TryExcept statement, look if
125122
# nodes are in exclusive branches
@@ -162,7 +159,6 @@ def are_exclusive(stmt1, stmt2, exceptions: Optional[typing.List[str]] = None) -
162159
return previous is not children[node]
163160
return False
164161
previous = node
165-
node = node.parent
166162
return False
167163

168164

@@ -4719,9 +4715,7 @@ def const_factory(value):
47194715

47204716
def is_from_decorator(node):
47214717
"""Return True if the given node is the child of a decorator"""
4722-
parent = node.parent
4723-
while parent is not None:
4718+
for parent in node.node_ancestors():
47244719
if isinstance(parent, Decorators):
47254720
return True
4726-
parent = parent.parent
47274721
return False

astroid/nodes/node_ng.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,13 @@ def last_child(self) -> Optional["NodeNG"]:
208208
return attr
209209
return None
210210

211+
def node_ancestors(self) -> Iterator["NodeNG"]:
212+
"""Yield parent, grandparent, etc until there are no more."""
213+
parent = self.parent
214+
while parent is not None:
215+
yield parent
216+
parent = parent.parent
217+
211218
def parent_of(self, node):
212219
"""Check if this node is the parent of the given node.
213220
@@ -218,11 +225,9 @@ def parent_of(self, node):
218225
False otherwise.
219226
:rtype: bool
220227
"""
221-
parent = node.parent
222-
while parent is not None:
228+
for parent in node.node_ancestors():
223229
if self is parent:
224230
return True
225-
parent = parent.parent
226231
return False
227232

228233
def statement(self):

0 commit comments

Comments
 (0)