Ignore ImportError when exec source code
Question: I have an application that reads test scripts in python and sends them across
the network for execution on a remote python instance. As the controlling
program does not need to run these scripts I do not want to have all the
modules the test scripts use installed on the controller's python environment.
However the controller does need information from the test script to tell it
how to run the test. Currently what I do to read and import test script data
is something like

    
    
    with open( 'test.py', 'r' ) as f:
        source = f.read()
    
    m = types.ModuleType( "imported-temp", "Test module" )
    co = compile( source, 'test.py', 'exec' )
    
    exec co in m.__dict__
    

which yields a new module that contains the test. Unfortunately exec will
raise ImportErrors if the test tries to import something the controller does
not have. And worse, the module will not be fully imported.

If I can guarantee that the controller will not use the missing modules, is
there someway I can ignore these exceptions? Or some other way to find out the
names and classes defined in the test?

Examples test:

    
    
    from controller import testUnit
    import somethingThatTheControllerDoesNotHave
    
    _testAttr = ['fast','foo','function']
    
    class PartOne( testUnit ):
        def run( self ):
            pass
    

What the controller needs to know is the data in _testAttr and the name of all
class definitions inheriting from testUnit.


Answer: Write an import hook that catches the exception and returns a dummy module if
the module doesn't exist.

    
    
    import __builtin__
    from types import ModuleType
    
    class DummyModule(ModuleType):
        def __getattr__(self, key):
            return None
        __all__ = []   # support wildcard imports
    
    def tryimport(name, globals={}, locals={}, fromlist=[], level=-1):
        try:
            return realimport(name, globals, locals, fromlist, level)
        except ImportError:
            return DummyModule(name)
    
    realimport, __builtin__.__import__ = __builtin__.__import__, tryimport
    
    import sys   # works as usual
    import foo   # no error
    
    from bar import baz     # also no error
    from quux import *      # ditto
    

You could also write it to _always_ return a dummy module, or to return a
dummy module if the specified module hasn't already been loaded (hint: if it's
in `sys.modules`, it has already been loaded).

