Search code examples
pythonpython-3.xpython-exec

Is it possible to access exec-provided globals dictionary from within a function?


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?


Solution

  • 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.