the python-co2 plugin API

a brief introduction on writing plugins for the python co2 satellite


relay-plugins, with some restrictions, work the same way.
while satellite plugins are necessary to populate a co2-network with
data ('content'), ie. to make it usefull in the first place, since
co2 itself provides nothing but an infrastructure for the exchange of
data, relay-plugins can extend the relay in terms of adding or modifying
transport- and communication-functionality.
relay plugins are undocumented and also untested. so, simply forget
about them for now...


for a hands-on description of how to write a co2 satellite plugin, see:
plugin/example/example.py


overview:

the API is meant to keep all co2- and xmlrpc-related stuff transparent, ie.
you don't have to worry about it at all.

basically, all there is to do is to subclass plugin.Co2Plugin, and stick to
a few conventions. the server takes care of loading and initializing the
plugin on start-up, (loading of plugins without a server-restart is not yet
possible but on the TODO list), and subsequently passed all data it's notify
method receives to the plugin's receive method. to relay data, the plugin
simply calls the superclass's send method (self.send(data)).

additionally it is possible to define class methods that will be added to the
server's registry (list of available methods). see below.


plugin-stages:

- the server on server start-up:
# scans the plugin directory and loads packages listed in plugin_list
  (config.py)
# for each loaded package, loads all modules listed in the package's
  __all__ variable
# for each loaded module, initializes classes that are subclasses of
  plugin.Co2Plugin and whose names begin with 'X_' and registers their
  __exit__() method.
# for each initialized class, registers its receive() method.
# scans each loaded module for class methods that begin with 'x_' and adds
  them to the server's registry, so that they are callable through xmlrpc.
  (currently broken!)

- at server-runtime:
# passes on data received from the relay (notify()) to each plugin's receive
  method
# relays data from the plugin's send method to the co2-network (relay())

- on server-shutdown:
# calls each registered __exit__ method


overriding Co2Plugin methods:

# __init__ can be overridden, but must call the superclass's init
  (plugin.Co2Plugin.__init__(self, var_dict)

# in order to process incoming data the plugin class must override the
  receive method. (def receive(self, data_dict))
  important note: your receive method must return (more or less) immediately!
  if processing takes a long time (long is like more than 1 second or so), do
  it in a thread! since http is based on request-reply, the client process will
  wait for the server's reply, ie. keep a thread around and use a local port,
  until a reply is received.

# if you want the plugin to log to a differnent file (or stdout/err) then the
  one supplied be the server, you can override the log method.

# the same goes for debug (which, up to now, is the same method as log, ie.
  writes to the same file, but will use it's own file in the future)
# __exit__ (called on server exit) must be overridden in case the plugin needs
  some cleaning up to do before exiting (closing files, notifying threads,
  sending out a goodbye message and the like)
# __str__ and __repr__: if you want to set the string representation (as it
  appears in the log, for instance).
  the default is <package_name>.<module_name>.<class_name>.


argument format:

all data sent and received is encapsulated in a dictionary (a structure of
key/value pairs), ie. each method call has exactly one argument of type
dictionary.
the only (mandatory) key for plugins is 'data'. it's value my contain any
data type(s) and, in case of sequences or lists, hierarchies thereof.

examples of valid send method calls:
 send({'data':2})
 send({'data':[1, 2, 3, 'shrubbery']})
 send({'data':({'john':'cleese', 'terry':'gilliam'}, 'nih!', 0.815)})

consequently, the receive method is passed equally formatted arguments,
plus the 'host_id' and 'client_address' fields added by the sending and
receiving servers.
so, for the second example above, a plugin would receive:
 {'host_id':'124571645@127.0.0.1:2004',
  'client_address':(127.0.0.1, 4567'),
  'data':[1, 2, 3, 'shrubbery']}

the key 'host_id' (mandatory in the co2 protocol) is set by the sending
server, that means it can be set by the plugin, but will be overwritten.
additionally, the relay will check if the host_id matches the host part of
request's client_address, which should be pretty spoof-proof. not that i see
a point in spoofing co2-messages...
(FYI: host_id's are generated as <random>@<client_ip>:<client_port>,
where client_port is the port the remote co2-server is listening on, not
the requests originating port.)


naming conventions:

reserved names:
sys, system, co2, relay, web
(plugins that use any of those as a package name are ignored by the loader.)

plugin class names:
classes that should be loaded by the server must be given a name that starts
with 'X_'. all other classes are ignored.

class methods:
it is also possible to expose single methods to the co2-network which then
can be called by other hosts bypassing the server's notify method. therefor
the method's name must begin with x_, and will be callable as
<package_name>.<module_name>.<method_name> (with the leading 'x_' stripped.
so method 'x_call_me' in module 'spam' of package 'ham' will be exposed as
'ham.spam.call_me'.
methods of this kind will show up in the server's answer to a call to
co2.get_method_names (or it's equivalents) and thus be known to all
satellites.
nb: in version 0.5.8 this is broken!...


last modified: 2003.08.28 / lu
