aboutsummaryrefslogtreecommitdiffstats
path: root/mediagoblin/tools/pluginapi.py
diff options
context:
space:
mode:
Diffstat (limited to 'mediagoblin/tools/pluginapi.py')
-rw-r--r--mediagoblin/tools/pluginapi.py100
1 files changed, 40 insertions, 60 deletions
diff --git a/mediagoblin/tools/pluginapi.py b/mediagoblin/tools/pluginapi.py
index 0c338540..bf3775d5 100644
--- a/mediagoblin/tools/pluginapi.py
+++ b/mediagoblin/tools/pluginapi.py
@@ -15,8 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-This module implements the plugin api bits and provides the plugin
-base.
+This module implements the plugin api bits.
Two things about things in this module:
@@ -30,8 +29,8 @@ How do plugins work?
====================
Plugins are structured like any Python project. You create a Python package.
-In that package, you define a high-level ``__init__.py`` that either defines
-or imports modules that define classes that inherit from the ``Plugin`` class.
+In that package, you define a high-level ``__init__.py`` module that has a
+``hooks`` dict that maps hooks to callables that implement those hooks.
Additionally, you want a LICENSE file that specifies the license and a
``setup.py`` that specifies the metadata for packaging your plugin. A rough
@@ -42,23 +41,19 @@ file structure could look like this::
|- README # holds plugin project information
|- LICENSE # holds license information
|- myplugin/ # plugin package directory
- |- __init__.py # imports myplugin.main
- |- main.py # code for plugin
+ |- __init__.py # has hooks dict and code
Lifecycle
=========
1. All the modules listed as subsections of the ``plugins`` section in
- the config file are imported. This causes any ``Plugin`` subclasses in
- those modules to be defined and when the classes are defined they get
- automatically registered with the ``PluginCache``.
+ the config file are imported. MediaGoblin registers any hooks in
+ the ``hooks`` dict of those modules.
-2. After all plugin modules are imported, registered plugin classes are
- instantiated and ``setup_plugin`` is called for each plugin object.
+2. After all plugin modules are imported, the ``setup`` hook is called
+ allowing plugins to do any set up they need to do.
- Plugins can do any setup they need to do in their ``setup_plugin``
- method.
"""
import logging
@@ -69,14 +64,19 @@ from mediagoblin import mg_globals
_log = logging.getLogger(__name__)
-class PluginCache(object):
- """Cache of plugin things"""
+class PluginManager(object):
+ """Manager for plugin things
+
+ .. Note::
+
+ This is a Borg class--there is one and only one of this class.
+ """
__state = {
# list of plugin classes
- "plugin_classes": [],
+ "plugins": [],
- # list of plugin objects
- "plugin_objects": [],
+ # map of hook names -> list of callables for that hook
+ "hooks": {},
# list of registered template paths
"template_paths": set(),
@@ -87,19 +87,31 @@ class PluginCache(object):
def clear(self):
"""This is only useful for testing."""
- del self.plugin_classes[:]
- del self.plugin_objects[:]
+ # Why lists don't have a clear is not clear.
+ del self.plugins[:]
+ del self.routes[:]
+ self.hooks.clear()
+ self.template_paths.clear()
def __init__(self):
self.__dict__ = self.__state
- def register_plugin_class(self, plugin_class):
+ def register_plugin(self, plugin):
"""Registers a plugin class"""
- self.plugin_classes.append(plugin_class)
+ self.plugins.append(plugin)
+
+ def register_hooks(self, hook_mapping):
+ """Takes a hook_mapping and registers all the hooks"""
+ for hook, callables in hook_mapping.items():
+ if isinstance(callables, (list, tuple)):
+ self.hooks.setdefault(hook, []).extend(list(callables))
+ else:
+ # In this case, it's actually a single callable---not a
+ # list of callables.
+ self.hooks.setdefault(hook, []).append(callables)
- def register_plugin_object(self, plugin_obj):
- """Registers a plugin object"""
- self.plugin_objects.append(plugin_obj)
+ def get_hook_callables(self, hook_name):
+ return self.hooks.get(hook_name, [])
def register_template_path(self, path):
"""Registers a template path"""
@@ -117,38 +129,6 @@ class PluginCache(object):
return tuple(self.routes)
-class MetaPluginClass(type):
- """Metaclass for PluginBase derivatives"""
- def __new__(cls, name, bases, attrs):
- new_class = super(MetaPluginClass, cls).__new__(cls, name, bases, attrs)
- parents = [b for b in bases if isinstance(b, MetaPluginClass)]
- if not parents:
- return new_class
- PluginCache().register_plugin_class(new_class)
- return new_class
-
-
-class Plugin(object):
- """Extend this class for plugins.
-
- Example::
-
- from mediagoblin.tools.pluginapi import Plugin
-
- class MyPlugin(Plugin):
- ...
-
- def setup_plugin(self):
- ....
-
- """
-
- __metaclass__ = MetaPluginClass
-
- def setup_plugin(self):
- pass
-
-
def register_routes(routes):
"""Registers one or more routes
@@ -182,9 +162,9 @@ def register_routes(routes):
"""
if isinstance(routes, (tuple, list)):
for route in routes:
- PluginCache().register_route(route)
+ PluginManager().register_route(route)
else:
- PluginCache().register_route(routes)
+ PluginManager().register_route(routes)
def register_template_path(path):
@@ -205,7 +185,7 @@ def register_template_path(path):
that will have no effect on template loading.
"""
- PluginCache().register_template_path(path)
+ PluginManager().register_template_path(path)
def get_config(key):