diff options
Diffstat (limited to 'module/HookManager.py')
-rw-r--r-- | module/HookManager.py | 285 |
1 files changed, 107 insertions, 178 deletions
diff --git a/module/HookManager.py b/module/HookManager.py index 16f692d76..0ad37b321 100644 --- a/module/HookManager.py +++ b/module/HookManager.py @@ -15,51 +15,21 @@ along with this program; if not, see <http://www.gnu.org/licenses/>. @author: RaNaN, mkaay - @interface-version: 0.1 """ import __builtin__ -import traceback +from traceback import print_exc from thread import start_new_thread from threading import RLock from types import MethodType -from module.PluginThread import HookThread +from module.threads.HookThread import HookThread from module.plugins.PluginManager import literal_eval -from utils import lock +from utils import lock, to_string class HookManager: - """Manages hooks, delegates and handles Events. - - Every plugin can define events, \ - but some very usefull events are called by the Core. - Contrary to overwriting hook methods you can use event listener, - which provides additional entry point in the control flow. - Only do very short tasks or use threads. - - **Known Events:** - Most hook methods exists as events. These are the additional known events. - - ===================== ============== ================================== - Name Arguments Description - ===================== ============== ================================== - downloadPreparing fid A download was just queued and will be prepared now. - downloadStarts fid A plugin will immediately starts the download afterwards. - linksAdded links, pid Someone just added links, you are able to modify the links. - allDownloadsProcessed Every link was handled, pyload would idle afterwards. - allDownloadsFinished Every download in queue is finished. - unrarFinished folder, fname An Unrar job finished - configChanged The config was changed via the api. - pluginConfigChanged The plugin config changed, due to api or internal process. - ===================== ============== ================================== - - | Notes: - | allDownloadsProcessed is *always* called before allDownloadsFinished. - | configChanged is *always* called before pluginConfigChanged. - - - """ + """ Manages hooks, loading, unloading. """ def __init__(self, core): self.core = core @@ -68,34 +38,36 @@ class HookManager: __builtin__.hookManager = self #needed to let hooks register themself self.log = self.core.log - self.plugins = [] - self.pluginMap = {} - self.methods = {} #dict of names and list of methods usable by rpc - - self.events = {} # contains events - - #registering callback for config event - self.config.pluginCB = MethodType(self.dispatchEvent, "pluginConfigChanged", basestring) - - self.addEvent("pluginConfigChanged", self.manageHooks) + self.plugins = {} + self.methods = {} # dict of names and list of methods usable by rpc + self.events = {} # Contains event that will be registred self.lock = RLock() self.createIndex() - def try_catch(func): - def new(*args): - try: - return func(*args) - except Exception, e: - args[0].log.error(_("Error executing hooks: %s") % str(e)) - if args[0].core.debug: - traceback.print_exc() + #registering callback for config event + self.config.changeCB = MethodType(self.dispatchEvent, "configChanged", basestring) - return new + # manage hooks an config change + self.addEvent("configChanged", self.manageHooks) + @lock + def callInHooks(self, event, *args): + """ Calls a method in all hooks and catch / log errors""" + for plugin in self.plugins.itervalues(): + self.call(plugin, event, *args) + self.dispatchEvent(event, *args) + + def call(self, hook, f, *args): + try: + func = getattr(hook, f) + return func(*args) + except Exception, e: + hook.logError(_("Error when executing %s" % f), e) + if self.core.debug: + print_exc() def addRPC(self, plugin, func, doc): - plugin = plugin.rpartition(".")[2] doc = doc.strip() if doc else "" if plugin in self.methods: @@ -103,33 +75,30 @@ class HookManager: else: self.methods[plugin] = {func: doc} - def callRPC(self, plugin, func, args, parse): - if not args: args = tuple() - if parse: - args = tuple([literal_eval(x) for x in args]) + def callRPC(self, plugin, func, args): + if not args: args = [] + else: + args = literal_eval(args) - plugin = self.pluginMap[plugin] + plugin = self.plugins[plugin] f = getattr(plugin, func) return f(*args) - + @lock def createIndex(self): - plugins = [] - active = [] deactive = [] - for pluginname in self.core.pluginManager.hookPlugins: + for pluginname in self.core.pluginManager.getPlugins("hooks"): try: #hookClass = getattr(plugin, plugin.__name__) - if self.core.config.getPlugin(pluginname, "activated"): + if self.core.config.get(pluginname, "activated"): pluginClass = self.core.pluginManager.loadClass("hooks", pluginname) if not pluginClass: continue - + plugin = pluginClass(self.core, self) - plugins.append(plugin) - self.pluginMap[pluginClass.__name__] = plugin + self.plugins[pluginClass.__name__] = plugin if plugin.isActivated(): active.append(pluginClass.__name__) else: @@ -139,25 +108,26 @@ class HookManager: except: self.log.warning(_("Failed activating %(name)s") % {"name": pluginname}) if self.core.debug: - traceback.print_exc() + print_exc() self.log.info(_("Activated plugins: %s") % ", ".join(sorted(active))) self.log.info(_("Deactivate plugins: %s") % ", ".join(sorted(deactive))) - self.plugins = plugins - def manageHooks(self, plugin, name, value): + # check if section was a plugin + if plugin not in self.core.pluginManager.getPlugins("hooks"): + return + if name == "activated" and value: self.activateHook(plugin) elif name == "activated" and not value: self.deactivateHook(plugin) + @lock def activateHook(self, plugin): - #check if already loaded - for inst in self.plugins: - if inst.__name__ == plugin: - return + if plugin in self.plugins: + return pluginClass = self.core.pluginManager.loadClass("hooks", plugin) @@ -166,150 +136,109 @@ class HookManager: self.log.debug("Plugin loaded: %s" % plugin) plugin = pluginClass(self.core, self) - self.plugins.append(plugin) - self.pluginMap[pluginClass.__name__] = plugin + self.plugins[pluginClass.__name__] = plugin - # call core Ready - start_new_thread(plugin.coreReady, tuple()) + # active the hook in new thread + start_new_thread(plugin.activate, tuple()) + self.registerEvents() + @lock def deactivateHook(self, plugin): + if plugin not in self.plugins: + return + else: + hook = self.plugins[plugin] - hook = None - for inst in self.plugins: - if inst.__name__ == plugin: - hook = inst - - if not hook: return - - self.log.debug("Plugin unloaded: %s" % plugin) - - hook.unload() + self.call(hook, "deactivate") + self.log.debug("Plugin deactivated: %s" % plugin) #remove periodic call self.log.debug("Removed callback %s" % self.core.scheduler.removeJob(hook.cb)) - self.plugins.remove(hook) - del self.pluginMap[hook.__name__] + del self.plugins[hook.__name__] + #remove event listener + for f in dir(hook): + if f.startswith("__") or type(getattr(hook, f)) != MethodType: + continue + self.core.eventManager.removeFromEvents(getattr(hook, f)) - @try_catch - def coreReady(self): - for plugin in self.plugins: + def activateHooks(self): + self.log.info(_("Activating Plugins...")) + for plugin in self.plugins.itervalues(): if plugin.isActivated(): - plugin.coreReady() - - self.dispatchEvent("coreReady") + self.call(plugin, "activate") - @try_catch - def coreExiting(self): - for plugin in self.plugins: - if plugin.isActivated(): - plugin.coreExiting() + self.registerEvents() - self.dispatchEvent("coreExiting") + def deactivateHooks(self): + """ Called when core is shutting down """ + self.log.info(_("Deactivating Plugins...")) + for plugin in self.plugins.itervalues(): + self.call(plugin, "deactivate") - @lock def downloadPreparing(self, pyfile): - for plugin in self.plugins: - if plugin.isActivated(): - plugin.downloadPreparing(pyfile) - - self.dispatchEvent("downloadPreparing", pyfile) + self.callInHooks("downloadPreparing", pyfile) - @lock def downloadFinished(self, pyfile): - for plugin in self.plugins: - if plugin.isActivated(): - if "downloadFinished" in plugin.__threaded__: - self.startThread(plugin.downloadFinished, pyfile) - else: - plugin.downloadFinished(pyfile) + self.callInHooks("downloadFinished", pyfile) - self.dispatchEvent("downloadFinished", pyfile) - - @lock - @try_catch def downloadFailed(self, pyfile): - for plugin in self.plugins: - if plugin.isActivated(): - if "downloadFailed" in plugin.__threaded__: - self.startThread(plugin.downloadFinished, pyfile) - else: - plugin.downloadFailed(pyfile) + self.callInHooks("downloadFailed", pyfile) - self.dispatchEvent("downloadFailed", pyfile) - - @lock def packageFinished(self, package): - for plugin in self.plugins: - if plugin.isActivated(): - if "packageFinished" in plugin.__threaded__: - self.startThread(plugin.packageFinished, package) - else: - plugin.packageFinished(package) - - self.dispatchEvent("packageFinished", package) + self.callInHooks("packageFinished", package) - @lock def beforeReconnecting(self, ip): - for plugin in self.plugins: - plugin.beforeReconnecting(ip) - - self.dispatchEvent("beforeReconnecting", ip) + self.callInHooks("beforeReconnecting", ip) - @lock def afterReconnecting(self, ip): - for plugin in self.plugins: - if plugin.isActivated(): - plugin.afterReconnecting(ip) - - self.dispatchEvent("afterReconnecting", ip) + self.callInHooks("afterReconnecting", ip) + @lock def startThread(self, function, *args, **kwargs): - t = HookThread(self.core.threadManager, function, args, kwargs) + HookThread(self.core.threadManager, function, args, kwargs) def activePlugins(self): """ returns all active plugins """ - return [x for x in self.plugins if x.isActivated()] + return [x for x in self.plugins.itervalues() if x.isActivated()] def getAllInfo(self): """returns info stored by hook plugins""" info = {} - for name, plugin in self.pluginMap.iteritems(): + for name, plugin in self.plugins.iteritems(): if plugin.info: #copy and convert so str - info[name] = dict([(x, str(y) if not isinstance(y, basestring) else y) for x, y in plugin.info.iteritems()]) + info[name] = dict( + [(x, to_string(y)) for x, y in plugin.info.iteritems()]) return info - def getInfo(self, plugin): info = {} - if plugin in self.pluginMap and self.pluginMap[plugin].info: - info = dict([(x, str(y) if not isinstance(y, basestring) else y) - for x, y in self.pluginMap[plugin].info.iteritems()]) + if plugin in self.plugins and self.plugins[plugin].info: + info = dict([(x, to_string(y)) + for x, y in self.plugins[plugin].info.iteritems()]) return info - def addEvent(self, event, func): - """Adds an event listener for event name""" - if event in self.events: - self.events[event].append(func) - else: - self.events[event] = [func] - - def removeEvent(self, event, func): - """removes previously added event listener""" - if event in self.events: - self.events[event].remove(func) - - def dispatchEvent(self, event, *args): - """dispatches event with args""" - if event in self.events: - for f in self.events[event]: - try: - f(*args) - except Exception, e: - self.log.warning("Error calling event handler %s: %s, %s, %s" - % (event, f, args, str(e))) - if self.core.debug: - traceback.print_exc() + def addEventListener(self, plugin, func, event): + if plugin not in self.events: + self.events[plugin] = [] + self.events[plugin].append((func, event)) + + def registerEvents(self): + for name, plugin in self.plugins.iteritems(): + if name in self.events: + for func, event in self.events[name]: + self.addEvent(event, getattr(plugin, func)) + # clean up + del self.events[name] + + def addConfigHandler(self, plugin, func): + pass #TODO + + def addEvent(self, *args): + self.core.eventManager.addEvent(*args) + + def dispatchEvent(self, *args): + self.core.eventManager.dispatchEvent(*args) |