I l@ve RuBoard Previous Section Next Section

15.4 Importing from a Module Whose Name Is Determined at Runtime

Credit: Jürgen Hermann

15.4.1 Problem

You need to import a name from a module, such as from module import name, but module and name are runtime-computed expressions. This need often arises, for example when you want to support user-written plug-ins.

15.4.2 Solution

The _ _import_ _ built-in function allows this:

def importName(modulename, name):
    """ Import a named object from a module in the context of this function.
        module = _ _import_ _(modulename, globals(), locals(  ), [name])
    except ImportError:
        return None
    return vars(module)[name]

15.4.3 Discussion

This recipe's function lets you perform the equivalent of from module import name, in which either or both module and name are dynamic values (i.e., expressions or variables) rather than constant strings. For example, this can be used to implement a plug-in mechanism to extend an application with external modules that adhere to a common interface. Some programmers' instinctive reaction to this task would be to use exec, but this instinct would be a pretty bad one. The exec statement is a last-ditch measure, to be used only when nothing else is available (which is basically never). It's just too easy to have horrid bugs and/or security weaknesses where exec is used. In almost all cases, there are better ways. This recipe shows one such way for an important problem.

For example, suppose you have in the MyApp/extensions/spam.py file:

class Handler:
    def handleSomething(self):
        print "spam!"

and in the MyApp/extensions/eggs.py file:

class Handler:
    def handleSomething(self):
        print "eggs!"

then as long as the MyApp directory is on sys.path, and both it and the extensions directory are identified as packages by containing a file named _ _init_ _.py, we can get and call both implementations with this code:

for extname in 'spam', 'eggs':
    HandlerClass = importName("MyApp.extensions." + extname, "Handler")
    handler = HandlerClass(  )
    handler.handleSomething(  )

It's possible to remove the constraints about sys.path and _ _init_ _.py, and dynamically import from anywhere, with the imp standard module. However, this is substantially harder to use than the _ _import_ _ built-in function, and you can generally arrange things to avoid imp's generality and difficulty.

This pattern is used in MoinMoin (http://moin.sourceforge.net/) to load extensions implementing variations of a common interface, such as "action", "macro", and "formatter".

15.4.4 See Also

Documentation on the _ _import_ _ and vars built-ins in the Library Reference; MoinMoin is available at http://moin.sourceforge.net.

    I l@ve RuBoard Previous Section Next Section