Completed
Push — master ( 2a692c...e7f9d6 )
by Felipe A.
01:07
created

browsepy.BlueprintPluginManager.load_plugin()   A

Complexity

Conditions 2

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 2
dl 0
loc 5
rs 9.4286
1
#!/usr/bin/env python
2
# -*- coding: UTF-8 -*-
3
4
import sys
5
import collections
6
7
from markupsafe import Markup
8
from flask import url_for
9
10
from . import mimetype
11
from .compat import isnonstriterable
12
13
14
class PluginNotFoundError(ImportError):
15
    pass
16
17
18
class PluginManagerBase(object):
19
20
    @property
21
    def namespaces(self):
22
        return self.app.config['plugin_namespaces']
23
24
    def __init__(self, app=None):
25
        if not app is None:
26
            self.init_app(app)
27
28
    def init_app(self, app):
29
        self.app = app
30
        if not hasattr(app, 'extensions'):
31
            app.extensions = {}
32
        app.extensions['plugin_manager'] = self
33
34
    def load_plugin(self, plugin):
35
        names = [
36
            '%s.%s' % (namespace, plugin) if namespace else plugin
37
            for namespace in self.namespaces
38
            ]
39
        for name in names:
40
            try:
41
                __import__(name)
42
                return sys.modules[name]
43
            except (ImportError, IndexError):
44
                pass
45
        raise PluginNotFoundError('No plugin module %r found, tried %r' % (plugin, names), plugin, names)
46
47
48
class BlueprintPluginManager(PluginManagerBase):
49
    def register_blueprint(self, blueprint):
50
        self.app.register_blueprint(blueprint)
51
52
    def load_plugin(self, plugin):
53
        module = super(BlueprintPluginManager, self).load_plugin(plugin)
54
        if hasattr(module, 'register_plugin'):
55
            module.register_plugin(self)
56
        return module
57
58
59
class WidgetBase(object):
60
    place = None
61
    def __init__(self, *args, **kwargs):
62
        self.args = args
63
        self.kwargs = kwargs
64
65
66
class ButtonWidget(WidgetBase):
67
    place = 'button'
68
    def __init__(self, html='', text='', css=""):
69
        self.content = Markup(html) if html else text
70
        self.css = css
71
72
73
class StyleWidget(WidgetBase):
74
    place = 'style'
75
76
    @property
77
    def href(self):
78
        return url_for(*self.args, **self.kwargs)
79
80
class JavascriptWidget(WidgetBase):
81
    place = 'javascript'
82
83
    @property
84
    def src(self):
85
        return url_for(*self.args, **self.kwargs)
86
87
88
class MimetypeActionPluginManager(PluginManagerBase):
89
    action_class = collections.namedtuple('MimetypeAction', ('endpoint', 'widget'))
90
    button_class = ButtonWidget
91
    style_class = StyleWidget
92
    javascript_class = JavascriptWidget
93
94
    def __init__(self, app=None):
95
        self._root = {}
96
        self._widgets = {}
97
        self._mimetype_functions = [
98
            mimetype.by_default,
99
            mimetype.by_file,
100
            mimetype.by_python
101
        ]
102
        super(MimetypeActionPluginManager, self).__init__(app=app)
103
104
    def get_mimetype(self, path):
105
        for fnc in reversed(self._mimetype_functions):
106
            mimetype = fnc(path)
107
            if mimetype:
108
                return mimetype
109
        return mimetype_by_default(path)
110
111
    def get_widgets(self, place):
112
        return self._widgets.get(place, [])
113
114
    def get_actions(self, mimetype):
115
        category, variant = mimetype.split('/')
116
        return [
117
            action
118
            for tree_category in (category, '*')
119
            for tree_variant in (variant, '*')
120
            for action in self._root.get(tree_category, {}).get(tree_variant, ())
121
            ]
122
123
    def register_mimetype_function(self, fnc):
124
        self._mimetype_functions.append(fnc)
125
126
    def register_widget(self, widget):
127
        self._widgets.setdefault(widget.place, []).append(widget)
128
129
    def register_action(self, endpoint, widget, mimetypes=(), **kwargs):
130
        mimetypes = mimetypes if isnonstriterable(mimetypes) else (mimetypes,)
131
        action = self.action_class(endpoint, widget)
132
        for mimetype in mimetypes:
133
            category, variant = mimetype.split('/')
134
            self._root.setdefault(category, {}).setdefault(variant, []).append(action)
135
136
137
class PluginManager(BlueprintPluginManager, MimetypeActionPluginManager):
138
    pass
139