While playing around with compile()
, the marshal module, and exec
. I've encountered some confusing behavior. Consider simple.py
def foo():
print "Inside foo()..."
def main():
print "This is a simple script that should count to 3."
for i in range(1, 4):
print "This is iteration number", i
foo()
if __name__ == "__main__":
main()
When I run this script using exec
like this
with open('simple.py', 'r') as f:
code = f.read()
exec code
it gives the expected output.
This is a simple script that should count to 3.
This is iteration number 1
This is iteration number 2
This is iteration number 3
Inside foo()...
However, when if I introduce compile()
, marshal.dump()
, and marshal.load()
like this
import marshal
def runme(file):
with open(file, "r") as f:
code = marshal.load(f)
exec code
with open("simple.py", "r") as f:
contents = f.read()
code = compile(contents, "simple.py", "exec")
with open("marshalled", "w") as f:
marshal.dump(code, f)
runme("marshalled")
it prints the beginning of the expected output and then errors out
This is a simple script that should count to 3.
This is iteration number 1
This is iteration number 2
This is iteration number 3
Traceback (most recent call last):
File "./exec_within_function.py", line 17, in <module>
runme("marshalled")
File "./exec_within_function.py", line 8, in runme
exec code
File "simple.py", line 15, in <module>
main()
File "simple.py", line 12, in main
foo()
NameError: global name 'foo' is not defined
Why does it say that foo
is not defined?
In order to understand, I tried using dir()
like this
import simple # imports simple.py
dir(simple)
and as expected, it shows that foo
is defined.
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'foo', 'main']
I've also noticed that when I use dis.dis()
on the deserialized code object (read via marshal.load()
), the only thing I see is the LOAD_NAME
and CALL_FUNCTION
for main()
, but when I do it with import
like this
import dis, sys
import simple
dis.dis(sys.modules["simple"])
it gives me the entire disassembly as expected.
I've even looked at some of the code that python uses for compiling and although I think import
uses some sort of lookup table for definitions, I'm not sure what the difference is with compile()
that's causing this behavior.