13

The following executes without an error in Python 3:

code = """
import math

def func(x):
    return math.sin(x)

func(10)
"""
_globals = {}
exec(code, _globals)

But if I try to capture the local variable dict as well, it fails with a NameError:

>>> _globals, _locals = {}, {}
>>> exec(code, _globals, _locals)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-9-aeda81bf0af1> in <module>()
----> 1 exec(code, {}, {})

<string> in <module>()

<string> in func(x)

NameError: name 'math' is not defined

Why is this happening, and how can I execute this code while capturing both global and local variables?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
jakevdp
  • 77,104
  • 11
  • 125
  • 160

1 Answers1

14

From the exec() documentation:

Remember that at module level, globals and locals are the same dictionary. If exec gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition.

You passed in two separate dictionaries, but tried to execute code that requires module-scope globals to be available. import math in a class would produce a local scope attribute, and the function you create won't be able to access that as class scope names are not considered for function closures.

See Naming and binding in the Python execution model reference:

Class definition blocks and arguments to exec() and eval() are special in the context of name resolution. A class definition is an executable statement that may use and define names. These references follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace. The namespace of the class definition becomes the attribute dictionary of the class. The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods[.]

You can reproduce the error by trying to execute the code in a class definition:

>>> class Demo:
...     import math
...     def func(x):
...         return math.sin(x)
...     func(10)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in Demo
  File "<stdin>", line 4, in func
NameError: name 'math' is not defined

Just pass in one dictionary.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Can you unpack the implications of the phrase "as if it were embedded in a class definition"? And/or give a concrete example of how "class scope names are not considered for function closures"? – jez Sep 22 '16 at 21:32
  • @jez: see [*Resolution of names*](https://docs.python.org/3/reference/executionmodel.html#resolution-of-names): *Class definition blocks and arguments to `exec()` and `eval()` are special in the context of name resolution. A class definition is an executable statement that may use and define names. These references follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace.* – Martijn Pieters Sep 22 '16 at 22:10
  • It may be worth stating explicitly that the behaviour seen here is precisely the same behaviour as that seen when defining methods inside a class definition (the current answer does say that, but never uses the word "method"), and repeat the code from the question inside a class definition for emphasis. Also, a neat trick to make it run as written by turning it into a closure instead: `exec("def _dummy():\n {}\n_dummy()".format("\n ".join(code.strip().splitlines())), {}, {})` – ncoghlan Sep 23 '16 at 03:47
  • 1
    @ncoghlan: technically, you define functions in a class. They only *become* methods when looking them up on an instance, triggering the descriptor protocol to bind them. – Martijn Pieters Sep 23 '16 at 09:40
  • @Martijn: I added that sentence to the `exec` doc. You explained it perfectly. – Terry Jan Reedy Sep 24 '16 at 21:14