1

If I have a "regular" function, I can do ast.parse and ast.walk like this:

import ast
import inspect

def a():
    pass

code = inspect.getsource(a)
nodes = ast.walk(ast.parse(code))
for node in nodes:
    ...

However, if the function is a method inside a class like:

class B:
    def c(self):
        pass

code = inspect.getsource(B.c)
nodes = ast.walk(ast.parse(code))

I get:

IndentationError: unexpected indent

Which makes sense, I guess, since B.c is indented by one level. So how do I ast.parse and ast.walk here instead?

It seems like simply calling textwrap.dedent is not the right approach here, because it doesn't work if the code looks like this:

class B:
    def c(self):
        """
foo
"""
        pass
2
  • 2
    You could parse the entire class and then find the matching function definition inside that. But that function definition may not correspond to the value of B.c at runtime, which may be a good thing or bad thing.
    – Alex Hall
    Commented Jun 6, 2019 at 14:43
  • OK, I retract my previous support for dedent as it doesn't work in all cases. I've actually seen a library that used it and people who used that library ran into the edge case and reported a bug that took a while to figure out. Basically it's easy for B.c to contain some code which is less indented than the def c part. So if you want something that works in all cases you'll need another solution. But you'll also need to explain your use case some more as the solution will depend on that. See my comment above, for example.
    – Alex Hall
    Commented Jan 6, 2020 at 13:29

1 Answer 1

10

Its because you grabbed the method than tried walking it without undoing the indents. Your class is:

class B:
    def c(self):
        pass

code = inspect.getsource(B.c)
nodes = ast.walk(ast.parse(code))

If you print code you see:

    def c(self):
        pass

Note: The above code has one indent. You need to un-indent it:

import inspect
import ast
import textwrap
class B:
    def c(self):
        pass
code = textwrap.dedent(inspect.getsource(B.c))
nodes = ast.walk(ast.parse(code))
2
  • 2
    ast is designed to read valid python code from text. You parsed a subset of valid code then passed it to ast as text, but since you only grabbed part of the code it is no longer valid (bad indenting). Commented Jun 6, 2019 at 14:32
  • 2
    I should add that this will work for the problem you have presented, but if you want to make modifications to the AST and compile it into a new function, then the context such as the class will matter and this solution won't be enough.
    – Alex Hall
    Commented Jun 6, 2019 at 14:40

Not the answer you're looking for? Browse other questions tagged or ask your own question.