summaryrefslogtreecommitdiffstats
path: root/module/AddonManager.py
diff options
context:
space:
mode:
authorGravatar RaNaN <Mast3rRaNaN@hotmail.de> 2012-09-18 17:59:50 +0200
committerGravatar RaNaN <Mast3rRaNaN@hotmail.de> 2012-09-18 17:59:50 +0200
commit6130a2377ca6754fee88773097ce220abef1aa47 (patch)
tree76bea0d76393100fcf393c164c96d34f286aba7a /module/AddonManager.py
parentAdded DuckcryptInfo decrypter, smaller fixes (diff)
parentdropdowns in navbar (diff)
downloadpyload-6130a2377ca6754fee88773097ce220abef1aa47.tar.xz
merged stable into default
Diffstat (limited to 'module/AddonManager.py')
-rw-r--r--module/AddonManager.py257
1 files changed, 257 insertions, 0 deletions
diff --git a/module/AddonManager.py b/module/AddonManager.py
new file mode 100644
index 000000000..4283b4652
--- /dev/null
+++ b/module/AddonManager.py
@@ -0,0 +1,257 @@
+# -*- coding: utf-8 -*-
+
+"""
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+ @author: RaNaN, mkaay
+"""
+import __builtin__
+
+from traceback import print_exc
+from thread import start_new_thread
+from threading import RLock
+
+from types import MethodType
+
+from module.threads.AddonThread import AddonThread
+from module.PluginManager import literal_eval
+from utils import lock, to_string
+
+class AddonManager:
+ """ Manages addons, loading, unloading. """
+
+ def __init__(self, core):
+ self.core = core
+ self.config = self.core.config
+
+ __builtin__.addonManager = self #needed to let addons register themselves
+
+ self.log = self.core.log
+ self.plugins = {}
+ self.methods = {} # dict of names and list of methods usable by rpc
+ self.events = {} # Contains event that will be registered
+
+ self.lock = RLock()
+ self.createIndex()
+
+ #registering callback for config event
+ self.config.changeCB = MethodType(self.dispatchEvent, "configChanged", basestring)
+
+ # manage addons an config change
+ self.addEvent("configChanged", self.manageAddons)
+
+ @lock
+ def callInHooks(self, event, *args):
+ """ Calls a method in all addons and catch / log errors"""
+ for plugin in self.plugins.itervalues():
+ self.call(plugin, event, *args)
+ self.dispatchEvent(event, *args)
+
+ def call(self, addon, f, *args):
+ try:
+ func = getattr(addon, f)
+ return func(*args)
+ except Exception, e:
+ addon.logError(_("Error when executing %s" % f), e)
+ if self.core.debug:
+ print_exc()
+
+ def addRPC(self, plugin, func, doc):
+ doc = doc.strip() if doc else ""
+
+ if plugin in self.methods:
+ self.methods[plugin][func] = doc
+ else:
+ self.methods[plugin] = {func: doc}
+
+ def callRPC(self, plugin, func, args):
+ if not args: args = []
+ else:
+ args = literal_eval(args)
+
+ plugin = self.plugins[plugin]
+ f = getattr(plugin, func)
+ return f(*args)
+
+ @lock
+ def createIndex(self):
+ active = []
+ deactive = []
+
+ for pluginname in self.core.pluginManager.getPlugins("addons"):
+ try:
+ # check first for builtin plugin
+ attrs = self.core.pluginManager.loadAttributes("addons", pluginname)
+ internal = attrs.get("internal", False)
+
+ if internal or self.core.config.get(pluginname, "activated"):
+ pluginClass = self.core.pluginManager.loadClass("addons", pluginname)
+
+ if not pluginClass: continue
+
+ plugin = pluginClass(self.core, self)
+ self.plugins[pluginClass.__name__] = plugin
+
+ # hide internals from printing
+ if not internal and plugin.isActivated():
+ active.append(pluginClass.__name__)
+ else:
+ self.log.debug("Loaded internal plugin: %s" % pluginClass.__name__)
+ else:
+ deactive.append(pluginname)
+
+
+ except:
+ self.log.warning(_("Failed activating %(name)s") % {"name": pluginname})
+ if self.core.debug:
+ print_exc()
+
+ self.log.info(_("Activated addons: %s") % ", ".join(sorted(active)))
+ self.log.info(_("Deactivate addons: %s") % ", ".join(sorted(deactive)))
+
+ def manageAddons(self, plugin, name, value):
+ # check if section was a plugin
+ if plugin not in self.core.pluginManager.getPlugins("addons"):
+ return
+
+ if name == "activated" and value:
+ self.activateAddon(plugin)
+ elif name == "activated" and not value:
+ self.deactivateAddon(plugin)
+
+ @lock
+ def activateAddon(self, plugin):
+ #check if already loaded
+ if plugin in self.plugins:
+ return
+
+ pluginClass = self.core.pluginManager.loadClass("addons", plugin)
+
+ if not pluginClass: return
+
+ self.log.debug("Plugin loaded: %s" % plugin)
+
+ plugin = pluginClass(self.core, self)
+ self.plugins[pluginClass.__name__] = plugin
+
+ # active the addon in new thread
+ start_new_thread(plugin.activate, tuple())
+ self.registerEvents() # TODO: BUG: events will be destroyed and not re-registered
+
+ @lock
+ def deactivateAddon(self, plugin):
+ if plugin not in self.plugins:
+ return
+ else:
+ addon = self.plugins[plugin]
+
+ if addon.__internal__: return
+
+ self.call(addon, "deactivate")
+ self.log.debug("Plugin deactivated: %s" % plugin)
+
+ #remove periodic call
+ self.log.debug("Removed callback %s" % self.core.scheduler.removeJob(addon.cb))
+ del self.plugins[addon.__name__]
+
+ #remove event listener
+ for f in dir(addon):
+ if f.startswith("__") or type(getattr(addon, f)) != MethodType:
+ continue
+ self.core.eventManager.removeFromEvents(getattr(addon, f))
+
+ def activateAddons(self):
+ self.log.info(_("Activating Plugins..."))
+ for plugin in self.plugins.itervalues():
+ if plugin.isActivated():
+ self.call(plugin, "activate")
+
+ self.registerEvents()
+
+ def deactivateAddons(self):
+ """ Called when core is shutting down """
+ self.log.info(_("Deactivating Plugins..."))
+ for plugin in self.plugins.itervalues():
+ self.call(plugin, "deactivate")
+
+ def downloadPreparing(self, pyfile):
+ self.callInHooks("downloadPreparing", pyfile)
+
+ def downloadFinished(self, pyfile):
+ self.callInHooks("downloadFinished", pyfile)
+
+ def downloadFailed(self, pyfile):
+ self.callInHooks("downloadFailed", pyfile)
+
+ def packageFinished(self, package):
+ self.callInHooks("packageFinished", package)
+
+ def beforeReconnecting(self, ip):
+ self.callInHooks("beforeReconnecting", ip)
+
+ def afterReconnecting(self, ip):
+ self.callInHooks("afterReconnecting", ip)
+
+ @lock
+ def startThread(self, function, *args, **kwargs):
+ AddonThread(self.core.threadManager, function, args, kwargs)
+
+ def activePlugins(self):
+ """ returns all active plugins """
+ return [x for x in self.plugins.itervalues() if x.isActivated()]
+
+ def getAllInfo(self):
+ """returns info stored by addon plugins"""
+ info = {}
+ for name, plugin in self.plugins.iteritems():
+ if plugin.info:
+ #copy and convert so str
+ 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.plugins and self.plugins[plugin].info:
+ info = dict([(x, to_string(y))
+ for x, y in self.plugins[plugin].info.iteritems()])
+
+ return info
+
+ def addEventListener(self, plugin, func, event):
+ """ add the event to the list """
+
+
+ if plugin not in self.events:
+ self.events[plugin] = []
+ self.events[plugin].append((func, event))
+
+ def registerEvents(self):
+ """ actually register all saved events """
+ 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)
+