Plugin basics
Fundamentally, a plugin in Higgins is just a python class which is contained in an Egg. Higgins uses setuptools to build a plugin into an Egg and load it into the application at runtime. This document hopefully explains enough about developing a Higgins plugin that a thorough understanding of setuptools is not necessary, but when in doubt the above links should prove helpful.
Necessary boilerplate
Plugins must be structured within a specific directory hierarchy, or else Higgins will not be able to 'find' the plugin, due to the way python loads packages. Suppose we have a new plugin called MyPlugin. The source directory must be laid out thus:
MyPlugin/
|
+-- setup.py
|
+-- higgins/
|
+-- __init__.py
|
+-- plugins/
|
+-- __init__.py
|
+-- myplugin
|
+-- <actual code goes in this directory>
The name of the toplevel source directory (MyPlugin in this case) doesn't matter, but the name of the package directory in MyPlugin/higgins/plugins/ must be unique across all Higgins plugins. This is because Higgins utilizes a feature of setuptools called namespace packages. Furthermore the files MyPlugin/higgins/__init__.py and MyPlugin/higgins/plugins/__init__.py have a a very specific format which must be followed. The only thing that these two files can contain (and they must contain) is the following line:
__import__('pkg_resources').declare_namespace(__name__)
The curious developer can read more about namespace packages here.
The file MyPlugin/setup.py is also part of the required boilerplate, and is described later. For now it suffices to know that it must be present.
At this point we have the appropriate directory structure to create a new plugin, but we don't have any actual plugin code yet. We need to decide what kind of plugin to make; the most basic choice is a Service plugin, which is good for an example. First, in the myplugin directory we create a new file __init__.py which will contain our plugin definition. Then we add the actual definition:
import higgins class MyService(higgins.service.Service): pass
This code is a legal plugin. However, it doesn't do anything (except waste a little bit of memory). We need to write some methods for the class, and the first one to write is our initialization method (also known as the constructor).
Plugin initialization
When Higgins starts up, one of the first things it does is scan the system for plugins. For each plugin found, Higgins creates a single instance of the plugin by calling the plugin constructor method __init__. Higgins does not pass any parameters to the constructor function. A plugin constructor is expected to throw an exception if initialization fails for some reason; otherwise Higgins expects that initialization succeeded and the plugin is thus viable. Plugins are not required to define an __init__ method, in which case the __init__ method of the base class is called, but if an __init__ method is defined it must call the parent constructor method. Here's a barebones example of an __init__ method:
import higgins class MyPlugin(higgins.service.Service): def __init__(self): try: import myfoomodule except: raise Exception("required module 'myfoomodule' wasn't found") higgins.service.Service.__init__(self)
