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