summaryrefslogtreecommitdiff
path: root/portato
diff options
context:
space:
mode:
authorRené 'Necoro' Neumann <necoro@necoro.net>2008-07-08 15:00:20 +0200
committerRené 'Necoro' Neumann <necoro@necoro.net>2008-07-08 15:00:20 +0200
commit276451a383052ffdc67f561082825cc84aa83bd7 (patch)
treea148e31c8d201ecc61903eae18a495b9cfdf80be /portato
parent0a8814713917548767f0ff823e34d412061b3ffe (diff)
parent8b6db38a2d27fca00d0fe037e86eefc941d559e4 (diff)
downloadportato-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.py24
-rw-r--r--portato/constants.py4
-rw-r--r--portato/gui/exception_handling.py5
-rw-r--r--portato/gui/templates/PluginWindow.glade180
-rw-r--r--portato/gui/windows/main.py22
-rw-r--r--portato/gui/windows/plugin.py162
-rw-r--r--portato/plugin.py825
-rw-r--r--portato/plugins/__init__.py7
-rw-r--r--portato/plugins/etc_proposals.py31
-rw-r--r--portato/plugins/exception.py2
-rw-r--r--portato/plugins/gpytage.py16
-rw-r--r--portato/plugins/new_version.py58
-rw-r--r--portato/plugins/notify.py22
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">&lt;b&gt;Author:&lt;/b&gt;</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__", "")