diff --git a/src/caosdb/apiutils.py b/src/caosdb/apiutils.py index a46e30375b924d358448e73aece61562c36c700b..3d0e3883637318f7cef1477e9d1765e761179b4b 100644 --- a/src/caosdb/apiutils.py +++ b/src/caosdb/apiutils.py @@ -404,7 +404,6 @@ def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_emp # Compare both entities: diff_r1, diff_r2 = compare_entities( entity_a, entity_b, compare_referenced_records=merge_references_with_empty_diffs) - # Go through the comparison and try to apply changes to entity_a: for key in diff_r2["parents"]: entity_a.add_parent(entity_b.get_parent(key)) @@ -430,6 +429,7 @@ def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_emp setattr(entity_a.get_property(key), attribute, diff_r2["properties"][key][attribute]) else: + print(entity_a, entity_b) raise EntityMergeConflictError( f"Entity a ({entity_a.id}, {entity_a.name}) " f"has a Property '{key}' with {attribute}=" @@ -556,13 +556,13 @@ def resolve_reference(prop: Property): referenced = [] for val in prop.value: - if isinstance(val, int): + if isinstance(val, (int, str)): referenced.append(retrieve_entity_with_id(val)) else: referenced.append(val) prop.value = referenced else: - if isinstance(prop.value, int): + if isinstance(prop.value, (int, str)): prop.value = retrieve_entity_with_id(prop.value) diff --git a/src/caosdb/common/models.py b/src/caosdb/common/models.py index ee478c4a0c55818ce3e78aec0d4580f0dd6ea864..35c6720589c37a4ee5b8160e29a262f180e628be 100644 --- a/src/caosdb/common/models.py +++ b/src/caosdb/common/models.py @@ -82,6 +82,30 @@ NONE = "NONE" SPECIAL_ATTRIBUTES = ["name", "role", "datatype", "description", "id", "path", "checksum", "size"] +_EXPERIMENTAL_STRING_IDS = None + + +def experimental_string_ids(switch_on=None): + global _EXPERIMENTAL_STRING_IDS + if switch_on is not None: + _EXPERIMENTAL_STRING_IDS = switch_on + if _EXPERIMENTAL_STRING_IDS is None: + _EXPERIMENTAL_STRING_IDS = get_config().getboolean("Misc", "experimental_string_ids", fallback=False) + return _EXPERIMENTAL_STRING_IDS + + +def entity_id(eid): + if experimental_string_ids(): + return str(eid) + else: + return int(eid) + + +def is_temporary_id(eid): + if experimental_string_ids(): + return entity_id(eid).startswith("-") + return entity(eid) < 0 + class Entity: @@ -213,7 +237,7 @@ class Entity: @id.setter def id(self, new_id): if new_id is not None: - self.__id = int(new_id) + self.__id = entity_id(new_id) else: self.__id = None @@ -880,12 +904,12 @@ out: List[Entity] if isinstance(key, int): for p in self.parents: - if p.id is not None and int(p.id) == int(key): + if p.id is not None and str(p.id) == str(key): return p elif isinstance(key, Entity): if key.id is not None: # first try by id - found = self.get_parent(int(key.id)) + found = self.get_parent(key.id) if found is not None: return found @@ -893,6 +917,10 @@ out: List[Entity] return self.get_parent(key.name) else: + # try id (case-sensitive), then name (case-insensitive) + for p in self.parents: + if p.id is not None and str(p.id) == str(key): + return p for p in self.parents: if (p.name is not None and str(p.name).lower() == str(key).lower()): @@ -940,10 +968,13 @@ out: List[Entity] # int given elif isinstance(pattern, int): for p in self.properties: - if p.id is not None and int(p.id) == int(pattern): + if p.id is not None and str(p.id) == str(pattern): return p # str given elif isinstance(pattern, str): + for p in self.properties: + if p.id is not None and str(p.id) == str(pattern): + return p for p in self.properties: if (p.name is not None and str(p.name).lower() == str(pattern).lower()): @@ -1260,13 +1291,13 @@ out: List[Entity] if isinstance(entity, Entity): entity.role = elem.tag entity._cuid = elem.get("cuid") - entity.id = elem.get("id") # @ReservedAssignment + entity.id = elem.get("id") entity.name = elem.get("name") entity.description = elem.get("description") entity.path = elem.get("path") entity._checksum = elem.get("checksum") entity._size = elem.get("size") - entity.datatype = elem.get("datatype") # @ReservedAssignment + entity.datatype = elem.get("datatype") entity.unit = elem.get("unit") entity.file = elem.get("file") @@ -1511,11 +1542,6 @@ def _parse_value(datatype, value): else: raise ValueError("Boolean value was {}.".format(value)) - # Datetime and text are returned as-is - if datatype in [DATETIME, TEXT]: - if isinstance(value, str): - return value - # deal with collections if isinstance(datatype, str): matcher = re.compile(r"^(?P<col>[^<]+)<(?P<dt>[^>]+)>$") @@ -1541,34 +1567,26 @@ def _parse_value(datatype, value): # This is for a special case, where the xml parser could not differentiate # between single values and lists with one element. As - if hasattr(value, "__len__") and len(value) == 1: + if hasattr(value, "__len__") and len(value) == 1 and not isinstance(value, str): return _parse_value(datatype, value[0]) + if isinstance(value, list): + raise TypeError( + "Invalid datatype: List valued properties must be announced by " + "the datatype.\n" + f"Datatype: {datatype}\nvalue: {value}") + + if datatype in [DATETIME, TEXT]: + return str(value) + # deal with references if isinstance(value, Entity): return value - if isinstance(value, str) and "@" in value: - # probably this is a versioned reference - + try: + return entity_id(value) + except ValueError: + # reference via name or we have string ids. return str(value) - else: - # for unversioned references - try: - return int(value) - except ValueError: - # reference via name - - return str(value) - except TypeError as te: - # deal with invalid XML: List of values without appropriate datatype - if isinstance(value, list): - raise TypeError( - "Invalid datatype: List valued properties must be announced by " - "the datatype.\n" + f"Datatype: {datatype}\nvalue: {value}") - else: - # Everything else that's not related to wrong list assignments - raise te def _log_request(request, xml_body=None): @@ -1592,7 +1610,7 @@ class QueryTemplate(): def __init__(self, id=None, name=None, query=None, description=None): # @ReservedAssignment - self.id = (int(id) if id is not None else None) + self.id = (entity_id(id) if id is not None else None) self.role = "QueryTemplate" self.name = name self.description = description @@ -1701,7 +1719,7 @@ class QueryTemplate(): q.version = child elif isinstance(child, Permissions): q.permissions = child - q.id = int(xml.get("id")) + q.id = entity_id(xml.get("id")) return q else: @@ -2718,13 +2736,8 @@ class Container(list): """ _debug = staticmethod( - lambda: ( - get_config().getint( - "Container", - "debug") if get_config().has_section("Container") and - get_config().get( - "Container", - "debug") is not None else 0)) + lambda: get_config().getint("Container", "debug", fallback=0) + ) def is_valid(self): for e in self: @@ -2784,7 +2797,7 @@ class Container(list): for e in self: if e.id: - if e.id == int(id): + if str(e.id) == str(id): return e raise KeyError("No entity with such id (" + str(id) + ")!") @@ -2873,15 +2886,15 @@ class Container(list): elif isinstance(entity, int): super().append(Entity(id=entity)) elif hasattr(entity, "encode"): - super().append(Entity(name=entity)) + if experimental_string_ids(): + super().append(Entity(id=entity)) + else: + super().append(Entity(name=entity)) elif isinstance(entity, QueryTemplate): super().append(entity) else: warn("Entity was neither an id nor a name nor an entity." + " (was " + str(type(entity)) + ":\n" + str(entity) + ")") - # raise TypeError( - # "Entity was neither an id nor a name nor an entity." + - # " (was " + str(type(entity)) + "\n" + str(entity) + ")") return self @@ -3040,7 +3053,7 @@ class Container(list): if e.has_errors() is True: e.is_valid = lambda: False - elif e.id is None or e.id < 0: + elif e.id is None or is_temporary_id(e.id): e.is_valid = lambda: False else: e.is_valid = lambda: True @@ -3201,17 +3214,22 @@ class Container(list): # match by name for local_entity in self: + find_by_name = local_entity.name + if (experimental_string_ids() + and find_by_name is None): + find_by_name = local_entity.id # match remotes id with local name + if (sync_dict[local_entity] is None - and local_entity.name is not None): + and find_by_name is not None): sync_remote_entities = [] for remote_entity in remote_container: if (remote_entity.name is not None - and (str(remote_entity.name) == str(local_entity.name) + and (str(remote_entity.name) == str(find_by_name) or (name_case_sensitive is False and str(remote_entity.name).lower() == str( - local_entity.name).lower())) + find_by_name).lower())) and remote_entity not in used_remote_entities): sync_remote_entities.append(remote_entity) used_remote_entities.append(remote_entity) @@ -3221,7 +3239,7 @@ class Container(list): if unique and len(sync_remote_entities) > 1: msg = "Request was not unique. Name " + \ - str(local_entity.name) + " was found " + \ + str(find_by_name) + " was found " + \ str(len(sync_remote_entities)) + " times." local_entity.add_message(Message(description=msg, type="Error")) @@ -3279,19 +3297,17 @@ class Container(list): if references.value is None: continue - elif isinstance(references.value, int): - is_being_referenced.add(references.value) + elif hasattr(references.value, "id"): + is_being_referenced.add(references.value.id) + elif is_list_datatype(references.datatype): for list_item in references.value: - if isinstance(list_item, int): - is_being_referenced.add(list_item) - else: + if hasattr(list_item, "id"): is_being_referenced.add(list_item.id) + else: + is_being_referenced.add(list_item) else: - try: - is_being_referenced.add(references.value.id) - except AttributeError: - pass + is_being_referenced.add(references.value) if hasattr(references, 'id'): is_property.add(references.id) @@ -3436,7 +3452,7 @@ class Container(list): if query is None: for entity in self: - if entity.id is not None and entity.id < 0: + if entity.id is not None and is_temporary_id(entity.id): entity.id = None entity.clear_server_messages() @@ -3567,7 +3583,7 @@ class Container(list): flags["uniquename"] = "true" for entity in self: - if (entity.id is None or entity.id < 0): + if (entity.id is None or is_temporary_id(entity.id)): ee = EntityError( "You tried to update an entity without a valid id.", entity) @@ -3788,15 +3804,25 @@ class Container(list): tmpid = 0 if entity.id is not None: - tmpid = min(tmpid, int(entity.id)) + try: + tmpid = min(tmpid, int(entity.id)) + except ValueError: + pass for p in entity.get_parents(): if p.id is not None: - tmpid = min(tmpid, int(p.id)) + try: + tmpid = min(tmpid, int(p.id)) + except ValueError: + pass for p in entity.get_properties(): if p.id is not None: - tmpid = min(tmpid, Container._get_smallest_tmpid(p)) + try: + tmpid = min(tmpid, int(p.id)) + except ValueError: + pass + tmpid = min(tmpid, Container._get_smallest_tmpid(p)) return tmpid diff --git a/src/caosdb/high_level_api.py b/src/caosdb/high_level_api.py index 3509a7b6bfe7ec322f2e0d2590334c6fc6f02cf8..4f2ac797888fed17c2b1780750a91f48e87611a9 100644 --- a/src/caosdb/high_level_api.py +++ b/src/caosdb/high_level_api.py @@ -60,23 +60,23 @@ def standard_type_for_high_level_type(high_level_record: "CaosDBPythonEntity", class in the standard CaosDB API or - if return_string is True - return the role as a string. """ - if type(high_level_record) == CaosDBPythonRecord: + if isinstance(high_level_record, CaosDBPythonRecord): if not return_string: return db.Record return "Record" - elif type(high_level_record) == CaosDBPythonFile: + elif isinstance(high_level_record, CaosDBPythonFile): if not return_string: return db.File return "File" - elif type(high_level_record) == CaosDBPythonProperty: + elif isinstance(high_level_record, CaosDBPythonProperty): if not return_string: return db.Property return "Property" - elif type(high_level_record) == CaosDBPythonRecordType: + elif isinstance(high_level_record, CaosDBPythonRecordType): if not return_string: return db.RecordType return "RecordType" - elif type(high_level_record) == CaosDBPythonEntity: + elif isinstance(high_level_record, CaosDBPythonEntity): if not return_string: return db.Entity return "Entity" @@ -101,15 +101,15 @@ def high_level_type_for_standard_type(standard_record: db.Entity): if not isinstance(standard_record, db.Entity): raise ValueError() role = standard_record.role - if role == "Record" or type(standard_record) == db.Record: + if role == "Record" or isinstance(standard_record, db.Record): return CaosDBPythonRecord - elif role == "File" or type(standard_record) == db.File: + elif role == "File" or isinstance(standard_record, db.File): return CaosDBPythonFile - elif role == "Property" or type(standard_record) == db.Property: + elif role == "Property" or isinstance(standard_record, db.Property): return CaosDBPythonProperty - elif role == "RecordType" or type(standard_record) == db.RecordType: + elif role == "RecordType" or isinstance(standard_record, db.RecordType): return CaosDBPythonRecordType - elif role == "Entity" or type(standard_record) == db.Entity: + elif role == "Entity" or isinstance(standard_record, db.Entity): return CaosDBPythonEntity raise RuntimeError("Incompatible type.") @@ -213,14 +213,14 @@ class CaosDBPythonEntity(object): """ Getter for the file. """ - if type(self) != CaosDBPythonFile: + if not isinstance(self, CaosDBPythonFile): raise RuntimeError("Please don't use the file attribute for entities" " that are no files.") return self._file @file.setter def file(self, val: str): - if val is not None and type(self) != CaosDBPythonFile: + if val is not None and not isinstance(self, CaosDBPythonFile): raise RuntimeError("Please don't use the file attribute for entities" " that are no files.") self._file = val @@ -230,14 +230,14 @@ class CaosDBPythonEntity(object): """ Getter for the path. """ - if type(self) != CaosDBPythonFile: + if not isinstance(self, CaosDBPythonFile): raise RuntimeError("Please don't use the path attribute for entities" " that are no files.") return self._path @path.setter def path(self, val: str): - if val is not None and type(self) != CaosDBPythonFile: + if val is not None and not isinstance(self, CaosDBPythonFile): raise RuntimeError("Please don't use the path attribute for entities" " that are no files.") self._path = val @@ -651,7 +651,7 @@ class CaosDBPythonEntity(object): if baseprop in serialization: entity.__setattr__(baseprop, serialization[baseprop]) - if type(entity) == CaosDBPythonFile: + if isinstance(entity, CaosDBPythonFile): entity.file = serialization["file"] entity.path = serialization["path"] @@ -721,9 +721,11 @@ class CaosDBPythonEntity(object): for baseprop in ("name", "id", "description", "version"): val = self.__getattribute__(baseprop) if val is not None: + if baseprop == "id": + val = str(val) fulldict[baseprop] = val - if type(self) == CaosDBPythonFile: + if isinstance(self, CaosDBPythonFile): fulldict["file"] = self.file fulldict["path"] = self.path diff --git a/unittests/test_add_property.py b/unittests/test_add_property.py index 0d3183b4c0ca5517ecea68d0e49bbf335bb2a13e..6a9c56ac509c8d5fd5675e5cfc3aa1fa986ea334 100644 --- a/unittests/test_add_property.py +++ b/unittests/test_add_property.py @@ -116,7 +116,7 @@ def test_property_parameter_with_entity(): concrete_property = rec.get_property("length") assert concrete_property is not None assert concrete_property.name == "length" - assert concrete_property.id == 512 + assert str(concrete_property.id) == str(512) assert concrete_property.description == "This is the length of something." assert concrete_property.unit == "m" assert concrete_property.datatype == db.DOUBLE @@ -138,7 +138,7 @@ def test_property_parameter_with_entity_and_value(): concrete_property = rec.get_property("length") assert concrete_property is not None assert concrete_property.name == "length" - assert concrete_property.id == 512 + assert str(concrete_property.id) == str(512) assert concrete_property.description == "This is the length of something." assert concrete_property.unit == "m" assert concrete_property.value == 3.14 @@ -154,7 +154,7 @@ def test_property_parameter_with_id(): assert 1 == len(rec.get_properties()) concrete_property = rec.get_property(512) assert concrete_property is not None - assert concrete_property.id == 512 + assert str(concrete_property.id) == str(512) def test_property_parameter_with_id_and_value(): @@ -165,7 +165,7 @@ def test_property_parameter_with_id_and_value(): assert 1 == len(rec.get_properties()) concrete_property = rec.get_property(512) assert concrete_property is not None - assert concrete_property.id == 512 + assert str(concrete_property.id) == str(512) assert concrete_property.value == 3.14 @@ -177,7 +177,7 @@ def test_datatype(): assert 1 == len(rec.get_properties()) concrete_property = rec.get_property(512) assert concrete_property is not None - assert concrete_property.id == 512 + assert str(concrete_property.id) == str(512) assert concrete_property.value == 3.14 @@ -196,7 +196,7 @@ def test_property_parameter_with_entity_and_datatype(): concrete_property = rec.get_property("length") assert concrete_property is not None assert concrete_property.name == "length" - assert concrete_property.id == 512 + assert str(concrete_property.id) == str(512) assert concrete_property.description == "This is the length of something." assert concrete_property.unit == "m" assert concrete_property.value == 300 @@ -262,7 +262,7 @@ def test_add_list_of_entitities(): i = 0 for e in rec.get_property("listOfEntities").value: - assert i == e.id + assert str(i) == str(e.id) i += 1 diff --git a/unittests/test_apiutils.py b/unittests/test_apiutils.py index bda381cf6427377194e272dfa14b83399b6f012f..fa818df3982735c3c9f8e99d3a333337a72a99ce 100644 --- a/unittests/test_apiutils.py +++ b/unittests/test_apiutils.py @@ -44,13 +44,13 @@ def test_apply_to_ids(): rec.add_property(p) def invert(id_): - return id_ * -1 + return int(id_) * -1 apply_to_ids([rec], invert) assert invert(3456) == -3456 - assert rec.parents[0].id == -3456 - assert rec.properties[0].id == -23345 - assert rec.id == -23 + assert str(rec.parents[0].id) == str(-3456) + assert str(rec.properties[0].id) == str(-23345) + assert str(rec.id) == str(-23) def test_id_query(): @@ -65,7 +65,7 @@ def test_resolve_reference(): prop = db.Property(id=1, datatype=db.REFERENCE, value=100) prop.is_valid = lambda: True - items = [200, 300, 400] + items = ["200", "300", "400"] prop_list = db.Property(datatype=db.LIST(db.REFERENCE), value=items) prop_list2 = db.Property(datatype=db.LIST(db.REFERENCE), @@ -73,18 +73,18 @@ def test_resolve_reference(): resolve_reference(prop) resolve_reference(prop_list) resolve_reference(prop_list2) - assert prop.value.id == 100 + assert str(prop.value.id) == str(100) assert isinstance(prop.value, db.Entity) prop_list_ids = [] for i in prop_list.value: - prop_list_ids.append(i.id) + prop_list_ids.append(str(i.id)) assert isinstance(i, db.Entity) assert prop_list_ids == items for i in prop_list2.value: - assert i.id == 500 + assert str(i.id) == str(500) assert isinstance(i, db.Entity) no_reference = db.Property(id=5000, datatype=db.INTEGER, value=2) @@ -206,8 +206,8 @@ def test_compare_special_properties(): assert diff_r1[key] == "bla 1" assert diff_r2[key] == "bla test" else: - assert diff_r1[key] == 1 - assert diff_r2[key] == 2 + assert str(diff_r1[key]) == str(1) + assert str(diff_r2[key]) == str(2) assert len(diff_r1["properties"]) == 0 assert len(diff_r2["properties"]) == 0 @@ -301,7 +301,7 @@ def test_merge_entities(): def test_merge_bug_conflict(): r = db.Record() - r.add_property(name="C", value=4) + r.add_property(name="C", value="4") r2 = db.Record() r2.add_property(name="C", value=4, datatype="TEXT") merge_entities(r, r2) diff --git a/unittests/test_cached.py b/unittests/test_cached.py index ce302d671d6077aed7d8457e70da2076ebe65d50..bc9e1804f871edb7cf070b91b83e607b1696f89f 100644 --- a/unittests/test_cached.py +++ b/unittests/test_cached.py @@ -52,7 +52,7 @@ def mocked_name_query(name): def mocked_id_query(eid): # copy the object, because Entities would normally be created from XML response - return deepcopy([el for el in DUMMY_SERVER_CONTENT if el.id == eid][0]) + return deepcopy([el for el in DUMMY_SERVER_CONTENT if str(el.id) == str(eid)][0]) def mocked_path_query(path): @@ -78,7 +78,7 @@ def test_get_by_name(mocked_get_by_name): mocked_get_by_name.side_effect = mocked_name_query # first call; not in cache -> mocked_execute is touched a = cached_get_entity_by(name='a') - assert a.id == 101 + assert str(a.id) == str(101) assert mocked_get_by_name.call_count == 1 # second call; in cache -> mocked_execute is NOT touched (count is still 1) b = cached_get_entity_by(name='a') @@ -98,13 +98,13 @@ def test_get_by_name(mocked_get_by_name): assert cache_info().currsize == 2 # we can retrieve the inserted element lol = cached_get_entity_by(name='lol') - assert lol.id == 10001 + assert str(lol.id) == str(10001) # this did not touch the mocked function assert mocked_get_by_name.call_count == 2 # make sure normal retrieval still works (count +1) c = cached_get_entity_by(name='c') assert mocked_get_by_name.call_count == 3 - assert c.id == 103 + assert str(c.id) == str(103) @patch("caosdb.utils.get_entity.get_entity_by_id") @@ -112,7 +112,7 @@ def test_get_by_id(mocked_get_by_id): mocked_get_by_id.side_effect = mocked_id_query # first call; not in cache -> mocked_execute is touched b = cached_get_entity_by(eid=102) - assert b.id == 102 + assert str(b.id) == str(102) assert b.name == 'b' assert mocked_get_by_id.call_count == 1 # second call; in cache -> mocked_execute is NOT touched (count is still 1) @@ -147,7 +147,7 @@ def test_get_by_path(mocked_get_by_path): mocked_get_by_path.side_effect = mocked_path_query # first call; not in cache -> mocked_execute is touched b = cached_get_entity_by(path='p') - assert b.id == 104 + assert str(b.id) == str(104) assert mocked_get_by_path.call_count == 1 # second call; in cache -> mocked_execute is NOT touched (count is still 1) a = cached_get_entity_by(path='p') @@ -167,13 +167,13 @@ def test_get_by_path(mocked_get_by_path): assert cache_info().currsize == 2 # we can retrieve the inserted element lol = cached_get_entity_by(path='lol') - assert lol.id == 10001 + assert str(lol.id) == str(10001) # this did not touch the mocked function assert mocked_get_by_path.call_count == 2 # make sure normal retrieval still works (count +1) c = cached_get_entity_by(path='pp') assert mocked_get_by_path.call_count == 3 - assert c.id == 105 + assert str(c.id) == str(105) @patch("caosdb.cached.execute_query") @@ -199,7 +199,7 @@ def test_get_by_query(mocked_query): # Existent entity a = cached_get_entity_by(query='a') assert a is not None - assert a.id == 101 + assert str(a.id) == str(101) assert cache_info().currsize == 2 assert cache_info().hits == 1 assert cache_info().misses == 2 @@ -234,13 +234,13 @@ def test_cached_query(mocked_query): assert cache_info().currsize == 2 # we can retrieve the inserted element lol = cached_query('lol') - assert lol[0].id == 10001 + assert str(lol[0].id) == str(10001) # this did not touch the mocked function assert mocked_query.call_count == 2 # make sure normal retrieval still works (count +1) c = cached_query('a') assert mocked_query.call_count == 3 - assert c[0].id == 101 + assert str(c[0].id) == str(101) @patch("caosdb.utils.get_entity.get_entity_by_name") diff --git a/unittests/test_container.py b/unittests/test_container.py index 0ac4be44826825aa3302119c8bca08f335ab68d3..15972b95739d57d09f5e8fb4513555b7ea0d4cbb 100644 --- a/unittests/test_container.py +++ b/unittests/test_container.py @@ -26,6 +26,7 @@ from __future__ import absolute_import import caosdb as db +from caosdb.common.models import entity_id def test_get_property_values(): @@ -126,7 +127,7 @@ def test_container_dependencies_for_deletion(): record_with_property_which_is_not_a_record ]) assert (db.Container()._test_dependencies_in_container(container) - == {2002, 1005, 1007}) + == {entity_id(2002), entity_id(1005), entity_id(1007)}) def test_container_dependencies_for_deletion_with_lists(): @@ -143,4 +144,4 @@ def test_container_dependencies_for_deletion_with_lists(): container = db.Container() container.extend([record_with_list, record_referenced]) - assert db.Container()._test_dependencies_in_container(container) == {2001} + assert db.Container()._test_dependencies_in_container(container) == {entity_id(2001)} diff --git a/unittests/test_datatype.py b/unittests/test_datatype.py index 9b3c6267fb018e2cd3085dea568d7396c4549ac8..a22dbd06441bb665d887667a19cff9d80afcce90 100644 --- a/unittests/test_datatype.py +++ b/unittests/test_datatype.py @@ -21,7 +21,7 @@ from pytest import raises import caosdb as db from caosdb.common import datatype -from caosdb.common.models import _parse_value +from caosdb.common.models import _parse_value, entity_id def test_list(): @@ -72,8 +72,8 @@ def test_parsing_of_references(): assert _parse_value(dtype, "Anna Lytik") == "Anna Lytik" assert _parse_value(None, "Anna Lytik") == "Anna Lytik" assert _parse_value(dtype, "2345@sdfg") == "2345@sdfg" - assert _parse_value(dtype, "2345") == 2345 - assert _parse_value(dtype, 2345) == 2345 + assert _parse_value(dtype, "2345") == entity_id(2345) + assert _parse_value(dtype, 2345) == entity_id(2345) entity = db.Record(name="bla") assert id(_parse_value(dtype, entity)) == id(entity) @@ -82,8 +82,8 @@ def test_parsing_of_references(): assert _parse_value(dtype, "Anna Lytik") == "Anna Lytik" assert _parse_value(None, "Anna Lytik") == "Anna Lytik" assert _parse_value(dtype, "2345@sdfg") == "2345@sdfg" - assert _parse_value(dtype, "2345") == 2345 - assert _parse_value(dtype, 2345) == 2345 + assert _parse_value(dtype, "2345") == entity_id(2345) + assert _parse_value(dtype, 2345) == entity_id(2345) entity = db.Record(name="bla") assert id(_parse_value(dtype, entity)) == id(entity) diff --git a/unittests/test_high_level_api.py b/unittests/test_high_level_api.py index ea5e635eadaa849480de5f3ece10b813a538a1b0..18f409f59721e15f5047f21b458766cf96d680b6 100644 --- a/unittests/test_high_level_api.py +++ b/unittests/test_high_level_api.py @@ -165,7 +165,7 @@ def test_convert_with_references(): # Parent does not automatically lead to a datatype: assert obj.get_property_metadata("ref").datatype == "bla" assert isinstance(obj.ref, CaosDBPythonUnresolvedReference) - assert obj.ref.id == 27 + assert str(obj.ref.id) == str(27) def test_resolve_references(): @@ -187,19 +187,19 @@ def test_resolve_references(): # Nothing is going to be resolved: obj.resolve_references(False, db.Container()) assert isinstance(obj.ref, CaosDBPythonUnresolvedReference) - assert obj.ref.id == 27 + assert str(obj.ref.id) == str(27) assert obj.ref_false == 27 # deep == True does not help: obj.resolve_references(True, db.Container()) assert isinstance(obj.ref, CaosDBPythonUnresolvedReference) - assert obj.ref.id == 27 + assert str(obj.ref.id) == str(27) # But adding the reference container will do: obj.resolve_references(False, references) assert not isinstance(obj.ref, CaosDBPythonUnresolvedReference) assert isinstance(obj.ref, CaosDBPythonRecord) - assert obj.ref.id == 27 + assert str(obj.ref.id) == str(27) assert obj.ref.a == 57 # Datatypes will not automatically be set: assert obj.ref.get_property_metadata("a").datatype is None @@ -214,19 +214,19 @@ def test_resolve_references(): obj.resolve_references(False, references) assert not isinstance(obj.ref, CaosDBPythonUnresolvedReference) assert isinstance(obj.ref.ref, CaosDBPythonUnresolvedReference) - assert obj.ref.ref.id == 225 + assert str(obj.ref.ref.id) == str(225) # Will not help, because ref2 is missing in container: obj.resolve_references(True, references) assert not isinstance(obj.ref, CaosDBPythonUnresolvedReference) assert isinstance(obj.ref.ref, CaosDBPythonUnresolvedReference) - assert obj.ref.ref.id == 225 + assert str(obj.ref.ref.id) == str(225) references.append(ref2) obj.resolve_references(False, references) assert not isinstance(obj.ref, CaosDBPythonUnresolvedReference) assert isinstance(obj.ref.ref, CaosDBPythonUnresolvedReference) - assert obj.ref.ref.id == 225 + assert str(obj.ref.ref.id) == str(225) obj.resolve_references(True, references) assert not isinstance(obj.ref, CaosDBPythonUnresolvedReference) @@ -279,7 +279,7 @@ def test_base_properties(): importance="RECOMMENDED", description="description") obj = convert_to_python_object(r) assert obj.name == "test" - assert obj.id == 5 + assert str(obj.id) == str(5) assert obj.description == "ok" metadata = obj.get_property_metadata("v") assert metadata.id is None @@ -290,7 +290,7 @@ def test_base_properties(): rconv = convert_to_entity(obj) assert rconv.name == "test" - assert rconv.id == 5 + assert str(rconv.id) == str(5) assert rconv.description == "ok" prop = rconv.get_property("v") assert prop.value == 15 @@ -328,7 +328,7 @@ def test_serialization(): obj = convert_to_python_object(r) text = str(obj) - teststrs = ["description: ok", "id: 5", "datatype: INTEGER", + teststrs = ["description: ok", "id: '5'", "datatype: INTEGER", "importance: RECOMMENDED", "unit: kpx", "name: test", "v: 15"] for teststr in teststrs: assert teststr in text @@ -385,21 +385,21 @@ def test_files(): rec = db.Record() rec.add_property(name="testfile", value=2, datatype=db.FILE) obj = convert_to_python_object(rec) - assert type(obj.testfile) == CaosDBPythonUnresolvedReference - assert obj.testfile.id == 2 + assert isinstance(obj.testfile, CaosDBPythonUnresolvedReference) + assert str(obj.testfile.id) == "2" assert obj.get_property_metadata("testfile").datatype == db.FILE # without resolving references: rconv = convert_to_entity(obj) p = rconv.get_property("testfile") - assert p.value == 2 + assert str(p.value) == "2" assert p.datatype == db.FILE # with previously resolved reference (should not work here, because id is missing): obj.resolve_references(True, db.Container().extend(r)) rconv = convert_to_entity(obj) p = rconv.get_property("testfile") - assert p.value == 2 + assert str(p.value) == "2" assert p.datatype == db.FILE # this time it must work: @@ -407,7 +407,7 @@ def test_files(): obj.resolve_references(True, db.Container().extend(r)) rconv = convert_to_entity(obj) p = rconv.get_property("testfile") - assert type(p.value) == db.File + assert isinstance(p.value, db.File) assert p.datatype == db.FILE assert p.value.file == "/local/path/test.dat" assert p.value.path == "test.dat" @@ -436,7 +436,7 @@ def test_list_types(): assert get_list_datatype(r.get_property("a").datatype) is None obj = convert_to_python_object(r) - assert type(obj.a) == list + assert isinstance(obj.a, list) assert len(obj.a) == 3 assert 4 in obj.a assert obj.get_property_metadata("a").datatype is None @@ -449,7 +449,7 @@ def test_list_types(): r.get_property("a").datatype = db.LIST(db.INTEGER) assert r.get_property("a").datatype == "LIST<INTEGER>" obj = convert_to_python_object(r) - assert type(obj.a) == list + assert isinstance(obj.a, list) assert len(obj.a) == 3 assert 4 in obj.a assert obj.get_property_metadata("a").datatype == "LIST<INTEGER>" @@ -463,22 +463,22 @@ def test_list_types(): r = db.Record() r.add_property(name="a", value=[1, 2, 4], datatype="LIST<TestReference>") obj = convert_to_python_object(r) - assert type(obj.a) == list + assert isinstance(obj.a, list) assert len(obj.a) == 3 assert obj.get_property_metadata("a").datatype == "LIST<TestReference>" for i in range(3): - assert type(obj.a[i]) == CaosDBPythonUnresolvedReference + assert isinstance(obj.a[i], CaosDBPythonUnresolvedReference) assert obj.a == [CaosDBPythonUnresolvedReference(id=i) for i in [1, 2, 4]] # Try resolving: # Should not work: obj.resolve_references(False, db.Container()) - assert type(obj.a) == list + assert isinstance(obj.a, list) assert len(obj.a) == 3 assert obj.get_property_metadata("a").datatype == "LIST<TestReference>" for i in range(3): - assert type(obj.a[i]) == CaosDBPythonUnresolvedReference + assert isinstance(obj.a[i], CaosDBPythonUnresolvedReference) assert obj.a == [CaosDBPythonUnresolvedReference(id=i) for i in [1, 2, 4]] references = db.Container() @@ -488,11 +488,11 @@ def test_list_types(): references.append(ref) obj.resolve_references(False, references) - assert type(obj.a) == list + assert isinstance(obj.a, list) assert len(obj.a) == 3 assert obj.get_property_metadata("a").datatype == "LIST<TestReference>" for i in range(3): - assert type(obj.a[i]) == CaosDBPythonRecord + assert isinstance(obj.a[i], CaosDBPythonRecord) assert obj.a[0].val == "1 bla" @@ -506,7 +506,7 @@ def test_list_types(): r.add_property(name="a", value=[r2, r3]) obj = convert_to_python_object(r) - assert type(obj.a) == list + assert isinstance(obj.a, list) assert len(obj.a) == 2 assert obj.a[0].a == 4 assert obj.a[1].b == 8 @@ -559,7 +559,7 @@ def test_deserialization(): obj_des = CaosDBPythonEntity.deserialize(serial) assert obj_des.name == "test" - assert obj_des.id == 17 + assert str(obj_des.id) == "17" assert obj_des.has_parent(CaosDBPythonUnresolvedParent(name="bla")) print(obj) print(obj_des) @@ -622,9 +622,9 @@ def get_record_container(): def test_recursion(get_record_container): r = convert_to_python_object(get_record_container[0]) r.resolve_references(r, get_record_container) - assert r.id == 109 - assert r.sources[0].id == 109 - assert r.sources[0].sources[0].id == 109 + assert str(r.id) == "109" + assert str(r.sources[0].id) == "109" + assert str(r.sources[0].sources[0].id) == "109" assert "&id001" in str(r) assert "*id001" in str(r) diff --git a/unittests/test_property.py b/unittests/test_property.py index 84f89b5a959192d7831e1bb3eab3a441912afe7e..0e1b4b7582d225d584f7026c6f5bff289b6412af 100644 --- a/unittests/test_property.py +++ b/unittests/test_property.py @@ -71,7 +71,7 @@ def test_null_empty_text_value_5(): def test_list_of_references_with_null(): assert testrecord.get_property("MultiRecRecording").value[0] is None - assert testrecord.get_property("MultiRecRecording").value[1] == 170651 + assert str(testrecord.get_property("MultiRecRecording").value[1]) == "170651" def test_role(): @@ -96,13 +96,13 @@ def test_selected_reference_list(): def test_is_reference(): PROPS = { - 10: db.INTEGER, - 20: db.REFERENCE, - 30: "SomeRT", + "10": db.INTEGER, + "20": db.REFERENCE, + "30": "SomeRT", } def dummy_retrieve(self): - self.datatype = PROPS[self.id] + self.datatype = PROPS[str(self.id)] self.is_valid = lambda: True # replace retrieve function by dummy real_retrieve = Entity.retrieve