comparison 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
comparison
equal deleted inserted replaced
422:5a18c5f08d9b 423:6c20c76abdcc
84 def loadGenParams(self): 84 def loadGenParams(self):
85 """Load general parameters data from storage 85 """Load general parameters data from storage
86 @return: deferred triggered once params are loaded""" 86 @return: deferred triggered once params are loaded"""
87 return self.storage.loadGenParams(self.params_gen) 87 return self.storage.loadGenParams(self.params_gen)
88 88
89 def loadIndParams(self, profile): 89 def loadIndParams(self, profile, cache=None):
90 """Load individual parameters 90 """Load individual parameters
91 set self.params cache 91 set self.params cache or a temporary cache
92 @param profile: profile to load (*must exist*) 92 @param profile: profile to load (*must exist*)
93 @param cache: if not None, will be used to store the value, as a short time cache
93 @return: deferred triggered once params are loaded""" 94 @return: deferred triggered once params are loaded"""
94 self.params[profile] = {} 95 if cache == None:
95 return self.storage.loadIndParams(self.params, profile) 96 self.params[profile] = {}
97 return self.storage.loadIndParams(self.params[profile] if cache==None else cache, profile)
96 98
97 def purgeProfile(self, profile): 99 def purgeProfile(self, profile):
98 """Remove cache data of a profile 100 """Remove cache data of a profile
99 @param profile: %(doc_profile)s""" 101 @param profile: %(doc_profile)s"""
100 try: 102 try:
127 if not self.host.trigger.point("ProfileCreation", profile): 129 if not self.host.trigger.point("ProfileCreation", profile):
128 return False 130 return False
129 self.storage.createProfile(profile) 131 self.storage.createProfile(profile)
130 return False 132 return False
131 133
132 def asyncCreateProfile(self, profile, callback, errback): 134 def asyncCreateProfile(self, profile):
133 """Create a new profile 135 """Create a new profile
134 @param profile: name of the profile 136 @param profile: name of the profile
135 @param callback: called when the profile actually exists in database and memory 137 @param callback: called when the profile actually exists in database and memory
136 @param errback: called with a string constant as parameter: 138 @param errback: called with a string constant as parameter:
137 - CONFLICT: the profile already exists 139 - CONFLICT: the profile already exists
138 - CANCELED: profile creation canceled 140 - CANCELED: profile creation canceled
139 - DATABASE: profile creation in database failed""" 141 """
140 #FIXME: must be asynchronous and call the callback once the profile actually exists
141 if self.storage.hasProfile(profile): 142 if self.storage.hasProfile(profile):
142 info (_('The profile name already exists')) 143 info (_('The profile name already exists'))
143 errback("CONFLICT") 144 return defer.fail("CONFLICT")
144 return
145 if not self.host.trigger.point("ProfileCreation", profile): 145 if not self.host.trigger.point("ProfileCreation", profile):
146 errback("CANCELED") 146 return defer.fail("CANCEL")
147 return 147 return self.storage.createProfile(profile)
148 d = self.storage.createProfile(profile)
149 d.addCallback(lambda ignore: callback())
150 d.addErrback(lambda ignore: errback("DATABASE"))
151 148
152 149
153 def deleteProfile(self, profile): 150 def deleteProfile(self, profile):
154 """Delete an existing profile 151 """Delete an existing profile
155 @param profile: name of the profile""" 152 @param profile: name of the profile"""
173 if not self.params: 170 if not self.params:
174 return "" 171 return ""
175 default = self.host.memory.getPrivate('Profile_default') 172 default = self.host.memory.getPrivate('Profile_default')
176 if not default or not default in self.params: 173 if not default or not default in self.params:
177 info(_('No default profile, returning first one')) #TODO: manage real default profile 174 info(_('No default profile, returning first one')) #TODO: manage real default profile
178 default = self.getProfilesList()[0] 175 default = self.storage.getProfilesList()[0]
179 self.host.memory.setPrivate('Profile_default', default) 176 self.host.memory.setPrivate('Profile_default', default)
180 return default #FIXME: temporary, must use real default value, and fallback to first one if it doesn't exists 177 return default #FIXME: temporary, must use real default value, and fallback to first one if it doesn't exists
181 if not self.storage.hasProfile(profile_key): 178 if not self.storage.hasProfile(profile_key):
182 info (_('Trying to access an unknown profile')) 179 info (_('Trying to access an unknown profile'))
183 return "" 180 return ""
272 value = self.__getParam(profile, category, name) 269 value = self.__getParam(profile, category, name)
273 return value if value!=None else node[1].getAttribute(attr) 270 return value if value!=None else node[1].getAttribute(attr)
274 else: 271 else:
275 return node[1].getAttribute(attr) 272 return node[1].getAttribute(attr)
276 273
277 def asyncGetParamA(self, name, category, attr="value", profile_key="@DEFAULT@", callback=None, errback=None): 274 def asyncGetParamA(self, name, category, attr="value", profile_key="@DEFAULT@"):
278 """Helper method to get a specific attribute 275 """Helper method to get a specific attribute
279 @param name: name of the parameter 276 @param name: name of the parameter
280 @param category: category of the parameter 277 @param category: category of the parameter
281 @param attr: name of the attribute (default: "value") 278 @param attr: name of the attribute (default: "value")
282 @param profile: owner of the param (@ALL@ for everyone) 279 @param profile: owner of the param (@ALL@ for everyone)"""
283 @param callback: called when the profile is connected
284 @param errback: called is the connection fail"""
285 node = self.__getParamNode(name, category) 280 node = self.__getParamNode(name, category)
286 if not node: 281 if not node:
287 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name':name, 'category':category}) 282 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name':name, 'category':category})
288 return None 283 return None
289 284
290 if node[0] == 'general': 285 if node[0] == 'general':
291 value = self.__getParam(None, category, name, 'general') 286 value = self.__getParam(None, category, name, 'general')
292 callback(value if value!=None else node[1].getAttribute(attr)) 287 return defer.succeed(value if value!=None else node[1].getAttribute(attr))
293 return
294 288
295 assert(node[0] == 'individual') 289 assert(node[0] == 'individual')
296 290
297 profile = self.getProfileName(profile_key) 291 profile = self.getProfileName(profile_key)
298 if not profile: 292 if not profile:
299 error(_('Requesting a param for a non-existant profile')) 293 error(_('Requesting a param for a non-existant profile'))
300 errback() 294 return defer.fail()
301 return
302 295
303 if attr != "value": 296 if attr != "value":
304 callback(node[1].getAttribute(attr)) 297 return defer.succeed(node[1].getAttribute(attr))
305 return
306 default = node[1].getAttribute(attr) 298 default = node[1].getAttribute(attr)
307 try: 299 try:
308 value = self.__getParam(profile, category, name) 300 value = self.__getParam(profile, category, name)
309 callback(value if value!=None else default) 301 return defer.succeed(value if value!=None else default)
310 except ProfileNotInCacheError: 302 except ProfileNotInCacheError:
311 #We have to ask data to the storage manager 303 #We have to ask data to the storage manager
312 d = self.storage.getIndParam(category, name, profile) 304 d = self.storage.getIndParam(category, name, profile)
313 d.addCallback(lambda value: callback(value if value!=None else default)) 305 return d.addCallback(lambda value: value if value!=None else default)
314 d.addErrback(lambda x:errback()) 306
315 307 def __getParam(self, profile, category, name, type='individual', cache=None):
316 def __getParam(self, profile, category, name, type='individual'):
317 """Return the param, or None if it doesn't exist 308 """Return the param, or None if it doesn't exist
318 @param profile: the profile name (not profile key, i.e. name and not something like @DEFAULT@) 309 @param profile: the profile name (not profile key, i.e. name and not something like @DEFAULT@)
319 @param category: param category 310 @param category: param category
320 @param name: param name 311 @param name: param name
312 @param type: "general" or "individual"
313 @param cache: temporary cache, to use when profile is not logged
314 @return: param value or None if it doesn't exist
321 """ 315 """
322 if type == 'general': 316 if type == 'general':
323 if self.params_gen.has_key((category, name)): 317 if self.params_gen.has_key((category, name)):
324 return self.params_gen[(category, name)] 318 return self.params_gen[(category, name)]
325 return None #This general param has the default value 319 return None #This general param has the default value
326 assert (type == 'individual') 320 assert (type == 'individual')
327 if not self.params.has_key(profile): 321 if self.params.has_key(profile):
322 cache = self.params[profile] # if profile is in main cache, we use it,
323 # ignoring the temporary cache
324 elif cache == None: #else we use the temporary cache if it exists, or raise an exception
328 raise ProfileNotInCacheError 325 raise ProfileNotInCacheError
329 if not self.params[profile].has_key((category, name)): 326 if not cache.has_key((category, name)):
330 return None 327 return None
331 return self.params[profile][(category, name)] 328 return cache[(category, name)]
332 329
333 def __constructProfileXml(self, profile): 330 def __constructProfileXml(self, profile):
334 """Construct xml for asked profile, filling values when needed 331 """Construct xml for asked profile, filling values when needed
335 /!\ as noticed in doc, don't forget to unlink the minidom.Document 332 /!\ as noticed in doc, don't forget to unlink the minidom.Document
336 @param profile: profile name (not key !) 333 @param profile: profile name (not key !)
337 @return: minidom.Document of the profile xml (cf warning above) 334 @return: a deferred that fire a minidom.Document of the profile xml (cf warning above)
338 """ 335 """
339 #TODO: asynchronous equivalent 336 def constructProfile(ignore,profile_cache):
340 prof_xml = minidom.parseString('<params/>') 337 prof_xml = minidom.parseString('<params/>')
341 cache = {} 338 cache = {}
342 339
343 for type_node in self.dom.documentElement.childNodes: 340 for type_node in self.dom.documentElement.childNodes:
344 if type_node.nodeName == 'general' or type_node.nodeName == 'individual': #we use all params, general and individual 341 if type_node.nodeName == 'general' or type_node.nodeName == 'individual': #we use all params, general and individual
345 for cat_node in type_node.childNodes: 342 for cat_node in type_node.childNodes:
346 if cat_node.nodeName == 'category': 343 if cat_node.nodeName == 'category':
347 category = cat_node.getAttribute('name') 344 category = cat_node.getAttribute('name')
348 if not cache.has_key(category): 345 if not cache.has_key(category):
349 cache[category] = dest_cat = cat_node.cloneNode(True) #we make a copy for the new xml 346 cache[category] = dest_cat = cat_node.cloneNode(True) #we make a copy for the new xml
350 new_node = True 347 new_node = True
351 else: 348 else:
352 dest_cat = cache[category] 349 dest_cat = cache[category]
353 new_node = False #It's not a new node, we will merge information 350 new_node = False #It's not a new node, we will merge information
354 params = cat_node.getElementsByTagName("param") 351 params = cat_node.getElementsByTagName("param")
355 dest_params = {} 352 dest_params = {}
356 for node in dest_cat.childNodes: 353 for node in dest_cat.childNodes:
357 if node.nodeName != "param": 354 if node.nodeName != "param":
358 continue 355 continue
359 dest_params[node.getAttribute('name')] = node 356 dest_params[node.getAttribute('name')] = node
360 357
361 for param_node in params: 358 for param_node in params:
362 name = param_node.getAttribute('name') 359 name = param_node.getAttribute('name')
363 360
364 if name not in dest_params: 361 if name not in dest_params:
365 dest_params[name] = param_node.cloneNode(True) 362 dest_params[name] = param_node.cloneNode(True)
366 dest_cat.appendChild(dest_params[name]) 363 dest_cat.appendChild(dest_params[name])
367 364
368 profile_value = self.__getParam(profile, category, name, type_node.nodeName) 365 profile_value = self.__getParam(profile, category, name, type_node.nodeName, cache=profile_cache)
369 if profile_value!=None: #there is a value for this profile, we must change the default 366 if profile_value!=None: #there is a value for this profile, we must change the default
370 dest_params[name].setAttribute('value', profile_value) 367 dest_params[name].setAttribute('value', profile_value)
371 if new_node: 368 if new_node:
372 prof_xml.documentElement.appendChild(dest_cat) 369 prof_xml.documentElement.appendChild(dest_cat)
373 return prof_xml 370 return prof_xml
374 371
372
373 if self.params.has_key(profile):
374 d = defer.succeed(None)
375 profile_cache = self.params[profile]
376 else:
377 #profile is not in cache, we load values in a short time cache
378 profile_cache = {}
379 d = self.loadIndParams(profile, profile_cache)
380
381 return d.addCallback(constructProfile, profile_cache)
375 382
376 def getParamsUI(self, profile_key): 383 def getParamsUI(self, profile_key):
377 """Return a SàT XMLUI for parameters, with given profile""" 384 """Return a SàT XMLUI for parameters, with given profile"""
378 #TODO: asynchronous equivalent
379 profile = self.getProfileName(profile_key) 385 profile = self.getProfileName(profile_key)
380 if not profile: 386 if not profile:
381 error(_("Asking params for inexistant profile")) 387 error(_("Asking params for inexistant profile"))
382 return "" 388 return ""
383 param_xml = self.getParams(profile) 389 d = self.getParams(profile)
384 return paramsXml2xmlUI(param_xml) 390 return d.addCallback(lambda param_xml:paramsXml2xmlUI(param_xml))
385 391
386 def getParams(self, profile_key): 392 def getParams(self, profile_key):
387 """Construct xml for asked profile 393 """Construct xml for asked profile
388 Take params xml as skeleton""" 394 Take params xml as skeleton"""
389 #TODO: asynchronous equivalent
390 profile = self.getProfileName(profile_key) 395 profile = self.getProfileName(profile_key)
391 if not profile: 396 if not profile:
392 error(_("Asking params for inexistant profile")) 397 error(_("Asking params for inexistant profile"))
393 return "" 398 return ""
394 prof_xml = self.__constructProfileXml(profile) 399
395 return_xml = prof_xml.toxml() 400 def returnXML(prof_xml):
396 prof_xml.unlink() 401 return_xml = prof_xml.toxml()
397 402 prof_xml.unlink()
398 return return_xml 403 return return_xml
404
405 return self.__constructProfileXml(profile).addCallback(returnXML)
399 406
400 def getParamsForCategory(self, category, profile_key): 407 def getParamsForCategory(self, category, profile_key):
401 """Return node's xml for selected category""" 408 """Return node's xml for selected category"""
402 #TODO: manage category of general type (without existant profile) 409 #TODO: manage category of general type (without existant profile)
403 #TODO: asynchronous equivalent
404 profile = self.getProfileName(profile_key) 410 profile = self.getProfileName(profile_key)
405 if not profile: 411 if not profile:
406 error(_("Asking params for inexistant profile")) 412 error(_("Asking params for inexistant profile"))
407 return "" 413 return ""
408 prof_xml = self.__constructProfileXml(profile) 414
409 415 def returnCategoryXml(prof_xml):
410 for node in prof_xml.getElementsByTagName("category"): 416 for node in prof_xml.getElementsByTagName("category"):
411 if node.nodeName == "category" and node.getAttribute("name") == category: 417 if node.nodeName == "category" and node.getAttribute("name") == category:
412 result = node.toxml() 418 result = node.toxml()
413 prof_xml.unlink() 419 prof_xml.unlink()
414 return result 420 return result
415 421
416 prof_xml.unlink() 422 prof_xml.unlink()
417 return "<category />" 423 return "<category />"
424
425 d = self.__constructProfileXml(profile)
426 return d.addCallback(returnCategoryXml)
418 427
419 def __getParamNode(self, name, category, type="@ALL@"): #FIXME: is type useful ? 428 def __getParamNode(self, name, category, type="@ALL@"): #FIXME: is type useful ?
420 """Return a node from the param_xml 429 """Return a node from the param_xml
421 @param name: name of the node 430 @param name: name of the node
422 @param category: category of the node 431 @param category: category of the node
461 return 470 return
462 471
463 if node[0] == 'general': 472 if node[0] == 'general':
464 self.params_gen[(category, name)] = value 473 self.params_gen[(category, name)] = value
465 self.storage.setGenParam(category, name, value) 474 self.storage.setGenParam(category, name, value)
466 for profile in self.getProfilesList(): 475 for profile in self.storage.getProfilesList():
467 if self.host.isConnected(profile): 476 if self.host.isConnected(profile):
468 self.host.bridge.paramUpdate(name, value, category, profile) 477 self.host.bridge.paramUpdate(name, value, category, profile)
469 return 478 return
470 479
471 assert (node[0] == 'individual') 480 assert (node[0] == 'individual')
623 """Create a new profile 632 """Create a new profile
624 @param name: Profile name 633 @param name: Profile name
625 """ 634 """
626 return self.params.createProfile(name) 635 return self.params.createProfile(name)
627 636
628 def asyncCreateProfile(self, name, callback, errback): 637 def asyncCreateProfile(self, name):
629 """Create a new profile 638 """Create a new profile
630 @param name: Profile name 639 @param name: Profile name
631 """ 640 """
632 return self.params.asyncCreateProfile(name, callback, errback) 641 return self.params.asyncCreateProfile(name)
633 642
634 def deleteProfile(self, name): 643 def deleteProfile(self, name):
635 """Delete an existing profile 644 """Delete an existing profile
636 @param name: Name of the profile""" 645 @param name: Name of the profile"""
637 return self.params.deleteProfile(name) 646 return self.params.deleteProfile(name)
837 return self.presenceStatus[profile] 846 return self.presenceStatus[profile]
838 847
839 def getParamA(self, name, category, attr="value", profile_key='@DEFAULT@'): 848 def getParamA(self, name, category, attr="value", profile_key='@DEFAULT@'):
840 return self.params.getParamA(name, category, attr, profile_key) 849 return self.params.getParamA(name, category, attr, profile_key)
841 850
842 def asyncGetParamA(self, name, category, attr="value", profile_key='@DEFAULT@', callback=None, errback=None): 851 def asyncGetParamA(self, name, category, attr="value", profile_key='@DEFAULT@'):
843 return self.params.asyncGetParamA(name, category, attr, profile_key, callback, errback) 852 return self.params.asyncGetParamA(name, category, attr, profile_key)
844 853
845 def getParamsUI(self, profile_key): 854 def getParamsUI(self, profile_key):
846 return self.params.getParamsUI(profile_key) 855 return self.params.getParamsUI(profile_key)
847 856
848 def getParams(self, profile_key): 857 def getParams(self, profile_key):