Search code examples
pythonpython-importpython-exec

How to extend scope to an exec'd function?


I have a module object containing several function definitions. Here is a simplified example.

The source code (makes up the module "my_module")

def functionA():
    print("hello")

def functionB():
    functionA()
    print("world")

The module is built with imp (I know it is depreciated, but my app is still on python 3.5)

ast_node = parse(source)
byte_code = compile(ast_node, 'my_module', 'exec')
my_module = imp.new_module('my_module')
exec(byte_code, __builtins__, my_module.__dict__)

I am trying to run functionB() using exec and passing in the full module __dict__ as the local dictionary. (I've also tried passing it in as the global dictionary without any luck)

exec("functionB()", None, my_module.__dict__)

The error I see is NameError: name 'functionA' is not defined

Is it possible to extend the local (or even global) scope to the executing function?


Solution

  • You don't need to "extend scope". Your problem is that you're doing things in the wrong namespaces. When you execute the module's code:

    exec(byte_code, __builtins__, my_module.__dict__)
    

    you're doing that with the built-ins namespace as globals, when you should be using the module's namespace as globals:

    exec(byte_code, my_module.__dict__)
    

    Then the module's functions will have the correct global namespace. Currently, they're all using the built-ins namespace as their global namespace.

    Also, don't use __builtins__ for the built-ins namespace. It's an implementation detail, and it doesn't even work the way you think - its value is different in different contexts. Sometimes it's set to the builtins module itself, rather than that module's dict. Instead, import builtins and use builtins.__dict__ if you need the built-ins dict.