summaryrefslogtreecommitdiffstats
path: root/module/HookManager.py
diff options
context:
space:
mode:
Diffstat (limited to 'module/HookManager.py')
-rw-r--r--module/HookManager.py285
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)