3
import gglobals, os.path, glob, sys
6
from defaults.defaults import loc
9
current_path = os.path.split(os.path.join(os.getcwd(),__file__))[0]
13
# This module provides a base class for loading plugins. Everything
14
# that is plug-in-able in Gourmet should subclass the plugin loader.
16
# Everything that is a plugin needs to provide a python module with a
17
# plugins attribute containing the plugin classes that make up the
18
# plugin. In addition, we need a .gourmet-plugin configuration file
19
# pointing to the module (with the module parameter) and giving the
20
# name and comment for the plugin.
24
# Singleton design pattern lifted from:
25
# http://www.python.org/workshops/1997-10/proceedings/savikko.html
26
# to get an instance, use the convenience function
29
default_active_plugin_sets = [
34
active_plugin_filename = os.path.join(gglobals.gourmetdir,'active_plugins')
37
if MasterLoader.__single:
38
raise MasterLoader.__single
39
MasterLoader.__single = self
40
curfile = gglobals.__file__ # we count on plugins/ being in the same dir as gglobals
41
self.plugin_directories = [os.path.join(gglobals.gourmetdir,'plugins/'), # user plug-ins
42
os.path.join(current_path,'plugins'), # pre-installed plugins
43
os.path.join(current_path,'plugins','import_export'), # pre-installed exporter plugins
44
os.path.join(gglobals.datad,'plugins/'), # system-wide plug-ins (?)
46
self.pluggables_by_class = {}
47
self.load_plugin_directories()
48
self.load_active_plugins()
50
def load_plugin_directories (self):
51
"""Look through plugin directories for plugins.
53
self.available_plugin_sets = {}
54
for d in self.plugin_directories:
55
plugins = glob.glob('%s/*.gourmet-plugin'%d)
56
print 'found plugins:',plugins,'in',d
58
plugin_set = PluginSet(ppath)
59
if self.available_plugin_sets.has_key(plugin_set.module):
60
print 'Ignoring duplicate plugin ',plugin_set.module,'found in ',ppath
62
self.available_plugin_sets[plugin_set.module] = plugin_set
64
def load_active_plugins (self):
65
"""Activate plugins that have been activated on startup
67
if os.path.exists(self.active_plugin_filename):
68
infi = file(self.active_plugin_filename,'r')
69
self.active_plugin_sets = [l.strip() for l in infi.readlines()]
70
print 'active_plugin_sets = ',self.active_plugin_sets
72
self.active_plugin_sets = self.default_active_plugin_sets[:]
73
self.active_plugins = []
74
self.instantiated_plugins = {}
75
for p in self.active_plugin_sets:
76
if self.available_plugin_sets.has_key(p):
77
self.active_plugins.extend(self.available_plugin_sets[p].plugins)
79
print 'Plugin ',p,'not found'
81
def save_active_plugins (self):
82
# If we have not changed from the defaults and no
83
# configuration file exists, don't bother saving one.
84
if ((self.active_plugin_sets != self.default_active_plugin_sets)
86
os.path.exists(self.active_plugin_filename)):
87
print 'Saving active plugins to',self.active_plugin_filename
88
ofi = file(self.active_plugin_filename,'w')
89
for plugin_set in self.active_plugin_sets:
90
print 'Save module...',plugin_set
91
ofi.write(plugin_set+'\n')
94
elif self.active_plugin_sets == self.default_active_plugin_sets:
95
print 'No change to plugins, nothing to save.'
97
def activate_plugin_set (self, plugin_set):
98
"""Activate a set of plugins.
100
if not plugin_set in self.active_plugin_sets:
101
print 'ADD ',plugin_set,'TO ACTIVE_PLUGIN_SETS'
102
self.active_plugin_sets.append(plugin_set.module)
103
self.active_plugins.extend(plugin_set.plugins)
104
for plugin in plugin_set.plugins:
105
for klass in self.pluggables_by_class:
106
if issubclass(plugin,klass):
107
for pluggable in self.pluggables_by_class[klass]:
108
pluggable.plugin_plugin(self.get_instantiated_plugin(plugin))
110
def deactivate_plugin_set (self, plugin_set):
111
if plugin_set in self.active_plugin_sets:
112
print 'REMOVE ',plugin_set,'FROM ACTIVE_PLUGIN_SETS'
113
self.active_plugin_sets.remove(plugin_set)
114
for plugin in plugin_set.plugins:
115
if self.instantiated_plugins.has_key(plugin):
116
self.instantiated_plugins[plugin].remove()
117
self.active_plugins.remove(plugin)
119
def get_instantiated_plugin (self, plugin):
120
if self.instantiated_plugins.has_key(plugin):
121
return self.instantiated_plugins[plugin]
123
self.instantiated_plugins[plugin] = plugin()
124
return self.instantiated_plugins[plugin]
126
def register_pluggable (self, pluggable, klass):
127
if not self.pluggables_by_class.has_key(klass):
128
self.pluggables_by_class[klass] = []
129
self.pluggables_by_class[klass].append(pluggable)
130
for plugin in self.active_plugins:
131
print 'checking active plugin',plugin
132
if issubclass(plugin,klass):
133
print 'plugin ',plugin
135
plugin_instance = self.get_instantiated_plugin(plugin)
137
print 'Failed to instantiate plugin'
138
import traceback; traceback.print_exc()
140
pluggable.plugin_plugin(plugin_instance)
142
def unregister_pluggable (self, pluggable, klass):
143
self.pluggables_by_class[klass].remove(pluggable)
145
def get_master_loader ():
146
# Singleton design pattern lifted from:
147
# http://www.python.org/workshops/1997-10/proceedings/savikko.html
149
return MasterLoader()
150
except MasterLoader, ml:
154
"""A lazy-loading set of plugins.
156
This class encapsulates what to the end-user is a plugin.
158
From our perspective, plugins can really be a bundle of plugins --
159
for example, your plugin might require a DatabasePlugin, a
160
RecCardDisplayPlugin and a MainPlugin to function.
165
def __init__ (self, plugin_info_path):
166
f = file(plugin_info_path,'r')
167
self.load_plugin_file_data(f)
169
self.curdir, plugin_info_file = os.path.split(plugin_info_path)
170
self.module = self.props['Module']
172
def get_module (self):
176
if not self.curdir in sys.path:
177
sys.path.append(self.curdir)
179
self._loaded = __import__(self.module)
181
print 'PATH:',sys.path
185
def __getattr__ (self, attr):
186
if attr == 'plugins': return self.get_plugins()
187
elif self.props.has_key(attr): return self.props[attr]
188
elif self.props.has_key(attr.capitalize()): return self.props[attr.capitalize()]
189
else: raise AttributeError
191
def get_plugins (self):
192
return self.get_module().plugins
194
def load_plugin_file_data (self,plugin_info_file):
195
# This should really use GKeyFile but there are no python
196
# bindings that I can find atm. One possibility would be to
198
# http://svn.async.com.br/cgi-bin/viewvc.cgi/kiwi/trunk/kiwi/desktopparser.py?revision=7336&view=markup
199
self.props = dict([(k,None) for k in ['Name','Comment','Authors','Version','API_Version','Website','Copyright']])
201
for line in plugin_info_file.readlines():
202
if line=='[Gourmet Plugin]\n': pass
203
elif line.find('=')>0:
204
key,val = line.split('=')
205
key = key.strip(); val = val.strip()
208
key,locale = key.strip(']').split('[')
210
self.props[key] = val
211
elif locale[:2]==loc[:2]:
212
self.props[key] = val
216
print 'Ignoring line',line
219
"""A plugin-able class."""
221
def __init__ (self, plugin_klasses):
222
"""plugin_klasses is the list class of which each of our
223
plugins should be a sub-class.
225
A pluggable can take multiple types of sub-classes if it
228
print 'Pluggabe.__init__([',plugin_klasses,'])'
229
self.pre_hooks = {} # stores hooks to be run before methods by
231
self.post_hooks = {} # stores hooks to be run after methods by
233
self.loader = get_master_loader()
234
self.klasses = plugin_klasses
236
for klass in self.klasses:
237
print 'register self ',self,'as pluggable for ',klass
238
self.loader.register_pluggable(self,klass)
240
def plugin_plugin (self, plugin_instance):
242
print 'plugging in ',plugin_instance
243
self.plugins.append(plugin_instance)
245
plugin_instance.activate(self)
247
print 'PLUGIN FAILED TO LOAD'
248
import traceback; traceback.print_exc()
251
self.loader.unregister_pluggable(self,self.klass)
252
for pi in self.plugins:
253
print 'deactivate plugin',pi
256
def run_pre_hook (self, fname, *args, **kwargs):
257
print 'Looking for pre hooks for',fname
258
for hook in self.pre_hooks.get(fname,[]):
259
print 'run hook',hook
260
hook(self,*args,**kwargs)
262
def run_post_hook (self, fname, retval, *args, **kwargs):
263
print 'Looking for post hooks for',fname
264
for hook in self.post_hooks.get(fname,[]):
265
print 'run hook',hook
266
hook(retval,self,*args,**kwargs)
268
def add_hook (self, type, name, hook):
269
if type==PRE: hookdic = self.pre_hooks
270
else: hookdic = self.post_hooks
271
if not hookdic.has_key(name):
273
hookdic[name].append(hook)
275
def remove_hook (self, type, name, hook):
276
if type==PRE: hookdic = self.pre_hooks
277
else: hookdic = self.post_hooks
280
def pluggable_method (f):
281
def _ (self, *args, **kwargs):
282
'''Run hooks around method'''
283
self.run_pre_hook(f.__name__,*args,**kwargs)
284
retval = f(self,*args,**kwargs)
285
self.run_post_hook(f.__name__,retval,*args,**kwargs)
289
if __name__ == '__main__':
290
class TestPlugin (plugin.Plugin):
296
class UniversalPluggable (Pluggable):
298
Pluggable.__init__(self,[plugin.Plugin])
300
up = UniversalPluggable()
301
#up.loader.activate_plugin(