From 3f7f762dc924bd8ab1c852bf59a77aefa21f42db Mon Sep 17 00:00:00 2001 From: zrgt Date: Tue, 5 May 2026 16:27:16 +0200 Subject: [PATCH] fix: NamespaceSet.pop() removes item from all backends pop() only removed item from first backend via popitem(), leaving stale entries in remaining backends and causing false AASConstraintViolation on subsequent add() for same semantic_id. Fixes #496 --- sdk/basyx/aas/model/base.py | 4 +++- sdk/test/model/test_base.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py index c9c8c8fe..1d3a18e5 100644 --- a/sdk/basyx/aas/model/base.py +++ b/sdk/basyx/aas/model/base.py @@ -2070,8 +2070,10 @@ def discard(self, x: _NSO) -> None: def pop(self) -> _NSO: _, value = next(iter(self._backend.values()))[0].popitem() + for key_attr_name, (backend_dict, case_sensitive) in self._backend.items(): + key_attr_value = self._get_attribute(value, key_attr_name, case_sensitive) + backend_dict.pop(key_attr_value, None) self._execute_item_del_hook(value) - value.parent = None return value def clear(self) -> None: diff --git a/sdk/test/model/test_base.py b/sdk/test/model/test_base.py index e300cc1f..65d49ea0 100644 --- a/sdk/test/model/test_base.py +++ b/sdk/test/model/test_base.py @@ -333,6 +333,18 @@ def setUp(self): self.namespace = self._namespace_class() self.namespace3 = self._namespace_class_qualifier() + def test_namespaceset_pop_removes_from_all_backends(self) -> None: + # set1 has two backends: id_short and semantic_id + self.namespace.set1.add(self.prop1) + popped = self.namespace.set1.pop() + self.assertIs(self.prop1, popped) + self.assertEqual(0, len(self.namespace.set1)) + # After pop, adding a new item with the same semantic_id must NOT raise AASConstraintViolation — + # it would if the popped item's semantic_id entry were still in the backend + new_prop = model.Property("NewProp", model.datatypes.Int, semantic_id=self.propSemanticID) + self.namespace.set1.add(new_prop) + self.assertEqual(1, len(self.namespace.set1)) + def test_NamespaceSet(self) -> None: self.namespace.set1.add(self.prop1) self.assertEqual(1, len(self.namespace.set1))