I looked at a number of existing questions about NameError exceptions when scripts are run with exec statements or execfile() in Python, but haven’t found a good explanation yet of the following behavior.
I want to make a simple game that creates script objects at runtime with execfile(). Below are 4 modules that demonstrate the problem (please bear with me, this is as simple as I could make it!). The main program just loads a script using execfile() and then calls a script manager to run the script objects:
# game.py
import script_mgr
import gamelib # must be imported here to prevent NameError, any place else has no effect
def main():
execfile("script.py")
script_mgr.run()
main()
The script file just creates an object that plays a sound and then adds the object to a list in the script manager:
script.py
import script_mgr
#import gamelib # (has no effect here)
class ScriptObject:
def action(self):
print("ScriptObject.action(): calling gamelib.play_sound()")
gamelib.play_sound()
obj = ScriptObject()
script_mgr.add_script_object(obj)
The script manager just calls the action() function of each script:
# script_mgr.py
#import gamelib # (has no effect here)
script_objects = []
def add_script_object(obj):
script_objects.append(obj)
def run():
for obj in script_objects:
obj.action()
The gamelib function is defined in a fourth module, which is the troublesome one to be accessed:
# gamelib.py
def play_sound():
print("boom!")
The above code works with the following output:
mhack:exec $ python game.py ScriptObject.action(): calling gamelib.play_sound() boom! mhack:exec $
However, if I comment-out the ‘import gamelib’ statement in game.py and uncomment the ‘import gamelib’ in script.py, I get the following error:
mhack:exec $ python game.py
ScriptObject.action(): calling gamelib.play_sound()
Traceback (most recent call last):
File "game.py", line 10, in
main()
File "game.py", line 8, in main
script_mgr.run()
File "/Users/williamknight/proj/test/python/exec/script_mgr.py", line 12, in run
obj.action()
File "script.py", line 9, in action
gamelib.play_sound()
NameError: global name 'gamelib' is not defined
My question is: 1) Why is the import needed in the ‘game.py’ module, the one that execs the script? 2) Why doesn’t it work to import ‘gamelib’ from the module where it is referenced (script.py) or the module where it is called (script_mgr.py)?
This happens on Python 2.5.1
From the Python documentation for execfile:
execfile(filename[, globals[, locals]])
If the locals dictionary is omitted it defaults to the globals dictionary. If both dictionaries are omitted, the expression is executed in the environment where execfile() is called.
There are two optional arguments for execfile. Since you omit them both, your script is being executed in the environment where execfile is called. Hence the reason the import in game.py changes the behaviour.
In addition, I concluded the following behaviour of import in game.py and script.py:
In game.py
import gamelibimports the gamelib module into both globals and locals. This is the environment passed to script.py which is why gamelib is accessible in the ScriptObject action method (accessed from globals).In script.py
import gamelibimports the gamelib module into locals only (not sure of the reason). So when trying to access gamelib from the ScriptObject action method from globals you have the NameError. It will work if you move the import into the scope of the action method as follows (gamelib will be accessed from locals):