comparison src/memory/memory.py @ 722:04aabc3f2684

core (memory): fixed setDefault behaviour + minor refactoring
author Goffi <goffi@goffi.org>
date Thu, 28 Nov 2013 17:23:08 +0100
parents 59c9a7ff903d
children e07afabc4a25
comparison
equal deleted inserted replaced
721:0077912bc9ba 722:04aabc3f2684
32 from sat.memory.persistent import PersistentDict 32 from sat.memory.persistent import PersistentDict
33 from sat.core import exceptions 33 from sat.core import exceptions
34 34
35 SAVEFILE_DATABASE = "/sat.db" 35 SAVEFILE_DATABASE = "/sat.db"
36 NO_SECURITY_LIMIT = -1 36 NO_SECURITY_LIMIT = -1
37 INDIVIDUAL = "individual"
38 GENERAL = "general"
37 39
38 40
39 class Params(object): 41 class Params(object):
40 """This class manage parameters with xml""" 42 """This class manage parameters with xml"""
41 ### TODO: add desciption in params 43 ### TODO: add desciption in params
237 import_node(node, child) 239 import_node(node, child)
238 240
239 import_node(self.dom.documentElement, src_dom.documentElement) 241 import_node(self.dom.documentElement, src_dom.documentElement)
240 242
241 def __default_ok(self, value, name, category): 243 def __default_ok(self, value, name, category):
242 #FIXME: gof: will not work with individual parameters 244 #FIXME: will not work with individual parameters
243 self.setParam(name, value, category) # FIXME: better to set param xml value ??? 245 self.setParam(name, value, category)
244 246
245 def __default_ko(self, failure, name, category): 247 def __default_ko(self, failure, name, category):
246 error(_("Can't determine default value for [%(category)s/%(name)s]: %(reason)s") % {'category': category, 'name': name, 'reason': str(failure.value)}) 248 error(_("Can't determine default value for [%(category)s/%(name)s]: %(reason)s") % {'category': category, 'name': name, 'reason': str(failure.value)})
247 249
248 def setDefault(self, name, category, callback, errback=None): 250 def setDefault(self, name, category, callback, errback=None):
252 @param category: category of the parameter 254 @param category: category of the parameter
253 @param callback: must return a string with the value (use deferred if needed) 255 @param callback: must return a string with the value (use deferred if needed)
254 @param errback: must manage the error with args failure, name, category 256 @param errback: must manage the error with args failure, name, category
255 """ 257 """
256 #TODO: send signal param update if value changed 258 #TODO: send signal param update if value changed
257 node = self.__getParamNode(name, category, '@ALL@') 259 #TODO: manage individual paramaters
260 debug ("setDefault called for %(category)s/%(name)s" % {"category": category, "name": name})
261 node = self._getParamNode(name, category, '@ALL@')
258 if not node: 262 if not node:
259 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name': name, 'category': category}) 263 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name': name, 'category': category})
260 return 264 return
261 if node[1].getAttribute('default_cb') == 'yes': 265 if node[1].getAttribute('default_cb') == 'yes':
262 del node[1].attributes['default_cb'] 266 # del node[1].attributes['default_cb'] # default_cb is not used anymore as a flag to know if we have to set the default value,
263 d = defer.maybeDeferred(callback) 267 # and we can still use it later e.g. to call a generic setDefault method
264 d.addCallback(self.__default_ok, name, category) 268 value = self._getParam(category, name, GENERAL)
265 d.addErrback(errback or self.__default_ko, name, category) 269 if value is None: # no value set by the user: we have the default value
266 270 debug ("Default value to set, using callback")
267 def __getAttr(self, node, attr, value): 271 d = defer.maybeDeferred(callback)
272 d.addCallback(self.__default_ok, name, category)
273 d.addErrback(errback or self.__default_ko, name, category)
274
275 def _getAttr(self, node, attr, value):
268 """ get attribute value 276 """ get attribute value
269 @param node: XML param node 277 @param node: XML param node
270 @param attr: name of the attribute to get (e.g.: 'value' or 'type') 278 @param attr: name of the attribute to get (e.g.: 'value' or 'type')
271 @param value: user defined value""" 279 @param value: user defined value"""
272 if attr == 'value': 280 if attr == 'value':
293 @param attr: name of the attribute (default: "value") 301 @param attr: name of the attribute (default: "value")
294 @param profile: owner of the param (@ALL@ for everyone) 302 @param profile: owner of the param (@ALL@ for everyone)
295 303
296 @return: attribute""" 304 @return: attribute"""
297 #FIXME: looks really dirty and buggy, need to be reviewed/refactored 305 #FIXME: looks really dirty and buggy, need to be reviewed/refactored
298 node = self.__getParamNode(name, category) 306 node = self._getParamNode(name, category)
299 if not node: 307 if not node:
300 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name': name, 'category': category}) 308 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name': name, 'category': category})
301 raise exceptions.NotFound 309 raise exceptions.NotFound
302 310
303 if node[0] == 'general': 311 if node[0] == GENERAL:
304 value = self.__getParam(None, category, name, 'general') 312 value = self._getParam(category, name, GENERAL)
305 return self.__getAttr(node[1], attr, value) 313 return self._getAttr(node[1], attr, value)
306 314
307 assert node[0] == 'individual' 315 assert node[0] == INDIVIDUAL
308 316
309 profile = self.getProfileName(profile_key) 317 profile = self.getProfileName(profile_key)
310 if not profile: 318 if not profile:
311 error(_('Requesting a param for an non-existant profile')) 319 error(_('Requesting a param for an non-existant profile'))
312 raise exceptions.ProfileUnknownError 320 raise exceptions.ProfileUnknownError
314 if profile not in self.params: 322 if profile not in self.params:
315 error(_('Requesting synchronous param for not connected profile')) 323 error(_('Requesting synchronous param for not connected profile'))
316 raise exceptions.NotConnectedProfileError(profile) 324 raise exceptions.NotConnectedProfileError(profile)
317 325
318 if attr == "value": 326 if attr == "value":
319 value = self.__getParam(profile, category, name) 327 value = self._getParam(category, name, profile=profile)
320 return self.__getAttr(node[1], attr, value) 328 return self._getAttr(node[1], attr, value)
321 329
322 def asyncGetStringParamA(self, name, category, attr="value", security_limit=NO_SECURITY_LIMIT, profile_key="@NONE@"): 330 def asyncGetStringParamA(self, name, category, attr="value", security_limit=NO_SECURITY_LIMIT, profile_key="@NONE@"):
323 d = self.asyncGetParamA(name, category, attr, security_limit, profile_key) 331 d = self.asyncGetParamA(name, category, attr, security_limit, profile_key)
324 d.addCallback(self.__type_to_string) 332 d.addCallback(self.__type_to_string)
325 return d 333 return d
328 """Helper method to get a specific attribute 336 """Helper method to get a specific attribute
329 @param name: name of the parameter 337 @param name: name of the parameter
330 @param category: category of the parameter 338 @param category: category of the parameter
331 @param attr: name of the attribute (default: "value") 339 @param attr: name of the attribute (default: "value")
332 @param profile: owner of the param (@ALL@ for everyone)""" 340 @param profile: owner of the param (@ALL@ for everyone)"""
333 node = self.__getParamNode(name, category) 341 node = self._getParamNode(name, category)
334 if not node: 342 if not node:
335 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name': name, 'category': category}) 343 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name': name, 'category': category})
336 return None 344 return None
337 345
338 if not self.checkSecurityLimit(node[1], security_limit): 346 if not self.checkSecurityLimit(node[1], security_limit):
339 warning(_("Trying to get parameter '%s' in category '%s' without authorization!!!" 347 warning(_("Trying to get parameter '%s' in category '%s' without authorization!!!"
340 % (name, category))) 348 % (name, category)))
341 return None 349 return None
342 350
343 if node[0] == 'general': 351 if node[0] == GENERAL:
344 value = self.__getParam(None, category, name, 'general') 352 value = self._getParam(category, name, GENERAL)
345 return defer.succeed(self.__getAttr(node[1], attr, value)) 353 return defer.succeed(self._getAttr(node[1], attr, value))
346 354
347 assert node[0] == 'individual' 355 assert node[0] == INDIVIDUAL
348 356
349 profile = self.getProfileName(profile_key) 357 profile = self.getProfileName(profile_key)
350 if not profile: 358 if not profile:
351 error(_('Requesting a param for a non-existant profile')) 359 error(_('Requesting a param for a non-existant profile'))
352 return defer.fail() 360 return defer.fail()
353 361
354 if attr != "value": 362 if attr != "value":
355 return defer.succeed(node[1].getAttribute(attr)) 363 return defer.succeed(node[1].getAttribute(attr))
356 try: 364 try:
357 value = self.__getParam(profile, category, name) 365 value = self._getParam(category, name, profile=profile)
358 return defer.succeed(self.__getAttr(node[1], attr, value)) 366 return defer.succeed(self._getAttr(node[1], attr, value))
359 except exceptions.ProfileNotInCacheError: 367 except exceptions.ProfileNotInCacheError:
360 #We have to ask data to the storage manager 368 #We have to ask data to the storage manager
361 d = self.storage.getIndParam(category, name, profile) 369 d = self.storage.getIndParam(category, name, profile)
362 return d.addCallback(lambda value: self.__getAttr(node[1], attr, value)) 370 return d.addCallback(lambda value: self._getAttr(node[1], attr, value))
363 371
364 def __getParam(self, profile, category, name, _type='individual', cache=None): 372 def _getParam(self, category, name, type_=INDIVIDUAL, cache=None, profile="@NONE@"):
365 """Return the param, or None if it doesn't exist 373 """Return the param, or None if it doesn't exist
366 @param profile: the profile name (not profile key, i.e. name and not something like @DEFAULT@)
367 @param category: param category 374 @param category: param category
368 @param name: param name 375 @param name: param name
369 @param _type: "general" or "individual" 376 @param type_: GENERAL or INDIVIDUAL
370 @param cache: temporary cache, to use when profile is not logged 377 @param cache: temporary cache, to use when profile is not logged
378 @param profile: the profile name (not profile key, i.e. name and not something like @DEFAULT@)
371 @return: param value or None if it doesn't exist 379 @return: param value or None if it doesn't exist
372 """ 380 """
373 if _type == 'general': 381 if type_ == GENERAL:
374 if (category, name) in self.params_gen: 382 if (category, name) in self.params_gen:
375 return self.params_gen[(category, name)] 383 return self.params_gen[(category, name)]
376 return None # This general param has the default value 384 return None # This general param has the default value
377 assert (_type == 'individual') 385 assert (type_ == INDIVIDUAL)
386 if profile == "@NONE@":
387 raise exceptions.ProfileNotSetError
378 if profile in self.params: 388 if profile in self.params:
379 cache = self.params[profile] # if profile is in main cache, we use it, 389 cache = self.params[profile] # if profile is in main cache, we use it,
380 # ignoring the temporary cache 390 # ignoring the temporary cache
381 elif cache is None: # else we use the temporary cache if it exists, or raise an exception 391 elif cache is None: # else we use the temporary cache if it exists, or raise an exception
382 raise exceptions.ProfileNotInCacheError 392 raise exceptions.ProfileNotInCacheError
398 # init the result document 408 # init the result document
399 prof_xml = minidom.parseString('<params/>') 409 prof_xml = minidom.parseString('<params/>')
400 cache = {} 410 cache = {}
401 411
402 for type_node in self.dom.documentElement.childNodes: 412 for type_node in self.dom.documentElement.childNodes:
403 if type_node.nodeName != 'general' and type_node.nodeName != 'individual': 413 if type_node.nodeName != GENERAL and type_node.nodeName != INDIVIDUAL:
404 continue 414 continue
405 # we use all params, general and individual 415 # we use all params, general and individual
406 for cat_node in type_node.childNodes: 416 for cat_node in type_node.childNodes:
407 if cat_node.nodeName != 'category': 417 if cat_node.nodeName != 'category':
408 continue 418 continue
434 if name not in dest_params: 444 if name not in dest_params:
435 # this is reached when a previous category exists 445 # this is reached when a previous category exists
436 dest_params[name] = param_node.cloneNode(True) 446 dest_params[name] = param_node.cloneNode(True)
437 dest_cat.appendChild(dest_params[name]) 447 dest_cat.appendChild(dest_params[name])
438 448
439 profile_value = self.__getParam(profile, category, 449 profile_value = self._getParam(category,
440 name, type_node.nodeName, 450 name, type_node.nodeName,
441 cache=profile_cache) 451 cache=profile_cache, profile=profile)
442 if profile_value is not None: 452 if profile_value is not None:
443 # there is a value for this profile, we must change the default 453 # there is a value for this profile, we must change the default
444 dest_params[name].setAttribute('value', profile_value) 454 dest_params[name].setAttribute('value', profile_value)
445 if new_node: 455 if new_node:
446 prof_xml.documentElement.appendChild(dest_cat) 456 prof_xml.documentElement.appendChild(dest_cat)
507 return "<category />" 517 return "<category />"
508 518
509 d = self.__constructProfileXml(security_limit, profile) 519 d = self.__constructProfileXml(security_limit, profile)
510 return d.addCallback(returnCategoryXml) 520 return d.addCallback(returnCategoryXml)
511 521
512 def __getParamNode(self, name, category, _type="@ALL@"): # FIXME: is _type useful ? 522 def _getParamNode(self, name, category, type_="@ALL@"): # FIXME: is type_ useful ?
513 """Return a node from the param_xml 523 """Return a node from the param_xml
514 @param name: name of the node 524 @param name: name of the node
515 @param category: category of the node 525 @param category: category of the node
516 @_type: keyword for search: 526 @type_: keyword for search:
517 @ALL@ search everywhere 527 @ALL@ search everywhere
518 @GENERAL@ only search in general type 528 @GENERAL@ only search in general type
519 @INDIVIDUAL@ only search in individual type 529 @INDIVIDUAL@ only search in individual type
520 @return: a tuple with the node type and the the node, or None if not found""" 530 @return: a tuple with the node type and the the node, or None if not found"""
521 531
522 for type_node in self.dom.documentElement.childNodes: 532 for type_node in self.dom.documentElement.childNodes:
523 if (((_type == "@ALL@" or _type == "@GENERAL@") and type_node.nodeName == 'general') 533 if (((type_ == "@ALL@" or type_ == "@GENERAL@") and type_node.nodeName == GENERAL)
524 or ((_type == "@ALL@" or _type == "@INDIVIDUAL@") and type_node.nodeName == 'individual')): 534 or ((type_ == "@ALL@" or type_ == "@INDIVIDUAL@") and type_node.nodeName == INDIVIDUAL)):
525 for node in type_node.getElementsByTagName('category'): 535 for node in type_node.getElementsByTagName('category'):
526 if node.getAttribute("name") == category: 536 if node.getAttribute("name") == category:
527 params = node.getElementsByTagName("param") 537 params = node.getElementsByTagName("param")
528 for param in params: 538 for param in params:
529 if param.getAttribute("name") == name: 539 if param.getAttribute("name") == name:
546 profile = self.getProfileName(profile_key) 556 profile = self.getProfileName(profile_key)
547 if not profile: 557 if not profile:
548 error(_('Trying to set parameter for an unknown profile')) 558 error(_('Trying to set parameter for an unknown profile'))
549 return # TODO: throw an error 559 return # TODO: throw an error
550 560
551 node = self.__getParamNode(name, category, '@ALL@') 561 node = self._getParamNode(name, category, '@ALL@')
552 if not node: 562 if not node:
553 error(_('Requesting an unknown parameter (%(category)s/%(name)s)') 563 error(_('Requesting an unknown parameter (%(category)s/%(name)s)')
554 % {'category': category, 'name': name}) 564 % {'category': category, 'name': name})
555 return 565 return
556 566
557 if not self.checkSecurityLimit(node[1], security_limit): 567 if not self.checkSecurityLimit(node[1], security_limit):
558 warning(_("Trying to set parameter '%s' in category '%s' without authorization!!!" 568 warning(_("Trying to set parameter '%s' in category '%s' without authorization!!!"
559 % (name, category))) 569 % (name, category)))
560 return 570 return
561 571
562 if node[0] == 'general': 572 if node[0] == GENERAL:
563 self.params_gen[(category, name)] = value 573 self.params_gen[(category, name)] = value
564 self.storage.setGenParam(category, name, value) 574 self.storage.setGenParam(category, name, value)
565 for profile in self.storage.getProfilesList(): 575 for profile in self.storage.getProfilesList():
566 if self.host.isConnected(profile): 576 if self.host.isConnected(profile):
567 self.host.bridge.paramUpdate(name, value, category, profile) 577 self.host.bridge.paramUpdate(name, value, category, profile)
568 self.host.trigger.point("paramUpdateTrigger", name, value, category, node[0], profile) 578 self.host.trigger.point("paramUpdateTrigger", name, value, category, node[0], profile)
569 return 579 return
570 580
571 assert (node[0] == 'individual') 581 assert (node[0] == INDIVIDUAL)
572 assert (profile_key != "@NONE@") 582 assert (profile_key != "@NONE@")
573 583
574 _type = node[1].getAttribute("type") 584 type_ = node[1].getAttribute("type")
575 if _type == "button": 585 if type_ == "button":
576 print "clique", node.toxml() 586 print "clique", node.toxml()
577 else: 587 else:
578 if self.host.isConnected(profile): # key can not exists if profile is not connected 588 if self.host.isConnected(profile): # key can not exists if profile is not connected
579 self.params[profile][(category, name)] = value 589 self.params[profile][(category, name)] = value
580 self.host.bridge.paramUpdate(name, value, category, profile) 590 self.host.bridge.paramUpdate(name, value, category, profile)
722 def deleteProfile(self, name): 732 def deleteProfile(self, name):
723 """Delete an existing profile 733 """Delete an existing profile
724 @param name: Name of the profile""" 734 @param name: Name of the profile"""
725 return self.params.deleteProfile(name) 735 return self.params.deleteProfile(name)
726 736
727 def addToHistory(self, from_jid, to_jid, message, _type='chat', extra=None, timestamp=None, profile="@NONE@"): 737 def addToHistory(self, from_jid, to_jid, message, type_='chat', extra=None, timestamp=None, profile="@NONE@"):
728 assert profile != "@NONE@" 738 assert profile != "@NONE@"
729 if extra is None: 739 if extra is None:
730 extra = {} 740 extra = {}
731 return self.storage.addToHistory(from_jid, to_jid, message, _type, extra, timestamp, profile) 741 return self.storage.addToHistory(from_jid, to_jid, message, type_, extra, timestamp, profile)
732 742
733 def getHistory(self, from_jid, to_jid, limit=0, between=True, profile="@NONE@"): 743 def getHistory(self, from_jid, to_jid, limit=0, between=True, profile="@NONE@"):
734 assert profile != "@NONE@" 744 assert profile != "@NONE@"
735 return self.storage.getHistory(jid.JID(from_jid), jid.JID(to_jid), limit, between, profile) 745 return self.storage.getHistory(jid.JID(from_jid), jid.JID(to_jid), limit, between, profile)
736 746
740 @param profile: which profile is using this server ?""" 750 @param profile: which profile is using this server ?"""
741 if profile not in self.server_features: 751 if profile not in self.server_features:
742 self.server_features[profile] = [] 752 self.server_features[profile] = []
743 self.server_features[profile].append(feature) 753 self.server_features[profile].append(feature)
744 754
745 def addServerIdentity(self, category, _type, entity, profile): 755 def addServerIdentity(self, category, type_, entity, profile):
746 """Add an identity discovered from server 756 """Add an identity discovered from server
747 @param feature: string of the feature 757 @param feature: string of the feature
748 @param profile: which profile is using this server ?""" 758 @param profile: which profile is using this server ?"""
749 if not profile in self.server_identities: 759 if not profile in self.server_identities:
750 self.server_identities[profile] = {} 760 self.server_identities[profile] = {}
751 if (category, _type) not in self.server_identities[profile]: 761 if (category, type_) not in self.server_identities[profile]:
752 self.server_identities[profile][(category, _type)] = set() 762 self.server_identities[profile][(category, type_)] = set()
753 self.server_identities[profile][(category, _type)].add(entity) 763 self.server_identities[profile][(category, type_)].add(entity)
754 764
755 def getServerServiceEntities(self, category, _type, profile): 765 def getServerServiceEntities(self, category, type_, profile):
756 """Return all available entities for a service""" 766 """Return all available entities for a service"""
757 if profile in self.server_identities: 767 if profile in self.server_identities:
758 return self.server_identities[profile].get((category, _type), set()) 768 return self.server_identities[profile].get((category, type_), set())
759 else: 769 else:
760 return None 770 return None
761 771
762 def getServerServiceEntity(self, category, _type, profile): 772 def getServerServiceEntity(self, category, type_, profile):
763 """Helper method to get first available entity for a service""" 773 """Helper method to get first available entity for a service"""
764 entities = self.getServerServiceEntities(category, _type, profile) 774 entities = self.getServerServiceEntities(category, type_, profile)
765 if entities is None: 775 if entities is None:
766 warning(_("Entities (%(category)s/%(type)s) not available, maybe they haven't been asked to server yet ?") % {"category": category, 776 warning(_("Entities (%(category)s/%(type)s) not available, maybe they haven't been asked to server yet ?") % {"category": category,
767 "type": _type}) 777 "type": type_})
768 return None 778 return None
769 else: 779 else:
770 return list(entities)[0] if entities else None 780 return list(entities)[0] if entities else None
771 781
772 def hasServerFeature(self, feature, profile_key): 782 def hasServerFeature(self, feature, profile_key):
885 try: 895 try:
886 del self.entitiesCache[profile][entity_jid.userhost()] 896 del self.entitiesCache[profile][entity_jid.userhost()]
887 except KeyError: 897 except KeyError:
888 pass 898 pass
889 899
890 def addWaitingSub(self, _type, entity_jid, profile_key): 900 def addWaitingSub(self, type_, entity_jid, profile_key):
891 """Called when a subcription request is received""" 901 """Called when a subcription request is received"""
892 profile = self.getProfileName(profile_key) 902 profile = self.getProfileName(profile_key)
893 assert profile 903 assert profile
894 if profile not in self.subscriptions: 904 if profile not in self.subscriptions:
895 self.subscriptions[profile] = {} 905 self.subscriptions[profile] = {}
896 self.subscriptions[profile][entity_jid] = _type 906 self.subscriptions[profile][entity_jid] = type_
897 907
898 def delWaitingSub(self, entity_jid, profile_key): 908 def delWaitingSub(self, entity_jid, profile_key):
899 """Called when a subcription request is finished""" 909 """Called when a subcription request is finished"""
900 profile = self.getProfileName(profile_key) 910 profile = self.getProfileName(profile_key)
901 assert profile 911 assert profile