Problem Description
I am curious if it is possible to exec
a string within a function as if the string were substituted for exec
directly (with appropriate indentation). I understand that in 99.9% of cases, you shouldn't be using exec
but I'm more interested in if this can be done rather than if it should be done.
The behavior I want is equivalent to:
GLOBAL_CONSTANT = 1
def test_func():
def A():
return GLOBAL_CONSTANT
def B():
return A()
return B
func = test_func()
assert func() == 1
But I am given instead:
GLOBAL_CONSTANT = 1
EXEC_STR = """
def A():
return GLOBAL_CONSTANT
def B():
return A()
"""
def exec_and_extract(exec_str, var_name):
# Insert code here
func = exec_and_extract(EXEC_STR, 'B')
assert func() == 1
Failed Attempts
def exec_and_extract(exec_str, var_name):
exec(EXEC_STR) # equivalent to exec(EXEC_STR, globals(), locals())
return locals()[var_name]
NameError: name 'A' is not defined
when calling func()
since A
and B
exist inside exec_and_extract
's locals()
but the execution context while running A
or B
is exec_and_extract
's globals()
.
def exec_and_extract(exec_str, var_name):
exec(EXEC_STR, locals()) # equivalent to exec(EXEC_STR, locals(), locals())
return locals()[var_name]
NameError: name 'GLOBAL_CONSTANT' is not defined
when calling A
from inside func()
since the execution context of A
is exec_and_extract
's locals()
which does not contain GLOBAL_CONSTANT
.
def exec_and_extract(exec_str, var_name):
exec(EXEC_STR, globals()) # equivalent to exec(EXEC_STR, globals(), globals())
return globals()[var_name]
Works but pollutes global namespace, not equivalent.
def exec_and_extract(exec_str, var_name):
locals().update(globals())
exec(EXEC_STR, locals()) # equivalent to exec(EXEC_STR, locals(), locals())
return locals()[var_name]
Works but requires copying the entire content of exec_and_extract
's globals()
into its locals()
which is a waste of time if globals()
is large (of course not applicable in this contrived example). Additionally, is subtly not the same as the "paste in code" version since if one of the arguments to exec_and_extract
happened to be GLOBAL_CONSTANT
(a terrible argument name), the behavior would be different ("paste in" version would use the argument value while this code would use the global constant value).
Further Constraints
Trying to cover any "loopholes" in the problem statement:
- The
exec_str
value should represent arbitrary code that can access global or local scope variables. - Solution should not require analysis of what global scope variables are accessed within
exec_str
. - There should be no "pollution" between subsequent calls to
exec_and_extract
(in global namespace or otherwise). i.e. In this example, execution ofEXEC_STR
should not leaveA
around to be referenceable in future calls toexec_and_extract
.