From bc2ff75c9dad057ca8120ca702aaf18f5180f03e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Fri, 3 Nov 2023 20:06:15 +0100 Subject: [PATCH] model._string_constraints: don't overwrite existing attributes in case of a conflict Currently a string constraints decorator silently overwrites existing attributes in case of a naming conflict. This behavior is changed such that the decorator checks for existing attributes beforehand and raises an exception in case of a conflict. Futhermore, tests for this behavior are added. --- basyx/aas/model/_string_constraints.py | 2 ++ test/model/test_string_constraints.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/basyx/aas/model/_string_constraints.py b/basyx/aas/model/_string_constraints.py index 0fd05d631..d471201f4 100644 --- a/basyx/aas/model/_string_constraints.py +++ b/basyx/aas/model/_string_constraints.py @@ -130,6 +130,8 @@ def _setter(self, value: Optional[str]) -> None: constraint_check_fn(value) setattr(self, "_" + pub_attr_name, value) + if hasattr(decorated_class, pub_attr_name): + raise AttributeError(f"{decorated_class.__name__} already has an attribute named '{pub_attr_name}'") setattr(decorated_class, pub_attr_name, property(_getter, _setter)) return decorated_class diff --git a/test/model/test_string_constraints.py b/test/model/test_string_constraints.py index 3b347ea2c..5adb15523 100644 --- a/test/model/test_string_constraints.py +++ b/test/model/test_string_constraints.py @@ -83,3 +83,19 @@ def test_ignore_none_values(self) -> None: dc = self.DummyClass(None) # type: ignore self.assertIsNone(dc.some_attr) dc.some_attr = None # type: ignore + + def test_attribute_name_conflict(self) -> None: + # We don't want to overwrite existing attributes in case of a name conflict + with self.assertRaises(AttributeError) as cm: + @_string_constraints.constrain_revision_type("foo") + class DummyClass: + foo = property() + self.assertEqual("DummyClass already has an attribute named 'foo'", cm.exception.args[0]) + + with self.assertRaises(AttributeError) as cm: + @_string_constraints.constrain_label_type("bar") + class DummyClass2: + @property + def bar(self): + return "baz" + self.assertEqual("DummyClass2 already has an attribute named 'bar'", cm.exception.args[0])