matchers.call_if_not_inside doesn't work for self-nested structures
- Dominant language
- Python
- Stars
- 1.9k
- Forks
- 229
- PR merge metrics
- No merged PRs in 30d
Description
I'm trying to extract functions that are not nested within other functions, and it seemed like `matchers.call_if_not_inside` would be the perfect tool - however, the code below results in nothing getting printed. Removing `matchers.FunctionDef()` returns both `f` and `g`. I'm fairly sure I could write raw leave_* statements to track this, but this failure seems somewhat counterintuitive - is this an inherent limitatation of the decorator approach, or might this be a bug?
```python
import libcst as cst
from libcst import matchers
class NonNestedFnVisitor(matchers.MatcherDecoratableVisitor):
@matchers.call_if_not_inside(matchers.ClassDef() | matchers.FunctionDef())
@matchers.leave(matchers.FunctionDef())
def collect_function_definitions(self, node: cst.FunctionDef) -> None:
print(node.name)
tree = cst.parse_module(
f"""
def f():
def g():
pass
return g()
class C:
def h(self):
pass
"""
)
collector = NonNestedFnVisitor()
tree.visit(collector)
```
Thanks for the hard work on the library - it has been 10x more productive than working with `ast` directly!
edit: for anyone stumbling upon this same use case, here's the workaround I settled on
Show code
```python
import libcst as cst
from libcst import matchers
class NonNestedFnVisitor(matchers.MatcherDecoratableVisitor):
def __init__(self) -> None:
super().__init__()
self.fn_stack = []
@matchers.call_if_not_inside(matchers.ClassDef())
@matchers.leave(matchers.FunctionDef())
def leave_function_def(self, node: cst.FunctionDef) -> None:
self.fn_stack.pop()
@matchers.call_if_not_inside(matchers.ClassDef())
@matchers.visit(matchers.FunctionDef())
def visit_function_def(self, node: cst.FunctionDef) -> None:
self.fn_stack.append(node)
if len(self.fn_stack) == 1:
print(node.name)
tree = cst.parse_module("\n".join(["def f():", " def g():", " pass", " return g()", "class C:", " def h(self):", " pass"]))
collector = NonNestedFnVisitor()
a = tree.visit(collector)
```
Contributor guide
Assessment
This issue has not been assessed yet.