You can avoid generating then exec
ing source code if you're ready to generate Abstract Syntax Trees (AST's) and compile them instead. It might be slightly better because data can stay structured all along.
from ast import *
from types import *
function_ast = FunctionDef(
name='f',
args=arguments(args=[], vararg=None, kwarg=None, defaults=[]),
body=[Return(value=Num(n=42, lineno=1, col_offset=0), lineno=1, col_offset=0)],
decorator_list=[],
lineno=1,
col_offset=0
)
module_ast = Module(body=[function_ast])
module_code = compile(module_ast, "<not_a_file>", "exec")
function_code = [c for c in module_code.co_consts if isinstance(c, CodeType)][0]
f = FunctionType(function_code, {})
print f()
The code above will print 42
.
To get inspiration about what the generated AST should be, you can use:
print(dump(parse("def f(): return 42"), include_attributes=True))
Of course, ASTs are different in Python 2 and Python 3.
Edit:
Tested and working in Python 3.8
from ast import *
from types import *
function_ast = FunctionDef(
name='f',
args=arguments(
args=[], vararg=None, kwarg=None, defaults=[],
kwonlyargs=[], kw_defaults=[], posonlyargs=[]
),
body=[Return(value=Num(n=42, lineno=1, col_offset=0), lineno=1, col_offset=0)],
decorator_list=[],
lineno=1,
col_offset=0
)
module_ast = Module(body=[function_ast], type_ignores=[])
module_code = compile(module_ast, "<not_a_file>", "exec")
function_code = [c for c in module_code.co_consts if isinstance(c, CodeType)][0]
f = FunctionType(function_code, {})
print(f())