Package libxyz :: Package core :: Package plugins :: Module manager
[hide private]
[frames] | no frames]

Source Code for Module libxyz.core.plugins.manager

  1  #-*- coding: utf8 -* 
  2  # 
  3  # Max E. Kuznecov ~syhpoon <syhpoon@syhpoon.name> 2008 
  4  # 
  5  # This file is part of XYZCommander. 
  6  # XYZCommander is free software: you can redistribute it and/or modify 
  7  # it under the terms of the GNU Lesser Public License as published by 
  8  # the Free Software Foundation, either version 3 of the License, or 
  9  # (at your option) any later version. 
 10  # XYZCommander is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 13  # GNU Lesser Public License for more details. 
 14  # You should have received a copy of the GNU Lesser Public License 
 15  # along with XYZCommander. If not, see <http://www.gnu.org/licenses/>. 
 16   
 17  import sys 
 18  import os.path 
 19   
 20  from libxyz.exceptions import PluginError 
 21  from libxyz.exceptions import XYZValueError 
 22  from libxyz.core.plugins import BasePlugin 
 23  from libxyz.core.plugins import Namespace 
 24  from libxyz.core.utils import ustring 
25 26 -def ns_transform(func):
27 """ 28 Transform passed ns plugin path to libxyz.core.plugins.Namespace instance 29 """ 30 31 def _trans(instance, *args, **kwargs): 32 _path, _rest = args[0], args[1:] 33 34 if not isinstance(_path, Namespace): 35 _path = Namespace(_path) 36 37 return func(instance, _path, *_rest, **kwargs)
38 39 return _trans 40
41 #++++++++++++++++++++++++++++++++++++++++++++++++ 42 43 -class PluginManager(object):
44 """ 45 Plugin manager class 46 It is supposed to provide easy access to plugin data 47 """ 48 49 PLUGIN_CLASS = u"XYZPlugin" 50 PLUGIN_FILE = u"main" 51 VIRTUAL_NAMESPACE = u"sys" 52
53 - def __init__(self, xyz, dirs):
54 """ 55 @param xyz: XYZ data 56 @param dirs: Plugin directories list 57 @type dirs: list 58 """ 59 60 if not isinstance(dirs, list): 61 raise XYZValueError(_(u"Invalid argument type %s. List expected" % 62 type(dirs))) 63 else: 64 sys.path.extend(dirs) 65 66 self.xyz = xyz 67 68 self.enabled = self._enabled_list() 69 # Do not load all the enabled plugin at once 70 # Do it on demand 71 self._loaded = {} 72 self._waiting = {}
73 74 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 75 76 @ns_transform
77 - def load(self, plugin, *initargs, **initkwargs):
78 """ 79 Load and initiate required plugin 80 @param plugin: Plugin namespace path 81 @param initargs: Necessary arguments to initiate plugin 82 @param initkwargs: Necessary kw arguments to initiate plugin 83 """ 84 85 virtual = self.is_virtual(plugin) 86 87 if plugin.pfull not in self.enabled: 88 raise PluginError(_(u"Plugin %s is disabled or does not exist" % 89 plugin)) 90 91 if self.is_loaded(plugin): 92 return self.get_loaded(plugin) 93 94 if virtual: 95 # Do not raise any error here, because virtual plugins may be 96 # initiated at runtime after keys conf is parsed. 97 return None 98 99 plugin.set_method(self.PLUGIN_FILE) 100 101 # Import plugin 102 # Plugin entry-point is XYZPlugin class in a main.py file 103 try: 104 _loaded = __import__(plugin.internal, globals(), locals(), 105 [self.PLUGIN_CLASS]) 106 except ImportError, e: 107 raise PluginError(_(u"Unable to load plugin %s: %s" % (plugin, e))) 108 109 try: 110 _loaded = getattr(_loaded, self.PLUGIN_CLASS) 111 except AttributeError, e: 112 raise PluginError(_(u"Unable to find required %s class" % \ 113 self.PLUGIN_CLASS)) 114 115 # Initiate plugin 116 _obj = _loaded(self.xyz, *initargs, **initkwargs) 117 118 # Run prepare (constructor) 119 _obj.prepare() 120 121 self.set_loaded(plugin, _obj) 122 123 return _obj
124 125 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 126 127 @ns_transform
128 - def reload(self, plugin, *initargs, **initkwargs):
129 """ 130 Force load plugin if it's already in cache. 131 """ 132 133 if self.is_virtual(plugin): 134 # Virtual plugins do not support reloading 135 return None 136 137 if self.is_loaded(plugin): 138 self.del_loaded(plugin) 139 140 return self.load(plugin, *initargs, **initkwargs)
141 142 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 143 144 @ns_transform
145 - def from_load(self, plugin, method):
146 """ 147 Load method from plugin. 148 If plugin was not loaded before, load and initiate it first. 149 150 @param plugin: Plugin namespace path 151 @param method: Public method name 152 """ 153 154 if not self.is_loaded(plugin): 155 _obj = self.load(plugin) 156 else: 157 _obj = self.get_loaded(plugin) 158 159 # Possible case for virtual plugins 160 if _obj is None: 161 return None 162 163 if method not in _obj.public: 164 raise PluginError(_(u"%s plugin instance does not export "\ 165 u"method %s" % (plugin, method))) 166 else: 167 return _obj.public[method]
168 169 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 170 171 @ns_transform
172 - def from_load_data(self, plugin, obj):
173 """ 174 Load data object from plugin. 175 If plugin was not loaded before, load and initiate it first. 176 177 @param plugin: Plugin namespace path 178 @param method: Public data object name 179 """ 180 181 if not self.is_loaded(plugin): 182 _obj = self.load(plugin) 183 else: 184 _obj = self.get_loaded(plugin) 185 186 if obj not in _obj.public_data: 187 raise PluginError(_(u"%s plugin instance does not export "\ 188 u"data object %s" % (plugin, obj))) 189 else: 190 return _obj.public_data[obj]
191 192 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 193 194 @ns_transform
195 - def is_loaded(self, plugin):
196 """ 197 Check if plugin already loaded 198 @param plugin: Plugin namespace path 199 """ 200 201 return plugin.full in self._loaded
202 203 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 204 205 @ns_transform
206 - def get_loaded(self, plugin=None):
207 """ 208 Return loaded and initiated inistance of plugin 209 @param plugin: Plugin namespace path 210 """ 211 212 return self._loaded[plugin.pfull]
213 214 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 215
216 - def get_all_loaded(self):
217 """ 218 Return all currenty loaded plugins as dictionary with plugins ns path 219 as keys and instances as values 220 """ 221 222 return self._loaded
223 224 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 225 226 @ns_transform
227 - def set_loaded(self, plugin, inst):
228 """ 229 Set loaded and initiated inistance of plugin 230 @param plugin: Plugin namespace path 231 @param inst: Plugin instance 232 """ 233 234 # Check for pending waiting plugins 235 if plugin.pfull in self._waiting: 236 # Try to run callback 237 for _cb, _args in self._waiting[plugin.pfull]: 238 try: 239 _cb(inst, *_args) 240 except Exception, e: 241 xyzlog.warning(_(u"Error in wait_for() callback: %s" % 242 ustring(str(e)))) 243 del(self._waiting[plugin.pfull]) 244 245 self._loaded[plugin.pfull] = inst
246 247 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 248 249 @ns_transform
250 - def del_loaded(self, plugin):
251 """ 252 Delete loaded instance from cache 253 @param plugin: Plugin namespace path 254 """ 255 256 try: 257 self.shutdown(plugin.pfull) 258 del(self._loaded[plugin.pfull]) 259 except KeyError: 260 pass
261 262 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 263 264 @ns_transform
265 - def wait_for(self, plugin, callback, *args):
266 """ 267 Some virtual plugins are not available at the parsing time. 268 This method is used to wait while plugin is loaded and then run 269 callback. 270 Arguments to callback: loaded plugin obj, and all optional *args passed 271 """ 272 273 # WTF? already loaded? No need to wait 274 if self.is_loaded(plugin): 275 return callback(self.get_loaded(plugin), *args) 276 277 # Initiate storage 278 if plugin.pfull not in self._waiting: 279 self._waiting[plugin.pfull] = [] 280 281 # Register for waiting 282 self._waiting[plugin.pfull].append((callback, args))
283 284 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 285
286 - def shutdown(self, plugin=None):
287 """ 288 Run destructors on specified or all loaded plugins 289 """ 290 291 def _fin(p): 292 try: 293 self._loaded[p].finalize() 294 except Exception: 295 pass
296 297 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 298 299 if plugin is not None: 300 _fin(plugin) 301 else: 302 for plugin_name in self._loaded: 303 _fin(plugin_name)
304 305 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 306
307 - def register(self, obj):
308 """ 309 Register new plugin. 310 @param obj: libxyz.core.BasePlugin inherited instance 311 """ 312 313 if not isinstance(obj, BasePlugin): 314 raise XYZValueError(_(u"BasePlugin instance expected, got: %s" % 315 type(obj))) 316 317 self.set_loaded(obj.ns, obj)
318 319 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 320
321 - def is_virtual(self, plugin):
322 return plugin.ns == self.VIRTUAL_NAMESPACE
323 324 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 325
326 - def _enabled_list(self):
327 """ 328 Make list of enabled plugins 329 """ 330 331 _data = self.xyz.conf[u"xyz"][u"plugins"] 332 return [_pname for _pname in _data if _data[_pname]]
333