forked from Surachai-kent/terminator
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathplugin.py
More file actions
189 lines (162 loc) · 6.14 KB
/
plugin.py
File metadata and controls
189 lines (162 loc) · 6.14 KB
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
# Terminator by Chris Jones <cmsj@tenshu.net>
# GPL v2 only
"""plugin.py - Base plugin system
Inspired by Armin Ronacher's post at
http://lucumr.pocoo.org/2006/7/3/python-plugin-system
Used with permission (the code in that post is to be
considered BSD licenced, per the authors wishes)
>>> registry = PluginRegistry()
>>> isinstance(registry.instances, dict)
True
>>> registry.enable('TestPlugin')
>>> registry.load_plugins()
>>> plugins = registry.get_plugins_by_capability('test')
>>> len(plugins)
1
>>> plugins[0] #doctest: +ELLIPSIS
<testplugin.TestPlugin object at 0x...>
>>> registry.get_plugins_by_capability('this_should_not_ever_exist')
[]
>>> plugins[0].do_test()
'TestPluginWin'
"""
import sys
import os
from . import borg
from .config import Config
from .util import dbg, err, get_config_dir
from .terminator import Terminator
class Plugin(object):
"""Definition of our base plugin class"""
capabilities = None
def __init__(self):
"""Class initialiser."""
pass
def unload(self):
"""Prepare to be unloaded"""
pass
class PluginRegistry(borg.Borg):
"""Definition of a class to store plugin instances"""
available_plugins = None
instances = None
path = None
done = None
def __init__(self):
"""Class initialiser"""
borg.Borg.__init__(self, self.__class__.__name__)
self.prepare_attributes()
def prepare_attributes(self):
"""Prepare our attributes"""
if not self.instances:
self.instances = {}
if not self.path:
self.path = []
(head, _tail) = os.path.split(borg.__file__)
self.path.append(os.path.join(head, 'plugins'))
self.path.append(os.path.join(get_config_dir(), 'plugins'))
dbg('Plugin path: %s' % self.path)
if not self.done:
self.done = False
if not self.available_plugins:
self.available_plugins = {}
def load_plugins(self):
"""Load all plugins present in the plugins/ directory in our module"""
if self.done:
dbg('Already loaded')
return
config = Config()
for plugindir in self.path:
sys.path.insert(0, plugindir)
try:
files = os.listdir(plugindir)
except OSError:
sys.path.remove(plugindir)
continue
for plugin in files:
if plugin == '__init__.py':
continue
pluginpath = os.path.join(plugindir, plugin)
if os.path.isfile(pluginpath) and plugin[-3:] == '.py':
dbg('Importing plugin %s' % plugin)
try:
module = __import__(plugin[:-3], None, None, [''])
for item in getattr(module, 'AVAILABLE'):
if item not in list(self.available_plugins.keys()):
func = getattr(module, item)
self.available_plugins[item] = func
if item not in config['enabled_plugins']:
dbg('plugin %s not enabled, skipping' % item)
continue
if item not in self.instances:
self.instances[item] = func()
except Exception as ex:
err('PluginRegistry::load_plugins: Importing plugin %s \
failed: %s' % (plugin, ex))
self.done = True
def get_plugins_by_capability(self, capability):
"""Return a list of plugins with a particular capability"""
result = []
dbg('searching %d plugins \
for %s' % (len(self.instances), capability))
for plugin in self.instances:
if capability in self.instances[plugin].capabilities:
result.append(self.instances[plugin])
return result
def get_all_plugins(self):
"""Return all plugins"""
return(self.instances)
def get_available_plugins(self):
"""Return a list of all available plugins whether they are enabled or
disabled"""
return(list(self.available_plugins.keys()))
def is_enabled(self, plugin):
"""Return a boolean value indicating whether a plugin is enabled or
not"""
return(plugin in self.instances)
def enable(self, plugin):
"""Enable a plugin"""
if plugin in self.instances:
err("Cannot enable plugin %s, already enabled" % plugin)
dbg("Enabling %s" % plugin)
self.instances[plugin] = self.available_plugins[plugin]()
def disable(self, plugin):
"""Disable a plugin"""
dbg("Disabling %s" % plugin)
self.instances[plugin].unload()
del(self.instances[plugin])
# This is where we should define a base class for each type of plugin we
# support
# URLHandler - This adds a regex match to the Terminal widget and provides a
# callback to turn that into a URL.
class URLHandler(Plugin):
"""Base class for URL handlers"""
capabilities = ['url_handler']
handler_name = None
match = None
nameopen = None
namecopy = None
def __init__(self):
"""Class initialiser"""
Plugin.__init__(self)
terminator = Terminator()
for terminal in terminator.terminals:
terminal.match_add(self.handler_name, self.match)
def callback(self, url):
"""Callback to transform the enclosed URL"""
raise NotImplementedError
def unload(self):
"""Handle being removed"""
if not self.handler_name:
err('unload called without self.handler_name being set')
return
terminator = Terminator()
for terminal in terminator.terminals:
terminal.match_remove(self.handler_name)
# MenuItem - This is able to execute code during the construction of the
# context menu of a Terminal.
class MenuItem(Plugin):
"""Base class for menu items"""
capabilities = ['terminal_menu']
def callback(self, menuitems, menu, terminal):
"""Callback to transform the enclosed URL"""
raise NotImplementedError