Is it possible to access exec-provided globals dictionary from within a function, if the function was defined outside of the exec-ed code (and thus already bound to different __globals__
)?
In other words, is there a way to make the following example work?
def f():
log("Hi")
exec('f()', {'f': f, 'log': print})
In general, is it possible to substitute the __globals__
of a function?
This is a pretty weird thing to do, but it's doable.
Your exec call executes the statement f()
in the provided globals. It does not execute the body of f
in the provided globals. The provided globals are being used in the wrong stack frame. To access those globals from f
, you can use stack inspection:
import inspect
def f():
log = inspect.currentframe().f_back.f_globals['log']
log('Hi')
exec('f()', {'f': f, 'log': print})
If you want to execute the body of f
with the provided globals rather than just gaining access to the globals, you need to make a copy of f
with your own custom globals:
import types
my_f = types.FunctionType(f.__code__,
{'log': print},
f.__name__,
f.__defaults__,
f.__closure__)
my_f()
The function type constructor is sort of documented; it's not in the online docs, but it is documented in the function type's docstring:
function(code, globals[, name[, argdefs[, closure]]])
Create a function object from a code object and a dictionary.
The optional name string overrides the name from the code object.
The optional argdefs tuple specifies the default argument values.
The optional closure tuple supplies the bindings for free variables.