Instagram / Instagram/LibCST

matchers.call_if_not_inside doesn't work for self-nested structures

Open
#1,051 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.