diff options
Diffstat (limited to 'src/pybind/mgr/dashboard/plugins/__init__.py')
-rw-r--r-- | src/pybind/mgr/dashboard/plugins/__init__.py | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/plugins/__init__.py b/src/pybind/mgr/dashboard/plugins/__init__.py new file mode 100644 index 00000000..43d71fde --- /dev/null +++ b/src/pybind/mgr/dashboard/plugins/__init__.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import abc +import six + +from .pluggy import HookspecMarker, HookimplMarker, PluginManager + + +@six.add_metaclass(abc.ABCMeta) +class Interface(object): + pass + + +class Mixin(object): + pass + + +class DashboardPluginManager(object): + def __init__(self, project_name): + self.__pm = PluginManager(project_name) + self.__add_spec = HookspecMarker(project_name) + self.__add_abcspec = lambda *args, **kwargs: abc.abstractmethod( + self.__add_spec(*args, **kwargs)) + self.__add_hook = HookimplMarker(project_name) + + pm = property(lambda self: self.__pm) + hook = property(lambda self: self.pm.hook) + + add_spec = property(lambda self: self.__add_spec) + add_abcspec = property(lambda self: self.__add_abcspec) + add_hook = property(lambda self: self.__add_hook) + + def add_interface(self, cls): + assert issubclass(cls, Interface) + self.pm.add_hookspecs(cls) + return cls + + @staticmethod + def final(func): + setattr(func, '__final__', True) + return func + + def add_plugin(self, plugin): + """ Provides decorator interface for PluginManager.register(): + @PLUGIN_MANAGER.add_plugin + class Plugin(...): + ... + Additionally it checks whether the Plugin instance has all Interface + methods implemented and marked with add_hook decorator. + As a con of this approach, plugins cannot call super() from __init__() + """ + assert issubclass(plugin, Interface) + from inspect import getmembers, ismethod + for interface in plugin.__bases__: + for method_name, _ in getmembers(interface, predicate=ismethod): + if hasattr(getattr(interface, method_name), '__final__'): + continue + + if self.pm.parse_hookimpl_opts(plugin, method_name) is None: + raise NotImplementedError( + "Plugin '{}' implements interface '{}' but existing" + " method '{}' is not declared added as hook".format( + plugin.__name__, + interface.__name__, + method_name)) + self.pm.register(plugin()) + return plugin + + +PLUGIN_MANAGER = DashboardPluginManager("ceph-mgr.dashboard") + +# Load all interfaces and their hooks +from . import interfaces |