From 4c658c5775db9cb520125b299c84f6601055b4ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Thu, 7 Mar 2024 23:15:20 +0100 Subject: [PATCH 1/5] model.base: add id_short path resolution Resolution of id_short paths is added via `UniqueIdShortNamespace.get_referable()`, such that it can be used on every object, that spans such a namespace. `ModelReference.resolve()` is simplified to make use of this new functionality. Furthermore, tests for this are added. --- basyx/aas/model/base.py | 72 ++++++++++++++++++++++++----------------- test/model/test_base.py | 34 +++++++++++++++---- 2 files changed, 70 insertions(+), 36 deletions(-) diff --git a/basyx/aas/model/base.py b/basyx/aas/model/base.py index 51112c9b5..ded73997a 100644 --- a/basyx/aas/model/base.py +++ b/basyx/aas/model/base.py @@ -1030,47 +1030,29 @@ def resolve(self, provider_: "provider.AbstractObjectProvider") -> _RT: :raises IndexError: If the list of keys is empty :raises TypeError: If one of the intermediate objects on the path is not a :class:`~basyx.aas.model.base.Namespace` + :raises ValueError: If a non-numeric index is given to resolve in a + :class:`~basyx.aas.model.submodel.SubmodelElementList` :raises UnexpectedTypeError: If the retrieved object is not of the expected type (or one of its subclasses). The object is stored in the ``value`` attribute of the exception :raises KeyError: If the reference could not be resolved """ - from . import SubmodelElementList - # For ModelReferences, the first key must be an AasIdentifiable. So resolve the first key via the provider. identifier: Optional[Identifier] = self.key[0].get_identifier() if identifier is None: raise AssertionError("Retrieving the identifier of the first key failed.") - resolved_keys: List[str] = [] # for more helpful error messages try: item: Referable = provider_.get_identifiable(identifier) except KeyError as e: raise KeyError("Could not resolve identifier {}".format(identifier)) from e - resolved_keys.append(str(identifier)) - # All keys following the first must not reference identifiables (AASd-125). Thus, we can just follow the path - # recursively. - for key in self.key[1:]: - if not isinstance(item, UniqueIdShortNamespace): - raise TypeError("Object retrieved at {} is not a Namespace".format(" / ".join(resolved_keys))) - is_submodel_element_list = isinstance(item, SubmodelElementList) - try: - if is_submodel_element_list: - # The key's value must be numeric, since this is checked for keys following keys of type - # SUBMODEL_ELEMENT_LIST on construction of ModelReferences. - # Additionally item is known to be a SubmodelElementList which supports __getitem__ because we're in - # the `is_submodel_element_list` branch, but mypy doesn't infer types based on isinstance checks - # stored in boolean variables. - item = item.value[int(key.value)] # type: ignore - resolved_keys[-1] += f"[{key.value}]" - else: - item = item.get_referable(key.value) - resolved_keys.append(item.id_short) - except (KeyError, IndexError) as e: - raise KeyError("Could not resolve {} {} at {}".format( - "index" if is_submodel_element_list else "id_short", key.value, " / ".join(resolved_keys)))\ - from e + # All keys following the first must not reference identifiables (AASd-125). Thus, we can just resolve the + # id_short path via get_referable(). + # This is cursed af, but at least it keeps the code DRY. get_referable() will check the type of self in the + # first iteration, so we can ignore the type here. + item = UniqueIdShortNamespace.get_referable(item, # type: ignore[arg-type] + map(lambda k: k.value, self.key[1:])) # Check type if not isinstance(item, self.type): @@ -1734,15 +1716,45 @@ def __init__(self) -> None: super().__init__() self.namespace_element_sets: List[NamespaceSet] = [] - def get_referable(self, id_short: NameType) -> Referable: + def get_referable(self, id_short: Union[NameType, Iterable[NameType]]) -> Referable: """ - Find a :class:`~.Referable` in this Namespace by its id_short + Find a :class:`~.Referable` in this Namespace by its id_short or by its id_short path. + The id_short path may contain :class:`~basyx.aas.model.submodel.SubmodelElementList` indices. - :param id_short: id_short + :param id_short: id_short or id_short path as any :class:`Iterable` :returns: :class:`~.Referable` + :raises TypeError: If one of the intermediate objects on the path is not a + :class:`~.UniqueIdShortNamespace` + :raises ValueError: If a non-numeric index is given to resolve in a + :class:`~basyx.aas.model.submodel.SubmodelElementList` :raises KeyError: If no such :class:`~.Referable` can be found """ - return super()._get_object(Referable, "id_short", id_short) # type: ignore + from .submodel import SubmodelElementList + if isinstance(id_short, NameType): + id_short = [id_short] + item: Union[UniqueIdShortNamespace, Referable] = self + for id_ in id_short: + # This is redundant on first iteration, but it's a negligible overhead. + # Also, ModelReference.resolve() relies on this check. + if not isinstance(item, UniqueIdShortNamespace): + raise TypeError(f"Cannot resolve id_short or index '{id_}', " + f"because it is not a {UniqueIdShortNamespace.__name__}!") + is_submodel_element_list = isinstance(item, SubmodelElementList) + try: + if is_submodel_element_list: + # item is known to be a SubmodelElementList which supports __getitem__ because we're in + # the `is_submodel_element_list` branch, but mypy doesn't infer types based on isinstance checks + # stored in boolean variables. + item = item.value[int(id_)] # type: ignore + else: + item = item._get_object(Referable, "id_short", id_) # type: ignore[type-abstract] + except ValueError as e: + raise ValueError(f"Cannot resolve '{id_}', because it is not a numeric index!") from e + except (KeyError, IndexError) as e: + raise KeyError("Referable with {} {} not found in this namespace".format( + "index" if is_submodel_element_list else "id_short", id_)) from e + # All UniqueIdShortNamespaces are Referables, and we only ever assign Referable to item. + return item # type: ignore[return-value] def add_referable(self, referable: Referable) -> None: """ diff --git a/test/model/test_base.py b/test/model/test_base.py index 44ac6861d..8eee42de1 100644 --- a/test/model/test_base.py +++ b/test/model/test_base.py @@ -358,6 +358,9 @@ def setUp(self): self.prop7 = model.Property("Prop2", model.datatypes.Int, semantic_id=self.propSemanticID3) self.prop8 = model.Property("ProP2", model.datatypes.Int, semantic_id=self.propSemanticID3) self.prop1alt = model.Property("Prop1", model.datatypes.Int, semantic_id=self.propSemanticID) + self.collection1 = model.SubmodelElementCollection(None) + self.list1 = model.SubmodelElementList("List1", model.SubmodelElementCollection, + semantic_id=self.propSemanticID) self.qualifier1 = model.Qualifier("type1", model.datatypes.Int, 1, semantic_id=self.propSemanticID) self.qualifier2 = model.Qualifier("type2", model.datatypes.Int, 1, semantic_id=self.propSemanticID2) self.qualifier1alt = model.Qualifier("type1", model.datatypes.Int, 1, semantic_id=self.propSemanticID) @@ -584,6 +587,26 @@ def test_Namespace(self) -> None: namespace.remove_referable("Prop2") self.assertEqual("'Referable with id_short Prop2 not found in this namespace'", str(cm4.exception)) + def test_id_short_path_resolution(self) -> None: + self.namespace.set2.add(self.list1) + self.list1.add_referable(self.collection1) + self.collection1.add_referable(self.prop1) + + with self.assertRaises(ValueError) as cm: + self.namespace.get_referable(["List1", "a"]) + self.assertEqual("Cannot resolve 'a', because it is not a numeric index!", str(cm.exception)) + + with self.assertRaises(KeyError) as cm_2: + self.namespace.get_referable(["List1", "0", "Prop2"]) + self.assertEqual("'Referable with id_short Prop2 not found in this namespace'", str(cm_2.exception)) + + with self.assertRaises(TypeError) as cm_3: + self.namespace.get_referable(["List1", "0", "Prop1", "Test"]) + self.assertEqual("Cannot resolve id_short or index 'Test', because it is not a UniqueIdShortNamespace!", + str(cm_3.exception)) + + self.namespace.get_referable(["List1", "0", "Prop1"]) + def test_renaming(self) -> None: self.namespace.set2.add(self.prop1) self.namespace.set2.add(self.prop2) @@ -887,7 +910,7 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable: model.Property) with self.assertRaises(KeyError) as cm: ref1.resolve(DummyObjectProvider()) - self.assertEqual("'Could not resolve id_short lst at urn:x-test:submodel'", str(cm.exception)) + self.assertEqual("'Referable with id_short lst not found in this namespace'", str(cm.exception)) ref2 = model.ModelReference((model.Key(model.KeyTypes.SUBMODEL, "urn:x-test:submodel"), model.Key(model.KeyTypes.SUBMODEL_ELEMENT_LIST, "list"), @@ -896,7 +919,7 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable: model.Property) with self.assertRaises(KeyError) as cm_2: ref2.resolve(DummyObjectProvider()) - self.assertEqual("'Could not resolve index 99 at urn:x-test:submodel / list'", str(cm_2.exception)) + self.assertEqual("'Referable with index 99 not found in this namespace'", str(cm_2.exception)) ref3 = model.ModelReference((model.Key(model.KeyTypes.SUBMODEL, "urn:x-test:submodel"), model.Key(model.KeyTypes.SUBMODEL_ELEMENT_LIST, "list"), @@ -913,8 +936,8 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable: model.Property) with self.assertRaises(TypeError) as cm_3: ref4.resolve(DummyObjectProvider()) - self.assertEqual("Object retrieved at urn:x-test:submodel / list[0] / prop is not a Namespace", - str(cm_3.exception)) + self.assertEqual("Cannot resolve id_short or index 'prop', " + "because it is not a UniqueIdShortNamespace!", str(cm_3.exception)) with self.assertRaises(AttributeError) as cm_4: ref1.key[2].value = "prop1" @@ -944,8 +967,7 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable: with self.assertRaises(KeyError) as cm_8: ref8.resolve(DummyObjectProvider()) - self.assertEqual("'Could not resolve id_short prop_false at urn:x-test:submodel / list[0]'", - str(cm_8.exception)) + self.assertEqual("'Referable with id_short prop_false not found in this namespace'", str(cm_8.exception)) with self.assertRaises(ValueError) as cm_9: ref9 = model.ModelReference((), model.Submodel) From 3dd07f93dc8a5be395b38be906f064617f6fc03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Thu, 7 Mar 2024 23:27:31 +0100 Subject: [PATCH 2/5] model.base: improve id_short resolution error messages Show the object, where the resolution failed, in the error messages. --- basyx/aas/model/base.py | 12 +++++------ test/model/test_base.py | 46 ++++++++++++++++++++++++++--------------- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/basyx/aas/model/base.py b/basyx/aas/model/base.py index ded73997a..6240c5087 100644 --- a/basyx/aas/model/base.py +++ b/basyx/aas/model/base.py @@ -503,7 +503,7 @@ def _get_object(self, object_type: Type[_NSO], attribute_name: str, attribute) - return ns_set.get_object_by_attribute(attribute_name, attribute) except KeyError: continue - raise KeyError(f"{object_type.__name__} with {attribute_name} {attribute} not found in this namespace") + raise KeyError(f"{object_type.__name__} with {attribute_name} {attribute} not found in {self!r}") def _add_object(self, attribute_name: str, obj: _NSO) -> None: """ @@ -531,7 +531,7 @@ def _remove_object(self, object_type: type, attribute_name: str, attribute) -> N return except KeyError: continue - raise KeyError(f"{object_type.__name__} with {attribute_name} {attribute} not found in this namespace") + raise KeyError(f"{object_type.__name__} with {attribute_name} {attribute} not found in {self!r}") class HasExtension(Namespace, metaclass=abc.ABCMeta): @@ -1737,7 +1737,7 @@ def get_referable(self, id_short: Union[NameType, Iterable[NameType]]) -> Refera # This is redundant on first iteration, but it's a negligible overhead. # Also, ModelReference.resolve() relies on this check. if not isinstance(item, UniqueIdShortNamespace): - raise TypeError(f"Cannot resolve id_short or index '{id_}', " + raise TypeError(f"Cannot resolve id_short or index '{id_}' at {item!r}, " f"because it is not a {UniqueIdShortNamespace.__name__}!") is_submodel_element_list = isinstance(item, SubmodelElementList) try: @@ -1749,10 +1749,10 @@ def get_referable(self, id_short: Union[NameType, Iterable[NameType]]) -> Refera else: item = item._get_object(Referable, "id_short", id_) # type: ignore[type-abstract] except ValueError as e: - raise ValueError(f"Cannot resolve '{id_}', because it is not a numeric index!") from e + raise ValueError(f"Cannot resolve '{id_}' at {item!r}, because it is not a numeric index!") from e except (KeyError, IndexError) as e: - raise KeyError("Referable with {} {} not found in this namespace".format( - "index" if is_submodel_element_list else "id_short", id_)) from e + raise KeyError("Referable with {} {} not found in {}".format( + "index" if is_submodel_element_list else "id_short", id_, repr(item))) from e # All UniqueIdShortNamespaces are Referables, and we only ever assign Referable to item. return item # type: ignore[return-value] diff --git a/test/model/test_base.py b/test/model/test_base.py index 8eee42de1..2bc5195c4 100644 --- a/test/model/test_base.py +++ b/test/model/test_base.py @@ -323,9 +323,11 @@ def test_update_commit_qualifier_extension_semantic_id(self): submodel.commit() -class ExampleNamespaceReferable(model.UniqueIdShortNamespace, model.UniqueSemanticIdNamespace): +class ExampleNamespaceReferable(model.UniqueIdShortNamespace, model.UniqueSemanticIdNamespace, model.Identifiable): def __init__(self, values=()): super().__init__() + # The 'id' is required by Referable.__repr__() in error messages. + self.id = self.__class__.__name__ self.set1 = model.NamespaceSet(self, [("id_short", False), ("semantic_id", True)]) self.set2 = model.NamespaceSet(self, [("id_short", False)], values) self.set3 = model.NamespaceSet(self, [("name", True)]) @@ -575,17 +577,19 @@ def test_Namespace(self) -> None: self.assertIs(self.prop2, namespace.get_referable("Prop2")) with self.assertRaises(KeyError) as cm2: namespace.get_referable("Prop3") - self.assertEqual("'Referable with id_short Prop3 not found in this namespace'", - str(cm2.exception)) + self.assertEqual("'Referable with id_short Prop3 not found in " + f"{self._namespace_class.__name__}[{self.namespace.id}]'", str(cm2.exception)) namespace.remove_referable("Prop2") with self.assertRaises(KeyError) as cm3: namespace.get_referable("Prop2") - self.assertEqual("'Referable with id_short Prop2 not found in this namespace'", str(cm3.exception)) + self.assertEqual("'Referable with id_short Prop2 not found in " + f"{self._namespace_class.__name__}[{self.namespace.id}]'", str(cm3.exception)) with self.assertRaises(KeyError) as cm4: namespace.remove_referable("Prop2") - self.assertEqual("'Referable with id_short Prop2 not found in this namespace'", str(cm4.exception)) + self.assertEqual("'Referable with id_short Prop2 not found in " + f"{self._namespace_class.__name__}[{self.namespace.id}]'", str(cm4.exception)) def test_id_short_path_resolution(self) -> None: self.namespace.set2.add(self.list1) @@ -594,16 +598,19 @@ def test_id_short_path_resolution(self) -> None: with self.assertRaises(ValueError) as cm: self.namespace.get_referable(["List1", "a"]) - self.assertEqual("Cannot resolve 'a', because it is not a numeric index!", str(cm.exception)) + self.assertEqual(f"Cannot resolve 'a' at SubmodelElementList[{self.namespace.id} / List1], " + "because it is not a numeric index!", str(cm.exception)) with self.assertRaises(KeyError) as cm_2: self.namespace.get_referable(["List1", "0", "Prop2"]) - self.assertEqual("'Referable with id_short Prop2 not found in this namespace'", str(cm_2.exception)) + self.assertEqual("'Referable with id_short Prop2 not found in " + f"SubmodelElementCollection[{self.namespace.id} / List1[0]]'", str(cm_2.exception)) with self.assertRaises(TypeError) as cm_3: self.namespace.get_referable(["List1", "0", "Prop1", "Test"]) - self.assertEqual("Cannot resolve id_short or index 'Test', because it is not a UniqueIdShortNamespace!", - str(cm_3.exception)) + self.assertEqual("Cannot resolve id_short or index 'Test' at " + f"Property[{self.namespace.id} / List1[0] / Prop1], " + "because it is not a UniqueIdShortNamespace!", str(cm_3.exception)) self.namespace.get_referable(["List1", "0", "Prop1"]) @@ -619,8 +626,8 @@ def test_renaming(self) -> None: self.assertIs(self.prop1, self.namespace.get_referable("Prop3")) with self.assertRaises(KeyError) as cm: self.namespace.get_referable('Prop1') - self.assertEqual("'Referable with id_short Prop1 not found in this namespace'", - str(cm.exception)) + self.assertEqual("'Referable with id_short Prop1 not found in " + f"{self._namespace_class.__name__}[{self.namespace.id}]'", str(cm.exception)) self.assertIs(self.prop2, self.namespace.get_referable("Prop2")) with self.assertRaises(model.AASConstraintViolation) as cm2: self.prop1.id_short = "Prop2" @@ -694,9 +701,11 @@ def test_aasd_117(self) -> None: property.id_short = "bar" -class ExampleOrderedNamespace(model.UniqueIdShortNamespace, model.UniqueSemanticIdNamespace): +class ExampleOrderedNamespace(model.UniqueIdShortNamespace, model.UniqueSemanticIdNamespace, model.Identifiable): def __init__(self, values=()): super().__init__() + # The 'id' is required by Referable.__repr__() in error messages. + self.id = self.__class__.__name__ self.set1 = model.OrderedNamespaceSet(self, [("id_short", False), ("semantic_id", True)]) self.set2 = model.OrderedNamespaceSet(self, [("id_short", False)], values) self.set3 = model.NamespaceSet(self, [("name", True)]) @@ -747,7 +756,8 @@ def test_OrderedNamespace(self) -> None: self.assertEqual(1, len(namespace2.set2)) with self.assertRaises(KeyError) as cm2: namespace2.get_referable("Prop1") - self.assertEqual("'Referable with id_short Prop1 not found in this namespace'", + self.assertEqual("'Referable with id_short Prop1 not found in " + f"{self._namespace_class.__name__}[{self.namespace.id}]'", # type: ignore[has-type] str(cm2.exception)) @@ -910,7 +920,7 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable: model.Property) with self.assertRaises(KeyError) as cm: ref1.resolve(DummyObjectProvider()) - self.assertEqual("'Referable with id_short lst not found in this namespace'", str(cm.exception)) + self.assertEqual("'Referable with id_short lst not found in Submodel[urn:x-test:submodel]'", str(cm.exception)) ref2 = model.ModelReference((model.Key(model.KeyTypes.SUBMODEL, "urn:x-test:submodel"), model.Key(model.KeyTypes.SUBMODEL_ELEMENT_LIST, "list"), @@ -919,7 +929,8 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable: model.Property) with self.assertRaises(KeyError) as cm_2: ref2.resolve(DummyObjectProvider()) - self.assertEqual("'Referable with index 99 not found in this namespace'", str(cm_2.exception)) + self.assertEqual("'Referable with index 99 not found in SubmodelElementList[urn:x-test:submodel / list]'", + str(cm_2.exception)) ref3 = model.ModelReference((model.Key(model.KeyTypes.SUBMODEL, "urn:x-test:submodel"), model.Key(model.KeyTypes.SUBMODEL_ELEMENT_LIST, "list"), @@ -936,7 +947,7 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable: model.Property) with self.assertRaises(TypeError) as cm_3: ref4.resolve(DummyObjectProvider()) - self.assertEqual("Cannot resolve id_short or index 'prop', " + self.assertEqual("Cannot resolve id_short or index 'prop' at Property[urn:x-test:submodel / list[0] / prop], " "because it is not a UniqueIdShortNamespace!", str(cm_3.exception)) with self.assertRaises(AttributeError) as cm_4: @@ -967,7 +978,8 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable: with self.assertRaises(KeyError) as cm_8: ref8.resolve(DummyObjectProvider()) - self.assertEqual("'Referable with id_short prop_false not found in this namespace'", str(cm_8.exception)) + self.assertEqual("'Referable with id_short prop_false not found in " + "SubmodelElementCollection[urn:x-test:submodel / list[0]]'", str(cm_8.exception)) with self.assertRaises(ValueError) as cm_9: ref9 = model.ModelReference((), model.Submodel) From 70932a748c54e9e7795f271b0bf1cc9c63d1df0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Thu, 7 Mar 2024 23:34:14 +0100 Subject: [PATCH 3/5] test_base: add test for non-numeric `SubmodelElementList` Key values A duplicate test is replaced by this test. --- test/model/test_base.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/model/test_base.py b/test/model/test_base.py index 2bc5195c4..21ae1f13d 100644 --- a/test/model/test_base.py +++ b/test/model/test_base.py @@ -981,9 +981,15 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable: self.assertEqual("'Referable with id_short prop_false not found in " "SubmodelElementCollection[urn:x-test:submodel / list[0]]'", str(cm_8.exception)) + ref9 = model.ModelReference((model.Key(model.KeyTypes.SUBMODEL, "urn:x-test:submodel"), + model.Key(model.KeyTypes.SUBMODEL_ELEMENT_COLLECTION, "list"), + model.Key(model.KeyTypes.SUBMODEL_ELEMENT_COLLECTION, "collection")), + model.SubmodelElementCollection) + with self.assertRaises(ValueError) as cm_9: - ref9 = model.ModelReference((), model.Submodel) - self.assertEqual('A reference must have at least one key!', str(cm_9.exception)) + ref9.resolve(DummyObjectProvider()) + self.assertEqual("Cannot resolve 'collection' at SubmodelElementList[urn:x-test:submodel / list], " + "because it is not a numeric index!", str(cm_9.exception)) def test_get_identifier(self) -> None: ref = model.ModelReference((model.Key(model.KeyTypes.SUBMODEL, "urn:x-test:x"),), model.Submodel) From ef0a17359503cccc3d2306678daf8b00cfbfd8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Thu, 7 Mar 2024 23:37:23 +0100 Subject: [PATCH 4/5] model.base: improve two docstrings --- basyx/aas/model/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/basyx/aas/model/base.py b/basyx/aas/model/base.py index 6240c5087..e6d380d01 100644 --- a/basyx/aas/model/base.py +++ b/basyx/aas/model/base.py @@ -866,7 +866,7 @@ def _direct_source_commit(self): class UnexpectedTypeError(TypeError): """ - Exception to be raised by :meth:`basyx.aas.model.base.ModelReference.resolve` if the retrieved object has not + Exception to be raised by :meth:`.ModelReference.resolve` if the retrieved object has not the expected type. :ivar value: The object of unexpected type @@ -1029,7 +1029,7 @@ def resolve(self, provider_: "provider.AbstractObjectProvider") -> _RT: :return: The referenced object (or a proxy object for it) :raises IndexError: If the list of keys is empty :raises TypeError: If one of the intermediate objects on the path is not a - :class:`~basyx.aas.model.base.Namespace` + :class:`~.UniqueIdShortNamespace` :raises ValueError: If a non-numeric index is given to resolve in a :class:`~basyx.aas.model.submodel.SubmodelElementList` :raises UnexpectedTypeError: If the retrieved object is not of the expected type (or one of its subclasses). The From 2243ea6f92cb1e45ccc482dce0b647a9a0f75eeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Fri, 8 Mar 2024 15:41:22 +0100 Subject: [PATCH 5/5] model.base: improve a `ModelReference.resolve()` error message --- basyx/aas/model/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/basyx/aas/model/base.py b/basyx/aas/model/base.py index e6d380d01..332161dcb 100644 --- a/basyx/aas/model/base.py +++ b/basyx/aas/model/base.py @@ -1040,7 +1040,7 @@ def resolve(self, provider_: "provider.AbstractObjectProvider") -> _RT: # For ModelReferences, the first key must be an AasIdentifiable. So resolve the first key via the provider. identifier: Optional[Identifier] = self.key[0].get_identifier() if identifier is None: - raise AssertionError("Retrieving the identifier of the first key failed.") + raise AssertionError(f"Retrieving the identifier of the first {self.key[0]!r} failed.") try: item: Referable = provider_.get_identifiable(identifier)