I'm working on dynamically generating code in Python.
To aid with this, I wrote a helper method which takes in a string of Python code and dumps out the AST. Here's that method:
# I want print treated as a function, not a statement.
import __future__
pfcf = __future__.print_function.compiler_flag
from ast import dump, PyCF_ONLY_AST
def d(s):
print(dump(compile(s, '<String>', 'exec', pfcf|PyCF_ONLY_AST))
When I run this function on a simple Hello World, it spits out the following (formatted for easier reading):
d("print('Hello World!')")
Module(body=[Expr(value=Call(func=Name(id='print',
ctx=Load()),
args=[Str(s='Hello World!')],
keywords=[],
starargs=None,
kwargs=None))])
I was able to dynamically generate this code and run it - everything was great.
Then I tried to dynamically generate
print(len('Hello World!'))
Should be pretty easy - just another function call. Here's what my code dynamically generated:
Module(body=[Expr(value=Call(func=Name(id='print',
ctx=Load()),
args=[Expr(value=Call(func=Name(id='len',
ctx=Load()),
args=[Str(s='Hello World!')],
keywords=[],
starargs=None,
kwargs=None))],
keywords=[],
starargs=None,
kwargs=None))])
Running it didn't work, though. Instead, I got this message:
TypeError: expected some sort of expr, but got <_ast.Expr object at 0x101812c10>
So I ran my helper method previously mentioned to see what it would output:
d("print(len('Hello World!')")
Module(body=[Expr(value=Call(func=Name(id='print',
ctx=Load()),
args=[Call(func=Name(id='len',
ctx=Load()),
args=[Str(s='Hello World!')],
keywords=[],
starargs=None,
kwargs=None)],
keywords=[],
starargs=None,
kwargs=None))])
The difference between what I'm generating (which doesn't work) and what it generates (which works), is that they passed Call
directly to args, whereas I wrapped mine in Expr
.
The problem is, in the very first line, I needed to wrap Call
in an Expr
. I'm confused - why is it sometimes necessary to wrap a Call
in an Expr
but not other times? Expr
seems like it should be just an abstract base class which Call
inherits from, but it's required at the top level right under the Module
. Why? Is there something subtle I'm missing? What are the rules for when Call
needs to be wrapped in an Expr
and when it can be used directly?