Skip to content

Commit

Permalink
model.base: improve id_short resolution error messages
Browse files Browse the repository at this point in the history
Show the object, where the resolution failed, in the error messages.
  • Loading branch information
jkhsjdhjs authored and s-heppner committed Mar 14, 2024
1 parent d77ceab commit b008187
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 23 deletions.
12 changes: 6 additions & 6 deletions basyx/aas/model/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
"""
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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:
Expand All @@ -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]

Expand Down
46 changes: 29 additions & 17 deletions test/model/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)])
Expand Down Expand Up @@ -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)
Expand All @@ -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"])

Expand All @@ -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"
Expand Down Expand Up @@ -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)])
Expand Down Expand Up @@ -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))


Expand Down Expand Up @@ -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"),
Expand All @@ -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"),
Expand All @@ -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:
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit b008187

Please sign in to comment.