Creating (rather) generic plugin framework?

bruno at modulix onurb at xiludom.gro
Wed Nov 16 14:38:09 EST 2005


Edvard Majakari wrote:
> Hi,
> 
> My idea is to create a system working as follows: each module knows
> path to plugin directory, and that directory contains modules which
> may add hooks to some points in the code.
> 
> Inspired by http://www.python.org/pycon/2005/papers/7/pyconHooking.html
> 
> I would create a class like this:
> 
> class Printer:
> 
>     def printit(self, msg):
>         stuff = self.beforePrintHook(msg)
>         if stuff:
>             msg = stuff
>         print msg
>         self.afterPrintHook(msg)
> 
>     def beforePrintHook(self, msg): pass
>     def afterPrintHook(self, msg): pass
> 
> Now, in the spirit of py.test, I'd like API to be practically no API at all :)
> moreover, deploying a plugin must consist simply of adding appropriate file to
> plugins directory, nothing more, and removing it would uninstall it. The
> plugin should be able to somehow result in all future invocations to
> Printer.printit() call hooks specified in the plugin. Now, the plugin module
> for class above /might/ be along the following lines (I'm thinking of stuff
> here, so I don't know yet what would be the most appropriate way):
> 
> ### a very simple plugin which uppercases all data fed to it.
> 
> extensions = {'Printer': 'PrinterHook'}
> 
> class PrinterHook:
> 
>     def beforePrintHook(self, msg):
>         return msg.upper()
>     def afterPrintHook(self, msg):
>         print "Called afterPrintHook with msg %s" % msg
> 
> 
> Now, I have a very rude (I think) implementation which has two methods, first
> the one that loads plugin modules:
> 

(snip code)

> 
> But hey, this has many downsides. First off, mechanism doesn't support
> arbitrary namespaces. Here, class identifier in the plugin must be as it is
> seen from the module which calls the plugin (not a problem for me, but could
> be more elegant; probably a mapping between logical class identifiers and
> actual class names, hmm?). Second, if one wants to use many hooks (with
> priority for conflicts), it is not possible now; set_hooks always overrides
> potentially existing hooks. And probably many other problems that are not
> obvious to me, but for the simple purpose I have in mind, it seems to work.

Just a couple of ideas:
- using decorators for plugin hooks ? ie:

import hooks

class Whatever(anything):
  @hooks.hook(for='Printer.beforePrintHook',priority=42)
  def my_function_with_a_long_name(self, *args, **kw):
     pass

The decorator would take care of "registering" the hook where relevant,
  ie, storing it in a class attribute of the hooked class ?

which leads to:

- in the hooked class, use a dict class attribute for hooks:

from hooks import run_hooks

class Printer
  # will be filled (and could even be created)
  # by the @hook decorator
  _hooks = {}

  def print(self, msg):
     # run_hooks will take care of selecting appropriate
     # hooks (by looking up the class attribute _hooks)
     # and running'em in order
     msg = run_hooks(self, 'Printer.beforePrintHook', msg)
     print msg
     run_hooks(self, 'Printer.afterPrintHook', msg)



My 2 cents... I don't even know if this can be implemented (but I don't
see why it couldn't).

> This is the first plugin system in Python I'm writing, so I can be a way off
> the correct path..

<aol>

-- 
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'onurb at xiludom.gro'.split('@')])"



More information about the Python-list mailing list