Why doesn't import prevent NameError in a python script run with execfile()?
Question: 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


Answer: From the [Python
documentation](http://docs.python.org/library/functions.html#execfile) 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 gamelib` imports 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 gamelib` imports 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):
    
        class ScriptObject:
        def action(self):
            import gamelib
            print("ScriptObject.action(): calling gamelib.play_sound()")
            gamelib.play_sound()
    

