class PluginController(Singleton): def construct(self, eventcontroller = None, configcontroller = None): self._loaded_plugins = {} if eventcontroller: self._eventcontroller = eventcontroller else: self._eventcontroller = EventController() if configcontroller: self._config = configcontroller else: self._config = ConfigController() ## # Unload all loaded plugins def unload_all(self): for plugin in self._loaded_plugins.keys(): self.unload_plugin(plugin) ## # Reload a plugin by name # @note The plugin will be unloaded but not loaded again if it's in a non-standard path def reload_plugin(self, name, search_dir = None): try: self.unload_plugin(name) except: pass return self.load_plugin(name, search_dir) def unload_plugin(self, name): name = name.strip().lower() if not self._loaded_plugins.has_key(name): raise PluginUnloadError("No such plugin loaded") basename, instance, import_name = self._loaded_plugins[name] # Release events related to this plugin self._eventcontroller.release_related(instance) # Try to call Cleanup if it exists try: instance.cleanup() except: pass # Delete instance del instance del self._loaded_plugins[name] for module in sys.modules.keys(): if module.startswith(import_name): del sys.modules[module] return True ## # Retrieve the names of all loaded plugins def get_loaded_plugins(self): return self._loaded_plugins.keys() ## # Generator for all candidate plugins # This will search through the given directory (or the plugin_paths if not specified) # and yield all candidate plugins in the form (path, name) # # @param search_dir Optional search directory. If not specified the plugin_paths from the # configuration will be used def plugin_candidates(self, search_dir = None): if search_dir: search_dirs = [search_dir] else: search_dirs = self._config.get('plugin_paths') ignore = shutil.ignore_patterns("*.pyc", "__init__.py", ".*") for search_dir in search_dirs: for root, dirs, files in os.walk(search_dir): ignored_files = ignore(root, files) files = [f for f in files if f not in ignored_files] # Look for plugins contained in directories for directory in dirs: path = root + "." + directory yield (root, directory) # We don't want to recurse dirs[:] = [] # Replace path separators with dots to allow # it to be loaded directly root = root.replace(os.sep, ".") # Look for plugins containes as single files for filename in files: base = filename.partition(".")[0] path = root + "." + base yield (root, base) ## # Find a plugin by name # # @param name Name of the plugin to search for # @param search_dir Optional search directory. If not specified the plugin_paths from the # configuration will be used def find_plugin(self, name, search_dir = None): for path, candidate_name in self.plugin_candidates(search_dir): if candidate_name.lower() == name.lower(): module_name = path + "." + candidate_name Logger.debug("Candidate plugin '%s'" % module_name) return module_name ## # Load a specified plugin # @param name Name of plugin file (case insensitive) # @param search_dir Directory too look for plugin. If not specified, plugin_path # from the configuration will be used def load_plugin(self, name, search_dir = None): name = name.strip() import_name = None if self._loaded_plugins.has_key(name): raise PluginLoadError("Plugin is already loaded") import_name = self.find_plugin(name, search_dir) if not import_name: raise PluginLoadError("No such plugin") basename = import_name.rpartition(".")[2] try: mod = __import__(import_name) cls = getattr(mod, basename) except Exception as e: # Remove the system-entry if import_name and sys.modules.has_key(import_name): del sys.modules[import_name] Logger.log_traceback(self) raise PluginLoadError("Failed to load " + import_name) # Find the plugin entry point for objname in dir(cls): obj = getattr(cls, objname) if objname != 'Plugin' and type(obj) == types.ClassType and issubclass(obj, Plugin): Logger.debug("Plugin entry is '%s'" % objname) instance = new.instance(obj) # Initialize plugin instance instance.store = DatastoreController().get_store(basename) instance.event = self._eventcontroller instance.config = self._config instance.nets = IRCNetsController() instance.plugin = self instance.__init__() self._loaded_plugins[basename.lower()] = (basename, instance, import_name) return True del sys.modules[import_name] raise PluginLoadError("Unable to find entry point")