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:
- initialization: loads all internal plugin variables without any interaction with other plugins
- preloading: perform initialization actions with other plugins and their initialized options (e.g. connecting to plugin events)
- loading: this is the last step, where all plugins could perform any action to interact with other 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',)