Source code for utils.findmodels

# -*- coding: utf-8 -*-
# utils/modelfinder.py

"""
For use in gui/modelwidget.py, to help find valid calculation models
"""

import logging
import inspect
import os
import sys
import imp
from collections import OrderedDict
from bases.model import ScatteringModel
from main import makeAbsolutePath
from utils import isList, classproperty

def _getValidClasses(mod, isTopLevel = True):
    if not inspect.ismodule(mod):
        return []
    try:
        if not isTopLevel and not mod.__isModelGroup__:
            return []
    except AttributeError:
        return []
    validClasses = []
    for attrName in dir(mod):
        attr = getattr(mod, attrName)
        if inspect.ismodule(attr):
            validClasses += _getValidClasses(attr, isTopLevel = False)
            continue
        if not inspect.isclass(attr):
            continue
        try: # ignore stuff defined elsewhere, imported into this module
            if attr.__module__ != mod.__name__:
                continue
        except AttributeError:
            continue
        if not issubclass(attr, ScatteringModel):
            continue # focus on scattering models
        if attr.name() is None or "dummy" in attrName.lower():
            continue # ignore dummy models, e.g. from testing code
        validClasses.append(attr)
    return validClasses

[docs]def findFiles(searchPath, extension): """ generator for files ending in .py code from: http://stackoverflow.com/questions/2186525 """ return [ (searchPath, os.path.join(dirpath, filename)[len(searchPath)+1:]) for dirpath, dirnames, filenames in os.walk(searchPath) for filename in filenames if filename.endswith(extension)]
[docs]def reorder(indata, priorityKeys): def getMatchingKeys(inmap, partialKey): return [key for key in inmap.keys() if partialKey in key] outdata = OrderedDict() # copy over the entries with priority keys for name in priorityKeys: keys = getMatchingKeys(indata, name) if not len(keys): continue keys.sort(key = lambda k: len(k)) # get the shortest key key = keys[0] outdata[key] = indata.pop(key) # finally get the remaining entries outdata.update(indata) return outdata
[docs]class FindModels(object): """ Finds all methods of type ScatteringModel in the subdirectories starting from searchPath. searchPath defaults to the root mcsas pwd + "models". returns a list of full paths, and a list of associated model names """ _modelFiles = {} _priorityModels = ( # ordered list of default models appear first "Sphere", "LMADenseSphere", "EllipsoidsIsotropic", "CylindersIsotropic", "SphericalCoreShell", "EllipsoidalCoreShell", "GaussianChain", "Kholodenko" ) _rootName = "models" _models = None @classproperty @classmethod
[docs] def rootName(cls): return cls._rootName
@classmethod
[docs] def libraryPath(cls): return os.path.dirname(os.path.dirname(__file__))
@classmethod
[docs] def getSearchPaths(cls): # base all relative paths on directory of main.py return list(set(( makeAbsolutePath(cls.rootName), os.path.join(cls.libraryPath(), cls.rootName))))
@classmethod
[docs] def candidateFiles(cls, *searchPaths): if not len(searchPaths): searchPaths = cls.getSearchPaths() logging.info("Using paths for model finding:") for searchPath in searchPaths: logging.info(" '{}'".format(searchPath)) for searchPath in searchPaths: for cand in findFiles(searchPath, '.py'): yield cand
def __init__(self, *searchPaths): self._models = OrderedDict() for path, relFile in self.candidateFiles(): if os.path.split(relFile)[-1].startswith("__init__"): continue filepath = os.path.join(path, relFile) validModelClasses = self._getModelClasses(path, relFile) if not isList(validModelClasses): continue for cls in validModelClasses: # get full class hierachy name, use that as key # there may be multiple models per file/module key = '.'.join((cls.__module__, cls.__name__)) self._models[key] = (cls, filepath) self._models = reorder(self._models, self._priorityModels) logging.debug("Ordered model files: {}".format(self._models)) def _getTailDirs(self, rel): """Returns the directory names following the *searchPath* base path as reversed list. Returns an empty list if the given *dirpath* equals the search path. """ if not len(rel) or rel == ".": return [] lst = [] while len(rel): rel, tail = os.path.split(rel) lst.append(tail) return reversed(lst) def _getModelClasses(self, searchPath, relFilePath): """extract the model class name from the module file """ relPath, filename = os.path.split(relFilePath) moduleName, ext = os.path.splitext(filename) if "scatteringmodel" in moduleName: # do not attempt to reload ScatteringModel class # otherwise issubclass() checks will fail (other obj ID) return [] modName = self.rootName # base name for all models full package name # treat each sub dir as module, import if necessary for name in self._getTailDirs(relPath): modName += "." + name if modName not in sys.modules: sys.modules[modName] = imp.new_module(modName) moduleName = modName + "." + moduleName dirpath = [ os.path.dirname(searchPath) ] mod = None for modName in moduleName.split('.'): try: result = imp.find_module(modName, dirpath) except ImportError: logging.warning("Could not load '{}' from {}! " .format(modName, dirpath) + "__init__.py missing?") # raise # for debugging, comment for proper operation return [] if mod is not None: modName = '.'.join((mod.__name__, modName)) # loads the file as standalone module subMod = imp.load_module(modName, *result) if mod is not None: subMod.__package__ = mod.__name__ if hasattr(subMod, "__path__"): dirpath = subMod.__path__ mod = subMod return _getValidClasses(mod) def __len__(self): return len(self._models) def __getitem__(self, key): return self._models[key] def __iter__(self): return iter(self._models.keys())
# vim: set ts=4 sts=4 sw=4 tw=0: