Bug in execfile?

F

Fernando Perez

Hi all,

I'm finding the following behavior truly puzzling, but before I post a bug
report on the site, I'd rather be corrected if I'm just missing somethin
obvious.

Consider the following trivial script:

# Simple script that imports something from the stdlib
from math import sin, pi

wav = lambda k,x: sin(2*pi*k*x)

print wav(1,0.25)

# EOF


The above runs just fine from a prompt, or even interactively via execfile().
Now, consider calling it by using this instead:

#!/usr/bin/env python
"""Test for a bug (?) in scope handling by the execfile() builtin."""


def runscript(fname):
"""Run a file by calling execfile()."""
execfile(fname)


# Note: if you activate this section so that execfile() is directly called
# first, then the bug below disappears!!!
if 0:
print '='*80
print '# Trying execfile:'
execfile('execfilebugscript.py')

# The bug: we get an exception from running the little script, where the 'sin'
# name imported from math is not visible to the lambda.
print '-'*80
print '# Trying the runscript wrapper:'
runscript('execfilebugscript.py')

##### EOF


If I run the above, calling the first script 'execfilebugscript.py' and the
second 'execfilebug.py', I get this:

planck[test]> ./execfilebug.py
--------------------------------------------------------------------------------
# Trying the runscript wrapper:
Traceback (most recent call last):
File "./execfilebug.py", line 21, in <module>
runscript('execfilebugscript.py')
File "./execfilebug.py", line 7, in runscript
execfile(fname)
File "execfilebugscript.py", line 6, in <module>
print wav(1,0.25)
File "execfilebugscript.py", line 4, in <lambda>
wav = lambda k,x: sin(2*pi*k*x)
NameError: global name 'sin' is not defined


WTF??? Now even weirder, if the 'if 0' is turned into 'if 1' so that *first*
execfile is called at the top-level (not inside a function), then *both* calls
work:

planck[test]> ./execfilebug.py
================================================================================
# Trying execfile:
1.0
--------------------------------------------------------------------------------
# Trying the runscript wrapper:
1.0


I'm really, really puzzled by this. From reading the execfile() docs, I had
the hunch to change the call to:

execfile(fname,{})

and now the problem disappears, so I can keep on working.

But I'm still very bothered by the fact that changing that first call 'if 0'
to 'if 1' has any effect on the later call to runscript(). That really
doesn't feel right to me...

Any wisdom will be much appreciated.

Cheers,

f
 
P

Peter Otten

Fernando said:
I'm finding the following behavior truly puzzling, but before I post a bug
report on the site, I'd rather be corrected if I'm just missing somethin
obvious.

Consider the following trivial script:

from math import sin, pi
wav = lambda k,x: sin(2*pi*k*x)
print wav(1,0.25)
The above runs just fine from a prompt, or even interactively via
execfile(). Now, consider calling it by using this instead:

#!/usr/bin/env python
def runscript(fname):
"""Run a file by calling execfile()."""
execfile(fname)
runscript('execfilebugscript.py')
If I run the above, calling the first script 'execfilebugscript.py' and
the second 'execfilebug.py', I get this:
NameError: global name 'sin' is not defined
I'm really, really puzzled by this. From reading the execfile() docs, I
had the hunch to change the call to:

execfile(fname,{})

and now the problem disappears, so I can keep on working.


If you execfile() a script names not known in the context of a function are
looked up in the global namespace, but assignments set names in the local
namespace.

Consider execfile(filename) for a file

x = 42
def f():
print x
f()

x and f are put into the local namespace, but x is not local to the function
f and therefore looked up in f's globals.

Now if you call execfile() in the global namespace of the client script the
global and local namespace are identical
True

so x although put into the local namespace will still be found when looked
up in the global namespace. That is the same situation as in your
workaround execfile(filename, {}).

This however is not the case if you call execfile() inside a function. Here
global and local namespace differ.

In your example, when you put another execfile() into the global namespace
of the client script the sin() imported there is the only one that your
wav() function sees. You can verify that by putting

def sin(phi): return "I'm not math.sin()"

instead of

if 1: ....

into execfilebug.py.

Can the current behaviour of execfile() be changed into a more intuitive
one? Not with only two namespaces. You'd probably need something similar to
closures, and I don't think the necessary work would be worth the effort.

Peter
 

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

Members online

No members online now.

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,705
Latest member
Stefkari24

Latest Threads

Top