Skip to content

Commit

Permalink
Merge pull request #642 from edreamleo/ekr-ast-docstrings
Browse files Browse the repository at this point in the history
PR: Add comments & docstrings related to call_for_nodes
  • Loading branch information
lieryan committed Jan 4, 2023
2 parents 31cbf92 + 08dada0 commit 545777d
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 1 deletion.
13 changes: 12 additions & 1 deletion rope/base/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,18 @@ def parse(source, filename="<string>", *args, **kwargs): # type: ignore


def call_for_nodes(node, callback):
"""If callback returns `True` the child nodes are skipped"""
"""
Pre-order depth-first traversal of AST nodes, calling `callback(node)` for
each node visited.
When each node is visited, `callback(node)` will be called with the visited
`node`, then its children node will be visited.
If `callback(node)` returns `True` for a node, then the descendants of that
node will not be visited.
See _ResultChecker._find_node for an example.
"""
result = callback(node)
if not result:
for child in ast.iter_child_nodes(node):
Expand Down
2 changes: 2 additions & 0 deletions rope/refactor/similarfinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ def __init__(self, body, pattern, does_match):
def find_matches(self):
if self.matches is None:
self.matches = []
# _check_nodes always returns None, so
# call_for_nodes traverses self.body's entire tree.
ast.call_for_nodes(self.body, self._check_node)
return self.matches

Expand Down
26 changes: 26 additions & 0 deletions ropetest/refactor/patchedasttest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1488,6 +1488,32 @@ def check_region(self, text, start, end):
self.test_case.assertEqual((start, end), node.region)

def _find_node(self, text):
"""
Find the node in `self.ast` whose type is named in `text`.
:param text: ast node name
Generally, the test should only have a single matching node, as it make
the test much harder to understand when there may be multiple matches.
If `self.ast` contains more than one nodes that matches `text`, then
the **outer-most last match** takes precedence.
For example, given that we are looking for `ast.Call` node:
checker._find_node("Call")
and given that `self.ast` is the AST for this code:
func_a(1, func_b(2, 3)) + func_c(4, func_d(5, 6))
the outer-most last match would be the ast node representing this bit:
func_c(4, func_d(5, 6))
Note that the order of traversal is based on the order of ast nodes,
which usually, but not always, match textual order.
"""
goal = text
if not isinstance(text, (tuple, list)):
goal = [text]
Expand Down

0 comments on commit 545777d

Please sign in to comment.