# HG changeset patch # User Goffi # Date 1320620962 -3600 # Node ID 6c20c76abdcc5c55098ed3efda40f3084d6fdbb1 # Parent 5a18c5f08d9b474f1fdd3a8571e9e5ee191bb8fe backend: - bridge async D-Bus method now automatically manage callback and errback, we just have to return a deferred - getParams, getParamsForCategory and getParamsUI are now asynchronous primitivus: management of asynchronous getParamsUI diff -r 5a18c5f08d9b -r 6c20c76abdcc frontends/src/bridge/DBus.py --- a/frontends/src/bridge/DBus.py Sun Nov 06 15:19:51 2011 +0100 +++ b/frontends/src/bridge/DBus.py Mon Nov 07 00:09:22 2011 +0100 @@ -111,17 +111,17 @@ def getParamA(self, name, category, attribute="value", profile_key="@DEFAULT@"): return unicode(self.db_core_iface.getParamA(name, category, attribute, profile_key)) - def getParams(self, profile_key="@DEFAULT@"): - return unicode(self.db_core_iface.getParams(profile_key)) + def getParams(self, profile_key="@DEFAULT@", callback=None, errback=None): + return unicode(self.db_core_iface.getParams(profile_key, reply_handler=callback, error_handler=lambda err:errback(err._dbus_error_name[len(const_ERROR_PREFIX)+1:]))) def getParamsCategories(self, ): return self.db_core_iface.getParamsCategories() - def getParamsForCategory(self, category, profile_key="@DEFAULT@"): - return unicode(self.db_core_iface.getParamsForCategory(category, profile_key)) + def getParamsForCategory(self, category, profile_key="@DEFAULT@", callback=None, errback=None): + return unicode(self.db_core_iface.getParamsForCategory(category, profile_key, reply_handler=callback, error_handler=lambda err:errback(err._dbus_error_name[len(const_ERROR_PREFIX)+1:]))) - def getParamsUI(self, profile_key="@DEFAULT@"): - return unicode(self.db_core_iface.getParamsUI(profile_key)) + def getParamsUI(self, profile_key="@DEFAULT@", callback=None, errback=None): + return unicode(self.db_core_iface.getParamsUI(profile_key, reply_handler=callback, error_handler=lambda err:errback(err._dbus_error_name[len(const_ERROR_PREFIX)+1:]))) def getPresenceStatus(self, profile_key="@DEFAULT@"): return self.db_core_iface.getPresenceStatus(profile_key) diff -r 5a18c5f08d9b -r 6c20c76abdcc frontends/src/primitivus/primitivus --- a/frontends/src/primitivus/primitivus Sun Nov 06 15:19:51 2011 +0100 +++ b/frontends/src/primitivus/primitivus Mon Nov 07 00:09:22 2011 +0100 @@ -468,8 +468,12 @@ self.bridge.disconnect(self.profile) def onParam(self, menu): - params = XMLUI(self,xml_data=self.bridge.getParamsUI(self.profile)) - self.addWindow(params) + def success(params): + self.addWindow(XMLUI(self,xml_data=params)) + def failure(error): + self.showPopUp(sat_widgets.Alert(_("Error"), _("Can't get parameters"), ok_cb=self.removePopUp)) + self.bridge.getParamsUI(self.profile, callback=success, errback=failure) + def onExitRequest(self, menu): QuickApp.onExit(self) diff -r 5a18c5f08d9b -r 6c20c76abdcc frontends/src/primitivus/profile_manager.py --- a/frontends/src/primitivus/profile_manager.py Sun Nov 06 15:19:51 2011 +0100 +++ b/frontends/src/primitivus/profile_manager.py Mon Nov 07 00:09:22 2011 +0100 @@ -148,5 +148,6 @@ self.host.bridge.setParam("Password", new_pass, "Connection", profile) self.host.plug_profile(profile) - def getParamError(self): - error(_("Can't get profile parameter")) + def getParamError(self, ignore): + popup = Alert("Error", _("Can't get profile parameter"), ok_cb=self.host.removePopUp) + self.host.showPopUp(popup) diff -r 5a18c5f08d9b -r 6c20c76abdcc src/bridge/DBus.py --- a/src/bridge/DBus.py Sun Nov 06 15:19:51 2011 +0100 +++ b/src/bridge/DBus.py Mon Nov 07 00:09:22 2011 +0100 @@ -24,7 +24,8 @@ import dbus import dbus.service import dbus.mainloop.glib -from logging import debug, info +from logging import debug, info, error +from twisted.internet.defer import Deferred const_INT_PREFIX = "org.goffi.SAT" #Interface prefix const_ERROR_PREFIX = const_INT_PREFIX+".error" @@ -32,10 +33,20 @@ const_CORE_SUFFIX = ".core" const_PLUGIN_SUFFIX = ".plugin" +class MethodNotRegistered(dbus.DBusException): + _dbus_error_name = const_ERROR_PREFIX + ".MethodNotRegistered" + +class InternalError(dbus.DBusException): + _dbus_error_name = const_ERROR_PREFIX + ".InternalError" + +class AsyncNotDeferred(dbus.DBusException): + _dbus_error_name = const_ERROR_PREFIX + ".AsyncNotDeferred" + class GenericException(dbus.DBusException): - def __init__(self, name): + def __init__(self, twisted_error): super(GenericException,self).__init__() - self._dbus_error_name = const_ERROR_PREFIX+"."+name + mess = twisted_error.getErrorMessage() + self._dbus_error_name = const_ERROR_PREFIX+"."+ (mess or str(err.__class__)) class DbusObject(dbus.service.Object): @@ -47,6 +58,32 @@ def register(self, name, cb): self.cb[name]=cb + def _callback(self, name, *args, **kwargs): + """call the callback if it exists, raise an exception else + if the callback return a deferred, use async methods""" + if not name in self.cb: + raise MethodNotRegistered + + if "callback" in kwargs: + #we must have errback too + if not "errback" in kwargs: + error("errback is missing in method call [%s]" % name) + raise InternalError + callback = kwargs.pop("callback") + errback = kwargs.pop("errback") + async = True + else: + async = False + result = self.cb[name](*args, **kwargs) + if async: + if not isinstance(result, Deferred): + error("Asynchrone method [%s] does not return a Deferred." % name) + raise AsyncNotDeferred + result.addCallback(callback) + result.addErrback(lambda err:errback(GenericException(err))) + else: + return result + ### signals ### @dbus.service.signal(const_INT_PREFIX+const_PLUGIN_SUFFIX, @@ -134,217 +171,217 @@ in_signature='ss', out_signature='', async_callbacks=None) def addContact(self, entity_jid, profile_key="@DEFAULT@"): - return self.cb["addContact"](unicode(entity_jid), unicode(profile_key)) + return self._callback("addContact", unicode(entity_jid), unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='', async_callbacks=('callback', 'errback')) def asyncConnect(self, profile_key="@DEFAULT@", callback=None, errback=None): - return self.cb["asyncConnect"](unicode(profile_key), callback, lambda arg:errback(GenericException(arg))) + return self._callback("asyncConnect", unicode(profile_key), callback=callback, errback=errback) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='', async_callbacks=('callback', 'errback')) def asyncCreateProfile(self, profile, callback=None, errback=None): - return self.cb["asyncCreateProfile"](unicode(profile), callback, lambda arg:errback(GenericException(arg))) + return self._callback("asyncCreateProfile", unicode(profile), callback=callback, errback=errback) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='ssss', out_signature='s', async_callbacks=('callback', 'errback')) def asyncGetParamA(self, name, category, attribute="value", profile_key="@DEFAULT@", callback=None, errback=None): - return self.cb["asyncGetParamA"](unicode(name), unicode(category), unicode(attribute), unicode(profile_key), callback, lambda arg:errback(GenericException(arg))) + return self._callback("asyncGetParamA", unicode(name), unicode(category), unicode(attribute), unicode(profile_key), callback=callback, errback=errback) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='ssss', out_signature='s', async_callbacks=None) def callMenu(self, category, name, menu_type, profile_key): - return self.cb["callMenu"](unicode(category), unicode(name), unicode(menu_type), unicode(profile_key)) + return self._callback("callMenu", unicode(category), unicode(name), unicode(menu_type), unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='sba{ss}', out_signature='', async_callbacks=None) def confirmationAnswer(self, id, accepted, data): - return self.cb["confirmationAnswer"](unicode(id), accepted, data) + return self._callback("confirmationAnswer", unicode(id), accepted, data) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='', async_callbacks=None) def connect(self, profile_key="@DEFAULT@"): - return self.cb["connect"](unicode(profile_key)) + return self._callback("connect", unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='i', async_callbacks=None) def createProfile(self, profile): - return self.cb["createProfile"](unicode(profile)) + return self._callback("createProfile", unicode(profile)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='ss', out_signature='', async_callbacks=None) def delContact(self, entity_jid, profile_key="@DEFAULT@"): - return self.cb["delContact"](unicode(entity_jid), unicode(profile_key)) + return self._callback("delContact", unicode(entity_jid), unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='i', async_callbacks=None) def deleteProfile(self, profile): - return self.cb["deleteProfile"](unicode(profile)) + return self._callback("deleteProfile", unicode(profile)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='', async_callbacks=None) def disconnect(self, profile_key="@DEFAULT@"): - return self.cb["disconnect"](unicode(profile_key)) + return self._callback("disconnect", unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='ss', out_signature='s', async_callbacks=None) def getConfig(self, section, name): - return self.cb["getConfig"](unicode(section), unicode(name)) + return self._callback("getConfig", unicode(section), unicode(name)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='a(sa{ss}as)', async_callbacks=None) def getContacts(self, profile_key="@DEFAULT@"): - return self.cb["getContacts"](unicode(profile_key)) + return self._callback("getContacts", unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='ssi', out_signature='a{i(ss)}', async_callbacks=None) def getHistory(self, from_jid, to_jid, size): - return self.cb["getHistory"](unicode(from_jid), unicode(to_jid), size) + return self._callback("getHistory", unicode(from_jid), unicode(to_jid), size) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='ss', out_signature='s', async_callbacks=None) def getLastResource(self, contact_jid, profile_key="@DEFAULT@"): - return self.cb["getLastResource"](unicode(contact_jid), unicode(profile_key)) + return self._callback("getLastResource", unicode(contact_jid), unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='sss', out_signature='s', async_callbacks=None) def getMenuHelp(self, category, name, menu_type): - return self.cb["getMenuHelp"](unicode(category), unicode(name), unicode(menu_type)) + return self._callback("getMenuHelp", unicode(category), unicode(name), unicode(menu_type)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='', out_signature='a(sss)', async_callbacks=None) def getMenus(self, ): - return self.cb["getMenus"]() + return self._callback("getMenus", ) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='ssss', out_signature='s', async_callbacks=None) def getParamA(self, name, category, attribute="value", profile_key="@DEFAULT@"): - return self.cb["getParamA"](unicode(name), unicode(category), unicode(attribute), unicode(profile_key)) + return self._callback("getParamA", unicode(name), unicode(category), unicode(attribute), unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='s', - async_callbacks=None) - def getParams(self, profile_key="@DEFAULT@"): - return self.cb["getParams"](unicode(profile_key)) + async_callbacks=('callback', 'errback')) + def getParams(self, profile_key="@DEFAULT@", callback=None, errback=None): + return self._callback("getParams", unicode(profile_key), callback=callback, errback=errback) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='', out_signature='as', async_callbacks=None) def getParamsCategories(self, ): - return self.cb["getParamsCategories"]() + return self._callback("getParamsCategories", ) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='ss', out_signature='s', - async_callbacks=None) - def getParamsForCategory(self, category, profile_key="@DEFAULT@"): - return self.cb["getParamsForCategory"](unicode(category), unicode(profile_key)) + async_callbacks=('callback', 'errback')) + def getParamsForCategory(self, category, profile_key="@DEFAULT@", callback=None, errback=None): + return self._callback("getParamsForCategory", unicode(category), unicode(profile_key), callback=callback, errback=errback) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='s', - async_callbacks=None) - def getParamsUI(self, profile_key="@DEFAULT@"): - return self.cb["getParamsUI"](unicode(profile_key)) + async_callbacks=('callback', 'errback')) + def getParamsUI(self, profile_key="@DEFAULT@", callback=None, errback=None): + return self._callback("getParamsUI", unicode(profile_key), callback=callback, errback=errback) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='a{sa{s(sia{ss})}}', async_callbacks=None) def getPresenceStatus(self, profile_key="@DEFAULT@"): - return self.cb["getPresenceStatus"](unicode(profile_key)) + return self._callback("getPresenceStatus", unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='s', async_callbacks=None) def getProfileName(self, profile_key="@DEFAULT@"): - return self.cb["getProfileName"](unicode(profile_key)) + return self._callback("getProfileName", unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='', out_signature='as', async_callbacks=None) def getProfilesList(self, ): - return self.cb["getProfilesList"]() + return self._callback("getProfilesList", ) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='a{ss}', async_callbacks=None) def getProgress(self, id): - return self.cb["getProgress"](unicode(id)) + return self._callback("getProgress", unicode(id)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='', out_signature='s', async_callbacks=None) def getVersion(self, ): - return self.cb["getVersion"]() + return self._callback("getVersion", ) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='a{ss}', async_callbacks=None) def getWaitingSub(self, profile_key="@DEFAULT@"): - return self.cb["getWaitingSub"](unicode(profile_key)) + return self._callback("getWaitingSub", unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='s', out_signature='b', async_callbacks=None) def isConnected(self, profile_key="@DEFAULT@"): - return self.cb["isConnected"](unicode(profile_key)) + return self._callback("isConnected", unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='sa{ss}s', out_signature='s', async_callbacks=None) def launchAction(self, action_type, data, profile_key="@DEFAULT@"): - return self.cb["launchAction"](unicode(action_type), data, unicode(profile_key)) + return self._callback("launchAction", unicode(action_type), data, unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='ssssi', out_signature='s', async_callbacks=None) def registerNewAccount(self, login, password, email, host, port=5222): - return self.cb["registerNewAccount"](unicode(login), unicode(password), unicode(email), unicode(host), port) + return self._callback("registerNewAccount", unicode(login), unicode(password), unicode(email), unicode(host), port) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='sssss', out_signature='', async_callbacks=None) def sendMessage(self, to_jid, message, subject='', mess_type="chat", profile_key="@DEFAULT@"): - return self.cb["sendMessage"](unicode(to_jid), unicode(message), unicode(subject), unicode(mess_type), unicode(profile_key)) + return self._callback("sendMessage", unicode(to_jid), unicode(message), unicode(subject), unicode(mess_type), unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='ssss', out_signature='', async_callbacks=None) def setParam(self, name, value, category, profile_key="@DEFAULT@"): - return self.cb["setParam"](unicode(name), unicode(value), unicode(category), unicode(profile_key)) + return self._callback("setParam", unicode(name), unicode(value), unicode(category), unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='ssia{ss}s', out_signature='', async_callbacks=None) def setPresence(self, to_jid='', show='', priority=0, statuses={}, profile_key="@DEFAULT@"): - return self.cb["setPresence"](unicode(to_jid), unicode(show), priority, statuses, unicode(profile_key)) + return self._callback("setPresence", unicode(to_jid), unicode(show), priority, statuses, unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='sss', out_signature='', async_callbacks=None) def subscription(self, sub_type, entity, profile_key="@DEFAULT@"): - return self.cb["subscription"](unicode(sub_type), unicode(entity), unicode(profile_key)) + return self._callback("subscription", unicode(sub_type), unicode(entity), unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='ssass', out_signature='', async_callbacks=None) def updateContact(self, entity_jid, name, groups, profile_key="@DEFAULT@"): - return self.cb["updateContact"](unicode(entity_jid), unicode(name), groups, unicode(profile_key)) + return self._callback("updateContact", unicode(entity_jid), unicode(name), groups, unicode(profile_key)) def __attributes(self, in_sign): diff -r 5a18c5f08d9b -r 6c20c76abdcc src/bridge/bridge_constructor/bridge_contructor.py --- a/src/bridge/bridge_constructor/bridge_contructor.py Sun Nov 06 15:19:51 2011 +0100 +++ b/src/bridge/bridge_constructor/bridge_contructor.py Mon Nov 07 00:09:22 2011 +0100 @@ -393,14 +393,14 @@ completion['args_result'] = self.getArguments(function['sig_in'], name=arg_doc, unicode_protect=self.options.unicode) completion['async_comma'] = ', ' if async and function['sig_in'] else '' completion['async_args_def'] = 'callback=None, errback=None' if async else '' - completion['async_args_call'] = 'callback, lambda arg:errback(GenericException(arg))' if async else '' + completion['async_args_call'] = 'callback=callback, errback=errback' if async else '' completion['async_callbacks'] = "('callback', 'errback')" if async else "None" methods_part.append("""\ @dbus.service.method(const_INT_PREFIX+const_%(category)s_SUFFIX, in_signature='%(sig_in)s', out_signature='%(sig_out)s', async_callbacks=%(async_callbacks)s) def %(name)s(self, %(args)s%(async_comma)s%(async_args_def)s): - %(debug)sreturn self.cb["%(name)s"](%(args_result)s%(async_comma)s%(async_args_call)s) + %(debug)sreturn self._callback("%(name)s", %(args_result)s%(async_comma)s%(async_args_call)s) """ % completion) #at this point, signals_part, methods_part and direct_calls should be filled, diff -r 5a18c5f08d9b -r 6c20c76abdcc src/bridge/bridge_constructor/bridge_template.ini --- a/src/bridge/bridge_constructor/bridge_template.ini Sun Nov 06 15:19:51 2011 +0100 +++ b/src/bridge/bridge_constructor/bridge_template.ini Mon Nov 07 00:09:22 2011 +0100 @@ -195,7 +195,6 @@ errback is called with error constant as parameter: - "CONFLICT": The profile name already exists - "CANCELED": profile creation canceled - - "DATABASE": profile creation in database failed [deleteProfile] type=method @@ -396,6 +395,7 @@ doc_param_3=%(doc_profile_key)s [getParamsUI] +async= type=method category=core sig_in=s @@ -405,6 +405,7 @@ doc_param_0=%(doc_profile_key)s [getParams] +async= type=method category=core sig_in=s @@ -414,6 +415,7 @@ doc_param_0=%(doc_profile_key)s [getParamsForCategory] +async= type=method category=core sig_in=ss diff -r 5a18c5f08d9b -r 6c20c76abdcc src/bridge/bridge_constructor/dbus_core_template.py --- a/src/bridge/bridge_constructor/dbus_core_template.py Sun Nov 06 15:19:51 2011 +0100 +++ b/src/bridge/bridge_constructor/dbus_core_template.py Mon Nov 07 00:09:22 2011 +0100 @@ -24,7 +24,8 @@ import dbus import dbus.service import dbus.mainloop.glib -from logging import debug, info +from logging import debug, info, error +from twisted.internet.defer import Deferred const_INT_PREFIX = "org.goffi.SAT" #Interface prefix const_ERROR_PREFIX = const_INT_PREFIX+".error" @@ -32,10 +33,20 @@ const_CORE_SUFFIX = ".core" const_PLUGIN_SUFFIX = ".plugin" +class MethodNotRegistered(dbus.DBusException): + _dbus_error_name = const_ERROR_PREFIX + ".MethodNotRegistered" + +class InternalError(dbus.DBusException): + _dbus_error_name = const_ERROR_PREFIX + ".InternalError" + +class AsyncNotDeferred(dbus.DBusException): + _dbus_error_name = const_ERROR_PREFIX + ".AsyncNotDeferred" + class GenericException(dbus.DBusException): - def __init__(self, name): + def __init__(self, twisted_error): super(GenericException,self).__init__() - self._dbus_error_name = const_ERROR_PREFIX+"."+name + mess = twisted_error.getErrorMessage() + self._dbus_error_name = const_ERROR_PREFIX+"."+ (mess or str(err.__class__)) class DbusObject(dbus.service.Object): @@ -47,6 +58,32 @@ def register(self, name, cb): self.cb[name]=cb + def _callback(self, name, *args, **kwargs): + """call the callback if it exists, raise an exception else + if the callback return a deferred, use async methods""" + if not name in self.cb: + raise MethodNotRegistered + + if "callback" in kwargs: + #we must have errback too + if not "errback" in kwargs: + error("errback is missing in method call [%s]" % name) + raise InternalError + callback = kwargs.pop("callback") + errback = kwargs.pop("errback") + async = True + else: + async = False + result = self.cb[name](*args, **kwargs) + if async: + if not isinstance(result, Deferred): + error("Asynchrone method [%s] does not return a Deferred." % name) + raise AsyncNotDeferred + result.addCallback(callback) + result.addErrback(lambda err:errback(GenericException(err))) + else: + return result + ### signals ### @dbus.service.signal(const_INT_PREFIX+const_PLUGIN_SUFFIX, diff -r 5a18c5f08d9b -r 6c20c76abdcc src/core/sat_main.py --- a/src/core/sat_main.py Sun Nov 06 15:19:51 2011 +0100 +++ b/src/core/sat_main.py Mon Nov 07 00:09:22 2011 +0100 @@ -205,22 +205,19 @@ """Connect to jabber server""" self.asyncConnect(profile_key) - def asyncConnect(self, profile_key = '@DEFAULT@', callback=None, errback=None): + def asyncConnect(self, profile_key = '@DEFAULT@'): """Connect to jabber server with asynchronous reply @param profile_key: %(doc_profile)s - @param callback: called when the profile is connected - @param errback: called is the connection fail""" + """ profile = self.memory.getProfileName(profile_key) if not profile: error (_('Trying to connect a non-exsitant profile')) - return + return defer.fail() if (self.isConnected(profile)): info(_("already connected !")) - if callback: - callback() - return + return defer.succeed("None") def afterMemoryInit(ignore): """This part must be called when we have loaded individual parameters from memory""" @@ -229,9 +226,6 @@ self.memory.getParamA("Password", "Connection", profile_key = profile), self.memory.getParamA("Server", "Connection", profile_key = profile), 5222) - if callback and errback: - current.getConnectionDeferred().addCallbacks(lambda x:callback(), errback) - current.messageProt = xmpp.SatMessageProtocol(self) current.messageProt.setHandlerParent(current) @@ -256,9 +250,9 @@ current.startService() - params_defer = self.memory.loadIndividualParams(profile).addCallback(afterMemoryInit) - if errback: - params_defer.addErrback(errback) + return current.getConnectionDeferred() + + return self.memory.loadIndividualParams(profile).addCallback(afterMemoryInit) def disconnect(self, profile_key): """disconnect from jabber server""" diff -r 5a18c5f08d9b -r 6c20c76abdcc src/tools/memory.py --- a/src/tools/memory.py Sun Nov 06 15:19:51 2011 +0100 +++ b/src/tools/memory.py Mon Nov 07 00:09:22 2011 +0100 @@ -86,13 +86,15 @@ @return: deferred triggered once params are loaded""" return self.storage.loadGenParams(self.params_gen) - def loadIndParams(self, profile): + def loadIndParams(self, profile, cache=None): """Load individual parameters - set self.params cache + set self.params cache or a temporary cache @param profile: profile to load (*must exist*) + @param cache: if not None, will be used to store the value, as a short time cache @return: deferred triggered once params are loaded""" - self.params[profile] = {} - return self.storage.loadIndParams(self.params, profile) + if cache == None: + self.params[profile] = {} + return self.storage.loadIndParams(self.params[profile] if cache==None else cache, profile) def purgeProfile(self, profile): """Remove cache data of a profile @@ -129,25 +131,20 @@ self.storage.createProfile(profile) return False - def asyncCreateProfile(self, profile, callback, errback): + def asyncCreateProfile(self, profile): """Create a new profile @param profile: name of the profile @param callback: called when the profile actually exists in database and memory @param errback: called with a string constant as parameter: - CONFLICT: the profile already exists - CANCELED: profile creation canceled - - DATABASE: profile creation in database failed""" - #FIXME: must be asynchronous and call the callback once the profile actually exists + """ if self.storage.hasProfile(profile): info (_('The profile name already exists')) - errback("CONFLICT") - return + return defer.fail("CONFLICT") if not self.host.trigger.point("ProfileCreation", profile): - errback("CANCELED") - return - d = self.storage.createProfile(profile) - d.addCallback(lambda ignore: callback()) - d.addErrback(lambda ignore: errback("DATABASE")) + return defer.fail("CANCEL") + return self.storage.createProfile(profile) def deleteProfile(self, profile): @@ -175,7 +172,7 @@ default = self.host.memory.getPrivate('Profile_default') if not default or not default in self.params: info(_('No default profile, returning first one')) #TODO: manage real default profile - default = self.getProfilesList()[0] + default = self.storage.getProfilesList()[0] self.host.memory.setPrivate('Profile_default', default) return default #FIXME: temporary, must use real default value, and fallback to first one if it doesn't exists if not self.storage.hasProfile(profile_key): @@ -274,14 +271,12 @@ else: return node[1].getAttribute(attr) - def asyncGetParamA(self, name, category, attr="value", profile_key="@DEFAULT@", callback=None, errback=None): + def asyncGetParamA(self, name, category, attr="value", profile_key="@DEFAULT@"): """Helper method to get a specific attribute @param name: name of the parameter @param category: category of the parameter @param attr: name of the attribute (default: "value") - @param profile: owner of the param (@ALL@ for everyone) - @param callback: called when the profile is connected - @param errback: called is the connection fail""" + @param profile: owner of the param (@ALL@ for everyone)""" node = self.__getParamNode(name, category) if not node: error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name':name, 'category':category}) @@ -289,132 +284,146 @@ if node[0] == 'general': value = self.__getParam(None, category, name, 'general') - callback(value if value!=None else node[1].getAttribute(attr)) - return + return defer.succeed(value if value!=None else node[1].getAttribute(attr)) assert(node[0] == 'individual') profile = self.getProfileName(profile_key) if not profile: error(_('Requesting a param for a non-existant profile')) - errback() - return + return defer.fail() if attr != "value": - callback(node[1].getAttribute(attr)) - return + return defer.succeed(node[1].getAttribute(attr)) default = node[1].getAttribute(attr) try: value = self.__getParam(profile, category, name) - callback(value if value!=None else default) + return defer.succeed(value if value!=None else default) except ProfileNotInCacheError: #We have to ask data to the storage manager d = self.storage.getIndParam(category, name, profile) - d.addCallback(lambda value: callback(value if value!=None else default)) - d.addErrback(lambda x:errback()) + return d.addCallback(lambda value: value if value!=None else default) - def __getParam(self, profile, category, name, type='individual'): + def __getParam(self, profile, category, name, type='individual', cache=None): """Return the param, or None if it doesn't exist @param profile: the profile name (not profile key, i.e. name and not something like @DEFAULT@) @param category: param category @param name: param name + @param type: "general" or "individual" + @param cache: temporary cache, to use when profile is not logged + @return: param value or None if it doesn't exist """ if type == 'general': if self.params_gen.has_key((category, name)): return self.params_gen[(category, name)] return None #This general param has the default value assert (type == 'individual') - if not self.params.has_key(profile): + if self.params.has_key(profile): + cache = self.params[profile] # if profile is in main cache, we use it, + # ignoring the temporary cache + elif cache == None: #else we use the temporary cache if it exists, or raise an exception raise ProfileNotInCacheError - if not self.params[profile].has_key((category, name)): + if not cache.has_key((category, name)): return None - return self.params[profile][(category, name)] + return cache[(category, name)] def __constructProfileXml(self, profile): """Construct xml for asked profile, filling values when needed /!\ as noticed in doc, don't forget to unlink the minidom.Document @param profile: profile name (not key !) - @return: minidom.Document of the profile xml (cf warning above) + @return: a deferred that fire a minidom.Document of the profile xml (cf warning above) """ - #TODO: asynchronous equivalent - prof_xml = minidom.parseString('') - cache = {} + def constructProfile(ignore,profile_cache): + prof_xml = minidom.parseString('') + cache = {} + + for type_node in self.dom.documentElement.childNodes: + if type_node.nodeName == 'general' or type_node.nodeName == 'individual': #we use all params, general and individual + for cat_node in type_node.childNodes: + if cat_node.nodeName == 'category': + category = cat_node.getAttribute('name') + if not cache.has_key(category): + cache[category] = dest_cat = cat_node.cloneNode(True) #we make a copy for the new xml + new_node = True + else: + dest_cat = cache[category] + new_node = False #It's not a new node, we will merge information + params = cat_node.getElementsByTagName("param") + dest_params = {} + for node in dest_cat.childNodes: + if node.nodeName != "param": + continue + dest_params[node.getAttribute('name')] = node - for type_node in self.dom.documentElement.childNodes: - if type_node.nodeName == 'general' or type_node.nodeName == 'individual': #we use all params, general and individual - for cat_node in type_node.childNodes: - if cat_node.nodeName == 'category': - category = cat_node.getAttribute('name') - if not cache.has_key(category): - cache[category] = dest_cat = cat_node.cloneNode(True) #we make a copy for the new xml - new_node = True - else: - dest_cat = cache[category] - new_node = False #It's not a new node, we will merge information - params = cat_node.getElementsByTagName("param") - dest_params = {} - for node in dest_cat.childNodes: - if node.nodeName != "param": - continue - dest_params[node.getAttribute('name')] = node + for param_node in params: + name = param_node.getAttribute('name') + + if name not in dest_params: + dest_params[name] = param_node.cloneNode(True) + dest_cat.appendChild(dest_params[name]) - for param_node in params: - name = param_node.getAttribute('name') - - if name not in dest_params: - dest_params[name] = param_node.cloneNode(True) - dest_cat.appendChild(dest_params[name]) + profile_value = self.__getParam(profile, category, name, type_node.nodeName, cache=profile_cache) + if profile_value!=None: #there is a value for this profile, we must change the default + dest_params[name].setAttribute('value', profile_value) + if new_node: + prof_xml.documentElement.appendChild(dest_cat) + return prof_xml + + + if self.params.has_key(profile): + d = defer.succeed(None) + profile_cache = self.params[profile] + else: + #profile is not in cache, we load values in a short time cache + profile_cache = {} + d = self.loadIndParams(profile, profile_cache) - profile_value = self.__getParam(profile, category, name, type_node.nodeName) - if profile_value!=None: #there is a value for this profile, we must change the default - dest_params[name].setAttribute('value', profile_value) - if new_node: - prof_xml.documentElement.appendChild(dest_cat) - return prof_xml - + return d.addCallback(constructProfile, profile_cache) def getParamsUI(self, profile_key): """Return a SàT XMLUI for parameters, with given profile""" - #TODO: asynchronous equivalent profile = self.getProfileName(profile_key) if not profile: error(_("Asking params for inexistant profile")) return "" - param_xml = self.getParams(profile) - return paramsXml2xmlUI(param_xml) + d = self.getParams(profile) + return d.addCallback(lambda param_xml:paramsXml2xmlUI(param_xml)) def getParams(self, profile_key): """Construct xml for asked profile Take params xml as skeleton""" - #TODO: asynchronous equivalent profile = self.getProfileName(profile_key) if not profile: error(_("Asking params for inexistant profile")) return "" - prof_xml = self.__constructProfileXml(profile) - return_xml = prof_xml.toxml() - prof_xml.unlink() + + def returnXML(prof_xml): + return_xml = prof_xml.toxml() + prof_xml.unlink() + return return_xml - return return_xml + return self.__constructProfileXml(profile).addCallback(returnXML) def getParamsForCategory(self, category, profile_key): """Return node's xml for selected category""" #TODO: manage category of general type (without existant profile) - #TODO: asynchronous equivalent profile = self.getProfileName(profile_key) if not profile: error(_("Asking params for inexistant profile")) return "" - prof_xml = self.__constructProfileXml(profile) + + def returnCategoryXml(prof_xml): + for node in prof_xml.getElementsByTagName("category"): + if node.nodeName == "category" and node.getAttribute("name") == category: + result = node.toxml() + prof_xml.unlink() + return result + + prof_xml.unlink() + return "" - for node in prof_xml.getElementsByTagName("category"): - if node.nodeName == "category" and node.getAttribute("name") == category: - result = node.toxml() - prof_xml.unlink() - return result - - prof_xml.unlink() - return "" + d = self.__constructProfileXml(profile) + return d.addCallback(returnCategoryXml) def __getParamNode(self, name, category, type="@ALL@"): #FIXME: is type useful ? """Return a node from the param_xml @@ -463,7 +472,7 @@ if node[0] == 'general': self.params_gen[(category, name)] = value self.storage.setGenParam(category, name, value) - for profile in self.getProfilesList(): + for profile in self.storage.getProfilesList(): if self.host.isConnected(profile): self.host.bridge.paramUpdate(name, value, category, profile) return @@ -625,11 +634,11 @@ """ return self.params.createProfile(name) - def asyncCreateProfile(self, name, callback, errback): + def asyncCreateProfile(self, name): """Create a new profile @param name: Profile name """ - return self.params.asyncCreateProfile(name, callback, errback) + return self.params.asyncCreateProfile(name) def deleteProfile(self, name): """Delete an existing profile @@ -839,8 +848,8 @@ def getParamA(self, name, category, attr="value", profile_key='@DEFAULT@'): return self.params.getParamA(name, category, attr, profile_key) - def asyncGetParamA(self, name, category, attr="value", profile_key='@DEFAULT@', callback=None, errback=None): - return self.params.asyncGetParamA(name, category, attr, profile_key, callback, errback) + def asyncGetParamA(self, name, category, attr="value", profile_key='@DEFAULT@'): + return self.params.asyncGetParamA(name, category, attr, profile_key) def getParamsUI(self, profile_key): return self.params.getParamsUI(profile_key) diff -r 5a18c5f08d9b -r 6c20c76abdcc src/tools/sqlite.py --- a/src/tools/sqlite.py Sun Nov 06 15:19:51 2011 +0100 +++ b/src/tools/sqlite.py Mon Nov 07 00:09:22 2011 +0100 @@ -124,7 +124,7 @@ def fillParams(result): for param in result: category,name,value = param - params_ind[profile][(category, name)] = value + params_ind[(category, name)] = value debug(_("loading individual parameters from database")) d = self.dbpool.runQuery("SELECT category,name,value FROM param_ind WHERE profile_id=?", (self.profiles[profile],)) d.addCallback(fillParams)