Wednesday, August 16, 2006

Python Plug-ins

Years ago I was writing an IRC bot in python and had the need to create a plug-in architecture so that I could both abstract specific pieces of functionality away from the core of the bot as well as dynamically load and unload bot behaviors at runtime. At that point in time the ability to do this type of thing was present in python but there wasn't a standardized paradigm for how to do it. Since then things have become a bit more standardized with the addition of standardized import hooks and my code is surely outdated.

There was however one particular aspect of my implementation that proved to be nice I wanted to share -- the ability to reload and reinitialize a plug-in at runtime. This ability was accomplished mostly as a natural side-effect of using the imp standard library module. Here's how my code looked:

class PluginManager:
def __init__(self):
self.plugins = {}

def load_plugin(self, name, config):
from imp import find_module, load_module
fh, fn, desc = find_module(name, ['./plugins'])
self.plugins[name] = load_module(name, fh, fn, desc)

if hasattr(plugin, '__loadplugin__'):
plugin.__loadplugin__(self, config)

def unload_plugin(self, name):
try:
plugin = self.plugins[name]

if hasattr(plugin, '__unloadplugin__'):
plugin.__unloadplugin__(self)

del self.plugins[name]
except KeyError:
raise PluginException("Plugin %s not loaded." % name)

Because I manage the set of loaded plug-ins myself in the plugins member variable I can control the lifetime of a plug-in. This is also coupled with the fact that imp.load_module() always reloads a module, even if it had been previously loaded. Also note the use of the __loadplugin__ and __unloadplugin__ module level methods. If a plug-in module defined either one of these then it would be called during (de)initialization. In essence it's a very simple plug-in protocol that's being defined here.

Hopefully this will be of some use to someone in the future. By the way I don't advocate writing your own IRC bot nowadays. There are plenty of high quality, extensible, already built options out there. My personal favorite is Supybot.

UPDATE: In browsing around I found a library that does something very similar to what I described here, Sprinkles. At first glance it doesn't seem to have equivalents for __loadplugin__ or __unloadplugin__ but they may be hidden in there somewhere.

No comments: