This is a rather interesting case:
>>> def func():
... exec('print "hi from func"')
... def subfunction():
... return True
...
File "<stdin>", line 2
SyntaxError: unqualified exec is not allowed in function 'func' because
it contains a nested function with free variables
The reason why this doesn't work indeed is that subfunction
contains a free variable, and since in Python 2, exec
could theoretically modify the locals in the containing scope, it would be impossible to decide if the variable should be bound from the global or the parent function scope. One of the verses in the Zen of Python is "In the face of ambiguity, refuse the temptation to guess." and this is what Python 2 does.
Now the question is: what is this free (unbound) variable? Well, it is True
!
Indeed it is reproduceable with None
:
>>> def func():
... exec('print "hi from func"')
... def subfunction():
... return None
...
File "<stdin>", line 2
SyntaxError: unqualified exec is not allowed in function 'test2' because it contains a nested
function with free variables
Even though None
cannot be assigned to, and it is considered as a constant in the bytecode, the buggy parser thinks it is an unbound variable here.
But if you replace it with 1
and it works without problems:
>>> def test2():
... exec('print "hi from func"')
... def subfunction():
... return 1
...
>>>
To avoid this error, specify explicitly the globals and possibly locals that are to be used by exec
, say:
>>> def test2():
... exec 'print "hi from test2"' in {}
... def subfunction():
... return None
...
>>>
In Python 3, exec
is just a simple function and isn't handled specially by the parser or the bytecode compiler. In Python 3 exec
cannot rebind function-local names, and thus this SyntaxError and ambiguity doesn't exist.
One peculiar case in Python 2 vs 3 compatibility is that while Python 2.7 documentation states that
The form exec(expr, globals)
is equivalent to exec expr in globals
, while the form exec(expr, globals, locals)
is equivalent to exec expr in globals, locals
. The tuple form of exec
provides compatibility with Python 3, where exec
is a function rather than a statement.
The tuple form has not always been 100 % compatible, as there was a bug in handling of exec
in functions with nested functions (issue 21591); up to Python 2.7.8 the following code might have thrown an exception:
def func():
exec('print "hi from test2"', {})
def subfunction():
return None
This was fixed in Python 2.7.9 and it no longer throws.