extremely simple plugin system with discovery

This commit is contained in:
Adrian Sampson 2010-07-08 16:35:15 -07:00
parent e834ffd44f
commit 78efe771b1
5 changed files with 82 additions and 2 deletions

73
beets/plugins.py Normal file
View file

@ -0,0 +1,73 @@
# This file is part of beets.
# Copyright 2010, Adrian Sampson.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
"""Support for beets plugins."""
import logging
import itertools
# Global logger.
log = logging.getLogger('beets')
PLUGIN_NAMESPACE = 'beetsplug'
DEFAULT_PLUGINS = []
class BeetsPlugin(object):
"""The base class for all beets plugins. Plugins provide
functionality by defining a subclass of BeetsPlugin and overriding
the abstract methods defined here.
"""
def commands(self):
"""Should return a list of beets.ui.Subcommand objects for
commands that should be added to beets' CLI.
"""
raise NotImplementedError
def load_plugins(names=()):
"""Imports the modules for a sequence of plugin names. Each name
must be the name of a Python module under the "beetsplug" namespace
package in sys.path; the module indicated should contain the
BeetsPlugin subclasses desired. A default set of plugins is also
loaded.
"""
for name in itertools.chain(names, DEFAULT_PLUGINS):
modname = '%s.%s' % (PLUGIN_NAMESPACE, name)
try:
__import__(modname, None, None)
except:
log.warn('plugin %s not found' % name)
_instances = {}
def find_plugins():
"""Returns a list of BeetsPlugin subclass instances from all
currently loaded beets plugins. Loads the default plugin set
first.
"""
load_plugins()
plugins = []
for cls in BeetsPlugin.__subclasses__():
# Only instantiate each plugin class once.
if cls not in _instances:
_instances[cls] = cls()
plugins.append(_instances[cls])
return plugins
def commands():
"""Returns a list of Subcommand objects from all loaded plugins.
"""
out = []
for plugin in find_plugins():
out += plugin.commands()
return out

View file

@ -24,6 +24,7 @@ import textwrap
import ConfigParser
from beets import library
from beets import plugins
# Constants.
CONFIG_FILE = os.path.expanduser('~/.beetsconfig')
@ -340,7 +341,9 @@ def main():
from beets.ui.commands import default_commands
# Construct the root parser.
parser = SubcommandsOptionParser(subcommands=default_commands)
commands = list(default_commands)
commands += plugins.commands()
parser = SubcommandsOptionParser(subcommands=commands)
parser.add_option('-l', '--library', dest='libpath',
help='library database file to use')
parser.add_option('-d', '--directory', dest='directory',
@ -378,3 +381,4 @@ def main():
except UserError, exc:
message = exc.args[0] if exc.args else None
subcommand.parser.error(message)

View file

@ -505,3 +505,4 @@ def stats_func(lib, config, opts, args):
show_stats(lib, ui.make_query(args))
stats_cmd.func = stats_func
default_commands.append(stats_cmd)

0
beetsplug/__init__.py Normal file
View file

View file

@ -22,7 +22,7 @@ def _read(fn):
return open(path).read()
setup(name='beets',
version='1.0b2',
version='1.0b3',
description='music tagger and library organizer',
author='Adrian Sampson',
author_email='adrian@radbox.org',
@ -37,7 +37,9 @@ setup(name='beets',
'beets.ui',
'beets.autotag',
'beets.player',
'beetsplug',
],
namespace_packages=['beetsplug'],
scripts=['beet'],
install_requires=[