From 717b411458da923dc795f121398de5d336355851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Tue, 29 Aug 2023 15:46:53 +0200 Subject: [PATCH 1/3] model.base: make extending a ConstrainedList atomic Previously partial additions of elements via a call to `.extend()` were possible, since the `.extend()` method of the superclass simply calls `.append()` for each item, which in turn calls `.insert()`. Thus, if the hook call for one item raised an exception, all previous items had already been added. --- basyx/aas/model/base.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/basyx/aas/model/base.py b/basyx/aas/model/base.py index a21185a0b..b20989769 100644 --- a/basyx/aas/model/base.py +++ b/basyx/aas/model/base.py @@ -1223,6 +1223,13 @@ def insert(self, index: int, value: _T) -> None: self._item_add_hook(value, self._list) self._list.insert(index, value) + def extend(self, values: Iterable[_T]) -> None: + v_list = list(values) + if self._item_add_hook is not None: + for idx, v in enumerate(v_list): + self._item_add_hook(v, self._list + v_list[:idx]) + self._list = self._list + v_list + @overload def __getitem__(self, index: int) -> _T: ... From 2dc720e17af3018f18fbba7d046122deff68d09c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Tue, 29 Aug 2023 15:52:58 +0200 Subject: [PATCH 2/3] test.model.test_base: add test for `add` and `del` atomicity of `ConstrainedList` --- test/model/test_base.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/model/test_base.py b/test/model/test_base.py index ace44a33d..2b91ee03e 100644 --- a/test/model/test_base.py +++ b/test/model/test_base.py @@ -1114,3 +1114,28 @@ def del_hook(itm: int, list_: List[int]) -> None: self.assertEqual(existing_items, [1, 2, 3, 4, 10, 11]) check_list.pop() self.assertEqual(c_list, check_list) + + def test_atomicity(self) -> None: + def hook(itm: int, _list: List[int]) -> None: + if itm > 2: + raise ValueError + + c_list: model.ConstrainedList[int] = model.ConstrainedList([], item_add_hook=hook) + with self.assertRaises(ValueError): + c_list = model.ConstrainedList([1, 2, 3], item_add_hook=hook) + self.assertEqual(c_list, []) + with self.assertRaises(ValueError): + c_list.extend([1, 2, 3]) + self.assertEqual(c_list, []) + c_list.extend([1, 2]) + self.assertEqual(c_list, [1, 2]) + + c_list = model.ConstrainedList([1, 2, 3], item_del_hook=hook) + with self.assertRaises(ValueError): + del c_list[0:3] + self.assertEqual(c_list, [1, 2, 3]) + with self.assertRaises(ValueError): + c_list.clear() + self.assertEqual(c_list, [1, 2, 3]) + del c_list[0:2] + self.assertEqual(c_list, [3]) From ad4cd3c8577aeffec1f1028a149fab5d60cbf61c Mon Sep 17 00:00:00 2001 From: s-heppner Date: Thu, 31 Aug 2023 15:40:10 +0200 Subject: [PATCH 3/3] test_base: Add missing empty line to fix codestyle --- test/model/test_base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/model/test_base.py b/test/model/test_base.py index 65f527123..cde414573 100644 --- a/test/model/test_base.py +++ b/test/model/test_base.py @@ -1114,6 +1114,7 @@ def del_hook(itm: int, list_: List[int]) -> None: self.assertEqual(existing_items, [1, 2, 3, 4, 10, 11]) check_list.pop() self.assertEqual(c_list, check_list) + def test_atomicity(self) -> None: def hook(itm: int, _list: List[int]) -> None: if itm > 2: