
problem with exec: import doesn't seem to work
Quote:
> I am loading a script into memory, making some substitutions, and then
> execing it. When I do, I get the error
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> File "UseCase.py", line 27, in __init__
> exec( NewProgram )
> File "<string>", line 177, in ?
> File "<string>", line 157, in main
> NameError: There is no variable named 'sys'
In other words: there is no 'sys' in either the local or
global namespace that are being searched at that point.
Quote:
> If I write the string NewProgram out to disk before I exec it, it contains
> exactly what I expect, including "import sys" as one of the first
> uncommented lines.
Yes, sys will be included in the current namespace 'X' where that
string is executed.
Quote:
> If I run the program from the file I saved, it works fine. If I load it
> into a string (manually in the interpreter), and exec it, it works!
Consider what the "current namespace 'X'" is in either of these cases...
Quote:
> However, it seems to not be able to resolve the namespace sys when exec'd
> from my class.
Or, to be precise: "when exec'd in a local namespace". I.e.: when
the 'X' is a _local_ namespace...
Quote:
> I've reduced the problem to its core. Consider the file simple.py (also
> attached):
> ------------ simple.py
> #!/usr/bin/env python
> import sys
> def goo():
> print sys.argv
> print sys.argv
> goo()
> ------------ simple.py
So far, so, indeed, simple.
Quote:
> If I read this in from the global space, it works:
> ---- ex
> >>> code = open( 'simple.py', 'r' ).read()
> >>> exec( code )
> ['']
> ['']
> ---- ex
Here, the 'X' is the global namespace. The 'import' sets 'sys'
in the global namespace that 'goo' will use...
Quote:
> However, if I do it in a function, it fails.
> ---- ex
> >>> def foo():
> ... code = open( 'simple.py', 'r' ).read()
> ... exec( code )
> ...
> >>> foo()
> ['']
...but, here, the 'import' is in a LOCAL namespace -- that of
'foo'. And 'goo' does NOT search other local namespaces,
apart from its own. Thus, it can't find 'sys'!
Quote:
> I don't see how this can't be a bug. Any help is appreciated.
It is, indeed, a bug -- specifically, a bug in your
code as it stands.
You can reproduce the issue without any 'exec'...:
def foo():
import sys
def goo():
print sys.argv
print sys.argv
goo()
this will fail in exactly the same way, while the very
same statement suite in global scope would succeed --
because when this suite is in local scope, the import sys
only sets a sys entry in that LOCAL scope, and nested
function goo does NOT search any local scope except its
own; when the suite is in global scope, the import sys
sets a sys entry in that GLOBAL scope, and goo DOES
search the global scope.
Among the various remedies, the most classic and idiomatic
is to change the def statement for goo to
def goo(sys=sys):
thus explicitly 'priming' the sys entry in goo's local
namespace so that it refers to the same thing inside
goo as 'sys' referred to at the point of goo's definition.
Given the 'exec' statement, you also have alternatives,
e.g:
def foo():
code = open('simple.py', 'r').read()
exec code in locals()
the 'in locals()' clause of the exec statement tells it
to use the same dictionary ('locals()') for both local
and global namespaces, so it fixed things again. As
it happens,
def foo():
code = open('simple.py', 'r').read()
exec code in {}
will work just as well (and not 'dirty' foo's local
namespace...).
Alex