exec throws an exception...why?

N

Nick Jacobson

This works fine:

x = 1
def execfunc():
print x
execfunc()

So why doesn't this?

s = \
"""
x = 1
def execfunc():
print x
execfunc()
"""

codeobj = compile(s, "<string>", "exec")
d = {}
e = {}
exec codeobj in d, e

Error message:
Traceback (most recent call last):
File "C:\Nick\proj\python1.py", line 17, in ?
exec codeobj in d, e
File "<string>", line 6, in ?
File "<string>", line 5, in execfunc
NameError: global name 'x' is not defined

I'm using ActiveState Python 2.3.2 on Windows XP Pro. Thanks!

P.S. It does work if I say
exec codeobj in d
but I don't understand why.
 
F

fishboy

This works fine:

x = 1
def execfunc():
print x
execfunc()

So why doesn't this?

s = \
"""
x = 1
def execfunc():
print x
execfunc()
"""

codeobj = compile(s, "<string>", "exec")
d = {}
e = {}
exec codeobj in d, e

Error message:
Traceback (most recent call last):
File "C:\Nick\proj\python1.py", line 17, in ?
exec codeobj in d, e
File "<string>", line 6, in ?
File "<string>", line 5, in execfunc
NameError: global name 'x' is not defined

I'm using ActiveState Python 2.3.2 on Windows XP Pro. Thanks!

P.S. It does work if I say
exec codeobj in d
but I don't understand why.

It works because the local and global namespaces are both 'd'. it
doesn't work in the first because it puts 'x' in the local and then
looks in global.

Now, why it puts 'x' in local, I don't know. If this was a quiz, I'd
put "Nested Scope" and pray.
 
N

Nick Jacobson

fishboy said:
It works because the local and global namespaces are both 'd'. it
doesn't work in the first because it puts 'x' in the local and then
looks in global.

Now, why it puts 'x' in local, I don't know. If this was a quiz, I'd
put "Nested Scope" and pray.

I don't know either, that's why I asked ;)

And I don't see why assigning both the local and global namespaces to
the variable 'd' fixes it. But to answer that, the latter question
has to be addressed first.

--Nick
 
H

Hung Jung Lu

I don't know either, that's why I asked ;)

The name binding in assignment seems to proceed only for locals,
unless a variable is declared as global explicitly. That is, by
default, the supplied global dictionary is read-only, unless the
'global' statment is used explicitly for those variables that you want
to override. Think of this behavior as if the code defined in the s
string were executed inside another nameless function.

#--------------- try this first in console
s='''
x = 1
def f():
print 'globals', globals().keys()
print 'locals', locals().keys()
print x
print 'globals', globals().keys()
print 'locals', locals().keys()
f()
'''
d={}
e={}
a = compile(s, '<string>', 'exec')
exec a in d, e
#--------------- output
globals ['__builtins__']
locals ['x', 'f']
globals ['__builtins__']
locals []
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<string>", line 9, in ?
File "<string>", line 6, in f
NameError: global name 'x' is not defined

#--------------- now try this in console
s='''
global x
x = 1
def f():
print 'globals', globals().keys()
print 'locals', locals().keys()
print x
print 'globals', globals().keys()
print 'locals', locals().keys()
f()
'''
d={}
e={}
a = compile(s, '<string>', 'exec')
exec a in d, e
#--------------- output
globals ['__builtins__', 'x']
locals ['f']
globals ['__builtins__', 'x']
locals []

#--------------- also try this from fresh console
s='''
x = 1
def f():
print 'globals', globals().keys()
print 'locals', locals().keys()
print x
print 'globals', globals().keys()
print 'locals', locals().keys()
f()
'''
a = compile(s, '<string>', 'exec')
exec a
#--------------- output
globals ['a', 's', 'x', '__builtins__', '__name__', 'f', '__doc__']
locals ['a', 's', 'x', '__builtins__', '__name__', 'f', '__doc__']
globals ['a', 's', 'x', '__builtins__', '__name__', 'f', '__doc__']
locals []
1

-------------------------------------------
Therefore, when no dictionary is supplied, it works because it uses
the current globals() and locals() of the current scope, which in the
case of console or module level (i.e., zero indentation) are referring
to the same dictionary. The assignment 'x=1' was performed on the
locals() dictionary for writing. But inside the f() function, the
statement 'print x' pulls the value from the globals() dictionary.
This coincidence of two dictionaries only happens when you run the
code from the module level. If you put the above code inside a
function, it won't work.

#--------------- from a fresh console
def test():
s='''
x = 1
def f():
print 'globals', globals().keys()
print 'locals', locals().keys()
print x
print 'globals', globals().keys()
print 'locals', locals().keys()
f()
'''
a = compile(s, '<string>', 'exec')
exec a

test()
#--------------- output
globals ['__builtins__', '__name__', 'test', '__doc__']
locals ['a', 'x', 's', 'f']
globals ['__builtins__', '__name__', 'test', '__doc__']
locals []
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 14, in test
File "<string>", line 9, in ?
File "<string>", line 6, in f
NameError: global name 'x' is not defined

regards,

Hung Jung
 
N

Nick Jacobson

Thank you very much for the detailed response. But there's still a
problem.
The name binding in assignment seems to proceed only for locals,
unless a variable is declared as global explicitly. That is, by
default, the supplied global dictionary is read-only, unless the
'global' statment is used explicitly for those variables that you want
to override.

Agreed. The global dictionary is not modified.

But here's the thing. From the Python Ref. Manual:

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

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

def main():
y = 3
def execfunc():
print y
execfunc()
if __name__ == '__main__':
main()

But, 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()

Why not? Because it doesn't realize it's in main()! It thinks it's
on the module level! (I guess the indentation is a clue.)

Now, if it WERE simply code on the module level, it would also work:

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

because as you explained, y goes in globals(). This is probably why
the scopes don't nest at this level: they normally don't need to.

Conclusion:

Is code from the exec statement on the module level or not? It
doesn't get its locals mapped to globals. But it also doesn't get its
local variables nested. So it gets neither benefit. IMO it should
get one or the other. i.e. the second piece of code should work.

--Nick
 
H

Hung Jung Lu

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

Strictly speaking, the above statement still holds true. That is, from
a lawyer's point of view. :)
But, 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()

Very good example.
Conclusion:

Is code from the exec statement on the module level or not? It
doesn't get its locals mapped to globals. But it also doesn't get its
local variables nested. So it gets neither benefit. IMO it should
get one or the other. i.e. the second piece of code should work.

The exec statement is usually considered an slightly advanced topic.
And frankly I think people that use it at the module level would be
minority. So, having its behavior parallel to the case of nested-scope
would seem to make more sense, to me. Remember also that the "global"
statement can be used inside the code string, which is reminiscence
that we are inside a local scope, just like the case of function. So,
if you wish, the inconsistency could be considered as a bug in Python,
and it may even be good idea to submit a bug report. After all, nested
scope is considered a relative new feature, given Python's history
(over a decade.)

Or there may be some obscure way of tweaking things so to convince
Python that it is inside a function scope at the moment of "def
execfunc():"... Anyway, we are talking about voodoo magic here... :)

regards,

Hung Jung
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top