classification
Title: An inconsistency with nested scopes
Type: behavior Stage:
Components: Interpreter Core Versions: Python 2.6
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: jhylton Nosy List: ajaksu2, arigo, georg.brandl, jhylton, josiahcarlson, nickjacobson (6)
Priority: normal Keywords

Created on 2004-07-14 21:30 by nickjacobson, last changed 2009-03-31 15:56 by jhylton.

Files
File name Uploaded Description Edit Remove
python1.py nickjacobson, 2004-07-14 21:30 Nested scope of y fails, and it shouldn't.
Messages (6)
msg21611 - (view) Author: Nick Jacobson (nickjacobson) Date: 2004-07-14 21:30
There's an inconsistency with nested scopes.

From the Python Ref. Manual:

"If [a local variable] definition occurs in a function block, 
the scope extends to any blocks contained within the 
defining one..."

i.e. So as long as code is not on the module level, 
scopes are extended.  Therefore this works:

def main():
	y = 3
	def execfunc():
		print y
	execfunc()

if __name__ == '__main__':
	main()

In addition, if code IS on the module level, its variables 
go in globals().  So this works too:

y = 3
def execfunc():
	print y
execfunc()

However, (here's the inconsistency) the following code 
fails, saying that y is undefined:

def main():
	s = \
"""
y = 3
def execfunc():
	print y
execfunc()
"""
	d = {}
	e = {}
	exec s in d, e

if __name__ == '__main__':
	main()

In this case, the code in s is treated like it's on the 
module level, and the nested scope treatment of y 
doesn't occur.  BUT, unlike normal code on the module 
level, y doesn't go in globals().  I think globals() is 
locked or something?


Conclusion:

The latter piece of code should work; that is, y should 
be nested and therefore recognized by execfunc().
msg21612 - (view) Author: Josiah Carlson (josiahcarlson) Date: 2004-07-22 16:55
Logged In: YES 
user_id=341410

>>> def f():
...     y = 5
...     print 'y' in globals(), 'y' in locals()
...     def i():
...         print 'y' in globals(), 'y' in locals()
...     i()
...
>>> f()
False True
False False
>>>
>>> def g():
...     gl = {};lo={}
...     exec '''y = 5
... print 'y' in globals(), 'y' in locals()
... def i():
...     print 'y' in globals(), 'y' in locals()
... i()
... ''' in gl, lo
...
>>> g()
False True
False False

That looks constant...but what if we print out 'y'?

>>> def s():
...     y = 5
...     print 'y' in globals(), 'y' in locals(), y
...     def i():
...         print 'y' in globals(), 'y' in locals(), y
...     i()
...
>>> s()
False True 5
False True 5
>>>
>>> def t():
...     gl = {};lo = {}
...     exec '''y = 5
... print 'y' in globals(), 'y' in locals(), y
... def i():
...     print 'y' in globals(), 'y' in locals(), y
... i()
... ''' in gl, lo
...
>>> t()
False True 5
False False
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 3, in t
  File "<string>", line 5, in ?
  File "<string>", line 4, in i
NameError: global name 'y' is not defined

Now why did 'y' stick itself into the locals() of i() in
s()?  Is this another bug?

What if we remove the namespaces gl and lo?

>>> def u():
...     exec '''y = 5
... print 'y' in globals(), 'y' in locals(), y
... def i():
...     print 'y' in globals(), 'y' in locals(), y
... i()
... '''
...
>>> u()
False True 5
False False
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 2, in u
  File "<string>", line 5, in ?
  File "<string>", line 4, in i
NameError: global name 'y' is not defined

Nope, still dies.  It does seem to be a bug in exec.  I'm
still curious why 'y' was placed into i()'s local namespace
in s().
msg21613 - (view) Author: Armin Rigo (arigo) Date: 2004-08-07 21:59
Logged In: YES 
user_id=4771

The behavior you get can be explained quite easy, but it seems nevertheless inconsistent with the documentation: in my opinion it is a serious bug.

The reason the "exec"ed code doesn't work like the same code put at the global module level is that code that runs at the module level always runs with the same dictionary for its globals and locals, whereas in your example you use two different dictionaries.

Assignments always go to the locals; that's why 'y' goes into the dictionary 'e'.  Now a function can only see its own locals and the surrounding globals; that's why execfunc() misses the value of 'y'.  This is the old way Python worked.  In recent versions, a special trick was added so that functions defined inside another function find variable bindings from the enclosing function.  I think you found a case where this trick fails to apply.
msg21614 - (view) Author: Georg Brandl (georg.brandl) Date: 2006-02-20 22:06
Logged In: YES 
user_id=849994

Closed #1167300 as a duplicate.
msg82038 - (view) Author: Daniel Diniz (ajaksu2) Date: 2009-02-14 12:47
Confirmed in trunk,
msg84813 - (view) Author: Jeremy Hylton (jhylton) Date: 2009-03-31 15:56
This code behaves as intended.  The module-level execution environment
is different than other environments.  The global scope and local scope
are the same dictionary.  Assignments at the top-level become globals
because of this behavior of the execution environment.  If you want exec
to mimic the top-level environment, you need to pass it a single dictionary.
History
Date User Action Args
2009-03-31 15:56:24jhyltonsetstatus: open -> closed
resolution: wont fix
messages: + msg84813
2009-03-31 14:37:13jhyltonsetassignee: jhylton

nosy: + jhylton
2009-02-14 12:47:44ajaksu2setnosy: + ajaksu2
type: behavior
messages: + msg82038
versions: + Python 2.6, - Python 2.3
2004-07-14 21:30:23nickjacobsoncreate