Architecture


1. Components

The main component of the framework is the application interface (IApplication) used for reference by all other components: it should be initialized before using any other component.

The application implementation should do nothing else that add some useful functions and hooks (such as adapters) to let the development be as the easier and faster as possible.

After the application initialization, registered plugins (IPlugin) must be found trough a plugin resource manager (IPluginResource). The plugin resource then adds internally all found plugins and register itself (the instance) as an adapter for IApplication, so that the application could implement its own plugin resource following the interface scheme.

At this point, the application is configured and ready to be loaded using the context resource (IContextResource), which groups some plugins and loads them passing for the following three states:

This loading feature has the purpose to remove all dependencies among plugins

Just after the initialization of each plugin, the plugin instance is registered as an adapter of IApplication so plugins could reach other plugins adapting the application to the requested plugin interface.

Note

It's important to specify a different interface for each plugins. Note that registering an adaption replaces the previous one, so there can be only one interface implementation in each context.

The last optional but recommended component is the engine (IEngine). It's something like a protocol that specifies how plugin communicate each other trough events and contain some useful utilities for a specified environment. Mandatory engine components are the event dispather (IEventDispatcher, registered as an adaptiong for IPlugin) and its events (IEvent, adaption for IEventDispatcher).

Then each plugin could have its own event dispatcher based on the current engine to let other plugins connect to it.

Event connecting is also simplified by the event connector. Its behavior consists in getting all objects contained in self starting with "on_" and connecting them as callbacks for the rest of the object name as the event name to the specified plugin. Usually, it doesn't need to be implemented for each different engine since it uses the engine abstraction.

2. Builtin plugin resources

2.1 Setuptools

The setuptools plugin resource is used to gain the list of application plugins registered using setuptools entry points.

A simple usage:

from plugboard import plugin
pr = plugin.SetuptoolsPluginResource(application, 'application.plugins')
pr.refresh()

3. Builtin context resources

3.1 XML

This resource gains context informations from a xml.dom.minidom.Element element, looking in its children.

A simple usage:

from xml.dom import minidom
xmlstr = """<root>
<context name='ctx1'>
<plugin path='app.plugins.APlugin' />
<plugin path='app.plugins.BPlugin' />
</context>
"""
xml_element = minidom.parseString(xmlstr).documentElement)

from plugboard import context
cr = context.XMLContextResource(application, xml_element)
cr.refresh()

4. Builtin engines

4.1 PlugBoard

The PlugBoard engine doesn't define any utility for applications, since it's the most general purpose engine. However, it has a really simple method for event dispatching: it stores observer callbacks and call them when an emit is requested.

4.2 GTK

This engine can be used in GTK 2.8 or above, since it exploits the creation of GTK signals into a generic GObject.

Note

This can be done using the special class variable __gsignals__, only available in PyGTK 2.8 or higher.

Read PyGTK FAQs for more informations.

So, everytime a signal is requested to be created, a new gobject is instantiated. To grant the integrity of the first argument passed to the callback to be the plugin instance and not the gobject when firing the event, connect_object() is used instead of connect().

5. Engine utilities

5.1 EventConnector

This class automatically connects its callable attributes beggining with "on_" to the specified plugin. The event name to be used is described in the object name itself, just after the "on_" string: for instance "on_new" would connect to the event "new".

These callable objects (usually methods) could hold an attribute called extra which contains extra arguments to be passed to the connect() method of IEvent.

A simple usage:

from plugboard import plugin, engine
from zope.interface import implements
import interfaces
class APlugin(plugin.Plugin):
  implements(interface.IAPlugin)
  def __init__(self, application):
    self.dispatcher = engine.IEventDispatcher(self)
    self.dispatcher.add_event('new', (str, 'Some data type'))
  def load(self, context):
    self.dispatcher['new'].emit('Some data')

class BToAEventConnector(engine.EventConnector):
  def on_new(self, plugin, data, extra):
    ...
class BPlugin(plugin.Plugin):
  implements(interfaces.IBPlugin)
  def preload(self, application):
    ec = BToAEventConnector(interfaces.IAPlugin(application))
    ec.on_new.extra = ('first extra argument',)