Mercurial > libervia-backend
diff src/tools/memory.py @ 423:6c20c76abdcc
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
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 07 Nov 2011 00:09:22 +0100 |
parents | acd908528ef7 |
children | e4e9187e3b5b |
line wrap: on
line diff
--- 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('<params/>') - cache = {} + def constructProfile(ignore,profile_cache): + prof_xml = minidom.parseString('<params/>') + 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 "<category />" - 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 "<category />" + 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)