Mercurial > libervia-backend
comparison sat/core/sat_main.py @ 2624:56f94936df1e
code style reformatting using black
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 27 Jun 2018 20:14:46 +0200 |
parents | 9446f1ea9eac |
children | 189e38fb11ff |
comparison
equal
deleted
inserted
replaced
2623:49533de4540b | 2624:56f94936df1e |
---|---|
25 from twisted.internet import reactor | 25 from twisted.internet import reactor |
26 from wokkel.xmppim import RosterItem | 26 from wokkel.xmppim import RosterItem |
27 from sat.core import xmpp | 27 from sat.core import xmpp |
28 from sat.core import exceptions | 28 from sat.core import exceptions |
29 from sat.core.log import getLogger | 29 from sat.core.log import getLogger |
30 | |
30 log = getLogger(__name__) | 31 log = getLogger(__name__) |
31 from sat.core.constants import Const as C | 32 from sat.core.constants import Const as C |
32 from sat.memory import memory | 33 from sat.memory import memory |
33 from sat.memory import cache | 34 from sat.memory import cache |
34 from sat.tools import trigger | 35 from sat.tools import trigger |
41 import sys | 42 import sys |
42 import os.path | 43 import os.path |
43 import uuid | 44 import uuid |
44 | 45 |
45 try: | 46 try: |
46 from collections import OrderedDict # only available from python 2.7 | 47 from collections import OrderedDict # only available from python 2.7 |
47 except ImportError: | 48 except ImportError: |
48 from ordereddict import OrderedDict | 49 from ordereddict import OrderedDict |
49 | 50 |
50 | 51 |
51 class SAT(service.Service): | 52 class SAT(service.Service): |
52 | |
53 def __init__(self): | 53 def __init__(self): |
54 self._cb_map = {} # map from callback_id to callbacks | 54 self._cb_map = {} # map from callback_id to callbacks |
55 self._menus = OrderedDict() # dynamic menus. key: callback_id, value: menu data (dictionnary) | 55 self._menus = ( |
56 OrderedDict() | |
57 ) # dynamic menus. key: callback_id, value: menu data (dictionnary) | |
56 self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path), value: menu id | 58 self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path), value: menu id |
57 self.initialised = defer.Deferred() | 59 self.initialised = defer.Deferred() |
58 self.profiles = {} | 60 self.profiles = {} |
59 self.plugins = {} | 61 self.plugins = {} |
60 self.ns_map = {u'x-data': u'jabber:x:data'} # map for short name to whole namespace, | 62 self.ns_map = { |
61 # extended by plugins with registerNamespace | 63 u"x-data": u"jabber:x:data" |
64 } # map for short name to whole namespace, | |
65 # extended by plugins with registerNamespace | |
62 self.memory = memory.Memory(self) | 66 self.memory = memory.Memory(self) |
63 self.trigger = trigger.TriggerManager() # trigger are used to change SàT behaviour | 67 self.trigger = ( |
64 | 68 trigger.TriggerManager() |
65 bridge_name = self.memory.getConfig('', 'bridge', 'dbus') | 69 ) # trigger are used to change SàT behaviour |
70 | |
71 bridge_name = self.memory.getConfig("", "bridge", "dbus") | |
66 | 72 |
67 bridge_module = dynamic_import.bridge(bridge_name) | 73 bridge_module = dynamic_import.bridge(bridge_name) |
68 if bridge_module is None: | 74 if bridge_module is None: |
69 log.error(u"Can't find bridge module of name {}".format(bridge_name)) | 75 log.error(u"Can't find bridge module of name {}".format(bridge_name)) |
70 sys.exit(1) | 76 sys.exit(1) |
77 self.bridge.register_method("getReady", lambda: self.initialised) | 83 self.bridge.register_method("getReady", lambda: self.initialised) |
78 self.bridge.register_method("getVersion", lambda: self.full_version) | 84 self.bridge.register_method("getVersion", lambda: self.full_version) |
79 self.bridge.register_method("getFeatures", self.getFeatures) | 85 self.bridge.register_method("getFeatures", self.getFeatures) |
80 self.bridge.register_method("profileNameGet", self.memory.getProfileName) | 86 self.bridge.register_method("profileNameGet", self.memory.getProfileName) |
81 self.bridge.register_method("profilesListGet", self.memory.getProfilesList) | 87 self.bridge.register_method("profilesListGet", self.memory.getProfilesList) |
82 self.bridge.register_method("getEntityData", lambda jid_, keys, profile: self.memory.getEntityData(jid.JID(jid_), keys, profile)) | 88 self.bridge.register_method( |
89 "getEntityData", | |
90 lambda jid_, keys, profile: self.memory.getEntityData( | |
91 jid.JID(jid_), keys, profile | |
92 ), | |
93 ) | |
83 self.bridge.register_method("getEntitiesData", self.memory._getEntitiesData) | 94 self.bridge.register_method("getEntitiesData", self.memory._getEntitiesData) |
84 self.bridge.register_method("profileCreate", self.memory.createProfile) | 95 self.bridge.register_method("profileCreate", self.memory.createProfile) |
85 self.bridge.register_method("asyncDeleteProfile", self.memory.asyncDeleteProfile) | 96 self.bridge.register_method("asyncDeleteProfile", self.memory.asyncDeleteProfile) |
86 self.bridge.register_method("profileStartSession", self.memory.startSession) | 97 self.bridge.register_method("profileStartSession", self.memory.startSession) |
87 self.bridge.register_method("profileIsSessionStarted", self.memory._isSessionStarted) | 98 self.bridge.register_method( |
99 "profileIsSessionStarted", self.memory._isSessionStarted | |
100 ) | |
88 self.bridge.register_method("profileSetDefault", self.memory.profileSetDefault) | 101 self.bridge.register_method("profileSetDefault", self.memory.profileSetDefault) |
89 self.bridge.register_method("connect", self._connect) | 102 self.bridge.register_method("connect", self._connect) |
90 self.bridge.register_method("disconnect", self.disconnect) | 103 self.bridge.register_method("disconnect", self.disconnect) |
91 self.bridge.register_method("getContacts", self.getContacts) | 104 self.bridge.register_method("getContacts", self.getContacts) |
92 self.bridge.register_method("getContactsFromGroup", self.getContactsFromGroup) | 105 self.bridge.register_method("getContactsFromGroup", self.getContactsFromGroup) |
93 self.bridge.register_method("getMainResource", self.memory._getMainResource) | 106 self.bridge.register_method("getMainResource", self.memory._getMainResource) |
94 self.bridge.register_method("getPresenceStatuses", self.memory._getPresenceStatuses) | 107 self.bridge.register_method( |
108 "getPresenceStatuses", self.memory._getPresenceStatuses | |
109 ) | |
95 self.bridge.register_method("getWaitingSub", self.memory.getWaitingSub) | 110 self.bridge.register_method("getWaitingSub", self.memory.getWaitingSub) |
96 self.bridge.register_method("messageSend", self._messageSend) | 111 self.bridge.register_method("messageSend", self._messageSend) |
97 self.bridge.register_method("getConfig", self._getConfig) | 112 self.bridge.register_method("getConfig", self._getConfig) |
98 self.bridge.register_method("setParam", self.setParam) | 113 self.bridge.register_method("setParam", self.setParam) |
99 self.bridge.register_method("getParamA", self.memory.getStringParamA) | 114 self.bridge.register_method("getParamA", self.memory.getStringParamA) |
100 self.bridge.register_method("asyncGetParamA", self.memory.asyncGetStringParamA) | 115 self.bridge.register_method("asyncGetParamA", self.memory.asyncGetStringParamA) |
101 self.bridge.register_method("asyncGetParamsValuesFromCategory", self.memory.asyncGetParamsValuesFromCategory) | 116 self.bridge.register_method( |
117 "asyncGetParamsValuesFromCategory", | |
118 self.memory.asyncGetParamsValuesFromCategory, | |
119 ) | |
102 self.bridge.register_method("getParamsUI", self.memory.getParamsUI) | 120 self.bridge.register_method("getParamsUI", self.memory.getParamsUI) |
103 self.bridge.register_method("getParamsCategories", self.memory.getParamsCategories) | 121 self.bridge.register_method( |
122 "getParamsCategories", self.memory.getParamsCategories | |
123 ) | |
104 self.bridge.register_method("paramsRegisterApp", self.memory.paramsRegisterApp) | 124 self.bridge.register_method("paramsRegisterApp", self.memory.paramsRegisterApp) |
105 self.bridge.register_method("historyGet", self.memory._historyGet) | 125 self.bridge.register_method("historyGet", self.memory._historyGet) |
106 self.bridge.register_method("setPresence", self._setPresence) | 126 self.bridge.register_method("setPresence", self._setPresence) |
107 self.bridge.register_method("subscription", self.subscription) | 127 self.bridge.register_method("subscription", self.subscription) |
108 self.bridge.register_method("addContact", self._addContact) | 128 self.bridge.register_method("addContact", self._addContact) |
133 | 153 |
134 @property | 154 @property |
135 def full_version(self): | 155 def full_version(self): |
136 """Return the full version of SàT (with release name and extra data when in development mode)""" | 156 """Return the full version of SàT (with release name and extra data when in development mode)""" |
137 version = self.version | 157 version = self.version |
138 if version[-1] == 'D': | 158 if version[-1] == "D": |
139 # we are in debug version, we add extra data | 159 # we are in debug version, we add extra data |
140 try: | 160 try: |
141 return self._version_cache | 161 return self._version_cache |
142 except AttributeError: | 162 except AttributeError: |
143 self._version_cache = u"{} « {} » ({})".format(version, C.APP_RELEASE_NAME, utils.getRepositoryData(sat)) | 163 self._version_cache = u"{} « {} » ({})".format( |
164 version, C.APP_RELEASE_NAME, utils.getRepositoryData(sat) | |
165 ) | |
144 return self._version_cache | 166 return self._version_cache |
145 else: | 167 else: |
146 return version | 168 return version |
147 | 169 |
148 @property | 170 @property |
156 try: | 178 try: |
157 self._import_plugins() | 179 self._import_plugins() |
158 ui_contact_list.ContactList(self) | 180 ui_contact_list.ContactList(self) |
159 ui_profile_manager.ProfileManager(self) | 181 ui_profile_manager.ProfileManager(self) |
160 except Exception as e: | 182 except Exception as e: |
161 log.error(_(u"Could not initialize backend: {reason}").format( | 183 log.error( |
162 reason = str(e).decode('utf-8', 'ignore'))) | 184 _(u"Could not initialize backend: {reason}").format( |
185 reason=str(e).decode("utf-8", "ignore") | |
186 ) | |
187 ) | |
163 sys.exit(1) | 188 sys.exit(1) |
164 self.initialised.callback(None) | 189 self.initialised.callback(None) |
165 log.info(_(u"Backend is ready")) | 190 log.info(_(u"Backend is ready")) |
166 | 191 |
167 def _unimport_plugin(self, plugin_path): | 192 def _unimport_plugin(self, plugin_path): |
178 # FIXME: should use imp | 203 # FIXME: should use imp |
179 # TODO: do not import all plugins if no needed: component plugins are not needed if we | 204 # TODO: do not import all plugins if no needed: component plugins are not needed if we |
180 # just use a client, and plugin blacklisting should be possible in sat.conf | 205 # just use a client, and plugin blacklisting should be possible in sat.conf |
181 plugins_path = os.path.dirname(sat.plugins.__file__) | 206 plugins_path = os.path.dirname(sat.plugins.__file__) |
182 plugin_glob = "plugin*." + C.PLUGIN_EXT | 207 plugin_glob = "plugin*." + C.PLUGIN_EXT |
183 plug_lst = [os.path.splitext(plugin)[0] for plugin in map(os.path.basename, glob(os.path.join(plugins_path, plugin_glob)))] | 208 plug_lst = [ |
209 os.path.splitext(plugin)[0] | |
210 for plugin in map( | |
211 os.path.basename, glob(os.path.join(plugins_path, plugin_glob)) | |
212 ) | |
213 ] | |
184 plugins_to_import = {} # plugins we still have to import | 214 plugins_to_import = {} # plugins we still have to import |
185 for plug in plug_lst: | 215 for plug in plug_lst: |
186 plugin_path = 'sat.plugins.' + plug | 216 plugin_path = "sat.plugins." + plug |
187 try: | 217 try: |
188 __import__(plugin_path) | 218 __import__(plugin_path) |
189 except exceptions.MissingModule as e: | 219 except exceptions.MissingModule as e: |
190 self._unimport_plugin(plugin_path) | 220 self._unimport_plugin(plugin_path) |
191 log.warning(u"Can't import plugin [{path}] because of an unavailale third party module:\n{msg}".format( | 221 log.warning( |
192 path=plugin_path, msg=e)) | 222 u"Can't import plugin [{path}] because of an unavailale third party module:\n{msg}".format( |
223 path=plugin_path, msg=e | |
224 ) | |
225 ) | |
193 continue | 226 continue |
194 except exceptions.CancelError as e: | 227 except exceptions.CancelError as e: |
195 log.info(u"Plugin [{path}] cancelled its own import: {msg}".format(path=plugin_path, msg=e)) | 228 log.info( |
229 u"Plugin [{path}] cancelled its own import: {msg}".format( | |
230 path=plugin_path, msg=e | |
231 ) | |
232 ) | |
196 self._unimport_plugin(plugin_path) | 233 self._unimport_plugin(plugin_path) |
197 continue | 234 continue |
198 except Exception as e: | 235 except Exception as e: |
199 import traceback | 236 import traceback |
200 log.error(_(u"Can't import plugin [{path}]:\n{error}").format(path=plugin_path, error=traceback.format_exc())) | 237 |
238 log.error( | |
239 _(u"Can't import plugin [{path}]:\n{error}").format( | |
240 path=plugin_path, error=traceback.format_exc() | |
241 ) | |
242 ) | |
201 self._unimport_plugin(plugin_path) | 243 self._unimport_plugin(plugin_path) |
202 continue | 244 continue |
203 mod = sys.modules[plugin_path] | 245 mod = sys.modules[plugin_path] |
204 plugin_info = mod.PLUGIN_INFO | 246 plugin_info = mod.PLUGIN_INFO |
205 import_name = plugin_info['import_name'] | 247 import_name = plugin_info["import_name"] |
206 | 248 |
207 plugin_modes = plugin_info[u'modes'] = set(plugin_info.setdefault(u"modes", C.PLUG_MODE_DEFAULT)) | 249 plugin_modes = plugin_info[u"modes"] = set( |
250 plugin_info.setdefault(u"modes", C.PLUG_MODE_DEFAULT) | |
251 ) | |
208 | 252 |
209 # if the plugin is an entry point, it must work in component mode | 253 # if the plugin is an entry point, it must work in component mode |
210 if plugin_info[u'type'] == C.PLUG_TYPE_ENTRY_POINT: | 254 if plugin_info[u"type"] == C.PLUG_TYPE_ENTRY_POINT: |
211 # if plugin is an entrypoint, we cache it | 255 # if plugin is an entrypoint, we cache it |
212 if C.PLUG_MODE_COMPONENT not in plugin_modes: | 256 if C.PLUG_MODE_COMPONENT not in plugin_modes: |
213 log.error(_(u"{type} type must be used with {mode} mode, ignoring plugin").format( | 257 log.error( |
214 type = C.PLUG_TYPE_ENTRY_POINT, mode = C.PLUG_MODE_COMPONENT)) | 258 _( |
259 u"{type} type must be used with {mode} mode, ignoring plugin" | |
260 ).format(type=C.PLUG_TYPE_ENTRY_POINT, mode=C.PLUG_MODE_COMPONENT) | |
261 ) | |
215 self._unimport_plugin(plugin_path) | 262 self._unimport_plugin(plugin_path) |
216 continue | 263 continue |
217 | 264 |
218 if import_name in plugins_to_import: | 265 if import_name in plugins_to_import: |
219 log.error(_(u"Name conflict for import name [{import_name}], can't import plugin [{name}]").format(**plugin_info)) | 266 log.error( |
267 _( | |
268 u"Name conflict for import name [{import_name}], can't import plugin [{name}]" | |
269 ).format(**plugin_info) | |
270 ) | |
220 continue | 271 continue |
221 plugins_to_import[import_name] = (plugin_path, mod, plugin_info) | 272 plugins_to_import[import_name] = (plugin_path, mod, plugin_info) |
222 while True: | 273 while True: |
223 try: | 274 try: |
224 self._import_plugins_from_dict(plugins_to_import) | 275 self._import_plugins_from_dict(plugins_to_import) |
225 except ImportError: | 276 except ImportError: |
226 pass | 277 pass |
227 if not plugins_to_import: | 278 if not plugins_to_import: |
228 break | 279 break |
229 | 280 |
230 def _import_plugins_from_dict(self, plugins_to_import, import_name=None, optional=False): | 281 def _import_plugins_from_dict( |
282 self, plugins_to_import, import_name=None, optional=False | |
283 ): | |
231 """Recursively import and their dependencies in the right order | 284 """Recursively import and their dependencies in the right order |
232 | 285 |
233 @param plugins_to_import(dict): key=import_name and values=(plugin_path, module, plugin_info) | 286 @param plugins_to_import(dict): key=import_name and values=(plugin_path, module, plugin_info) |
234 @param import_name(unicode, None): name of the plugin to import as found in PLUGIN_INFO['import_name'] | 287 @param import_name(unicode, None): name of the plugin to import as found in PLUGIN_INFO['import_name'] |
235 @param optional(bool): if False and plugin is not found, an ImportError exception is raised | 288 @param optional(bool): if False and plugin is not found, an ImportError exception is raised |
236 """ | 289 """ |
237 if import_name in self.plugins: | 290 if import_name in self.plugins: |
238 log.debug(u'Plugin {} already imported, passing'.format(import_name)) | 291 log.debug(u"Plugin {} already imported, passing".format(import_name)) |
239 return | 292 return |
240 if not import_name: | 293 if not import_name: |
241 import_name, (plugin_path, mod, plugin_info) = plugins_to_import.popitem() | 294 import_name, (plugin_path, mod, plugin_info) = plugins_to_import.popitem() |
242 else: | 295 else: |
243 if not import_name in plugins_to_import: | 296 if not import_name in plugins_to_import: |
244 if optional: | 297 if optional: |
245 log.warning(_(u"Recommended plugin not found: {}").format(import_name)) | 298 log.warning( |
299 _(u"Recommended plugin not found: {}").format(import_name) | |
300 ) | |
246 return | 301 return |
247 msg = u"Dependency not found: {}".format(import_name) | 302 msg = u"Dependency not found: {}".format(import_name) |
248 log.error(msg) | 303 log.error(msg) |
249 raise ImportError(msg) | 304 raise ImportError(msg) |
250 plugin_path, mod, plugin_info = plugins_to_import.pop(import_name) | 305 plugin_path, mod, plugin_info = plugins_to_import.pop(import_name) |
251 dependencies = plugin_info.setdefault("dependencies", []) | 306 dependencies = plugin_info.setdefault("dependencies", []) |
252 recommendations = plugin_info.setdefault("recommendations", []) | 307 recommendations = plugin_info.setdefault("recommendations", []) |
253 for to_import in dependencies + recommendations: | 308 for to_import in dependencies + recommendations: |
254 if to_import not in self.plugins: | 309 if to_import not in self.plugins: |
255 log.debug(u'Recursively import dependency of [%s]: [%s]' % (import_name, to_import)) | 310 log.debug( |
311 u"Recursively import dependency of [%s]: [%s]" | |
312 % (import_name, to_import) | |
313 ) | |
256 try: | 314 try: |
257 self._import_plugins_from_dict(plugins_to_import, to_import, to_import not in dependencies) | 315 self._import_plugins_from_dict( |
316 plugins_to_import, to_import, to_import not in dependencies | |
317 ) | |
258 except ImportError as e: | 318 except ImportError as e: |
259 log.warning(_(u"Can't import plugin {name}: {error}").format(name=plugin_info['name'], error=e)) | 319 log.warning( |
320 _(u"Can't import plugin {name}: {error}").format( | |
321 name=plugin_info["name"], error=e | |
322 ) | |
323 ) | |
260 if optional: | 324 if optional: |
261 return | 325 return |
262 raise e | 326 raise e |
263 log.info("importing plugin: {}".format(plugin_info['name'])) | 327 log.info("importing plugin: {}".format(plugin_info["name"])) |
264 # we instanciate the plugin here | 328 # we instanciate the plugin here |
265 try: | 329 try: |
266 self.plugins[import_name] = getattr(mod, plugin_info['main'])(self) | 330 self.plugins[import_name] = getattr(mod, plugin_info["main"])(self) |
267 except Exception as e: | 331 except Exception as e: |
268 log.warning(u'Error while loading plugin "{name}", ignoring it: {error}' | 332 log.warning( |
269 .format(name=plugin_info['name'], error=e)) | 333 u'Error while loading plugin "{name}", ignoring it: {error}'.format( |
334 name=plugin_info["name"], error=e | |
335 ) | |
336 ) | |
270 if optional: | 337 if optional: |
271 return | 338 return |
272 raise ImportError(u"Error during initiation") | 339 raise ImportError(u"Error during initiation") |
273 if C.bool(plugin_info.get(C.PI_HANDLER, C.BOOL_FALSE)): | 340 if C.bool(plugin_info.get(C.PI_HANDLER, C.BOOL_FALSE)): |
274 self.plugins[import_name].is_handler = True | 341 self.plugins[import_name].is_handler = True |
275 else: | 342 else: |
276 self.plugins[import_name].is_handler = False | 343 self.plugins[import_name].is_handler = False |
277 # we keep metadata as a Class attribute | 344 # we keep metadata as a Class attribute |
278 self.plugins[import_name]._info = plugin_info | 345 self.plugins[import_name]._info = plugin_info |
279 #TODO: test xmppclient presence and register handler parent | 346 # TODO: test xmppclient presence and register handler parent |
280 | 347 |
281 def pluginsUnload(self): | 348 def pluginsUnload(self): |
282 """Call unload method on every loaded plugin, if exists | 349 """Call unload method on every loaded plugin, if exists |
283 | 350 |
284 @return (D): A deferred which return None when all method have been called | 351 @return (D): A deferred which return None when all method have been called |
294 continue | 361 continue |
295 else: | 362 else: |
296 defers_list.append(defer.maybeDeferred(unload)) | 363 defers_list.append(defer.maybeDeferred(unload)) |
297 return defers_list | 364 return defers_list |
298 | 365 |
299 def _connect(self, profile_key, password='', options=None): | 366 def _connect(self, profile_key, password="", options=None): |
300 profile = self.memory.getProfileName(profile_key) | 367 profile = self.memory.getProfileName(profile_key) |
301 return self.connect(profile, password, options) | 368 return self.connect(profile, password, options) |
302 | 369 |
303 def connect(self, profile, password='', options=None, max_retries=C.XMPP_MAX_RETRIES): | 370 def connect(self, profile, password="", options=None, max_retries=C.XMPP_MAX_RETRIES): |
304 """Connect a profile (i.e. connect client.component to XMPP server) | 371 """Connect a profile (i.e. connect client.component to XMPP server) |
305 | 372 |
306 Retrieve the individual parameters, authenticate the profile | 373 Retrieve the individual parameters, authenticate the profile |
307 and initiate the connection to the associated XMPP server. | 374 and initiate the connection to the associated XMPP server. |
308 @param profile: %(doc_profile)s | 375 @param profile: %(doc_profile)s |
314 - True if the XMPP connection was already established | 381 - True if the XMPP connection was already established |
315 - False if the XMPP connection has been initiated (it may still fail) | 382 - False if the XMPP connection has been initiated (it may still fail) |
316 @raise exceptions.PasswordError: Profile password is wrong | 383 @raise exceptions.PasswordError: Profile password is wrong |
317 """ | 384 """ |
318 if options is None: | 385 if options is None: |
319 options={} | 386 options = {} |
387 | |
320 def connectProfile(dummy=None): | 388 def connectProfile(dummy=None): |
321 if self.isConnected(profile): | 389 if self.isConnected(profile): |
322 log.info(_("already connected !")) | 390 log.info(_("already connected !")) |
323 return True | 391 return True |
324 | 392 |
373 except AttributeError: | 441 except AttributeError: |
374 features_d = defer.succeed({}) | 442 features_d = defer.succeed({}) |
375 features.append(features_d) | 443 features.append(features_d) |
376 | 444 |
377 d_list = defer.DeferredList(features) | 445 d_list = defer.DeferredList(features) |
446 | |
378 def buildFeatures(result, import_names): | 447 def buildFeatures(result, import_names): |
379 assert len(result) == len(import_names) | 448 assert len(result) == len(import_names) |
380 ret = {} | 449 ret = {} |
381 for name, (success, data) in zip (import_names, result): | 450 for name, (success, data) in zip(import_names, result): |
382 if success: | 451 if success: |
383 ret[name] = data | 452 ret[name] = data |
384 else: | 453 else: |
385 log.warning(u"Error while getting features for {name}: {failure}".format( | 454 log.warning( |
386 name=name, failure=data)) | 455 u"Error while getting features for {name}: {failure}".format( |
456 name=name, failure=data | |
457 ) | |
458 ) | |
387 ret[name] = {} | 459 ret[name] = {} |
388 return ret | 460 return ret |
389 | 461 |
390 d_list.addCallback(buildFeatures, self.plugins.keys()) | 462 d_list.addCallback(buildFeatures, self.plugins.keys()) |
391 return d_list | 463 return d_list |
392 | 464 |
393 def getContacts(self, profile_key): | 465 def getContacts(self, profile_key): |
394 client = self.getClient(profile_key) | 466 client = self.getClient(profile_key) |
467 | |
395 def got_roster(dummy): | 468 def got_roster(dummy): |
396 ret = [] | 469 ret = [] |
397 for item in client.roster.getItems(): # we get all items for client's roster | 470 for item in client.roster.getItems(): # we get all items for client's roster |
398 # and convert them to expected format | 471 # and convert them to expected format |
399 attr = client.roster.getAttributes(item) | 472 attr = client.roster.getAttributes(item) |
465 | 538 |
466 @param profile_key: %(doc_profile_key)s | 539 @param profile_key: %(doc_profile_key)s |
467 @return: list of clients | 540 @return: list of clients |
468 """ | 541 """ |
469 if not profile_key: | 542 if not profile_key: |
470 raise exceptions.DataError(_(u'profile_key must not be empty')) | 543 raise exceptions.DataError(_(u"profile_key must not be empty")) |
471 try: | 544 try: |
472 profile = self.memory.getProfileName(profile_key, True) | 545 profile = self.memory.getProfileName(profile_key, True) |
473 except exceptions.ProfileUnknownError: | 546 except exceptions.ProfileUnknownError: |
474 return [] | 547 return [] |
475 if profile == C.PROF_KEY_ALL: | 548 if profile == C.PROF_KEY_ALL: |
476 return self.profiles.values() | 549 return self.profiles.values() |
477 elif profile[0] == '@': # only profile keys can start with "@" | 550 elif profile[0] == "@": # only profile keys can start with "@" |
478 raise exceptions.ProfileKeyUnknown | 551 raise exceptions.ProfileKeyUnknown |
479 return [self.profiles[profile]] | 552 return [self.profiles[profile]] |
480 | 553 |
481 def _getConfig(self, section, name): | 554 def _getConfig(self, section, name): |
482 """Get the main configuration option | 555 """Get the main configuration option |
483 | 556 |
484 @param section: section of the config file (None or '' for DEFAULT) | 557 @param section: section of the config file (None or '' for DEFAULT) |
485 @param name: name of the option | 558 @param name: name of the option |
486 @return: unicode representation of the option | 559 @return: unicode representation of the option |
487 """ | 560 """ |
488 return unicode(self.memory.getConfig(section, name, '')) | 561 return unicode(self.memory.getConfig(section, name, "")) |
489 | 562 |
490 def logErrback(self, failure_): | 563 def logErrback(self, failure_): |
491 """generic errback logging | 564 """generic errback logging |
492 | 565 |
493 can be used as last errback to show unexpected error | 566 can be used as last errback to show unexpected error |
494 """ | 567 """ |
495 log.error(_(u"Unexpected error: {}".format(failure_))) | 568 log.error(_(u"Unexpected error: {}".format(failure_))) |
496 return failure_ | 569 return failure_ |
497 | 570 |
498 # namespaces | 571 # namespaces |
499 | 572 |
500 def registerNamespace(self, short_name, namespace): | 573 def registerNamespace(self, short_name, namespace): |
501 """associate a namespace to a short name""" | 574 """associate a namespace to a short name""" |
502 if short_name in self.ns_map: | 575 if short_name in self.ns_map: |
503 raise exceptions.ConflictError(u'this short name is already used') | 576 raise exceptions.ConflictError(u"this short name is already used") |
504 self.ns_map[short_name] = namespace | 577 self.ns_map[short_name] = namespace |
505 | 578 |
506 def getNamespaces(self): | 579 def getNamespaces(self): |
507 return self.ns_map | 580 return self.ns_map |
508 | 581 |
509 def getSessionInfos(self, profile_key): | 582 def getSessionInfos(self, profile_key): |
510 """compile interesting data on current profile session""" | 583 """compile interesting data on current profile session""" |
511 client = self.getClient(profile_key) | 584 client = self.getClient(profile_key) |
512 data = { | 585 data = {"jid": client.jid.full(), "started": unicode(int(client.started))} |
513 "jid": client.jid.full(), | |
514 "started": unicode(int(client.started)), | |
515 } | |
516 return defer.succeed(data) | 586 return defer.succeed(data) |
517 | 587 |
518 # local dirs | 588 # local dirs |
519 | 589 |
520 def getLocalPath(self, client, dir_name, *extra_path, **kwargs): | 590 def getLocalPath(self, client, dir_name, *extra_path, **kwargs): |
529 @param *extra_path: extra path element(s) to use | 599 @param *extra_path: extra path element(s) to use |
530 @return (unicode): path | 600 @return (unicode): path |
531 """ | 601 """ |
532 # FIXME: component and profile are parsed with **kwargs because of python 2 limitations | 602 # FIXME: component and profile are parsed with **kwargs because of python 2 limitations |
533 # once moved to python 3, this can be fixed | 603 # once moved to python 3, this can be fixed |
534 component = kwargs.pop('component', False) | 604 component = kwargs.pop("component", False) |
535 profile = kwargs.pop('profile', True) | 605 profile = kwargs.pop("profile", True) |
536 assert not kwargs | 606 assert not kwargs |
537 | 607 |
538 path_elts = [self.memory.getConfig('', 'local_dir')] | 608 path_elts = [self.memory.getConfig("", "local_dir")] |
539 if component: | 609 if component: |
540 path_elts.append(C.COMPONENTS_DIR) | 610 path_elts.append(C.COMPONENTS_DIR) |
541 path_elts.append(regex.pathEscape(dir_name)) | 611 path_elts.append(regex.pathEscape(dir_name)) |
542 if extra_path: | 612 if extra_path: |
543 path_elts.extend([regex.pathEscape(p) for p in extra_path]) | 613 path_elts.extend([regex.pathEscape(p) for p in extra_path]) |
559 @param profile_key: key_word or profile name to determine profile name | 629 @param profile_key: key_word or profile name to determine profile name |
560 @return: True if connected | 630 @return: True if connected |
561 """ | 631 """ |
562 profile = self.memory.getProfileName(profile_key) | 632 profile = self.memory.getProfileName(profile_key) |
563 if not profile: | 633 if not profile: |
564 log.error(_('asking connection status for a non-existant profile')) | 634 log.error(_("asking connection status for a non-existant profile")) |
565 raise exceptions.ProfileUnknownError(profile_key) | 635 raise exceptions.ProfileUnknownError(profile_key) |
566 if profile not in self.profiles: | 636 if profile not in self.profiles: |
567 return False | 637 return False |
568 return self.profiles[profile].isConnected() | 638 return self.profiles[profile].isConnected() |
569 | 639 |
570 ## XMPP methods ## | 640 ## XMPP methods ## |
571 | 641 |
572 def _messageSend(self, to_jid_s, message, subject=None, mess_type='auto', extra=None, profile_key=C.PROF_KEY_NONE): | 642 def _messageSend( |
643 self, | |
644 to_jid_s, | |
645 message, | |
646 subject=None, | |
647 mess_type="auto", | |
648 extra=None, | |
649 profile_key=C.PROF_KEY_NONE, | |
650 ): | |
573 client = self.getClient(profile_key) | 651 client = self.getClient(profile_key) |
574 to_jid = jid.JID(to_jid_s) | 652 to_jid = jid.JID(to_jid_s) |
575 #XXX: we need to use the dictionary comprehension because D-Bus return its own types, and pickle can't manage them. TODO: Need to find a better way | 653 # XXX: we need to use the dictionary comprehension because D-Bus return its own types, and pickle can't manage them. TODO: Need to find a better way |
576 return client.sendMessage(to_jid, message, subject, mess_type, {unicode(key): unicode(value) for key, value in extra.items()}) | 654 return client.sendMessage( |
655 to_jid, | |
656 message, | |
657 subject, | |
658 mess_type, | |
659 {unicode(key): unicode(value) for key, value in extra.items()}, | |
660 ) | |
577 | 661 |
578 def _setPresence(self, to="", show="", statuses=None, profile_key=C.PROF_KEY_NONE): | 662 def _setPresence(self, to="", show="", statuses=None, profile_key=C.PROF_KEY_NONE): |
579 return self.setPresence(jid.JID(to) if to else None, show, statuses, profile_key) | 663 return self.setPresence(jid.JID(to) if to else None, show, statuses, profile_key) |
580 | 664 |
581 def setPresence(self, to_jid=None, show="", statuses=None, profile_key=C.PROF_KEY_NONE): | 665 def setPresence( |
666 self, to_jid=None, show="", statuses=None, profile_key=C.PROF_KEY_NONE | |
667 ): | |
582 """Send our presence information""" | 668 """Send our presence information""" |
583 if statuses is None: | 669 if statuses is None: |
584 statuses = {} | 670 statuses = {} |
585 profile = self.memory.getProfileName(profile_key) | 671 profile = self.memory.getProfileName(profile_key) |
586 assert profile | 672 assert profile |
587 priority = int(self.memory.getParamA("Priority", "Connection", profile_key=profile)) | 673 priority = int( |
674 self.memory.getParamA("Priority", "Connection", profile_key=profile) | |
675 ) | |
588 self.profiles[profile].presence.available(to_jid, show, statuses, priority) | 676 self.profiles[profile].presence.available(to_jid, show, statuses, priority) |
589 #XXX: FIXME: temporary fix to work around openfire 3.7.0 bug (presence is not broadcasted to generating resource) | 677 # XXX: FIXME: temporary fix to work around openfire 3.7.0 bug (presence is not broadcasted to generating resource) |
590 if '' in statuses: | 678 if "" in statuses: |
591 statuses[C.PRESENCE_STATUSES_DEFAULT] = statuses.pop('') | 679 statuses[C.PRESENCE_STATUSES_DEFAULT] = statuses.pop("") |
592 self.bridge.presenceUpdate(self.profiles[profile].jid.full(), show, | 680 self.bridge.presenceUpdate( |
593 int(priority), statuses, profile) | 681 self.profiles[profile].jid.full(), show, int(priority), statuses, profile |
682 ) | |
594 | 683 |
595 def subscription(self, subs_type, raw_jid, profile_key): | 684 def subscription(self, subs_type, raw_jid, profile_key): |
596 """Called to manage subscription | 685 """Called to manage subscription |
597 @param subs_type: subsciption type (cf RFC 3921) | 686 @param subs_type: subsciption type (cf RFC 3921) |
598 @param raw_jid: unicode entity's jid | 687 @param raw_jid: unicode entity's jid |
599 @param profile_key: profile""" | 688 @param profile_key: profile""" |
600 profile = self.memory.getProfileName(profile_key) | 689 profile = self.memory.getProfileName(profile_key) |
601 assert profile | 690 assert profile |
602 to_jid = jid.JID(raw_jid) | 691 to_jid = jid.JID(raw_jid) |
603 log.debug(_(u'subsciption request [%(subs_type)s] for %(jid)s') % {'subs_type': subs_type, 'jid': to_jid.full()}) | 692 log.debug( |
693 _(u"subsciption request [%(subs_type)s] for %(jid)s") | |
694 % {"subs_type": subs_type, "jid": to_jid.full()} | |
695 ) | |
604 if subs_type == "subscribe": | 696 if subs_type == "subscribe": |
605 self.profiles[profile].presence.subscribe(to_jid) | 697 self.profiles[profile].presence.subscribe(to_jid) |
606 elif subs_type == "subscribed": | 698 elif subs_type == "subscribed": |
607 self.profiles[profile].presence.subscribed(to_jid) | 699 self.profiles[profile].presence.subscribed(to_jid) |
608 elif subs_type == "unsubscribe": | 700 elif subs_type == "unsubscribe": |
669 return self.memory.disco.findServiceEntities(*args, **kwargs) | 761 return self.memory.disco.findServiceEntities(*args, **kwargs) |
670 | 762 |
671 def findFeaturesSet(self, *args, **kwargs): | 763 def findFeaturesSet(self, *args, **kwargs): |
672 return self.memory.disco.findFeaturesSet(*args, **kwargs) | 764 return self.memory.disco.findFeaturesSet(*args, **kwargs) |
673 | 765 |
674 def _findByFeatures(self, namespaces, identities, bare_jids, service, roster, own_jid, local_device, profile_key): | 766 def _findByFeatures( |
767 self, | |
768 namespaces, | |
769 identities, | |
770 bare_jids, | |
771 service, | |
772 roster, | |
773 own_jid, | |
774 local_device, | |
775 profile_key, | |
776 ): | |
675 client = self.getClient(profile_key) | 777 client = self.getClient(profile_key) |
676 return self.findByFeatures(client, namespaces, identities, bare_jids, service, roster, own_jid, local_device) | 778 return self.findByFeatures( |
779 client, | |
780 namespaces, | |
781 identities, | |
782 bare_jids, | |
783 service, | |
784 roster, | |
785 own_jid, | |
786 local_device, | |
787 ) | |
677 | 788 |
678 @defer.inlineCallbacks | 789 @defer.inlineCallbacks |
679 def findByFeatures(self, client, namespaces, identities=None, bare_jids=False, service=True, roster=True, own_jid=True, local_device=False): | 790 def findByFeatures( |
791 self, | |
792 client, | |
793 namespaces, | |
794 identities=None, | |
795 bare_jids=False, | |
796 service=True, | |
797 roster=True, | |
798 own_jid=True, | |
799 local_device=False, | |
800 ): | |
680 """retrieve all services or contacts managing a set a features | 801 """retrieve all services or contacts managing a set a features |
681 | 802 |
682 @param namespaces(list[unicode]): features which must be handled | 803 @param namespaces(list[unicode]): features which must be handled |
683 @param identities(list[tuple[unicode,unicode]], None): if not None or empty, only keep those identities | 804 @param identities(list[tuple[unicode,unicode]], None): if not None or empty, only keep those identities |
684 tuple must by (category, type) | 805 tuple must by (category, type) |
695 - roster entities | 816 - roster entities |
696 """ | 817 """ |
697 if not identities: | 818 if not identities: |
698 identities = None | 819 identities = None |
699 if not namespaces and not identities: | 820 if not namespaces and not identities: |
700 raise exceptions.DataError("at least one namespace or one identity must be set") | 821 raise exceptions.DataError( |
822 "at least one namespace or one identity must be set" | |
823 ) | |
701 found_service = {} | 824 found_service = {} |
702 found_own = {} | 825 found_own = {} |
703 found_roster = {} | 826 found_roster = {} |
704 if service: | 827 if service: |
705 services_jids = yield self.findFeaturesSet(client, namespaces) | 828 services_jids = yield self.findFeaturesSet(client, namespaces) |
706 for service_jid in services_jids: | 829 for service_jid in services_jids: |
707 infos = yield self.getDiscoInfos(client, service_jid) | 830 infos = yield self.getDiscoInfos(client, service_jid) |
708 if identities is not None and not set(infos.identities.keys()).issuperset(identities): | 831 if identities is not None and not set(infos.identities.keys()).issuperset( |
832 identities | |
833 ): | |
709 continue | 834 continue |
710 found_identities = [(cat, type_, name or u'') for (cat, type_), name in infos.identities.iteritems()] | 835 found_identities = [ |
836 (cat, type_, name or u"") | |
837 for (cat, type_), name in infos.identities.iteritems() | |
838 ] | |
711 found_service[service_jid.full()] = found_identities | 839 found_service[service_jid.full()] = found_identities |
712 | 840 |
713 jids = [] | 841 jids = [] |
714 if roster: | 842 if roster: |
715 jids.extend(client.roster.getJids()) | 843 jids.extend(client.roster.getJids()) |
716 if own_jid: | 844 if own_jid: |
717 jids.append(client.jid.userhostJID()) | 845 jids.append(client.jid.userhostJID()) |
718 | 846 |
719 for found, jids in ((found_own, [client.jid.userhostJID()]), | 847 for found, jids in ( |
720 (found_roster, client.roster.getJids())): | 848 (found_own, [client.jid.userhostJID()]), |
849 (found_roster, client.roster.getJids()), | |
850 ): | |
721 for jid_ in jids: | 851 for jid_ in jids: |
722 if jid_.resource: | 852 if jid_.resource: |
723 if bare_jids: | 853 if bare_jids: |
724 continue | 854 continue |
725 resources = [jid_.resource] | 855 resources = [jid_.resource] |
735 full_jid = jid.JID(tuple=(jid_.user, jid_.host, resource)) | 865 full_jid = jid.JID(tuple=(jid_.user, jid_.host, resource)) |
736 if full_jid == client.jid and not local_device: | 866 if full_jid == client.jid and not local_device: |
737 continue | 867 continue |
738 infos = yield self.getDiscoInfos(client, full_jid) | 868 infos = yield self.getDiscoInfos(client, full_jid) |
739 if infos.features.issuperset(namespaces): | 869 if infos.features.issuperset(namespaces): |
740 if identities is not None and not set(infos.identities.keys()).issuperset(identities): | 870 if identities is not None and not set( |
871 infos.identities.keys() | |
872 ).issuperset(identities): | |
741 continue | 873 continue |
742 found_identities = [(cat, type_, name or u'') for (cat, type_), name in infos.identities.iteritems()] | 874 found_identities = [ |
875 (cat, type_, name or u"") | |
876 for (cat, type_), name in infos.identities.iteritems() | |
877 ] | |
743 found[full_jid.full()] = found_identities | 878 found[full_jid.full()] = found_identities |
744 | 879 |
745 defer.returnValue((found_service, found_own, found_roster)) | 880 defer.returnValue((found_service, found_own, found_roster)) |
746 | 881 |
747 ## Generic HMI ## | 882 ## Generic HMI ## |
748 | 883 |
749 def _killAction(self, keep_id, client): | 884 def _killAction(self, keep_id, client): |
750 log.debug(u"Killing action {} for timeout".format(keep_id)) | 885 log.debug(u"Killing action {} for timeout".format(keep_id)) |
751 client.actions[keep_id] | 886 client.actions[keep_id] |
752 | 887 |
753 def actionNew(self, action_data, security_limit=C.NO_SECURITY_LIMIT, keep_id=None, profile=C.PROF_KEY_NONE): | 888 def actionNew( |
889 self, | |
890 action_data, | |
891 security_limit=C.NO_SECURITY_LIMIT, | |
892 keep_id=None, | |
893 profile=C.PROF_KEY_NONE, | |
894 ): | |
754 """Shortcut to bridge.actionNew which generate and id and keep for retrieval | 895 """Shortcut to bridge.actionNew which generate and id and keep for retrieval |
755 | 896 |
756 @param action_data(dict): action data (see bridge documentation) | 897 @param action_data(dict): action data (see bridge documentation) |
757 @param security_limit: %(doc_security_limit)s | 898 @param security_limit: %(doc_security_limit)s |
758 @param keep_id(None, unicode): if not None, used to keep action for differed retrieval | 899 @param keep_id(None, unicode): if not None, used to keep action for differed retrieval |
761 @param profile: %(doc_profile)s | 902 @param profile: %(doc_profile)s |
762 """ | 903 """ |
763 id_ = unicode(uuid.uuid4()) | 904 id_ = unicode(uuid.uuid4()) |
764 if keep_id is not None: | 905 if keep_id is not None: |
765 client = self.getClient(profile) | 906 client = self.getClient(profile) |
766 action_timer = reactor.callLater(60*30, self._killAction, keep_id, client) | 907 action_timer = reactor.callLater(60 * 30, self._killAction, keep_id, client) |
767 client.actions[keep_id] = (action_data, id_, security_limit, action_timer) | 908 client.actions[keep_id] = (action_data, id_, security_limit, action_timer) |
768 | 909 |
769 self.bridge.actionNew(action_data, id_, security_limit, profile) | 910 self.bridge.actionNew(action_data, id_, security_limit, profile) |
770 | 911 |
771 def actionsGet(self, profile): | 912 def actionsGet(self, profile): |
774 @param profile: %(doc_profile)s | 915 @param profile: %(doc_profile)s |
775 """ | 916 """ |
776 client = self.getClient(profile) | 917 client = self.getClient(profile) |
777 return [action_tuple[:-1] for action_tuple in client.actions.itervalues()] | 918 return [action_tuple[:-1] for action_tuple in client.actions.itervalues()] |
778 | 919 |
779 def registerProgressCb(self, progress_id, callback, metadata=None, profile=C.PROF_KEY_NONE): | 920 def registerProgressCb( |
921 self, progress_id, callback, metadata=None, profile=C.PROF_KEY_NONE | |
922 ): | |
780 """Register a callback called when progress is requested for id""" | 923 """Register a callback called when progress is requested for id""" |
781 if metadata is None: | 924 if metadata is None: |
782 metadata = {} | 925 metadata = {} |
783 client = self.getClient(profile) | 926 client = self.getClient(profile) |
784 if progress_id in client._progress_cb: | 927 if progress_id in client._progress_cb: |
793 except KeyError: | 936 except KeyError: |
794 log.error(_(u"Trying to remove an unknow progress callback")) | 937 log.error(_(u"Trying to remove an unknow progress callback")) |
795 | 938 |
796 def _progressGet(self, progress_id, profile): | 939 def _progressGet(self, progress_id, profile): |
797 data = self.progressGet(progress_id, profile) | 940 data = self.progressGet(progress_id, profile) |
798 return {k: unicode(v) for k,v in data.iteritems()} | 941 return {k: unicode(v) for k, v in data.iteritems()} |
799 | 942 |
800 def progressGet(self, progress_id, profile): | 943 def progressGet(self, progress_id, profile): |
801 """Return a dict with progress information | 944 """Return a dict with progress information |
802 | 945 |
803 @param progress_id(unicode): unique id of the progressing element | 946 @param progress_id(unicode): unique id of the progressing element |
835 progress_all = {} | 978 progress_all = {} |
836 for client in clients: | 979 for client in clients: |
837 profile = client.profile | 980 profile = client.profile |
838 progress_dict = {} | 981 progress_dict = {} |
839 progress_all[profile] = progress_dict | 982 progress_all[profile] = progress_dict |
840 for progress_id, (dummy, progress_metadata) in client._progress_cb.iteritems(): | 983 for ( |
984 progress_id, | |
985 (dummy, progress_metadata), | |
986 ) in client._progress_cb.iteritems(): | |
841 progress_dict[progress_id] = progress_metadata | 987 progress_dict[progress_id] = progress_metadata |
842 return progress_all | 988 return progress_all |
843 | 989 |
844 def progressGetAll(self, profile_key): | 990 def progressGetAll(self, profile_key): |
845 """Return all progress status at once | 991 """Return all progress status at once |
868 with_data(bool): True if the callback use the optional data dict | 1014 with_data(bool): True if the callback use the optional data dict |
869 force_id(unicode): id to avoid generated id. Can lead to name conflict, avoid if possible | 1015 force_id(unicode): id to avoid generated id. Can lead to name conflict, avoid if possible |
870 one_shot(bool): True to delete callback once it have been called | 1016 one_shot(bool): True to delete callback once it have been called |
871 @return: id of the registered callback | 1017 @return: id of the registered callback |
872 """ | 1018 """ |
873 callback_id = kwargs.pop('force_id', None) | 1019 callback_id = kwargs.pop("force_id", None) |
874 if callback_id is None: | 1020 if callback_id is None: |
875 callback_id = str(uuid.uuid4()) | 1021 callback_id = str(uuid.uuid4()) |
876 else: | 1022 else: |
877 if callback_id in self._cb_map: | 1023 if callback_id in self._cb_map: |
878 raise exceptions.ConflictError(_(u"id already registered")) | 1024 raise exceptions.ConflictError(_(u"id already registered")) |
879 self._cb_map[callback_id] = (callback, args, kwargs) | 1025 self._cb_map[callback_id] = (callback, args, kwargs) |
880 | 1026 |
881 if "one_shot" in kwargs: # One Shot callback are removed after 30 min | 1027 if "one_shot" in kwargs: # One Shot callback are removed after 30 min |
1028 | |
882 def purgeCallback(): | 1029 def purgeCallback(): |
883 try: | 1030 try: |
884 self.removeCallback(callback_id) | 1031 self.removeCallback(callback_id) |
885 except KeyError: | 1032 except KeyError: |
886 pass | 1033 pass |
1034 | |
887 reactor.callLater(1800, purgeCallback) | 1035 reactor.callLater(1800, purgeCallback) |
888 | 1036 |
889 return callback_id | 1037 return callback_id |
890 | 1038 |
891 def removeCallback(self, callback_id): | 1039 def removeCallback(self, callback_id): |
904 - xmlui: a XMLUI need to be displayed | 1052 - xmlui: a XMLUI need to be displayed |
905 - validated: if present, can be used to launch a callback, it can have the values | 1053 - validated: if present, can be used to launch a callback, it can have the values |
906 - C.BOOL_TRUE | 1054 - C.BOOL_TRUE |
907 - C.BOOL_FALSE | 1055 - C.BOOL_FALSE |
908 """ | 1056 """ |
909 # FIXME: security limit need to be checked here | 1057 # FIXME: security limit need to be checked here |
910 try: | 1058 try: |
911 client = self.getClient(profile_key) | 1059 client = self.getClient(profile_key) |
912 except exceptions.NotFound: | 1060 except exceptions.NotFound: |
913 # client is not available yet | 1061 # client is not available yet |
914 profile = self.memory.getProfileName(profile_key) | 1062 profile = self.memory.getProfileName(profile_key) |
915 if not profile: | 1063 if not profile: |
916 raise exceptions.ProfileUnknownError(_(u'trying to launch action with a non-existant profile')) | 1064 raise exceptions.ProfileUnknownError( |
1065 _(u"trying to launch action with a non-existant profile") | |
1066 ) | |
917 else: | 1067 else: |
918 profile = client.profile | 1068 profile = client.profile |
919 # we check if the action is kept, and remove it | 1069 # we check if the action is kept, and remove it |
920 try: | 1070 try: |
921 action_tuple = client.actions[callback_id] | 1071 action_tuple = client.actions[callback_id] |
922 except KeyError: | 1072 except KeyError: |
923 pass | 1073 pass |
924 else: | 1074 else: |
925 action_tuple[-1].cancel() # the last item is the action timer | 1075 action_tuple[-1].cancel() # the last item is the action timer |
926 del client.actions[callback_id] | 1076 del client.actions[callback_id] |
927 | 1077 |
928 try: | 1078 try: |
929 callback, args, kwargs = self._cb_map[callback_id] | 1079 callback, args, kwargs = self._cb_map[callback_id] |
930 except KeyError: | 1080 except KeyError: |
931 raise exceptions.DataError(u"Unknown callback id {}".format(callback_id)) | 1081 raise exceptions.DataError(u"Unknown callback id {}".format(callback_id)) |
932 | 1082 |
933 if kwargs.get("with_data", False): | 1083 if kwargs.get("with_data", False): |
934 if data is None: | 1084 if data is None: |
935 raise exceptions.DataError("Required data for this callback is missing") | 1085 raise exceptions.DataError("Required data for this callback is missing") |
936 args,kwargs=list(args)[:],kwargs.copy() # we don't want to modify the original (kw)args | 1086 args, kwargs = ( |
1087 list(args)[:], | |
1088 kwargs.copy(), | |
1089 ) # we don't want to modify the original (kw)args | |
937 args.insert(0, data) | 1090 args.insert(0, data) |
938 kwargs["profile"] = profile | 1091 kwargs["profile"] = profile |
939 del kwargs["with_data"] | 1092 del kwargs["with_data"] |
940 | 1093 |
941 if kwargs.pop('one_shot', False): | 1094 if kwargs.pop("one_shot", False): |
942 self.removeCallback(callback_id) | 1095 self.removeCallback(callback_id) |
943 | 1096 |
944 return defer.maybeDeferred(callback, *args, **kwargs) | 1097 return defer.maybeDeferred(callback, *args, **kwargs) |
945 | 1098 |
946 #Menus management | 1099 # Menus management |
947 | 1100 |
948 def _getMenuCanonicalPath(self, path): | 1101 def _getMenuCanonicalPath(self, path): |
949 """give canonical form of path | 1102 """give canonical form of path |
950 | 1103 |
951 canonical form is a tuple of the path were every element is stripped and lowercase | 1104 canonical form is a tuple of the path were every element is stripped and lowercase |
952 @param path(iterable[unicode]): untranslated path to menu | 1105 @param path(iterable[unicode]): untranslated path to menu |
953 @return (tuple[unicode]): canonical form of path | 1106 @return (tuple[unicode]): canonical form of path |
954 """ | 1107 """ |
955 return tuple((p.lower().strip() for p in path)) | 1108 return tuple((p.lower().strip() for p in path)) |
956 | 1109 |
957 def importMenu(self, path, callback, security_limit=C.NO_SECURITY_LIMIT, help_string="", type_=C.MENU_GLOBAL): | 1110 def importMenu( |
1111 self, | |
1112 path, | |
1113 callback, | |
1114 security_limit=C.NO_SECURITY_LIMIT, | |
1115 help_string="", | |
1116 type_=C.MENU_GLOBAL, | |
1117 ): | |
958 """register a new menu for frontends | 1118 """register a new menu for frontends |
959 | 1119 |
960 @param path(iterable[unicode]): path to go to the menu (category/subcategory/.../item) (e.g.: ("File", "Open")) | 1120 @param path(iterable[unicode]): path to go to the menu (category/subcategory/.../item) (e.g.: ("File", "Open")) |
961 /!\ use D_() instead of _() for translations (e.g. (D_("File"), D_("Open"))) | 1121 /!\ use D_() instead of _() for translations (e.g. (D_("File"), D_("Open"))) |
962 untranslated/lower case path can be used to identity a menu, for this reason it must be unique independently of case. | 1122 untranslated/lower case path can be used to identity a menu, for this reason it must be unique independently of case. |
987 callback_id = callback | 1147 callback_id = callback |
988 try: | 1148 try: |
989 callback, args, kwargs = self._cb_map[callback_id] | 1149 callback, args, kwargs = self._cb_map[callback_id] |
990 except KeyError: | 1150 except KeyError: |
991 raise exceptions.DataError("Unknown callback id") | 1151 raise exceptions.DataError("Unknown callback id") |
992 kwargs["with_data"] = True # we have to be sure that we use extra data | 1152 kwargs["with_data"] = True # we have to be sure that we use extra data |
993 else: | 1153 else: |
994 raise exceptions.DataError("Unknown callback type") | 1154 raise exceptions.DataError("Unknown callback type") |
995 | 1155 |
996 for menu_data in self._menus.itervalues(): | 1156 for menu_data in self._menus.itervalues(): |
997 if menu_data['path'] == path and menu_data['type'] == type_: | 1157 if menu_data["path"] == path and menu_data["type"] == type_: |
998 raise exceptions.ConflictError(_("A menu with the same path and type already exists")) | 1158 raise exceptions.ConflictError( |
1159 _("A menu with the same path and type already exists") | |
1160 ) | |
999 | 1161 |
1000 path_canonical = self._getMenuCanonicalPath(path) | 1162 path_canonical = self._getMenuCanonicalPath(path) |
1001 menu_key = (type_, path_canonical) | 1163 menu_key = (type_, path_canonical) |
1002 | 1164 |
1003 if menu_key in self._menus_paths: | 1165 if menu_key in self._menus_paths: |
1004 raise exceptions.ConflictError(u"this menu path is already used: {path} ({menu_key})".format( | 1166 raise exceptions.ConflictError( |
1005 path=path_canonical, menu_key=menu_key)) | 1167 u"this menu path is already used: {path} ({menu_key})".format( |
1006 | 1168 path=path_canonical, menu_key=menu_key |
1007 menu_data = {'path': tuple(path), | 1169 ) |
1008 'path_canonical': path_canonical, | 1170 ) |
1009 'security_limit': security_limit, | 1171 |
1010 'help_string': help_string, | 1172 menu_data = { |
1011 'type': type_ | 1173 "path": tuple(path), |
1012 } | 1174 "path_canonical": path_canonical, |
1175 "security_limit": security_limit, | |
1176 "help_string": help_string, | |
1177 "type": type_, | |
1178 } | |
1013 | 1179 |
1014 self._menus[callback_id] = menu_data | 1180 self._menus[callback_id] = menu_data |
1015 self._menus_paths[menu_key] = callback_id | 1181 self._menus_paths[menu_key] = callback_id |
1016 | 1182 |
1017 return callback_id | 1183 return callback_id |
1018 | 1184 |
1019 def getMenus(self, language='', security_limit=C.NO_SECURITY_LIMIT): | 1185 def getMenus(self, language="", security_limit=C.NO_SECURITY_LIMIT): |
1020 """Return all menus registered | 1186 """Return all menus registered |
1021 | 1187 |
1022 @param language: language used for translation, or empty string for default | 1188 @param language: language used for translation, or empty string for default |
1023 @param security_limit: %(doc_security_limit)s | 1189 @param security_limit: %(doc_security_limit)s |
1024 @return: array of tuple with: | 1190 @return: array of tuple with: |
1030 - icon: name of the icon to use (TODO) | 1196 - icon: name of the icon to use (TODO) |
1031 - help_url: link to a page with more complete documentation (TODO) | 1197 - help_url: link to a page with more complete documentation (TODO) |
1032 """ | 1198 """ |
1033 ret = [] | 1199 ret = [] |
1034 for menu_id, menu_data in self._menus.iteritems(): | 1200 for menu_id, menu_data in self._menus.iteritems(): |
1035 type_ = menu_data['type'] | 1201 type_ = menu_data["type"] |
1036 path = menu_data['path'] | 1202 path = menu_data["path"] |
1037 menu_security_limit = menu_data['security_limit'] | 1203 menu_security_limit = menu_data["security_limit"] |
1038 if security_limit!=C.NO_SECURITY_LIMIT and (menu_security_limit==C.NO_SECURITY_LIMIT or menu_security_limit>security_limit): | 1204 if security_limit != C.NO_SECURITY_LIMIT and ( |
1205 menu_security_limit == C.NO_SECURITY_LIMIT | |
1206 or menu_security_limit > security_limit | |
1207 ): | |
1039 continue | 1208 continue |
1040 languageSwitch(language) | 1209 languageSwitch(language) |
1041 path_i18n = [_(elt) for elt in path] | 1210 path_i18n = [_(elt) for elt in path] |
1042 languageSwitch() | 1211 languageSwitch() |
1043 extra = {} # TODO: manage extra data like icon | 1212 extra = {} # TODO: manage extra data like icon |
1044 ret.append((menu_id, type_, path, path_i18n, extra)) | 1213 ret.append((menu_id, type_, path, path_i18n, extra)) |
1045 | 1214 |
1046 return ret | 1215 return ret |
1047 | 1216 |
1048 def _launchMenu(self, menu_type, path, data=None, security_limit=C.NO_SECURITY_LIMIT, profile_key=C.PROF_KEY_NONE): | 1217 def _launchMenu( |
1218 self, | |
1219 menu_type, | |
1220 path, | |
1221 data=None, | |
1222 security_limit=C.NO_SECURITY_LIMIT, | |
1223 profile_key=C.PROF_KEY_NONE, | |
1224 ): | |
1049 client = self.getClient(profile_key) | 1225 client = self.getClient(profile_key) |
1050 return self.launchMenu(client, menu_type, path, data, security_limit) | 1226 return self.launchMenu(client, menu_type, path, data, security_limit) |
1051 | 1227 |
1052 def launchMenu(self, client, menu_type, path, data=None, security_limit=C.NO_SECURITY_LIMIT): | 1228 def launchMenu( |
1229 self, client, menu_type, path, data=None, security_limit=C.NO_SECURITY_LIMIT | |
1230 ): | |
1053 """launch action a menu action | 1231 """launch action a menu action |
1054 | 1232 |
1055 @param menu_type(unicode): type of menu to launch | 1233 @param menu_type(unicode): type of menu to launch |
1056 @param path(iterable[unicode]): canonical path of the menu | 1234 @param path(iterable[unicode]): canonical path of the menu |
1057 @params data(dict): menu data | 1235 @params data(dict): menu data |
1062 canonical_path = self._getMenuCanonicalPath(path) | 1240 canonical_path = self._getMenuCanonicalPath(path) |
1063 menu_key = (menu_type, canonical_path) | 1241 menu_key = (menu_type, canonical_path) |
1064 try: | 1242 try: |
1065 callback_id = self._menus_paths[menu_key] | 1243 callback_id = self._menus_paths[menu_key] |
1066 except KeyError: | 1244 except KeyError: |
1067 raise exceptions.NotFound(u"Can't find menu {path} ({menu_type})".format( | 1245 raise exceptions.NotFound( |
1068 path=canonical_path, menu_type=menu_type)) | 1246 u"Can't find menu {path} ({menu_type})".format( |
1247 path=canonical_path, menu_type=menu_type | |
1248 ) | |
1249 ) | |
1069 return self.launchCallback(callback_id, data, client.profile) | 1250 return self.launchCallback(callback_id, data, client.profile) |
1070 | 1251 |
1071 def getMenuHelp(self, menu_id, language=''): | 1252 def getMenuHelp(self, menu_id, language=""): |
1072 """return the help string of the menu | 1253 """return the help string of the menu |
1073 | 1254 |
1074 @param menu_id: id of the menu (same as callback_id) | 1255 @param menu_id: id of the menu (same as callback_id) |
1075 @param language: language used for translation, or empty string for default | 1256 @param language: language used for translation, or empty string for default |
1076 @param return: translated help | 1257 @param return: translated help |
1079 try: | 1260 try: |
1080 menu_data = self._menus[menu_id] | 1261 menu_data = self._menus[menu_id] |
1081 except KeyError: | 1262 except KeyError: |
1082 raise exceptions.DataError("Trying to access an unknown menu") | 1263 raise exceptions.DataError("Trying to access an unknown menu") |
1083 languageSwitch(language) | 1264 languageSwitch(language) |
1084 help_string = _(menu_data['help_string']) | 1265 help_string = _(menu_data["help_string"]) |
1085 languageSwitch() | 1266 languageSwitch() |
1086 return help_string | 1267 return help_string |