Plugins system¶
Goals¶
The plugin system (pkgcore.plugin
) is used to pick up extra code
(potentially distributed separately from pkgcore itself) at a place
where using the config system is not a good idea for some reason. This
means that for a lot of things that most people would call “plugins”
you should not actually use pkgcore.plugin
, you should use the
config system. Things like extra repository types should simply be
used as “class” value in the configuration. The plugin system is
currently mainly used in places where handing in a ConfigManager
is too inconvenient.
Using plugins¶
Plugins are looked up based on a string “key”. You can always look up
all available plugins matching this key with
pkgcore.plugin.get_plugins(key)
. For some kinds of plugin (the
ones defining a “priority” attribute) you can also get the “best”
plugin with pkgcore.plugin.get_plugin(key)
. This does not make
sense for all kinds of plugin, so not all of them define this.
The plugin system does not care about what kind of object plugins are, this depends entirely on the key.
Adding plugins¶
Basics, caching¶
Plugins for pkgcore are loaded from modules inside the
pkgcore.plugins
package. This package has some magic to make
plugins in any subdirectory pkgcore/plugins
under a directory on
sys.path
work. So if pkgcore itself is installed in site-packages
you can still add plugins to /home/you/pythonlib/pkgcore/plugins
if /home/you/pythonlib
is in PYTHONPATH
. You should not put an
__init__.py
in this extra plugin directory.
Plugin modules should contain a pkgcore_plugins
directory that
maps the “key” strings to a sequence of plugins. This dictionary has
to be constant, since pkgcore keeps track of what plugin module
provides plugins for what keys in a cache file to avoid unnecessary
imports. So this is invalid:
try:
import spork_package
except ImportError:
pkgcore_plugins = {}
else:
pkgcore_plugins = {'myplug': [spork_package.ThePlugin]}
since if the plugin cache is generated while the package is not
available pkgcore will cache the module as not providing any
myplug
plugins, and the cache will not be updated if the package
becomes available (only changes to the mtime of actual plugin modules
invalidate the cache). Instead you should do something like this:
try:
from spork_package import ThePlugin
except ImportError:
class ThePlugin:
disabled = True
pkgcore_plugins = {'myplug': [ThePlugin]}
If a plugin has a “disabled” attribute the plugin system will never
return it from get_plugin
or get_plugins
.
Priority¶
If you want your plugin to support get_plugin
it should have a
priority
attribute: an integer indicating how “preferred” this
plugin is. The plugin with the highest priority (that is not disabled)
is returned from get_plugin
.
Some types of plugins need more information to determine a priority
value. Those should not have a priority attribute. They should use
get_plugins
instead and have a method that gets passed the extra
data and returns the priority.
Import behaviour¶
Assuming the cache is working correctly (it was generated after installing a plugin as root) pkgcore will import all plugin modules containing plugins for a requested key in priority order until it hits one that is not disabled. The “disabled” value is not cached (a plugin that is unconditionally disabled makes no sense), but the priority value is. You can fake a dynamic priority by having two instances of your plugin registered and only one of them enabled at the same time.
This means it makes sense to have only one kind of plugin per plugin module (unless the required imports overlap): this avoids pulling in imports for other kinds of plugin when one kind of plugin is requested.
The disabled value is not cached by the plugin system after the plugin module is imported. This means it should be a simple attribute (either completely constant or set at import time) or property that does its own caching.
Adding a plugin package¶
Both get_plugin
and get_plugins
take a plugin package as
second argument. This means you can use the plugin system for external
pkgcore-related tools without cluttering up the main pkgcore plugin
directory. If you do this you will probably want to copy the
__path__
trick from pkgcore/plugin/__init__.py
to support
plugins elsewhere on sys.path
.