From 44890e66ef64f2ac65ad6e6a823e29d1d722e55d Mon Sep 17 00:00:00 2001 From: zrgt Date: Tue, 5 May 2026 16:29:48 +0200 Subject: [PATCH] fix: NamespaceSet.clear() calls del-hook once per item not per backend clear() iterated all backends firing _execute_item_del_hook for each, resulting in N*M calls (N items, M backends) instead of N. Fix collects items from first backend only before firing hooks. Fixes #497 --- sdk/basyx/aas/model/base.py | 6 +++--- sdk/test/model/test_base.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py index c9c8c8fe..ed0511bc 100644 --- a/sdk/basyx/aas/model/base.py +++ b/sdk/basyx/aas/model/base.py @@ -2075,9 +2075,9 @@ def pop(self) -> _NSO: return value def clear(self) -> None: - for attr_name, (backend, case_sensitive) in self._backend.items(): - for value in backend.values(): - self._execute_item_del_hook(value) + first_backend = next(iter(self._backend.values()))[0] + for value in first_backend.values(): + self._execute_item_del_hook(value) for attr_name, (backend, case_sensitive) in self._backend.items(): backend.clear() diff --git a/sdk/test/model/test_base.py b/sdk/test/model/test_base.py index e300cc1f..5cbfcd96 100644 --- a/sdk/test/model/test_base.py +++ b/sdk/test/model/test_base.py @@ -333,6 +333,21 @@ def setUp(self): self.namespace = self._namespace_class() self.namespace3 = self._namespace_class_qualifier() + def test_namespaceset_clear_hook_called_once_per_item(self) -> None: + # set1 has two backends (id_short + semantic_id); clear() must call del-hook exactly once per item + del_hook_calls: list = [] + + def counting_del_hook(item: model.Property) -> None: + del_hook_calls.append(item) + + ns = self._namespace_class() + ns.set1.add(self.prop1) + ns.set1.add(self.prop7) # different id_short and semantic_id from prop1 + ns.set1._item_id_del_hook = counting_del_hook + ns.set1.clear() + self.assertEqual(0, len(ns.set1)) + self.assertEqual(2, len(del_hook_calls), f"del-hook called {len(del_hook_calls)} times, expected 2") + def test_NamespaceSet(self) -> None: self.namespace.set1.add(self.prop1) self.assertEqual(1, len(self.namespace.set1))