I’d like to use a plugin system within my code. I’ve looked around for simple (yet powerful) python modules, and found Yapsy (among some others).
It is quite what I was looking for, but the way Yapsy discover plugins is not very flexible and require a plugin info file to be present. I’d like to get rid of it, without having to fork the code (if I start relying on Yapsy, I want to be sure I’ll get all the updates from it without having to refork it each time).
I came out with this quick and dirty solution which is working fine, but do not improve the flexibility of the “discovering” process:
#!/usr/bin/env python
import os
import logging
from cStringIO import StringIO
from yapsy.PluginManager import PluginManager
from yapsy.IPlugin import IPlugin
from yapsy.PluginInfo import PluginInfo
class MyPluginManager(PluginManager):
"""
My attempt to get rid of the plugin info file...
"""
def __init__(self,
categories_filter={"Default":IPlugin},
directories_list=None,
plugin_info_ext="plugin.py"):
"""
Initialize the mapping of the categories and set the list of
directories where plugins may be. This can also be set by
direct call the methods:
- ``setCategoriesFilter`` for ``categories_filter``
- ``setPluginPlaces`` for ``directories_list``
- ``setPluginInfoExtension`` for ``plugin_info_ext``
You may look at these function's documentation for the meaning
of each corresponding arguments.
"""
self.setPluginInfoClass(PluginInfo)
self.setCategoriesFilter(categories_filter)
self.setPluginPlaces(directories_list)
self.setPluginInfoExtension(plugin_info_ext)
def _gatherCorePluginInfo(self, directory, filename):
"""
Gather the core information (name, and module to be loaded)
about a plugin described by it's info file (found at
'directory/filename').
Return an instance of ``self.plugin_info_cls`` and the
config_parser used to gather the core data *in a tuple*, if the
required info could be localised, else return ``(None,None)``.
.. note:: This is supposed to be used internally by subclasses
and decorators.
"""
# now we can consider the file as a serious candidate
candidate_infofile = os.path.join(directory,filename)
print candidate_infofile
# My hack : just create a StringIO file with basic plugin info
_fname = filename.rstrip(".py")
_file = StringIO()
_file.write("""[Core]
Name = %s
Module = %s
""" % (_fname, _fname))
_file.seek(0)
# parse the information file to get info about the plugin
name,moduleName,config_parser = self._getPluginNameAndModuleFromStream(_file, candidate_infofile)
print name, moduleName, config_parser
if (name,moduleName,config_parser)==(None,None,None):
return (None,None)
# start collecting essential info
plugin_info = self._plugin_info_cls(name,os.path.join(directory,moduleName))
return (plugin_info,config_parser)
This hack just assumes that the plugin has an extension “.plugin.py” (or “.plugin” for directory, but I did not test it). Then I create a cSringIO file to fool Yapsy and make it think he found a plugin info file. (One can still provide additional informations in the plugin by setting the proper variables: author, description…).
I’m wondering if there is a better way or if people have already done that. This hack is clearly too rough to be really useful, and I’d like to have something more flexible: a plugin may be discovered by its plugin info file (as in the original code) or by a pattern for the plugin name (probably using re, allowing the usage of prefix, suffix…). As far as I see, having these ideas implemented would require a much more complex hack than what I’ve already done…
Ok, I’ve implemented a fork of the Yapsy plugin manager, and am actually in touch with the author of the package. As soon as the documentation and tests are done, I think this may be included in the next release of Yapsy.