diff options
author | René 'Necoro' Neumann <necoro@necoro.net> | 2008-07-08 15:00:20 +0200 |
---|---|---|
committer | René 'Necoro' Neumann <necoro@necoro.net> | 2008-07-08 15:00:20 +0200 |
commit | 276451a383052ffdc67f561082825cc84aa83bd7 (patch) | |
tree | a148e31c8d201ecc61903eae18a495b9cfdf80be /portato | |
parent | 0a8814713917548767f0ff823e34d412061b3ffe (diff) | |
parent | 8b6db38a2d27fca00d0fe037e86eefc941d559e4 (diff) | |
download | portato-276451a383052ffdc67f561082825cc84aa83bd7.tar.gz portato-276451a383052ffdc67f561082825cc84aa83bd7.tar.bz2 portato-276451a383052ffdc67f561082825cc84aa83bd7.zip |
Merged in the new plugin system
Diffstat (limited to 'portato')
-rw-r--r-- | portato/config_parser.py | 24 | ||||
-rw-r--r-- | portato/constants.py | 4 | ||||
-rw-r--r-- | portato/gui/exception_handling.py | 5 | ||||
-rw-r--r-- | portato/gui/templates/PluginWindow.glade | 180 | ||||
-rw-r--r-- | portato/gui/windows/main.py | 22 | ||||
-rw-r--r-- | portato/gui/windows/plugin.py | 162 | ||||
-rw-r--r-- | portato/plugin.py | 825 | ||||
-rw-r--r-- | portato/plugins/__init__.py | 7 | ||||
-rw-r--r-- | portato/plugins/etc_proposals.py | 31 | ||||
-rw-r--r-- | portato/plugins/exception.py | 2 | ||||
-rw-r--r-- | portato/plugins/gpytage.py | 16 | ||||
-rw-r--r-- | portato/plugins/new_version.py | 58 | ||||
-rw-r--r-- | portato/plugins/notify.py | 22 |
13 files changed, 759 insertions, 599 deletions
diff --git a/portato/config_parser.py b/portato/config_parser.py index 1383d69..6515d1b 100644 --- a/portato/config_parser.py +++ b/portato/config_parser.py @@ -285,8 +285,8 @@ class ConfigParser: :Exceptions: - KeyNotFoundException : Raised if the specified key could not be found. - SectionNotFoundException : Raised if the specified section could not be found. + - `KeyNotFoundException` : Raised if the specified key could not be found. + - `SectionNotFoundException` : Raised if the specified section could not be found. """ try: @@ -315,8 +315,8 @@ class ConfigParser: :Exceptions: - KeyNotFoundException : Raised if the specified key could not be found. - SectionNotFoundException : Raised if the specified section could not be found. + - `KeyNotFoundException` : Raised if the specified key could not be found. + - `SectionNotFoundException` : Raised if the specified section could not be found. """ section = section.upper() @@ -339,9 +339,9 @@ class ConfigParser: :Exceptions: - KeyNotFoundException : Raised if the specified key could not be found. - SectionNotFoundException : Raised if the specified section could not be found. - ValueError : Raised if the key accessed is not a boolean. + - `KeyNotFoundException` : Raised if the specified key could not be found. + - `SectionNotFoundException` : Raised if the specified section could not be found. + - `ValueError` : Raised if the key accessed is not a boolean. """ section = section.upper() @@ -369,8 +369,8 @@ class ConfigParser: :Exceptions: - KeyNotFoundException : Raised if the specified key could not be found. - SectionNotFoundException : Raised if the specified section could not be found. + - `KeyNotFoundException` : Raised if the specified key could not be found. + - `SectionNotFoundException` : Raised if the specified section could not be found. """ section = section.upper() @@ -394,9 +394,9 @@ class ConfigParser: :Exceptions: - KeyNotFoundException : Raised if the specified key could not be found. - SectionNotFoundException : Raised if the specified section could not be found. - ValueError : if the old/new value is not a boolean + - `KeyNotFoundException` : Raised if the specified key could not be found. + - `SectionNotFoundException` : Raised if the specified section could not be found. + - `ValueError` : if the old/new value is not a boolean """ section = section.upper() diff --git a/portato/constants.py b/portato/constants.py index 32f0f9b..93e4240 100644 --- a/portato/constants.py +++ b/portato/constants.py @@ -45,8 +45,6 @@ These should be set during the installation. @type SETTINGS_DIR: string @var TEMPLATE_DIR: Directory containing the UI template files. @type TEMPLATE_DIR: string -@var XSD_LOCATION: Path of the plugin schema. -@type XSD_LOCATION: string """ import os from os.path import join as pjoin @@ -73,5 +71,3 @@ LOCALE_DIR = "i18n/" PLUGIN_DIR = pjoin(DATA_DIR, "plugins/") SETTINGS_DIR = pjoin(HOME, "."+APP) TEMPLATE_DIR = "portato/gui/templates/" - -XSD_LOCATION = pjoin(DATA_DIR, "plugin.xsd") diff --git a/portato/gui/exception_handling.py b/portato/gui/exception_handling.py index eadf124..dae95ed 100644 --- a/portato/gui/exception_handling.py +++ b/portato/gui/exception_handling.py @@ -100,16 +100,13 @@ def convert (version): def get_version_infos(): from ..constants import VERSION from ..backend import system - from lxml import etree return "\n".join(( "Portato version: %s" % VERSION, "Python version: %s" % sys.version, "Used backend: %s" % system.get_version(), "pygtk: %s (using GTK+: %s)" % (convert(gtk.pygtk_version), convert(gtk.gtk_version)), - "pygobject: %s (using GLib: %s)" % (convert(gobject.pygobject_version), convert(gobject.glib_version)), - "lxml: %s" % convert(etree.LXML_VERSION), - "")) + "pygobject: %s (using GLib: %s)" % (convert(gobject.pygobject_version), convert(gobject.glib_version)))) def get_trace(type, value, tb): trace = StringIO() diff --git a/portato/gui/templates/PluginWindow.glade b/portato/gui/templates/PluginWindow.glade index 1de5fc0..f76193e 100644 --- a/portato/gui/templates/PluginWindow.glade +++ b/portato/gui/templates/PluginWindow.glade @@ -1,11 +1,12 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> -<!--Generated with glade3 3.4.1 on Fri Feb 29 00:03:30 2008 --> +<!--Generated with glade3 3.4.5 on Fri Jul 4 15:24:27 2008 --> <glade-interface> <widget class="GtkWindow" id="PluginWindow"> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="border_width">5</property> <property name="title" translatable="yes">Plugins</property> + <property name="resizable">False</property> <property name="modal">True</property> <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> <property name="destroy_with_parent">True</property> @@ -26,6 +27,7 @@ <child> <widget class="GtkTreeView" id="pluginList"> <property name="visible">True</property> + <property name="headers_visible">False</property> <property name="rules_hint">True</property> <property name="enable_search">False</property> </widget> @@ -33,6 +35,180 @@ </widget> </child> <child> + <widget class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <child> + <widget class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_rows">4</property> + <property name="n_columns">2</property> + <property name="row_spacing">10</property> + <child> + <widget class="GtkButton" id="installBtn"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="label" translatable="yes">_Install dependencies</property> + <property name="use_underline">True</property> + <property name="response_id">0</property> + <signal name="clicked" handler="cb_install_clicked"/> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + <property name="x_padding">10</property> + </packing> + </child> + <child> + <widget class="GtkExpander" id="depExpander"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <widget class="GtkTreeView" id="depList"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="headers_clickable">True</property> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="label" translatable="yes">Needed dependencies</property> + <property name="single_line_mode">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_padding">10</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="descrLabel"> + <property name="visible">True</property> + <property name="label" translatable="yes">label</property> + </widget> + <packing> + <property name="right_attach">2</property> + <property name="y_options">GTK_FILL</property> + <property name="y_padding">10</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Author:</b></property> + <property name="use_markup">True</property> + <property name="single_line_mode">True</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="authorLabel"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">label</property> + <property name="single_line_mode">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkHButtonBox" id="buttonBox"> + <property name="visible">True</property> + <property name="spacing">5</property> + <property name="homogeneous">True</property> + <property name="layout_style">GTK_BUTTONBOX_EDGE</property> + <child> + <widget class="GtkRadioButton" id="enabledRB"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Enabled</property> + <property name="response_id">0</property> + <property name="active">True</property> + <signal name="toggled" handler="cb_state_toggled"/> + </widget> + </child> + <child> + <widget class="GtkRadioButton" id="tempEnabledRB"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Temporarily enabled</property> + <property name="response_id">0</property> + <property name="group">enabledRB</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkRadioButton" id="tempDisabledRB"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Temporarily disabled</property> + <property name="response_id">0</property> + <property name="group">enabledRB</property> + <signal name="toggled" handler="cb_state_toggled"/> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + <child> + <widget class="GtkRadioButton" id="disabledRB"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Disabled</property> + <property name="response_id">0</property> + <property name="group">enabledRB</property> + <signal name="toggled" handler="cb_state_toggled"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + </widget> + <packing> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + </widget> + </child> + <child> + <placeholder/> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> <widget class="GtkHButtonBox" id="hbuttonbox2"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> @@ -68,7 +244,7 @@ </widget> <packing> <property name="expand">False</property> - <property name="position">1</property> + <property name="position">2</property> </packing> </child> </widget> diff --git a/portato/gui/windows/main.py b/portato/gui/windows/main.py index 364810d..265d4dd 100644 --- a/portato/gui/windows/main.py +++ b/portato/gui/windows/main.py @@ -607,13 +607,13 @@ class MainWindow (Window): # set plugins and plugin-menu splash(_("Loading Plugins")) - plugin.load_plugins("gtk") - menus = plugin.get_plugin_queue().get_plugin_menus() + plugin.load_plugins() + menus = [p.menus for p in plugin.get_plugin_queue().get_plugins()] if menus: self.tree.get_widget("pluginMenuItem").set_no_show_all(False) pluginMenu = self.tree.get_widget("pluginMenu") - for m in menus: + for m in itt.chain(*menus): item = gtk.MenuItem(m.label) item.connect("activate", m.call) pluginMenu.append(item) @@ -1082,13 +1082,11 @@ class MainWindow (Window): def save_plugin (p): def _save (): - stat_on = p.status >= p.STAT_ENABLED - hard_on = not p.get_option("disabled") - - if stat_on != hard_on: - return int(stat_on) - else: + if p.status == p.STAT_HARD_DISABLED: return "" + + return int(p.status >= p.STAT_ENABLED) + return _save # SESSION VERSION @@ -1553,9 +1551,9 @@ class MainWindow (Window): if queue is None: plugins = [] else: - plugins = queue.get_plugins() - - PluginWindow(self.window, plugins) + plugins = list(queue.get_plugins()) + + PluginWindow(self.window, plugins, self.queue) return True def cb_show_updates_clicked (self, *args): diff --git a/portato/gui/windows/plugin.py b/portato/gui/windows/plugin.py index fb9446e..392654e 100644 --- a/portato/gui/windows/plugin.py +++ b/portato/gui/windows/plugin.py @@ -15,6 +15,9 @@ from __future__ import absolute_import import gtk from .basic import AbstractDialog +from ..dialogs import blocked_dialog, unmask_dialog +from ...backend import system +from ...backend.exceptions import PackageNotFoundException, BlockedException from ...helper import debug class PluginWindow (AbstractDialog): @@ -24,7 +27,7 @@ class PluginWindow (AbstractDialog): for s in (_("Disabled"), _("Temporarily enabled"), _("Enabled"), _("Temporarily disabled")): statsStore.append([s]) - def __init__ (self, parent, plugins): + def __init__ (self, parent, plugins, queue = None): """Constructor. @param parent: the parent window @@ -32,50 +35,77 @@ class PluginWindow (AbstractDialog): AbstractDialog.__init__(self, parent) self.plugins = plugins + self.queue = queue self.changedPlugins = {} + self.inst = [] + self.ninst = [] - view = self.tree.get_widget("pluginList") - self.store = gtk.ListStore(str,str,str) + self.buttons = map(self.tree.get_widget, ("disabledRB", "tempEnabledRB", "enabledRB", "tempDisabledRB")) + map(lambda b: b.set_mode(False), self.buttons) + + self.descrLabel = self.tree.get_widget("descrLabel") + self.authorLabel = self.tree.get_widget("authorLabel") + + self.depExpander = self.tree.get_widget("depExpander") + self.installBtn = self.tree.get_widget("installBtn") + self.depList = self.tree.get_widget("depList") + self.build_dep_list() + + self.buttonBox = self.tree.get_widget("buttonBox") + + self.instIcon = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU) - view.set_model(self.store) + self.view = self.tree.get_widget("pluginList") + self.store = gtk.ListStore(str) - cell = gtk.CellRendererText() - col = gtk.TreeViewColumn(_("Plugin"), cell, markup = 0) - view.append_column(col) + self.view.set_model(self.store) - col = gtk.TreeViewColumn(_("Authors"), cell, text = 1) - view.append_column(col) - - ccell = gtk.CellRendererCombo() - ccell.set_property("model", self.statsStore) - ccell.set_property("text-column", 0) - ccell.set_property("has-entry", False) - ccell.set_property("editable", True) - ccell.connect("edited", self.cb_status_changed) - col = gtk.TreeViewColumn(_("Status"), ccell, markup = 2) - view.append_column(col) + cell = gtk.CellRendererText() + col = gtk.TreeViewColumn("Plugin", cell, markup = 0) + self.view.append_column(col) - for p in (("<b>"+p.name+"</b>", p.author, _(self.statsStore[p.status][0])) for p in plugins): - self.store.append(p) + for p in plugins: + self.store.append(["<b>%s</b>" % p.name]) + + self.view.get_selection().connect("changed", self.cb_list_selection) self.window.show_all() - def cb_status_changed (self, cell, path, new_text): - path = int(path) - - self.store[path][2] = "<b>%s</b>" % new_text + def build_dep_list (self): + store = gtk.ListStore(gtk.gdk.Pixbuf, str) + + self.depList.set_model(store) + + col = gtk.TreeViewColumn() + + cell = gtk.CellRendererPixbuf() + col.pack_start(cell, False) + col.add_attribute(cell, "pixbuf", 0) + + cell = gtk.CellRendererText() + col.pack_start(cell, True) + col.add_attribute(cell, "text", 1) + + self.depList.append_column(col) + + def fill_dep_list (self, inst = [], ninst = []): + store = self.depList.get_model() + store.clear() + + for dep in inst: + store.append([self.instIcon, dep]) + for dep in ninst: + store.append([None, dep]) - # convert string to constant - const = None - for num, val in enumerate(self.statsStore): - if val[0] == new_text: - const = num - break + def cb_state_toggled (self, rb): + + plugin = self.get_actual() - assert (const is not None) + if plugin: + state = self.buttons.index(rb) - self.changedPlugins.update({self.plugins[path] : const}) - debug("new changed plugins: %s => %d", self.plugins[path].name, const) + self.changedPlugins[plugin] = state + debug("new changed plugins: %s => %d", plugin.name, state) def cb_ok_clicked (self, btn): for plugin, val in self.changedPlugins.iteritems(): @@ -83,3 +113,69 @@ class PluginWindow (AbstractDialog): self.close() return True + + def cb_list_selection (self, selection): + plugin = self.get_actual() + self.inst = [] + self.ninst = [] + + if plugin: + if not plugin.description: + self.descrLabel.hide() + else: + self.descrLabel.set_markup(plugin.description) + self.descrLabel.show() + + self.authorLabel.set_label(plugin.author) + + status = self.changedPlugins.get(plugin, plugin.status) + self.buttons[status].set_active(True) + + if plugin.deps: + + for dep in plugin.deps: + if system.find_packages(dep, pkgSet = "installed", with_version = False): + self.inst.append(dep) + else: + self.ninst.append(dep) + + self.fill_dep_list(self.inst, self.ninst) + self.depExpander.show() + + self.installBtn.show() + self.installBtn.set_sensitive(bool(self.ninst)) + + else: + self.installBtn.hide() + self.depExpander.hide() + + self.buttonBox.set_sensitive(not plugin._unresolved_deps and plugin.status != plugin.STAT_HARD_DISABLED) + + def cb_install_clicked (self, *args): + if not self.queue: + return False + + for cpv in self.ninst: + + pkg = system.find_best_match(cpv, masked = False, only_cpv = True) + if not pkg: + pkg = system.find_best_match(cpv, masked = True, only_cpv = True) + + try: + try: + self.queue.append(pkg, type = "install") + except PackageNotFoundException, e: + if unmask_dialog(e[0]) == gtk.RESPONSE_YES: + self.queue.append(pkg, type = "install", unmask = True) + except BlockedException, e: + blocked_dialog(e[0], e[1]) + + return True + + def get_actual (self): + store, it = self.view.get_selection().get_selected() + + if it: + return self.plugins[int(store.get_path(it)[0])] + else: + return None diff --git a/portato/plugin.py b/portato/plugin.py index 5926922..0119909 100644 --- a/portato/plugin.py +++ b/portato/plugin.py @@ -3,349 +3,421 @@ # File: portato/plugin.py # This file is part of the Portato-Project, a graphical portage-frontend. # -# Copyright (C) 2007 René 'Necoro' Neumann +# Copyright (C) 2008 René 'Necoro' Neumann # This is free software. You may redistribute copies of it under the terms of # the GNU General Public License version 2. # There is NO WARRANTY, to the extent permitted by law. # # Written by René 'Necoro' Neumann <necoro@necoro.net> -"""A module containing the management of the plugin system.""" +""" +A module managing the plugins for Portato. +""" from __future__ import absolute_import - -import os, os.path -from xml.dom.minidom import parse -from lxml import etree - -from .constants import PLUGIN_DIR, XSD_LOCATION -from .helper import debug, info, warning, error, flatten - -class PluginImportException (ImportError): +__docformat__ = "restructuredtext" + +import os +import os.path as osp +import traceback +from collections import defaultdict +from functools import wraps + +from .helper import debug, warning, info, error +from .constants import PLUGIN_DIR +from .backend import system +from . import plugins as plugin_module + +class PluginLoadException (Exception): + """ + Exception signaling a failed plugin loading. + """ pass -class Options (object): - """The <options>-element.""" +class Menu (object): + """ + One single menu entry. - __options = ("disabled", "blocking") + :IVariables: - def __init__ (self, options = None): + label : string + The label of the entry. Can have underscores to define the shortcut. - self.disabled = False - self.blocking = False + call + The function to call, if the entry is clicked. + """ + __slots__ = ("label", "call") - if options: - self.parse(options) - - def parse (self, options): - for opt in options: - nodes = opt.childNodes - type = str(nodes[0].nodeValue.strip()) - if type in self.__options: - self.set(type, True) + def __init__ (self, label, call): + self.label = label + self.call = call - def get (self, name): - return self.__getattribute__(name) +class Call (object): + """ + This class represents an object, which is attached to a specified hook. - def set (self, name, value): - return self.__setattr__(name, value) + :IVariables: -class Menu: - """A single <menu>-element.""" - def __init__ (self, plugin, label, call): - """Constructor. + plugin : `Plugin` + The plugin where this call belongs to. - @param plugin: the plugin this menu belongs to - @type plugin: Plugin - @param label: the label to show - @type label: string - @param call: the function to call relative to the import statement - @type call: string + hook : string + The name of the corresponding hook. - @raises PluginImportException: if the plugin's import could not be imported""" + call + The function to call. - self.label = label - self.plugin = plugin + type : string + This is either ``before``, ``after`` or ``override`` and defines the type of the call: - if self.plugin.needs_import(): # get import - imp = self.plugin.get_import() - try: - mod = __import__(imp, globals(), locals(), [call]) - except ImportError: - raise PluginImportException, imp + before + access before the original function + override + access *instead of* the original function. **USE THIS ONLY IF YOU KNOW WHAT YOU ARE DOING** + after + access after the original function has been called - try: - self.call = eval("mod."+call) # build function - except AttributeError: - raise PluginImportException, imp - else: - try: - self.call = eval(call) - except AttributeError: - raise PluginImportException, imp + Default: ``before`` -class Connect: - """A single <connect>-element.""" + dep : string + This defines a plugin which should be executed after/before this one. + ``"*"`` means all and ``"-*"`` means none. + """ + __slots__ = ("plugin", "hook", "call", "type", "dep") - def __init__ (self, hook, type, depend_plugin): - """Constructor. + def __init__ (self, plugin, hook, call, type = "before", dep = None): + self.plugin = plugin + self.hook = hook + self.call = call + self.type = type + self.dep = dep - @param hook: the parent Hook - @type hook: Hook - @param type: the type of the connect ("before", "after", "override") - @type type: string - @param depend_plugin: a plugin we are dependant on - @type depend_plugin: string or None""" +class Hook (object): + """ + Representing a hook with all the `Call` s for the different types. + """ + + __slots__ = ("before", "override", "after") - self.type = type - self.hook = hook - self.depend_plugin = depend_plugin + def __init__ (self): + self.before = [] + self.override = None + self.after = [] - def is_before_type (self): - return self.type == "before" +class Plugin (object): + """ + This is the main plugin object. It is used where ever a plugin is wanted, and it is the one, which needs to be subclassed by plugin authors. - def is_after_type (self): - return self.type == "after" + :CVariables: - def is_override_type (self): - return self.type == "override" + STAT_DISABLED : status + Status: Disabled. -class Hook: - """A single <hook>-element.""" + STAT_TEMP_ENABLED : status + Status: Enabled for this session only. - def __init__ (self, plugin, hook, call): - """Constructor. + STAT_ENABLED : status + Status: Enabled. - @param plugin: the parent Plugin - @type plugin: Plugin - @param hook: the hook to add to - @type hook: string - @param call: the call to make - @type call: string""" + STAT_TEMP_DISABLED : status + Status: Disabled for this session only. - self.plugin = plugin - self.hook = hook - self.call = call - self.connects = [] + STAT_HARD_DISABLED : status + Status: Forced disabled by program (i.e. because of errors in the plugin). + """ - def parse_connects (self, connects): - """This gets a list of <connect>-elements and parses them. - - @param connects: the list of <connect>'s - @type connects: NodeList""" - - if not connects: # no connects - assume "before" connect - self.connects.append(Connect(self, "before", None)) + (STAT_DISABLED, STAT_TEMP_ENABLED, STAT_ENABLED, STAT_TEMP_DISABLED) = range(4) + STAT_HARD_DISABLED = -1 + + def __init__ (self, disable = False): + """ + :param disable: Forcefully disable the plugin + :type disable: bool + """ + self.__menus = [] #: List of `Menu` + self.__calls = [] #: List of `Call` + self._unresolved_deps = False #: Does this plugin has unresolved dependencies? + + self.status = self.STAT_ENABLED #: The status of this plugin - for c in connects: - type = c.getAttribute("type") - if type == '': - type = "before" - - # get dep_plugin if available - dep_plugin = None - if c.hasChildNodes(): - nodes = c.childNodes - dep_plugin = nodes[0].nodeValue.strip() - - connect = Connect(self, type, dep_plugin) - self.connects.append(connect) + if disable: + self.status = self.STAT_HARD_DISABLED + + def _init (self): + """ + Method called from outside to init the extension parts of this plugin. + If the current status is `STAT_HARD_DISABLED` or there are unresolved dependencies, the init process is not started. + """ + + for d in self.deps: + if not system.find_packages(d, pkgSet="installed", with_version = False): + self._unresolved_deps = True + break -class Plugin: - """A complete plugin.""" - - (STAT_DISABLED, STAT_TEMP_ENABLED, STAT_ENABLED, STAT_TEMP_DISABLED) = range(4) - - def __init__ (self, file, name, author): - """Constructor. - - @param file: the file name of the plugin.xml - @type file: string - @param name: the name of the plugin - @type name: Node - @param author: the author of the plugin - @type author: Node""" + if self.status != self.STAT_HARD_DISABLED and not self._unresolved_deps: + self.init() + + def init (self): + """ + This method is called by `_init` and should be overriden by the plugin author. + + :precond: No unresolved deps and the status is not `STAT_HARD_DISABLED`. + """ + pass + + @property + def author (self): + """ + Returns the plugin's author. + The author is given by the ``__author__`` variable. + + :rtype: string + """ + return getattr(self, "__author__", "") |