-
Notifications
You must be signed in to change notification settings - Fork 0
/
plugin.py
211 lines (172 loc) · 7.12 KB
/
plugin.py
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# -*- coding: utf-8 -*-
"""The plugin system of Collector"""
from abc import ABCMeta, abstractproperty, abstractmethod
from provider import UrlProvider
import glob
import os
import sys
import logging
from config import Config
class Plugin(object):
"""Base class for all the plugins."""
__metaclass__ = ABCMeta
@abstractproperty
def get_name(self):
"""Returns the name of the plugin."""
@abstractproperty
def get_author(self):
"""Returns the name of the author."""
def get_id(self):
"""Returns the identifier of the plugin."""
return self.__class__.__name__
@abstractproperty
def icon(self):
"""Returns the plugin icon"""
class PluginRunnable(Plugin):
"""A plug-in that is runnable, that means that the method *run* will be
executed when it is called or loaded if *autorun* is enabled."""
# This is only to avoid the pylint warning:
# PluginRunnable.autorun: Method could be a function
___autorun_default = False
@abstractmethod
def run(self):
"""The code to run when the plugin is executed."""
def autorun(self):
"""By default autorun is disabled."""
return self.___autorun_default
class PluginCollector(Plugin):
"""This plugin allows recover information from websites or HTML for fill
or discover new entries for your collections"""
def search(self, query, provider=None):
"""Searches results that match the requested query"""
if provider is None:
provider = UrlProvider(self.search_uri())
html = provider.get(query)
return self.search_filter(html)
def get(self, uri, provider=None):
"""Returns the collection file of the requested URI"""
if provider is None:
provider = UrlProvider(uri)
html = provider.get('')
return self.file_filter(html)
@abstractproperty
def search_uri(self):
""""Returns the URI for search"""
@abstractmethod
def search_filter(self, html):
"""Looks for entries in the HTML code"""
@abstractmethod
def file_filter(self, html):
"""Looks for fields in the HTML code"""
class PluginImporter(PluginRunnable):
"""Marker for importer plugins"""
class PluginExporter(PluginRunnable):
"""Marker for exporter plugins"""
class PluginManager(object):
"""Manager for the plugin system"""
_instance = None
def __init__(self, enabled=None, plugins=None, paths=None):
"""PluginManager manages the avaible, enable/disable and discover
plugins.
Don't call directly the constructor use:
PluginManager.get_instance()
The arguments optional arguments of the constructor are:
*enabled* a list of all the enabled plugins by default
*plugins* a dictionary of plugins {id: Plugin} that are preloaded
*paths* a list of paths to look for plugins"""
if PluginManager._instance is not None:
raise Exception("Called more that once")
PluginManager._instance = self
# else
if plugins is None:
plugins = {}
if enabled is None:
enabled = []
self.enabled = enabled
if paths is None:
paths = []
self.paths = []
self.plugins = plugins
self.look_for_plugins(paths)
@staticmethod
def get_instance(enabled=None, plugins=None, paths=None):
"""Returns the instance of the plugin manager, it will create one if it
doesn't exits"""
if PluginManager._instance is None:
PluginManager._instance = PluginManager(enabled, plugins, paths)
return PluginManager._instance
def look_for_plugins(self, paths):
"""Discovers all the plug-ins that exists in all the paths received
as argument"""
# Append existing non existing paths to self.paths
self.paths.extend([path for path in paths if path not in self.paths])
# Look for new plug-ins
for path in paths:
f_path = os.path.abspath(path)
pyfiles = glob.glob(os.path.join(f_path, '*.py'))
# register pyfile in sys.path
if len(pyfiles) > 0 and not f_path in sys.path:
sys.path.append(f_path)
# import the plugin
for i in pyfiles:
try:
module = os.path.basename(i)[:-3]
classname = 'Plugin' + module.capitalize()
temp = __import__(module,
globals(), locals(),
fromlist=[classname])
class_definition = getattr(temp, classname)
if issubclass(class_definition, Plugin):
logging.info("PluginManager: discovered plug-in %s",
module)
plugin = class_definition()
self.register_plugin(plugin)
# Auto-execute plug-ins
if (issubclass(class_definition, PluginRunnable) and
plugin.get_id() in self.enabled and
plugin.autorun()):
plugin.run()
except Exception as e:
logging.exception(e)
def is_enabled(self, _id):
"""Checks if the plugin *_id* is enabled and if it was returns True"""
return _id in self.enabled
def get(self, _id):
"""Returns the plugin with identifier _id"""
return self.plugins[_id]
def filter(self, subclass):
"""Returns all the plugins that implements the subclass"""
return [i for i in self.enabled
if isinstance(self.plugins[i], subclass)]
def get_enabled(self):
"""Returns a list of all the enabled plug-ins"""
return self.enabled
def get_disabled(self):
"""Returns a list with all the disabled plug-ins"""
return [plugin for plugin in self.plugins
if plugin not in self.enabled]
def enable(self, pluginlist):
"""Turns on all the plug-ins of *pluginlist*, *pluginlist* must be a
*list* of identifiers."""
if not isinstance(pluginlist, list):
raise TypeError()
logging.info("Enabling plug-ins %s", pluginlist)
for i in pluginlist:
# Check if plug-in exists and is not yet enabled
if i in self.plugins and i not in self.enabled:
self.enabled.append(i)
def disable(self, pluginlist):
"""Disables all the plug-ins which identifier is in *pluginlist*"""
if not isinstance(pluginlist, list):
raise TypeError()
for i in pluginlist:
if i in self.plugins and i in self.enabled:
self.enabled.remove(i)
def register_plugin(self, plugin):
"""Add to the available plug-ins the plug-in received as argument"""
self.plugins[plugin.get_id()] = plugin
def save(self):
"""Save the enabled plugins using config"""
config = Config.get_instance()
config.set('plugins_enabled', self.get_enabled())
config.save()