[Python-Dev] variable name resolution in exec is incorrect

Mark Dickinson dickinsm at gmail.com
Wed May 26 11:48:51 CEST 2010


On Wed, May 26, 2010 at 10:15 AM, Colin H <hawkett at gmail.com> wrote:
>   issue991196 was closed being described as intentional.  I've added
> a comment in that issue which argues that this is a serious bug (also
> aserted by a previous commenter - Armin Rigo), because it creates a
> unique, undocumented, oddly behaving scope that doesn't apply closures
> correctly. At the very least I think this should be acknowledged as a
> plain old bug (rather than a feature), and then a discussion about
> whether it will be fixed or not.

Here's a quick recap of the issue so that people don't have to go
searching through the bug archive.  In Python 2.x, we get the
following behaviour:

>>> code = """\
... y = 3
... def f():
...     return y
... f()
... """
>>> exec code in {}   # works fine
>>> exec code in {}, {}   # dies with a NameError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 4, in <module>
  File "<string>", line 3, in f
NameError: global name 'y' is not defined

The issue is whether the second example should work, given that two
different dictionaries have been passed.

The cause of the NameError can be seen by looking at the bytecode: y
is bound using STORE_NAME, which stores y into the locals dictionary
(which here is *not* the same as the globals dictionary) but the
attempt to retrieve the value of y uses LOAD_GLOBAL, which only looks
in the globals.

>>> co = compile(code, 'mycode', 'exec')
>>> dis.dis(co)
  1           0 LOAD_CONST               0 (3)
              3 STORE_NAME               0 (y)

  2           6 LOAD_CONST               1 (<code object f at
0xa22b40, file "mycode", line 2>)
              9 MAKE_FUNCTION            0
             12 STORE_NAME               1 (f)

  4          15 LOAD_NAME                1 (f)
             18 CALL_FUNCTION            0
             21 POP_TOP
             22 LOAD_CONST               2 (None)
             25 RETURN_VALUE
>>> dis.dis(co.co_consts[1])  # disassembly of 'f'
  3           0 LOAD_GLOBAL              0 (y)
              3 RETURN_VALUE

This is a long way from my area of expertise (I'm commenting here
because it was me who sent Colin here in the first place), and it's
not clear to me whether this is a bug, and if it is a bug, how it
could be resolved.  What would the impact be of having the compiler
produce 'LOAD_NAME' rather than 'LOAD_GLOBAL' here?

Mark


More information about the Python-Dev mailing list