# -*- coding: utf-8 -*-## This file is subject to the terms and conditions defined in# file 'LICENSE.txt', which is part of this source code package.#fromfunctoolsimportwrapsuseFroze_itDecorator=True# https://stackoverflow.com/questions/3603502/prevent-creating-new-attributes-outside-init# https://hynek.me/articles/hasattr/sentinel=object()# for the moment if a class is frozen no heritage is possible ... working on a solution
[docs]defalways_froze_it(cls):#cls.__frozen = Falsesetattr(cls,"__frozen",False)setattr(cls,"UnFrozen",UnFrozen)setattr(cls,"IsFrozen",IsFrozen)deffrozensetattr(self,key,value):#y = getattr(self, key, sentinel)ifself.__frozenandnothasattr(self,key):raiseException(f"Class {cls.__name__} is frozen. Cannot set {key} = {value}")else:object.__setattr__(self,key,value)definit_decorator(func):@wraps(func)defwrapper(self,*args,**kwargs):func(self,*args,**kwargs)self.__frozen=Truereturnwrappercls.__setattr__=frozensetattrcls.__init__=init_decorator(cls.__init__)returncls
[docs]deffroze_it(cls):""" Decorator to make the class immutable (no more attributes can be added after initialization) For the moment if a class is frozen no heritage is possible. The user must unfrozen the class before adding new attributes. .. code-block:: python self.UnFrozen() the module level attribute useFroze_itDecorator is use to determine if this decorator must be applied. """ifnotuseFroze_itDecorator:# pragma: no coveragesetattr(cls,"UnFrozen",lambdax:None)setattr(cls,"IsFrozen",lambdax:False)returnclsreturnalways_froze_it(cls)
[docs]defCheckIntegrity(GUI:bool=False):@always_froze_itclassFrozenTest():def__init__(self)->None:self.a=3@always_froze_itclassFrozenSubClass(FrozenTest):def__init__(self)->None:super().__init__()self.UnFrozen()self.b=50obj1=FrozenTest()assertobj1.IsFrozen()obj2=FrozenSubClass()ifuseFroze_itDecorator:obj=FrozenTest()obj.a=5try:obj.b=7return"Error in a frozen class"# pragma: no coverageexceptExceptionasinst:passprint(inst)print("This error message is normal")obj.__frozen=Falseobj.b=7obj=FrozenSubClass()obj.b=5try:obj.c=7return"Error in frozen subclass"# pragma: no coverageexceptExceptionasinst:passobj.UnFrozen()obj.c=7return"ok"
if__name__=='__main__':print(CheckIntegrity(True))# pragma: no cover