# guppy Copyright (C) 2010-2011 guppy team members. # # This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. # This is free software, and you are welcome to redistribute it # under certain conditions; type `show c' for details. import os, inspect, collections pList = { } mList = collections.defaultdict(list) class NoSuchPluginError(Exception): pass def plugin(cls): #Using inspect, figure out what file is calling this function (using @plugin) clsFile = inspect.stack()[1][1] #Split the path, because it will be in the form "plugins" + os.sep + "*.py" modFile = clsFile.split(os.sep)[1] #Append the class name to the list indexed by module file name mList[modFile].append(cls.__name__.lower()) #Set class (plugin) name to the class (plugin) reference pList[cls.__name__.lower()] = cls #return cls, since decorators must return the "new" type return cls def refresh(pluginName = None): """ Refreshes the list of module/class pairs, and plugin/class pairs. If pluginName is not None, it can raise NoSuchPluginError. Regardless of the value of pluginName, it can raise any error a python file can raise, samme as using import. """ #if we are asked to do a general refresh of list if pluginName is None: #we were asked to refresh, so clear out our lists so we can start fresh pList.clear() mList.clear() #load every module in the plugins package current_dir = os.path.abspath(os.path.dirname(__file__)) _files = [] for f in os.listdir(current_dir): # ignore __init__.py files if f == '__init__.py': continue # if the file doesn't end in .py, skip if not f.endswith('.py'): continue # if the path is a directory, skip if os.path.isdir(os.path.join(current_dir, f)): continue # add the path _files.append(os.path.join(current_dir, f)) #for file (module) in the file list for f in _files: #Set "plugin" in the environment so that @plugin decorators work correctly env = {"plugin":plugin} #Execute the file. The file will automatically update the list of plugins, due to the @plugin decorator exec(compile(open(f).read(), f, 'exec'), env, env) else: #We're trying to refresh a module, so use _reload instead. found = __reload(pluginName) #if it wasn't found, try refreshing all modules and try again if not found: refresh() found = __reload(pluginName) #if it's still not found, raise an error. if not found: raise NoSuchPluginError() def __reload(pluginName): #iterate over each "file", "module" pair in mList for f,classes in mList: #if the requested if pluginName in classes: #Set "plugin" in the environment so that @plugin decorators work correctly env = {"plugin":plugin} #Execute the file. The file will automatically update the list of plugins, due to the @plugin decorator exec(compile(open(f).read(), f, 'exec'), env, env) #return True, indicating we have found and reloaded the module. return True #Else, we have not found it, so return False. return False def getPlugin(name): """ Helper function to get a plugin's class. """ try: return pList[name.lower()] except KeyError: return None refresh()