From b008187945d65e561f611798a91d94026b566e36 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] 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)