comparison src/core/sat_main.py @ 1535:c9ef16de3f13

core: more robust plugins loading: - avoid stopping loading remaining plugins when a plugin raise an error - new error exceptions.MissingModule allows to detect a missing third party module, and to give instructions to get it - error during instanciation are catched too
author Goffi <goffi@goffi.org>
date Tue, 29 Sep 2015 17:54:23 +0200
parents d749922300d0
children 465d4d484e7c
comparison
equal deleted inserted replaced
1534:a5e0393a06cd 1535:c9ef16de3f13
149 plugins_to_import = {} # plugins we still have to import 149 plugins_to_import = {} # plugins we still have to import
150 for plug in plug_lst: 150 for plug in plug_lst:
151 plugin_path = 'sat.plugins.' + plug 151 plugin_path = 'sat.plugins.' + plug
152 try: 152 try:
153 __import__(plugin_path) 153 __import__(plugin_path)
154 except ImportError as e: 154 except exceptions.MissingModule as e:
155 log.error(_(u"Can't import plugin [%(path)s]: %(error)s") % {'path': plugin_path, 'error':e}) 155 try:
156 del sys.modules[plugin_path]
157 except KeyError:
158 pass
159 log.warning(u"Can't import plugin [{path}] because of an unavailale third party module:\n{msg}".format(
160 path=plugin_path, msg=e))
161 continue
162 except Exception as e:
163 import traceback
164 log.error(_(u"Can't import plugin [{path}]:\n{error}").format(path=plugin_path, error=traceback.format_exc()))
156 continue 165 continue
157 mod = sys.modules[plugin_path] 166 mod = sys.modules[plugin_path]
158 plugin_info = mod.PLUGIN_INFO 167 plugin_info = mod.PLUGIN_INFO
159 import_name = plugin_info['import_name'] 168 import_name = plugin_info['import_name']
160 if import_name in plugins_to_import: 169 if import_name in plugins_to_import:
161 log.error(_(u"Name conflict for import name [{import_name}], can't import plugin [{name}]").format(**plugin_info)) 170 log.error(_(u"Name conflict for import name [{import_name}], can't import plugin [{name}]").format(**plugin_info))
162 continue 171 continue
163 plugins_to_import[import_name] = (plugin_path, mod, plugin_info) 172 plugins_to_import[import_name] = (plugin_path, mod, plugin_info)
164 while True: 173 while True:
165 self._import_plugins_from_dict(plugins_to_import) 174 try:
175 self._import_plugins_from_dict(plugins_to_import)
176 except ImportError:
177 pass
166 if not plugins_to_import: 178 if not plugins_to_import:
167 break 179 break
168 180
169 def _import_plugins_from_dict(self, plugins_to_import, import_name=None, optional=False): 181 def _import_plugins_from_dict(self, plugins_to_import, import_name=None, optional=False):
170 """Recursively import and their dependencies in the right order 182 """Recursively import and their dependencies in the right order
171 @param plugins_to_import: dict where key=import_name and values= (plugin_path, module, plugin_info) 183
172 @param import_name: name of the plugin to import as found in PLUGIN_INFO['import_name'] 184 @param plugins_to_import(dict): key=import_name and values=(plugin_path, module, plugin_info)
173 @param optional: if False and plugin is not found, an ImportError exception is raised 185 @param import_name(unicode, None): name of the plugin to import as found in PLUGIN_INFO['import_name']
174 186 @param optional(bool): if False and plugin is not found, an ImportError exception is raised
175 """ 187 """
176 if import_name in self.plugins: 188 if import_name in self.plugins:
177 log.debug(u'Plugin [%s] already imported, passing' % import_name) 189 log.debug(u'Plugin {} already imported, passing'.format(import_name))
178 return 190 return
179 if not import_name: 191 if not import_name:
180 import_name, (plugin_path, mod, plugin_info) = plugins_to_import.popitem() 192 import_name, (plugin_path, mod, plugin_info) = plugins_to_import.popitem()
181 else: 193 else:
182 if not import_name in plugins_to_import: 194 if not import_name in plugins_to_import:
183 if optional: 195 if optional:
184 log.warning(_(u"Recommended plugin not found: %s") % import_name) 196 log.warning(_(u"Recommended plugin not found: {}").format(import_name))
185 return 197 return
186 log.error(_(u"Dependency not found: %s") % import_name) 198 msg = u"Dependency not found: {}".format(import_name)
187 raise ImportError(_('Dependency plugin not found: [%s]') % import_name) 199 log.error(msg)
200 raise ImportError(msg)
188 plugin_path, mod, plugin_info = plugins_to_import.pop(import_name) 201 plugin_path, mod, plugin_info = plugins_to_import.pop(import_name)
189 dependencies = plugin_info.setdefault("dependencies", []) 202 dependencies = plugin_info.setdefault("dependencies", [])
190 recommendations = plugin_info.setdefault("recommendations", []) 203 recommendations = plugin_info.setdefault("recommendations", [])
191 for to_import in dependencies + recommendations: 204 for to_import in dependencies + recommendations:
192 if to_import not in self.plugins: 205 if to_import not in self.plugins:
193 log.debug(u'Recursively import dependency of [%s]: [%s]' % (import_name, to_import)) 206 log.debug(u'Recursively import dependency of [%s]: [%s]' % (import_name, to_import))
194 try: 207 try:
195 self._import_plugins_from_dict(plugins_to_import, to_import, to_import not in dependencies) 208 self._import_plugins_from_dict(plugins_to_import, to_import, to_import not in dependencies)
196 except ImportError as e: 209 except ImportError as e:
197 log.error(_(u"Can't import plugin %(name)s: %(error)s") % {'name':plugin_info['name'], 'error':e}) 210 log.warning(_(u"Can't import plugin {name}: {error}").format(name=plugin_info['name'], error=e))
198 return 211 if optional:
199 log.info(_("importing plugin: %s") % plugin_info['name']) 212 return
200 self.plugins[import_name] = getattr(mod, plugin_info['main'])(self) 213 raise e
214 log.info("importing plugin: {}".format(plugin_info['name']))
215 # we instanciate the plugin here
216 try:
217 self.plugins[import_name] = getattr(mod, plugin_info['main'])(self)
218 except Exception as e:
219 log.warning(u'Error while loading plugin "{name}", ignoring it: {error}'
220 .format(name=plugin_info['name'], error=e))
221 if optional:
222 return
223 raise ImportError(u"Error during initiation")
201 if 'handler' in plugin_info and plugin_info['handler'] == 'yes': 224 if 'handler' in plugin_info and plugin_info['handler'] == 'yes':
202 self.plugins[import_name].is_handler = True 225 self.plugins[import_name].is_handler = True
203 else: 226 else:
204 self.plugins[import_name].is_handler = False 227 self.plugins[import_name].is_handler = False
205 #TODO: test xmppclient presence and register handler parent 228 #TODO: test xmppclient presence and register handler parent