1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import abc
from .pluggy import HookimplMarker, HookspecMarker, PluginManager
class Interface(object, metaclass=abc.ABCMeta):
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 # noqa pylint: disable=C0413,W0406
|