diff --git a/python/podio/pythonizations/freeze_class.py b/python/podio/pythonizations/freeze_class.py new file mode 100644 index 000000000..a3aeace00 --- /dev/null +++ b/python/podio/pythonizations/freeze_class.py @@ -0,0 +1,31 @@ +"""Prevent creating new attributes for existing objects +The new attributes created in Python won't be visible for podio IO +therefore preventing the addition of new attributes for podio objects +might be desirable and help detecting mis-assignments""" + +from .utils.pythonizer import Pythonizer + + +class FreezeClassPythonizer(Pythonizer): + """Prevent setting new attributes""" + + @classmethod + def priority(cls): + """This most likely should be the last pythonization loaded + otherwise it may interfere with creating attributes during other pythonizations""" + return 99 + + @classmethod + def filter(cls, class_, name): + return True + + @classmethod + def modify(cls, class_, name): + def freeze_setattr(self, attr, val): + object_type = type(self) + if attr not in object_type.__dict__: + raise AttributeError(f"'{object_type}' object has no attribute '{attr}'") + old_setattr(self, attr, val) + + old_setattr = class_.__setattr__ + class_.__setattr__ = freeze_setattr diff --git a/python/podio/test_CodeGen.py b/python/podio/test_CodeGen.py index 70bff5660..7545e834d 100644 --- a/python/podio/test_CodeGen.py +++ b/python/podio/test_CodeGen.py @@ -49,3 +49,15 @@ def test_bound_check(self): with self.assertRaises(IndexError): _ = collection[20] _ = collection[0] + + +class AttributeCreationTest(unittest.TestCase): + """Setting new attributes test""" + + def test_disable_new_attribute_creation(self): + component = nsp.AnotherNamespaceStruct() + self.assertEqual(component.x, 0) + component.x = 1 + self.assertEqual(component.x, 1) + with self.assertRaises(AttributeError): + component.not_existing_attribute = 0 diff --git a/tests/datalayout.yaml b/tests/datalayout.yaml index eb3e40c82..06cd5ebee 100644 --- a/tests/datalayout.yaml +++ b/tests/datalayout.yaml @@ -42,6 +42,11 @@ components : Members: - ex2::NamespaceStruct data + nsp::AnotherNamespaceStruct: + Members: + - int x + - int y + StructWithFixedWithTypes: Members: - uint16_t fixedUnsigned16 // unsigned int with exactly 16 bits