Dynamically Loading a Module From a Package/Subpackage

I have seen plenty of recipes for this on the net, but nothing that would suit my needs so I made a few functions which may prove useful to others.....

But first, here's what I ideally needed. Basically I'm working on an application that stores a package qualified class name for a data handler type object in the database.

For example...

package.subpackage.module.TheClass

And at runtime, I want my data object to just return an initialized one ready to use.

[f.get_handler() for f in fields]

The imp module is what you need to do this, but it doesn't accept the package qualified class name format. So, I guess you have to load the package,subpackage(s) recursively until you can derive  the full path where the module resides.

So first, given a string "package.subpackage.module.TheClass", you need a variable for "TheClass" and "package.subpackage.module".  This is pretty simple to do with a split and some slices/joins....

 

def parse_class(module):
    '''
    separates the (module_name,class_name) from a fully qualified name
    '''
    parts = module.split(".")
    module_name =".".join(parts[:-1])
    class_name = "".join(parts[-1:])
    return (module_name,class_name)

The tricky part for me at least was then loading the package paths. The recursion logic was not so obvious to me at 6AM before being fully caffeinated. The strategy I came up with was if get_module returns an object with an attribute "__path__" then it's a package thus, call again recursively, else we have a module.

def get_module(module_name,parent_path=None):
    '''
    finds and returns module object given by module_name
    '''
    log.debug("get_module(%s,%s)" % (module_name,parent_path))
    import imp
    import sys
    result = None
 
    if not sys.modules.has_key(module_name):
 
        if not parent_path:
            for p in module_name.split("."):
                x = get_module(p,parent_path)
                if hasattr(x,"__path__"):
                    #package
                    parent_path = x.__path__
                else:
                    #module
                    return x
        else:
            log.debug((parent_path,module_name))        
            fp,pathname,description = imp.find_module(module_name,parent_path)
 
        try:
            result = imp.load_module(module_name,fp,pathname,description)
            log.debug("module %s loaded..." % module_name)
            return result
        finally:
            if fp:
                fp.close
    else:
        log.debug("module %s already loaded" % module_name)
        return sys.modules[module_name]

Once you have the module, you can load the class from it via attribute access.

def get_cls(module,clsname):
    '''
    gets class from module object
    '''
    result = None
    if module:
        log.debug("Getting cls %s from module %s" % (clsname,module.__name__))
        if hasattr(module,clsname):
            result = getattr(module,clsname)
        else:
            result = None
            log.debug("cls %s not set we\'re doomed" % clsname)
    return result

There's probably a cleaner way to do this. But it looks to be fitting the bill nicely.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd><p> <h1><h2><h3><h4><h5><h6> <img>
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>. Beside the tag style "<foo>" it is also possible to use "[foo]".
  • E-Mail addresses are hidden with reCAPTCHA Mailhide.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.