diff --git a/basyx/aas/model/base.py b/basyx/aas/model/base.py index bacd8b005..e948a255b 100644 --- a/basyx/aas/model/base.py +++ b/basyx/aas/model/base.py @@ -610,12 +610,17 @@ def __init__(self): def __repr__(self) -> str: reversed_path = [] item = self # type: Any + from .submodel import SubmodelElementList while item is not None: if isinstance(item, Identifiable): - reversed_path.append(str(item.id)) + reversed_path.append(item.id) break elif isinstance(item, Referable): - reversed_path.append(item.id_short) + if isinstance(item.parent, SubmodelElementList): + reversed_path.append(f"{item.parent.id_short}[{item.parent.value.index(item)}]") + item = item.parent + else: + reversed_path.append(item.id_short) item = item.parent else: raise AttributeError('Referable must have an identifiable as root object and only parents that are ' diff --git a/basyx/aas/model/submodel.py b/basyx/aas/model/submodel.py index 5b05e9ff3..104eff995 100644 --- a/basyx/aas/model/submodel.py +++ b/basyx/aas/model/submodel.py @@ -715,8 +715,19 @@ def __init__(self, # Items must be added after the above contraint has been checked. Otherwise, it can lead to errors, since the # constraints in _check_constraints() assume that this constraint has been checked. - self._value: base.OrderedNamespaceSet[_SE] = base.OrderedNamespaceSet(self, [("id_short", True)], value, + self._value: base.OrderedNamespaceSet[_SE] = base.OrderedNamespaceSet(self, [("id_short", True)], (), self._check_constraints) + # SubmodelElements need to be added after the assignment of the ordered NamespaceSet, otherwise, if a constraint + # check fails, Referable.__repr__ may be called for an already-contained item during the AASd-114 check, which + # in turn tries to access the SubmodelElementLists value / _value attribute, which wouldn't be set yet if all + # elements are passed to the OrderedNamespaceSet initializer. + try: + for i in value: + self._value.add(i) + except Exception: + # Remove all SubmodelElements if an exception occurs during initialization of the SubmodelElementList + self._value.clear() + raise def _check_constraints(self, new: _SE, existing: Iterable[_SE]) -> None: # We can't use isinstance(new, self.type_value_list_element) here, because each subclass of diff --git a/test/examples/test_helpers.py b/test/examples/test_helpers.py index 5eea373f8..d898d1db0 100644 --- a/test/examples/test_helpers.py +++ b/test/examples/test_helpers.py @@ -94,13 +94,13 @@ def test_submodel_element_list_checker(self): checker.check_submodel_element_list_equal(list_, list_expected) self.assertEqual(4, sum(1 for _ in checker.failed_checks)) checker_iterator = checker.failed_checks - self.assertEqual("FAIL: Attribute id_short of Range[test_list / range1] must be == range2 (value='range1')", + self.assertEqual("FAIL: Attribute id_short of Range[test_list[0]] must be == range2 (value='range1')", repr(next(checker_iterator))) - self.assertEqual("FAIL: Attribute max of Range[test_list / range1] must be == 1337 (value=142857)", + self.assertEqual("FAIL: Attribute max of Range[test_list[0]] must be == 1337 (value=142857)", repr(next(checker_iterator))) - self.assertEqual("FAIL: Attribute id_short of Range[test_list / range2] must be == range1 (value='range2')", + self.assertEqual("FAIL: Attribute id_short of Range[test_list[1]] must be == range1 (value='range2')", repr(next(checker_iterator))) - self.assertEqual("FAIL: Attribute max of Range[test_list / range2] must be == 142857 (value=1337)", + self.assertEqual("FAIL: Attribute max of Range[test_list[1]] must be == 142857 (value=1337)", repr(next(checker_iterator))) # order_relevant diff --git a/test/model/test_submodel.py b/test/model/test_submodel.py index ea523ebc1..87e60d7bb 100644 --- a/test/model/test_submodel.py +++ b/test/model/test_submodel.py @@ -127,7 +127,7 @@ def test_constraints(self): model.SubmodelElementList("test_list", model.MultiLanguageProperty, [mlp1, mlp2]) self.assertEqual("Element to be added MultiLanguageProperty[mlp2] has semantic_id " "ExternalReference(key=(Key(type=GLOBAL_REFERENCE, value=urn:x-test:different),)), " - "while already contained element MultiLanguageProperty[test_list / mlp1] has semantic_id " + "while already contained element MultiLanguageProperty[test_list[0]] has semantic_id " "ExternalReference(key=(Key(type=GLOBAL_REFERENCE, value=urn:x-test:test),)), " "which aren't equal. (Constraint AASd-114)", str(cm.exception)) mlp2.semantic_id = semantic_id1