Skip to content
Adrian Sampson edited this page Mar 14, 2014 · 7 revisions

Plugin API Overhaul

A number of warts have developed on the plugin API.

Stop using a namespace package for plugins. This is causing headaches because [pip-installed packages have problems with namespace packages][pipbug]. [Flask has moved away from a flaskext package][flaskfix], so it might be wise to use Armin's example there. Plugins should be called beets_X or, for plugins distributed as part of beets, beets.plugin.X.

__import__('beets.plug.{}'.format(...)) # built-in
modname = 'beets_{}'.format(...)
import imp

    imp.find_module(modname, pluginpaths) # on path

imp.find_module(modname) # installed in Python tree

Use instances instead of classes. It's very confusing that so many aspects of plugins happen at the level of the class itself. It would be much more natural for plugins to be treated as singletons, using self to store data. Use Trac's trick to force singleton usage -- __init__ called twice returns an existing instance.

class BeetsPlugin(object):
    _instance = None
    def __new__(cls):
        if cls._instance is None:
            cls._instance = type.__new__(cls)
        return _instance

plugin_classes = []
...
for obj in mod.__dict__.values():
    if BeetsPlugin in obj.__mro__:
        plugin_classes.append(obj)
plugin_instances = [cls() for cls in plugin_classes]

Now we can abandon decorators for events and the like. Everything should be an ordinary method on the plugin class. And stuff like commands() should not be a method; it should be a field on the object.

... still not sure about stuff like item_fields and template_funcs. Decorators seem alright for them, and assigning to self.* is somewhat cumbersome... all of that would have to go in __init__?

At the same time, I would like to move away from optparse. In particular, Argh is a really clean-looking wrapper for the newer argparse. To use it, however, we'll need to do something horrible to monkey-patch 2.7's argparse to support aliases. I wrote the patch that adds alias support in 3.2, but it is not backported to 2.7: http://hg.python.org/cpython/rev/4c0426261148/

Clone this wiki locally