diff --git a/src/caosdb/common/models.py b/src/caosdb/common/models.py index 15d460b2c2a128e9dd878f5c88bccb6220591815..85ea72f115c2cde037c8756eebd0e650059efd03 100644 --- a/src/caosdb/common/models.py +++ b/src/caosdb/common/models.py @@ -23,29 +23,31 @@ # """missing docstring.""" -from __future__ import unicode_literals, print_function -from builtins import str -from hashlib import sha512 -from sys import hexversion -from random import randint -from functools import cmp_to_key -from os.path import isdir -from os import listdir +from __future__ import print_function, unicode_literals + import re import sys import traceback -from lxml import etree +from builtins import str +from functools import cmp_to_key +from hashlib import sha512 +from os import listdir +from os.path import isdir +from random import randint +from sys import hexversion from tempfile import NamedTemporaryFile -from caosdb.common.utils import xml2str, uuid +from caosdb.common.utils import uuid, xml2str +from caosdb.configuration import get_config from caosdb.connection.connection import get_connection -from caosdb.exceptions import CaosDBException, EntityDoesNotExistError, \ - EntityError, UnqualifiedPropertiesError, UnqualifiedParentsError, \ - TransactionError, AmbiguityException, UniqueNamesError, \ - EntityHasNoDatatypeError, ConsistencyError, URITooLongException, \ - AuthorizationException from caosdb.connection.encode import MultipartParam, multipart_encode -from caosdb.configuration import get_config +from caosdb.exceptions import (AmbiguityException, AuthorizationException, + CaosDBException, ConsistencyError, + EntityDoesNotExistError, EntityError, + EntityHasNoDatatypeError, TransactionError, + UniqueNamesError, UnqualifiedParentsError, + UnqualifiedPropertiesError, URITooLongException) +from lxml import etree _ENTITY_URI_SEGMENT = "Entity" @@ -70,6 +72,7 @@ NONE = "NONE" def LIST(datatype): if isinstance(datatype, Entity): datatype = datatype.name + return "LIST<" + str(datatype) + ">" @@ -123,16 +126,20 @@ class Entity(object): def size(self): if self._size is not None: return int(self._size) + if self._wrapped_entity is None: return None + return self._wrapped_entity.size @property def id(self): if self.__id is not None: return self.__id + if self._wrapped_entity is None: return None + return self._wrapped_entity.id @id.setter @@ -146,6 +153,7 @@ class Entity(object): def name(self): if self.__name is not None or self._wrapped_entity is None: return self.__name + return self._wrapped_entity.name @name.setter @@ -156,6 +164,7 @@ class Entity(object): def datatype(self): if self.__datatype is not None or self._wrapped_entity is None: return self.__datatype + return self._wrapped_entity.datatype @datatype.setter @@ -166,6 +175,7 @@ class Entity(object): def description(self): if self.__description is not None or self._wrapped_entity is None: return self.__description + return self._wrapped_entity.description @property @@ -180,6 +190,7 @@ class Entity(object): def unit(self): if self.__unit is not None or self._wrapped_entity is None: return self.__unit + return self._wrapped_entity.unit @unit.setter @@ -190,6 +201,7 @@ class Entity(object): def value(self): if self.__value is not None or self._wrapped_entity is None: return self.__value + return self._wrapped_entity.value @value.setter @@ -200,6 +212,7 @@ class Entity(object): def path(self): if self.__path is not None or self._wrapped_entity is None: return self.__path + return self._wrapped_entity.path @path.setter @@ -210,6 +223,7 @@ class Entity(object): def thumbnail(self): if self.__thumbnail is not None or self._wrapped_entity is None: return self.__thumbnail + return self._wrapped_entity.thumbnail @thumbnail.setter @@ -220,6 +234,7 @@ class Entity(object): def file(self): if self.__file is not None or self._wrapped_entity is None: return self.__file + return self._wrapped_entity.file @file.setter @@ -230,6 +245,7 @@ class Entity(object): def pickup(self): if self.__pickup is not None or self._wrapped_entity is None: return self.__pickup + return self._wrapped_entity.pickup @pickup.setter @@ -267,6 +283,7 @@ class Entity(object): def is_permitted(self, permission, role=None): if role is None: # pylint: disable=unsupported-membership-test + return permission in self.permissions else: self.acl.is_permitted(permission=permission) @@ -274,31 +291,40 @@ class Entity(object): def get_all_messages(self): ret = _Messages() ret.append(self.messages) + for p in self.properties: ret.extend(p.get_all_messages()) + for p in self.parents: ret.extend(p.get_all_messages()) + return ret def clear_server_messages(self): self.messages.clear_server_messages() + for p in self.properties: p.clear_server_messages() + for p in self.parents: p.clear_server_messages() + return self def get_importance(self, property): # @ReservedAssignment """Get the importance of a given property regarding this entity.""" + if self.properties is not None: return self.properties.get_importance(property) def remove_property(self, property): # @ReservedAssignment self.properties.remove(property) + return self def remove_parent(self, parent): self.parents.remove(parent) + return self def add_property(self, property=None, value=None, **kwargs): # @ReservedAssignment @@ -328,6 +354,7 @@ class Entity(object): name = (kwargs['name'] if 'name' in kwargs else None) pid = (kwargs['id'] if 'id' in kwargs else None) abstract_property = None + if isinstance(property, Entity): abstract_property = property elif isinstance(property, int): @@ -353,6 +380,7 @@ class Entity(object): new_property._wrap(property) # FIXME: this really necessary? + if new_property.datatype is None and isinstance( property, (RecordType, Record, File)): new_property.datatype = property @@ -360,6 +388,7 @@ class Entity(object): property=new_property, importance=( kwargs['importance'] if 'importance' in kwargs else None), inheritance=( kwargs['inheritance'] if 'inheritance' in kwargs else None)) + return self def add_message(self, msg=None, type=None, code=None, # @ReservedAssignment @@ -380,6 +409,7 @@ class Entity(object): else: msg = Message(type, code, description, body) self.messages.append(msg) + return self def add_parent(self, parent=None, **kwargs): # @ReservedAssignment @@ -397,6 +427,7 @@ class Entity(object): name = (kwargs['name'] if 'name' in kwargs else None) pid = (kwargs['id'] if 'id' in kwargs else None) parent_entity = None + if isinstance(parent, Entity): parent_entity = parent elif isinstance(parent, int): @@ -412,9 +443,11 @@ class Entity(object): if 'inheritance' in kwargs else None) addp = Parent(id=pid, name=name, inheritance=inheritance) + if parent_entity is not None: addp._wrap(parent_entity) self.parents.append(addp) + return self def has_parent(self, parent, recursive=True, @@ -432,6 +465,7 @@ class Entity(object): @param check_id: Whether to use the ID for ancestry check. @return: True if 'parent' is a true parent, False otherwise. """ + if recursive: parents = self.get_parents_recursively() else: @@ -456,6 +490,7 @@ class Entity(object): @return: _Parents(list) """ + return self.parents def get_parents_recursively(self): @@ -466,6 +501,7 @@ class Entity(object): all_parents = _Parents() self._get_parent_recursively(all_parents) + return all_parents def _get_parent_recursively(self, all_parents): @@ -481,6 +517,7 @@ class Entity(object): for parent in self.parents: w_parent = parent._wrapped_entity + if w_parent not in all_parents: all_parents.append(w_parent) w_parent._get_parent_recursively(all_parents) @@ -499,6 +536,7 @@ class Entity(object): for p in self.parents: if p.name is not None and str(p.name) == str(key): return p + return None def get_properties(self): @@ -506,6 +544,7 @@ class Entity(object): @return: _Properties(list) """ + return self.properties def get_property(self, key): @@ -517,6 +556,7 @@ class Entity(object): for p in self.properties: if p.name is not None and str(p.name) == str(key): return p + return None def get_messages(self): @@ -524,6 +564,7 @@ class Entity(object): @return: _Messages(list) """ + return self.messages def get_warnings(self): @@ -532,9 +573,11 @@ class Entity(object): @return _Messages(list): Warning messages. """ ret = _Messages() + for m in self.messages: if m.type.lower() == "warning": ret.append(m) + return ret def get_errors(self): @@ -543,11 +586,14 @@ class Entity(object): @return _Messages(list): Error messages. """ ret = _Messages() + for m in self.messages: if m.type.lower() == "error": ret.append(m) + if self._wrapped_entity is not None: ret.extend(self._wrapped_entity.get_errors()) + return ret def get_errors_deep(self, roots=[]): @@ -563,10 +609,12 @@ class Entity(object): ret_self = self.get_errors() result_list.extend([ (m, roots) for m in ret_self]) + for parent in self.get_parents(): result_list.extend( parent.get_errors_deep( roots + [parent])) + return result_list def print_error_tree(self, error_tree=None, print_out=True): @@ -576,12 +624,15 @@ class Entity(object): If error_tree is set to None, call get_errors_deep and print the resulting tree. """ + if error_tree is None: error_tree = self.get_errors_deep() text = "" + for i in error_tree: text += "ERROR " + str(i[0].code) + \ ": " + str(i[0].description) + "\n" + for tree_entry in reversed(i[1]): # print(tree_entry) text += " in " + str(tree_entry.__class__) + \ @@ -590,17 +641,21 @@ class Entity(object): str(tree_entry.id) + ")" text += "\n" text += "\n" + if print_out: print(text) + return text def has_errors(self): ''' @return True: if and only if this entities has any error messages. ''' + for m in self.messages: if m.type.lower() == "error": return True + return False def to_xml(self, xml=None, add_properties=ALL, local_serialization=False): @@ -615,22 +670,28 @@ class Entity(object): are to be added. @return: xml representation of this entity. """ + if xml is None: xml = etree.Element("Entity") assert isinstance(xml, etree._Element) ''' unwrap wrapped entity ''' + if self._wrapped_entity is not None: xml = self._wrapped_entity.to_xml(xml, add_properties) if self.id is not None: xml.set("id", str(self.id)) + if self._cuid is not None: xml.set("cuid", str(self._cuid)) + if self.name is not None: xml.set("name", str(self.name)) + if self.description is not None: xml.set("description", str(self.description)) + if self.value is not None and self.value != "": if isinstance(self.value, Entity): if self.value.id is not None: @@ -642,6 +703,7 @@ class Entity(object): elif isinstance(self.value, list): for v in self.value: v_elem = etree.Element("Value") + if isinstance(v, Entity): if v.id is not None: v_elem.text = str(v.id) @@ -656,6 +718,7 @@ class Entity(object): xml.append(v_elem) else: xml.text = str(self.value) + if self.datatype is not None: if isinstance(self.datatype, Entity): if self.datatype.id is not None: @@ -666,31 +729,43 @@ class Entity(object): xml.set("datatype", str(self.datatype)) else: xml.set("datatype", str(self.datatype)) + if self.path is not None: xml.set("path", self.path) + if self.file is not None and local_serialization: xml.set("file", self.file) + if self._checksum is not None: xml.set("checksum", self._checksum) + if self.size is not None: xml.set("size", str(self.size)) + if self.unit is not None: xml.set("unit", str(self.unit)) + if self.messages is not None: self.messages.to_xml(xml) + if self.parents is not None: self.parents.to_xml(xml) + if self.properties is not None: self.properties.to_xml(xml, add_properties) + if len(self._flags) > 0: flagattr = "" + for key in self._flags.keys(): flag = self._flags[key] + if flag is not None and flag != "": flagattr += str(key) + ":" + str(flag) + "," else: flagattr += str(key) + "," xml.set("flag", flagattr) + if self.acl is not None: xml.append(self.acl.to_xml()) @@ -714,10 +789,12 @@ class Entity(object): entity.datatype = elem.get("datatype") # @ReservedAssignment entity.unit = elem.get("unit") entity.file = elem.get("file") + if hasattr(entity, "affiliation"): entity.affiliation = elem.get("affiliation") vals = list() + for celem in elem: child = _parse_single_xml_element(celem) @@ -742,11 +819,13 @@ class Entity(object): Was ' + str(type(child))) # parse VALUE + if len(vals): entity.value = _parse_col_values(entity.datatype, vals) elif elem.text is not None: if elem.text.strip() != "": text_val = elem.text.strip() + if entity.datatype == DOUBLE: entity.value = float(text_val) elif entity.datatype == DATETIME or entity.datatype == TEXT: @@ -756,6 +835,7 @@ class Entity(object): entity.value = int(text_val) except BaseException: entity.value = text_val + return entity def __repr__(self): @@ -768,6 +848,7 @@ class Entity(object): def update_acl(self): if self.id is None: c = Container().retrieve(query=self.name, sync=False) + if len(c == 1): e = c[0] else: @@ -777,6 +858,7 @@ class Entity(object): e = Container().retrieve(query=self.id, sync=False)[0] e.acl = ACL(self.acl.to_xml()) e.update() + return e def delete(self, raise_exception_on_error=True): @@ -800,11 +882,14 @@ class Entity(object): Container with the returned entities or single entity if and only if unique was True and no exception was raised. """ + if unique: c = Container().append(self).retrieve( unique=unique, raise_exception_on_error=raise_exception_on_error, flags=flags) + if len(c) == 1: c[0].messages.extend(c.messages) + return c[0] else: raise AmbiguityException("This retrieval was not unique!!!") @@ -834,6 +919,7 @@ class Entity(object): insertion of elements with unique names. @param flags: A dictionary of flags to be send with the insertion. """ + return Container().append(self).insert( strict=strict, raise_exception_on_error=raise_exception_on_error, @@ -873,6 +959,7 @@ class Entity(object): @param strict=False: Flag for strict mode. """ + return Container().append(self).update( strict=strict, raise_exception_on_error=raise_exception_on_error, @@ -881,10 +968,12 @@ class Entity(object): def _wrap(self, entity): self._wrapped_entity = entity + return self def set_flag(self, key, value=None): self._flags[key] = value + return self @@ -895,6 +984,7 @@ def _parse_col_values(cdt, vals): if m: col = m.group("col") dt = m.group("dt") + if col == "LIST": ret = list() add = ret.append @@ -911,16 +1001,19 @@ def _parse_col_values(cdt, vals): add(int(v)) except BaseException: add(v) + return ret except BaseException: traceback.print_exc(file=sys.stderr) + return vals def _log_request(request, xml_body=None): if Container._debug() > 0: print("\n" + request) + if xml_body is not None: print("======== Request body ========\n") print(xml2str(xml_body)) @@ -930,6 +1023,7 @@ def _log_request(request, xml_body=None): def _log_response(body): if Container._debug() > 0: print("\n======== Response body ========\n") + if hexversion < 0x03000000: print(body) else: @@ -964,6 +1058,7 @@ class QueryTemplate(): def retrieve(self, strict=True, raise_exception_on_error=True, unique=True, sync=True, flags=None): + return Container().append(self).retrieve( raise_exception_on_error=raise_exception_on_error, unique=unique, @@ -972,6 +1067,7 @@ class QueryTemplate(): def insert(self, strict=True, raise_exception_on_error=True, unique=True, sync=True, flags=None): + return Container().append(self).insert( strict=strict, raise_exception_on_error=raise_exception_on_error, @@ -981,6 +1077,7 @@ class QueryTemplate(): def update(self, strict=True, raise_exception_on_error=True, unique=True, sync=True, flags=None): + return Container().append(self).update( strict=strict, raise_exception_on_error=raise_exception_on_error, @@ -998,18 +1095,24 @@ class QueryTemplate(): def to_xml(self, xml=None): if xml is None: xml = etree.Element("QueryTemplate") + if self.name is not None: xml.set("name", self.name) + if self.id is not None: xml.set("id", str(self.id)) + if self.description is not None: xml.set("description", self.description) + if self.query is not None: queryElem = etree.Element("Query") queryElem.text = self.query xml.append(queryElem) + if self.messages is not None: self.messages.to_xml(xml) + if self.acl is not None: xml.append(self.acl.to_xml()) @@ -1020,11 +1123,13 @@ class QueryTemplate(): if xml.tag.lower() == "querytemplate": q = QueryTemplate(name=xml.get("name"), description=xml.get("description"), query=None) + for e in xml: if e.tag.lower() == "query": q.query = e.text else: child = _parse_single_xml_element(e) + if isinstance(child, Message): q.messages.append(child) elif isinstance(child, ACL): @@ -1032,6 +1137,7 @@ class QueryTemplate(): elif isinstance(child, Permissions): q.permissions = child q.id = int(xml.get("id")) + return q else: return None @@ -1050,9 +1156,11 @@ class QueryTemplate(): def get_errors(self): ret = _Messages() + for m in self.messages: if m.type.lower() == "error": ret.append(m) + return ret def get_messages(self): @@ -1071,6 +1179,7 @@ class Parent(Entity): return self.__affiliation elif hasattr(self._wrapped_entity, "affiliation"): return self._wrapped_entity.affiliation + return @affiliation.setter @@ -1079,6 +1188,7 @@ class Parent(Entity): def __init__(self, id=None, name=None, description=None, inheritance=None): # @ReservedAssignment Entity.__init__(self, id=id, name=name, description=description) + if inheritance is not None: self.set_flag("inheritance", inheritance) self.__affiliation = None @@ -1086,6 +1196,7 @@ class Parent(Entity): def to_xml(self, xml=None, add_properties=None): if xml is None: xml = etree.Element("Parent") + return super(Parent, self).to_xml( xml=xml, add_properties=add_properties) @@ -1104,20 +1215,25 @@ class Property(Entity): def add_property(self, property=None, value=None, **kwargs): # @ReservedAssignment copy_kwargs = kwargs.copy() + if 'importance' not in copy_kwargs: # set default importance copy_kwargs['importance'] = FIX + if 'inheritance' not in copy_kwargs: # set default importance copy_kwargs['inheritance'] = FIX + return super(Property, self).add_property( property=property, value=value, **copy_kwargs) def add_parent(self, parent=None, **kwargs): copy_kwargs = kwargs.copy() + if 'inheritance' not in copy_kwargs: # set default importance copy_kwargs['inheritance'] = FIX + return super(Property, self).add_parent(parent=parent, **copy_kwargs) def __init__(self, name=None, id=None, description=None, datatype=None, @@ -1129,6 +1245,7 @@ class Property(Entity): def to_xml(self, xml=None, add_properties=ALL): if xml is None: xml = etree.Element("Property") + return super(Property, self).to_xml(xml, add_properties) @@ -1145,12 +1262,16 @@ class Message(object): def to_xml(self, xml=None): if xml is None: xml = etree.Element(str(self.type)) + if self.code: xml.set("code", str(self.code)) + if self.description: xml.set("description", str(self.description)) + if self.body: xml.text = str(self.body) + return xml def __repr__(self): @@ -1159,6 +1280,7 @@ class Message(object): def __eq__(self, obj): if isinstance(obj, Message): return self.type == obj.type and self.code == obj.codes + return False def get_code(self): @@ -1171,20 +1293,25 @@ class RecordType(Entity): def add_property(self, property=None, value=None, **kwargs): # @ReservedAssignment copy_kwargs = kwargs.copy() + if 'importance' not in copy_kwargs: # set default importance copy_kwargs['importance'] = RECOMMENDED + if 'inheritance' not in copy_kwargs: # set default importance copy_kwargs['inheritance'] = FIX + return super(RecordType, self).add_property( property=property, value=value, **copy_kwargs) def add_parent(self, parent=None, **kwargs): copy_kwargs = kwargs.copy() + if 'inheritance' not in copy_kwargs: # set default importance copy_kwargs['inheritance'] = OBLIGATORY + return super(RecordType, self).add_parent(parent=parent, **copy_kwargs) def __init__(self, name=None, id=None, description=None, datatype=None): # @ReservedAssignment @@ -1194,6 +1321,7 @@ class RecordType(Entity): def to_xml(self, xml=None, add_properties=ALL): if xml is None: xml = etree.Element("RecordType") + return Entity.to_xml(self, xml, add_properties) @@ -1203,12 +1331,15 @@ class Record(Entity): def add_property(self, property=None, value=None, **kwargs): # @ReservedAssignment copy_kwargs = kwargs.copy() + if 'importance' not in copy_kwargs: # set default importance copy_kwargs['importance'] = FIX + if 'inheritance' not in copy_kwargs: # set default importance copy_kwargs['inheritance'] = FIX + return super(Record, self).add_property( property=property, value=value, **copy_kwargs) @@ -1219,6 +1350,7 @@ class Record(Entity): def to_xml(self, xml=None, add_properties=ALL): if xml is None: xml = etree.Element("Record") + return Entity.to_xml(self, xml, add_properties=ALL) @@ -1253,6 +1385,7 @@ class File(Record): self.thumbnail = thumbnail self.pickup = pickup + if self.pickup is None: self.pickup = from_location @@ -1261,8 +1394,10 @@ class File(Record): @return: xml element """ + if xml is None: xml = etree.Element("File") + return Entity.to_xml(self, xml=xml, add_properties=add_properties, local_serialization=local_serialization) @@ -1295,28 +1430,34 @@ class File(Record): data = response.read(8000) checksum = sha512() + while data: target_file.write(data) checksum.update(data) data = response.read(8000) target_file.close() + return checksum @staticmethod def _get_checksum(files): import locale + if hasattr(files, "name"): return File._get_checksum_single_file(files.name) else: if isdir(files): checksumappend = "" + for child in sorted(listdir(files), key=cmp_to_key(locale.strcoll)): + if isdir(files + '/' + child): checksumappend += child checksumappend += File._get_checksum(files + "/" + child) checksum = sha512() checksum.update(checksumappend.encode('utf-8')) + return checksum.hexdigest() else: return File._get_checksum_single_file(files) @@ -1326,6 +1467,7 @@ class File(Record): _file = open(single_file, 'rb') data = _file.read(1000) checksum = sha512() + while data: checksum.update(data) data = _file.read(1000) @@ -1335,12 +1477,15 @@ class File(Record): def add_property(self, property=None, value=None, **kwargs): # @ReservedAssignment copy_kwargs = kwargs.copy() + if 'importance' not in copy_kwargs: # set default importance copy_kwargs['importance'] = FIX + if 'inheritance' not in copy_kwargs: # set default importance copy_kwargs['inheritance'] = FIX + return super(File, self).add_property( property=property, value=value, **copy_kwargs) @@ -1358,6 +1503,7 @@ class _Properties(list): if property is not None: if hasattr(property, "encode"): property = self.get_by_name(property) # @ReservedAssignment + return self._importance.get(property) def set_importance(self, property, importance): # @ReservedAssignment @@ -1371,36 +1517,45 @@ class _Properties(list): @param name: the name of the property to be returned. @return: A property """ + return self._element_by_name[name] def extend(self, parents): self.append(parents) + return self def append(self, property, importance=None, inheritance=None): # @ReservedAssignment if isinstance(property, list): for p in property: self.append(p, importance, inheritance) + return + if isinstance(property, Entity): if importance is not None: self._importance[property] = importance + if inheritance is not None: self._inheritance[property] = inheritance else: self._inheritance[property] = FIX + if property.id is not None: self._element_by_id[str(property.id)] = property + if property.name is not None: self._element_by_name[property.name] = property list.append(self, property) else: raise TypeError("Argument was not an entity") + return self def to_xml(self, add_to_element, add_properties): for p in self: importance = self._importance.get(p) + if add_properties == FIX and not importance == FIX: continue @@ -1408,15 +1563,18 @@ class _Properties(list): if p in self._importance: pelem.set("importance", importance) + if p in self._inheritance: pelem.set("flag", "inheritance:" + str(self._inheritance.get(p))) add_to_element.append(pelem) + return self def __repr__(self): xml = etree.Element("PropertyList") self.to_xml(xml, add_properties=FIX) + return xml2str(xml) def _get_entity_by_cuid(self, cuid): @@ -1426,6 +1584,7 @@ class _Properties(list): @param name: The cuid of the entity to be returned. @return: Entity with the given cuid. ''' + for e in self: if e._cuid is not None: if str(e._cuid) == str(cuid): @@ -1439,28 +1598,36 @@ class _Properties(list): else: if prop.id is not None: # by id + for e in self: if e.id is not None and e.id == prop.id: list.remove(self, e) + return if prop.name is not None: # by name + for e in self: if e.name is not None and e.name == prop.name: list.remove(self, e) + return elif hasattr(prop, "encode"): # by name + for e in self: if e.name is not None and str(e.name) == str(prop): list.remove(self, e) + return elif isinstance(prop, int): # by id + for e in self: if e.id is not None and e.id == prop: list.remove(self, e) + return raise KeyError(str(prop) + " not found.") @@ -1474,6 +1641,7 @@ class _Parents(list): @param name: The cuid of the entity to be returned. @return: Entity with the given cuid. ''' + for e in self: if e._cuid is not None: if str(e._cuid) == str(cuid): @@ -1487,38 +1655,50 @@ class _Parents(list): def extend(self, parents): self.append(parents) + return self def append(self, parent): # @ReservedAssignment if isinstance(parent, list): for p in parent: self.append(p) + return + if isinstance(parent, Entity): if parent.id: self._element_by_id[str(parent.id)] = parent + if parent.name: self._element_by_name[parent.name] = parent list.append(self, parent) else: raise TypeError("Argument was not an Entity") + return self def to_xml(self, add_to_element): for p in self: pelem = etree.Element("Parent") + if p.id is not None: pelem.set("id", str(p.id)) + if p._cuid is not None: pelem.set("cuid", str(p._cuid)) + if p.name is not None: pelem.set("name", str(p.name)) + if p.description is not None: pelem.set("description", str(p.description)) + if len(p._flags) > 0: flagattr = "" + for key in p._flags.keys(): flag = p._flags[key] + if flag is not None and flag != "": flagattr += str(key) + ":" + str(flag) + "," else: @@ -1529,6 +1709,7 @@ class _Parents(list): def __repr__(self): xml = etree.Element("ParentList") self.to_xml(xml) + return xml2str(xml) def remove(self, parent): @@ -1538,28 +1719,36 @@ class _Parents(list): else: if parent.id is not None: # by id + for e in self: if e.id is not None and e.id == parent.id: list.remove(self, e) + return if parent.name is not None: # by name + for e in self: if e.name is not None and e.name == parent.name: list.remove(self, e) + return elif hasattr(parent, "encode"): # by name + for e in self: if e.name is not None and e.name == parent: list.remove(self, e) + return elif isinstance(parent, int): # by id + for e in self: if e.id is not None and e.id == parent: list.remove(self, e) + return raise KeyError(str(parent) + " not found.") @@ -1630,11 +1819,14 @@ class _Messages(dict): def clear_server_messages(self): """Removes all error, warning and info messages.""" rem = [] + for m in self: if m.type.lower() in ["error", "warning", "info"]: rem.append(m) + for m in rem: self.remove(m) + return self def __setitem__(self, key, value): # @ReservedAssignment @@ -1651,6 +1843,7 @@ class _Messages(dict): else: type = key # @ReservedAssignment code = None + if isinstance(value, tuple): if len(value) == 2: description = value[0] @@ -1690,6 +1883,7 @@ class _Messages(dict): type = key # @ReservedAssignment code = None m = dict.__getitem__(self, _Messages._msg_key(type, code)) + if m.description: return (m.description, m.body) else: @@ -1712,11 +1906,13 @@ class _Messages(dict): else: type = key # @ReservedAssignment code = None + return dict.__delitem__(self, _Messages._msg_key(type, code)) def remove(self, obj, obj2=None): if isinstance(obj, Message): return dict.__delitem__(self, _Messages._msg_key.get(obj)) + return self.__delitem__((obj, obj2)) def get(self, type, code=None, default=None): # @ReservedAssignment @@ -1727,18 +1923,23 @@ class _Messages(dict): def extend(self, messages): self.append(messages) + return self def append(self, msg): if hasattr(msg, "__iter__"): for m in msg: self.append(m) + return self + if isinstance(msg, Message): dict.__setitem__(self, _Messages._msg_key.get(msg), msg) + return self else: raise TypeError("Argument was not a Message") + return self def __iter__(self): @@ -1769,11 +1970,13 @@ class _Messages(dict): for m in self: melem = m.to_xml() add_to_element.append(melem) + return self def __repr__(self): xml = etree.Element("Messages") self.to_xml(xml) + return xml2str(xml) @@ -1796,10 +1999,13 @@ def _basic_sync(e_local, e_remote): e_local.permissions = e_remote.permissions e_local.is_valid = e_remote.is_valid e_local.is_deleted = e_remote.is_deleted + if hasattr(e_remote, "query"): e_local.query = e_remote.query + if hasattr(e_remote, "affiliation"): e_local.affiliation = e_remote.affiliation + return e_local @@ -1837,6 +2043,7 @@ class Container(list): for e in self: if not e.is_valid(): return False + return True def __hash__(self): @@ -1851,15 +2058,18 @@ class Container(list): @param entity: The entity to be removed. """ + if entity in self: super(Container, self).remove(entity) else: for ee in self: if entity == ee.id: super(Container, self).remove(ee) + return ee raise ValueError( "Container.remove(entity): entity not in Container") + return entity def _get_entity_by_cuid(self, cuid): @@ -1869,6 +2079,7 @@ class Container(list): @param name: The cuid of the entity to be returned. @return: Entity with the given cuid. ''' + for e in self: if e._cuid is not None: if str(e._cuid) == str(cuid): @@ -1883,6 +2094,7 @@ class Container(list): @param name: The id of the entity to be returned. @return: Entity with the given id. """ + for e in self: if e.id: if e.id == int(id): @@ -1897,11 +2109,14 @@ class Container(list): contained an error. """ error_list = dict() + for e in self: if isinstance(e, Entity): el = e.get_errors_deep() + if len(el) > 0: error_list[str(e.id)] = el + return error_list def get_entity_by_name(self, name, case_sensitive=True): @@ -1913,6 +2128,7 @@ class Container(list): @param case_sensitive (True/False): Do a case-sensitive search for name (or not). @return: Entity with the given name. """ + for e in self: if e.name is not None: if case_sensitive and e.name == str(name): @@ -1935,6 +2151,7 @@ class Container(list): @param entities: list of entities. """ + if isinstance(entities, Container): for entity in entities: self.append(entity) @@ -1950,6 +2167,7 @@ class Container(list): else: raise TypeError( "Expected a list or a container (was " + str(type(entities)) + ").") + return self def append(self, entity): @@ -1962,6 +2180,7 @@ class Container(list): @param entity: The entity to be appended. """ + if isinstance(entity, Entity): super(Container, self).append(entity) elif isinstance(entity, int): @@ -1974,6 +2193,7 @@ class Container(list): raise TypeError( "Entity was neither an id nor a name nor an entity." + " (was " + str(type(entity)) + ")") + return self def to_xml(self, add_to_element=None, local_serialization=False): @@ -1985,24 +2205,29 @@ class Container(list): """ tmpid = 0 ''' users might already have specified some tmpids. -> look for smallest.''' + for e in self: tmpid = min(tmpid, Container._get_smallest_tmpid(e)) tmpid -= 1 if add_to_element is None: add_to_element = etree.Element("Entities") + for m in self.messages: add_to_element.append(m.to_xml()) + for e in self: if e.id is None: e.id = tmpid tmpid -= 1 + for e in self: if isinstance(e, File): elem = e.to_xml(local_serialization=local_serialization) else: elem = e.to_xml() add_to_element.append(elem) + return add_to_element def get_errors(self): @@ -2010,8 +2235,10 @@ class Container(list): @return _Messages: Error messages. """ + if self.has_errors(): ret = _Messages() + for m in self.messages: if m.type.lower() == "error": ret.append(m) @@ -2025,23 +2252,29 @@ class Container(list): @return _Messages: Warning messages. """ + if self.has_warnings(): ret = _Messages() + for m in self.messages: if m.type.lower() == "warning": ret.append(m) + return ret else: return None def get_all_messages(self): ret = _Messages() + for e in self: ret.extend(e.get_all_messages()) + return ret def add_message(self, m): self.messages.append(m) + return self def has_warnings(self): @@ -2052,6 +2285,7 @@ class Container(list): for m in self.messages: if m.type.lower() == "warning": return True + return False def has_errors(self): @@ -2062,6 +2296,7 @@ class Container(list): for m in self.messages: if m.type.lower() == "error": return True + return False def __str__(self): @@ -2079,9 +2314,11 @@ class Container(list): c = Container() xml = etree.fromstring(xml_str) + for element in xml: e = _parse_single_xml_element(element) c.append(e) + return c @staticmethod @@ -2097,16 +2334,20 @@ class Container(list): if xml.tag.lower() == "response": c = Container() + for child in xml: e = _parse_single_xml_element(child) + if isinstance(e, Message): c.messages.append(e) elif isinstance(e, Query): c.query = e + if e.messages is not None: c.messages.extend(e.messages) elif isinstance(e, (Entity, QueryTemplate)): e.is_deleted = lambda: False + if e.has_errors() is True: e.is_valid = lambda: False elif e.id is None or e.id < 0: @@ -2119,6 +2360,7 @@ class Container(list): pass c._timestamp = xml.get("timestamp") c._srid = xml.get("srid") + return c else: raise CaosDBException( @@ -2142,11 +2384,14 @@ class Container(list): name_case_sensitive=name_case_sensitive) # sync every entity in this container + for entity in self: try: e_sync = sync_dict[entity] + if e_sync is not None: strategy(entity, e_sync.pop()) + for e in e_sync: self.append(e) except KeyError: @@ -2161,6 +2406,7 @@ class Container(list): pass # messages: + for m in container.messages: self.add_message(m) @@ -2179,50 +2425,63 @@ class Container(list): used_remote_entities = [] ''' match by cuid ''' + for local_entity in self: sync_dict[local_entity] = None + if local_entity._cuid is not None: # a list of remote entities which are equivalents of # local_entity sync_remote_entities = [] + for remote_entity in remote_container: if remote_entity._cuid is not None and str(remote_entity._cuid) == str( local_entity._cuid) and remote_entity not in used_remote_entities: sync_remote_entities.append(remote_entity) used_remote_entities.append(remote_entity) + if len(sync_remote_entities) > 0: sync_dict[local_entity] = sync_remote_entities + if unique and len(sync_remote_entities) > 1: msg = "Request was not unique. CUID " + \ str(local_entity._cuid) + " was found " + \ str(len(sync_remote_entities)) + " times." local_entity.add_message(Message("Error", None, msg)) + if raise_exception_on_error: raise AmbiguityException(msg) ''' match by id ''' + for local_entity in self: if sync_dict[local_entity] is None and local_entity.id is not None: sync_remote_entities = [] + for remote_entity in remote_container: if remote_entity.id is not None and remote_entity.id == local_entity.id and remote_entity not in used_remote_entities: sync_remote_entities.append(remote_entity) used_remote_entities.append(remote_entity) + if len(sync_remote_entities) > 0: sync_dict[local_entity] = sync_remote_entities + if unique and len(sync_remote_entities) > 1: msg = "Request was not unique. ID " + \ str(local_entity.id) + " was found " + \ str(len(sync_remote_entities)) + " times." local_entity.add_message(Message("Error", None, msg)) + if raise_exception_on_error: raise AmbiguityException(msg) ''' match by path ''' + for local_entity in self: if sync_dict[local_entity] is None and local_entity.path is not None: sync_remote_entities = [] + for remote_entity in remote_container: if remote_entity.path is not None and str( remote_entity.path) == ( @@ -2230,20 +2489,25 @@ class Container(list): local_entity.path) and remote_entity not in used_remote_entities: sync_remote_entities.append(remote_entity) used_remote_entities.append(remote_entity) + if len(sync_remote_entities) > 0: sync_dict[local_entity] = sync_remote_entities + if unique and len(sync_remote_entities) > 1: msg = "Request was not unique. Path " + \ str(local_entity.path) + " was found " + \ str(len(sync_remote_entities)) + " times." local_entity.add_message(Message("Error", None, msg)) + if raise_exception_on_error: raise AmbiguityException(msg) ''' match by name ''' + for local_entity in self: if sync_dict[local_entity] is None and local_entity.name is not None: sync_remote_entities = [] + for remote_entity in remote_container: if remote_entity.name is not None \ and ( @@ -2258,30 +2522,38 @@ class Container(list): and remote_entity not in used_remote_entities: sync_remote_entities.append(remote_entity) used_remote_entities.append(remote_entity) + if len(sync_remote_entities) > 0: sync_dict[local_entity] = sync_remote_entities + if unique and len(sync_remote_entities) > 1: msg = "Request was not unique. Name " + \ str(local_entity.name) + " was found " + \ str(len(sync_remote_entities)) + " times." local_entity.add_message(Message("Error", None, msg)) + if raise_exception_on_error: raise AmbiguityException(msg) # add remaining entities to this remote_container sync_remote_entities = [] + for remote_entity in remote_container: if not (remote_entity in used_remote_entities): sync_remote_entities.append(remote_entity) + if len(sync_remote_entities) > 0: sync_dict[self] = sync_remote_entities + if unique and len(sync_remote_entities) != 0: msg = "Request was not unique. There are " + \ str(len(sync_remote_entities)) + \ " entities which could not be matched to one of the requested ones." remote_container.add_message(Message("Error", None, msg)) + if raise_exception_on_error: raise AmbiguityException(msg) + return sync_dict def delete(self, raise_exception_on_error=True, flags=None): @@ -2295,19 +2567,23 @@ class Container(list): this happens, none of them will be deleted. It occurs an error instead. """ + if len(self) == 0: if raise_exception_on_error: raise TransactionError( self, "There are no entities to be deleted. This container is empty.") + return self self.clear_server_messages() c = get_connection() id_str = [] + for entity in self: if entity.is_deleted(): continue entity._cuid = None + if entity.id is not None: id_str.append(str(entity.id)) elif entity.name is not None: @@ -2317,15 +2593,18 @@ class Container(list): Message( type="Error", description="This entity has no identifier. It cannot be deleted.")) + if raise_exception_on_error: raise EntityError( "This entity has no identifier. It cannot be deleted.", entity) else: entity.is_valid = lambda: False + if len(id_str) == 0: if raise_exception_on_error: raise TransactionError( self, "There are no entities to be deleted.") + return self entity_url_segments = [_ENTITY_URI_SEGMENT, "&".join(id_str)] @@ -2336,8 +2615,10 @@ class Container(list): cresp = Container._response_to_entities(http_response) self._sync(cresp, raise_exception_on_error=raise_exception_on_error, unique=True, strategy=_deletion_sync) + if raise_exception_on_error: raise_errors(self) + return self def retrieve(self, query=None, unique=True, @@ -2360,11 +2641,13 @@ class Container(list): query = None cresp = Container() entities_str = [] + if query is None: for entity in self: if entity.id is not None and entity.id < 0: entity.id = None entity.clear_server_messages() + if entity.id is not None: entities_str.append(str(entity.id)) elif entity.name is not None: @@ -2378,6 +2661,7 @@ class Container(list): Message( type="Error", description="This entity has no identifier. It cannot be retrieved.")) + if raise_exception_on_error: raise EntityError( "This entity has no identifier. It cannot be retrieved.", entity) @@ -2390,11 +2674,14 @@ class Container(list): cresp2 = self._retrieve(entities=entities_str, flags=flags) cresp.extend(cresp2) cresp.messages.extend(cresp2.messages) + if raise_exception_on_error: raise_errors(cresp) + if sync: self._sync(cresp, unique=unique, raise_exception_on_error=raise_exception_on_error) + return self else: return cresp @@ -2406,6 +2693,7 @@ class Container(list): hl = len(entities) // 2 # split in two uris + return (entities[0:hl], entities[hl:len(entities)]) def _retrieve(self, entities, flags): @@ -2429,18 +2717,22 @@ class Container(list): c2 = self._retrieve(entities=uri2, flags=flags) c1.extend(c2) c1.messages.extend(c2.messages) + return c1 def clear_server_messages(self): self.messages.clear_server_messages() + for entity in self: entity.clear_server_messages() + return self @staticmethod def _dir_to_http_parts(root, d, upload): # @ReservedAssignment ret = [] x = (root + '/' + d if d is not None else root) + for f in listdir(x): if isdir(x + '/' + f): part = MultipartParam(name=hex(randint(0, sys.maxsize)), value="") @@ -2454,6 +2746,7 @@ class Container(list): part.filename = upload + \ ('/' + d + '/' if d is not None else '/') + f ret.append(part) + return ret def update(self, strict=False, raise_exception_on_error=True, @@ -2468,10 +2761,13 @@ class Container(list): self.clear_server_messages() insert_xml = etree.Element("Update") http_parts = [] + if flags is None: flags = {} + if strict is True: flags["strict"] = "true" + if unique is True: flags["uniquename"] = "true" @@ -2481,6 +2777,7 @@ class Container(list): self, "You tried to update an entity without a valid id.") self._linearize() + for entity in self: # process files if present @@ -2501,6 +2798,7 @@ class Container(list): ('' if flags is None else "?" + str(flags)), insert_xml) con = get_connection() + if http_parts is not None and len(http_parts) > 0: http_parts.insert( 0, MultipartParam("FileRepresentation", xml2str(insert_xml))) @@ -2517,11 +2815,14 @@ class Container(list): body=xml2str(insert_xml)) cresp = Container._response_to_entities(http_response) + if raise_exception_on_error: raise_errors(cresp) + if sync: self._sync(cresp, unique=unique, raise_exception_on_error=raise_exception_on_error) + return self else: return cresp @@ -2533,8 +2834,10 @@ class Container(list): new_checksum = File._get_checksum(entity.file) # do not transfer unchanged files. + if entity._checksum is not None and entity._checksum.lower() == new_checksum.lower(): entity._upload = None + return entity._size = None @@ -2545,6 +2848,7 @@ class Container(list): _file = entity.file.name else: _file = entity.file + if isdir(_file): http_parts.extend( Container._dir_to_http_parts(_file, None, entity._upload)) @@ -2556,6 +2860,7 @@ class Container(list): paramname=hex(randint(0, sys.maxsize)), filename=_file) part.filename = entity._upload http_parts.append(part) + if entity.thumbnail is not None: part = MultipartParam.from_file(paramname=hex( randint(0, sys.maxsize)), filename=entity.thumbnail) @@ -2587,10 +2892,13 @@ class Container(list): self.clear_server_messages() insert_xml = etree.Element("Insert") http_parts = [] + if flags is None: flags = {} + if strict: flags["strict"] = "true" + if unique: flags["uniquename"] = "true" @@ -2643,6 +2951,7 @@ class Container(list): ('' if flags is None else "?" + str(flags)), insert_xml) con = get_connection() + if http_parts is not None and len(http_parts) > 0: http_parts.insert( 0, MultipartParam("FileRepresentation", xml2str(insert_xml))) @@ -2665,48 +2974,60 @@ class Container(list): self._sync(cresp, unique=unique, raise_exception_on_error=raise_exception_on_error) + if raise_exception_on_error: raise_errors(self) + return self else: if raise_exception_on_error: raise_errors(cresp) + return cresp @staticmethod def _get_smallest_tmpid(entity): tmpid = 0 + if entity.id is not None: tmpid = min(tmpid, int(entity.id)) + for p in entity.get_parents(): if p.id is not None: tmpid = min(tmpid, int(p.id)) + for p in entity.get_properties(): if p.id is not None: tmpid = min(tmpid, Container._get_smallest_tmpid(p)) + return tmpid def _linearize(self): tmpid = 0 ''' users might already have specified some tmpids. -> look for smallest.''' + for e in self: tmpid = min(tmpid, Container._get_smallest_tmpid(e)) tmpid -= 1 '''a tmpid for every entity''' + for e in self: if e.id is None: e.id = tmpid tmpid -= 1 # CUID + if e._cuid is None or e._cuid == 'None' or e._cuid == '': e._cuid = str(e.id) + "--" + str(uuid()) '''dereference properties and parents''' + for e in self: """properties.""" + for p in e.get_properties(): if p.id is None: if p.name is not None: @@ -2717,6 +3038,7 @@ class Container(list): pass '''parents''' + for p in e.get_parents(): if p.id is None: if p.name is not None: @@ -2740,6 +3062,7 @@ def sync_global_acl(): for child in xml: if child.tag == "EntityPermissions": Permissions.known_permissions = Permissions(child) + for pelem in child: if pelem.tag == "EntityACL": ACL.global_acl = ACL(xml=pelem) @@ -2751,12 +3074,14 @@ def sync_global_acl(): def get_known_permissions(): if Permissions.known_permissions is None: sync_global_acl() + return Permissions.known_permissions def get_global_acl(): if ACL.global_acl is None: sync_global_acl() + return ACL.global_acl @@ -2780,6 +3105,7 @@ class ACI(): e.set("role", self.role) else: e.set("username", self.username) + if self.realm is not None: e.set("realm", self.realm) p = etree.Element("Permission") @@ -2807,9 +3133,11 @@ class ACL(): username = e.get("username") realm = e.get("realm") priority = e.get("priority") + for p in e: if p.tag == "Permission": permission = p.get("name") + if e.tag == "Grant": self.grant(username=username, realm=realm, role=role, permission=permission, priority=priority) @@ -2839,6 +3167,7 @@ class ACL(): self._grants.remove(item) except KeyError: pass + if priority: try: self._priority_denials.remove(item) @@ -2854,9 +3183,11 @@ class ACL(): priority = self._get_boolean_priority(priority) item = ACI(role=role, username=username, realm=realm, permission=permission) + if priority: if item in self._priority_grants: self._priority_grants.remove(item) + if item in self._grants: self._grants.remove(item) @@ -2865,9 +3196,11 @@ class ACL(): priority = self._get_boolean_priority(priority) item = ACI(role=role, username=username, realm=realm, permission=permission) + if priority: if item in self._priority_denials: self._priority_denials.remove(item) + if item in self._denials: self._denials.remove(item) @@ -2877,6 +3210,7 @@ class ACL(): item = ACI(role=role, username=username, realm=realm, permission=permission) self._remove_item(item, priority) + if priority is True: self._priority_grants.add(item) else: @@ -2888,6 +3222,7 @@ class ACL(): item = ACI(role=role, username=username, realm=realm, permission=permission) self._remove_item(item, priority) + if priority is True: self._priority_denials.add(item) else: @@ -2896,76 +3231,95 @@ class ACL(): def to_xml(self, xml=None): if xml is None: xml = etree.Element("EntityACL") + for aci in self._grants: e = etree.Element("Grant") e.set("priority", "False") aci.add_to_element(e) xml.append(e) + for aci in self._denials: e = etree.Element("Deny") e.set("priority", "False") aci.add_to_element(e) xml.append(e) + for aci in self._priority_grants: e = etree.Element("Grant") e.set("priority", "True") aci.add_to_element(e) xml.append(e) + for aci in self._priority_denials: e = etree.Element("Deny") e.set("priority", "True") aci.add_to_element(e) xml.append(e) + return xml def get_acl_for_role(self, role): ret = ACL() + for aci in self._grants: if aci.role == role: ret._grants.add(aci) + for aci in self._denials: if aci.role == role: ret._denials.add(aci) + for aci in self._priority_grants: if aci.role == role: ret._priority_grants.add(aci) + for aci in self._priority_denials: if aci.role == role: ret._priority_denials.add(aci) + return ret def get_acl_for_user(self, username, realm=None): ret = ACL() + for aci in self._grants: if aci.username == username and ( realm is None or aci.realm == realm): ret._grants.add(aci) + for aci in self._denials: if aci.username == username and ( realm is None or aci.realm == realm): ret._denials.add(aci) + for aci in self._priority_grants: if aci.username == username and ( realm is None or aci.realm == realm): ret._priority_grants.add(aci) + for aci in self._priority_denials: if aci.username == username and ( realm is None or aci.realm == realm): ret._priority_denials.add(aci) + return ret def get_permissions_for_user(self, username, realm=None): acl = self.get_acl_for_user(username, realm) _grants = set() + for aci in acl._grants: _grants.add(aci.permission) _denials = set() + for aci in acl._denials: _denials.add(aci.permission) _priority_grants = set() + for aci in acl._priority_grants: _priority_grants.add(aci.permission) _priority_denials = set() + for aci in acl._priority_denials: _priority_denials.add(aci.permission) @@ -2974,15 +3328,19 @@ class ACL(): def get_permissions_for_role(self, role): acl = self.get_acl_for_role(role) _grants = set() + for aci in acl._grants: _grants.add(aci.permission) _denials = set() + for aci in acl._denials: _denials.add(aci.permission) _priority_grants = set() + for aci in acl._priority_grants: _priority_grants.add(aci.permission) _priority_denials = set() + for aci in acl._priority_denials: _priority_denials.add(aci.permission) @@ -2999,6 +3357,7 @@ class Query(): def putFlag(self, key, value=None): self.flags[key] = value + return self def removeFlag(self, key): @@ -3010,9 +3369,11 @@ class Query(): def __init__(self, q): self.flags = dict() self.messages = _Messages() + if isinstance(q, etree._Element): self.q = q.get("string") self.results = int(q.get("results")) + for m in q: if m.tag.lower() == 'warning' or m.tag.lower() == 'error': self.messages.append(_parse_single_xml_element(m)) @@ -3030,11 +3391,15 @@ class Query(): query_dict=query_dict, **kwargs) cresp = Container._response_to_entities(http_response) self.results = cresp.query.results + if self.q.lower().startswith('count') and len(cresp) == 0: # this was a count query + return self.results + if raise_exception_on_error: raise_errors(cresp) + if unique: if len(cresp) > 1 and raise_exception_on_error: raise AmbiguityException("This query wasn't unique") @@ -3043,16 +3408,20 @@ class Query(): elif len(cresp) == 1: r = cresp[0] r.messages.extend(cresp.messages) + return r self.messages = cresp.messages + return cresp def execute_query(q, unique=False, raise_exception_on_error=True, flags=None, **kwargs): query = Query(q) + if flags is not None: query.flags = flags + return query.execute(unique=unique, raise_exception_on_error=raise_exception_on_error, **kwargs) @@ -3070,18 +3439,24 @@ class DropOffBox(list): _log_response(body) xml = etree.fromstring(body) + for child in xml: if child.tag.lower() == "stats": infoelem = child + break + for child in infoelem: if child.tag.lower() == "dropoffbox": dropoffboxelem = child + break del self[:] self.path = dropoffboxelem.get('path') + for f in dropoffboxelem: self.append(f.get('path')) + return self @@ -3095,6 +3470,7 @@ class Info(): c = get_connection() http_response = c.retrieve(["Info"]) xml = etree.fromstring(http_response.read()) + for e in xml: m = _parse_single_xml_element(e) self.messages.append(m) @@ -3115,6 +3491,7 @@ class Permission(): def __eq__(self, p): if isinstance(p, Permission): return p.name == self.name + return False def __hash__(self): @@ -3133,6 +3510,7 @@ class Permissions(): def parse_xml(self, xml): self.clear() + for e in xml: if e.tag == "Permission": self._perms.add(Permission(name=e.get("name"), @@ -3163,6 +3541,7 @@ def parse_xml(xml): elem = xml else: elem = etree.fromstring(xml) + return _parse_single_xml_element(elem) @@ -3174,14 +3553,17 @@ def _parse_single_xml_element(elem): 'file': File, 'parent': Parent, 'entity': Entity} + if elem.tag.lower() in classmap: klass = classmap.get(elem.tag.lower()) entity = klass() Entity._from_xml(entity, elem) + return entity elif elem.tag.lower() == "value": if elem.text is None: return None + return str(elem.text.strip()) elif elem.tag.lower() == "querytemplate": return QueryTemplate._from_xml(elem) @@ -3191,6 +3573,7 @@ def _parse_single_xml_element(elem): return Message(type='History', description=elem.get("transaction")) elif elem.tag.lower() == 'stats': counts = elem.find("counts") + return Message(type="Counts", body=counts.attrib) elif elem.tag == "EntityACL": return ACL(xml=elem) @@ -3207,6 +3590,7 @@ def raise_errors(arg0): entity=arg0, error=Message('Error', 0, 'EntityMultiError')) found114 = False found116 = False + for e in arg0.get_errors(): try: if e.code is not None: @@ -3220,6 +3604,7 @@ def raise_errors(arg0): found114 = True unqualified_properties_error = UnqualifiedPropertiesError( error=e, entity=arg0) + for p in arg0.get_properties(): try: raise_errors(p) @@ -3230,6 +3615,7 @@ def raise_errors(arg0): found116 = True unqualified_parents_error = UnqualifiedParentsError( error=e, entity=arg0) + for p in arg0.get_parents(): try: raise_errors(p) @@ -3241,18 +3627,21 @@ def raise_errors(arg0): raise EntityError(error=e, entity=arg0) except EntityError as ee: entity_error.add_error(ee) + if not found114: for p in arg0.get_properties(): try: raise_errors(p) except EntityError as pe: entity_error.add_error(pe) + if not found116: for p in arg0.get_parents(): try: raise_errors(p) except EntityError as pe: entity_error.add_error(pe) + if len(entity_error.get_errors()) == 1: r = entity_error.get_errors().pop() raise r @@ -3264,6 +3653,7 @@ def raise_errors(arg0): container=arg0, msg="This transaction terminated with Errors.") doRaise = False found12 = False + if arg0.get_errors() is not None: for er in arg0.get_errors(): if er.code is not None: @@ -3271,11 +3661,13 @@ def raise_errors(arg0): found12 = True atomic_error = TransactionError( container=arg0, error=er, msg=er.description) + for e in arg0: try: raise_errors(e) except EntityError as ee: atomic_error.add_error(ee) + if len(atomic_error.get_errors()) > 0: transaction_error.add_error( atomic_error._convert()) @@ -3288,8 +3680,10 @@ def raise_errors(arg0): container=arg0, error=er, msg=er.description) transaction_error.add_error(te) doRaise = True + if len(transaction_error.get_errors()) == 1: transaction_error = transaction_error.get_errors().pop() + if not found12: for e in arg0: try: @@ -3297,6 +3691,7 @@ def raise_errors(arg0): except EntityError as ee: transaction_error.add_error(ee) doRaise = True + if len(transaction_error.get_errors()) == 1: t = transaction_error.get_errors().pop() raise t @@ -3311,9 +3706,11 @@ def raise_errors(arg0): def delete(ids, raise_exception_on_error=True): c = Container() + if isinstance(ids, list) or isinstance(ids, range): for i in ids: c.append(Entity(id=i)) else: c.append(Entity(id=ids)) + return c.delete(raise_exception_on_error=raise_exception_on_error)