# -*- 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.
#
from functools import wraps
useFroze_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]def UnFrozen(self):
self.__frozen = False
[docs]def IsFrozen(self):
return self.__frozen
[docs]def always_froze_it(cls):
#cls.__frozen = False
setattr(cls,"__frozen",False)
setattr(cls,"UnFrozen",UnFrozen)
setattr(cls,"IsFrozen",IsFrozen)
def frozensetattr(self, key, value):
#y = getattr(self, key, sentinel)
if self.__frozen and not hasattr(self,key) :
raise Exception(f"Class {cls.__name__} is frozen. Cannot set {key} = {value}")
else:
object.__setattr__(self, key, value)
def init_decorator(func):
@wraps(func)
def wrapper(self, *args, **kwargs):
func(self, *args, **kwargs)
self.__frozen = True
return wrapper
cls.__setattr__ = frozensetattr
cls.__init__ = init_decorator(cls.__init__)
return cls
[docs]def froze_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.
"""
if not useFroze_itDecorator: # pragma: no coverage
setattr(cls, "UnFrozen", lambda x: None)
setattr(cls, "IsFrozen", lambda x: False)
return cls
return always_froze_it(cls)
[docs]def CheckIntegrity(GUI:bool=False):
@always_froze_it
class FrozenTest():
def __init__(self) -> None:
self.a = 3
@always_froze_it
class FrozenSubClass(FrozenTest):
def __init__(self) -> None:
super().__init__()
self.UnFrozen()
self.b = 50
obj1 = FrozenTest()
assert obj1.IsFrozen()
obj2 = FrozenSubClass()
if useFroze_itDecorator:
obj = FrozenTest()
obj.a = 5
try:
obj.b = 7
return "Error in a frozen class" # pragma: no coverage
except Exception as inst:
pass
print(inst)
print("This error message is normal")
obj.__frozen = False
obj.b = 7
obj = FrozenSubClass()
obj.b = 5
try:
obj.c = 7
return "Error in frozen subclass" # pragma: no coverage
except Exception as inst:
pass
obj.UnFrozen()
obj.c = 7
return "ok"
if __name__ == '__main__':
print(CheckIntegrity(True)) # pragma: no cover