1from typing import Any 2 3_frozen_classes: dict[type, type] = {} 4 5# make a derived class freezable (ie, disallow modifications). 6# we do this by changing the class of an instance at runtime when freeze() 7# is called, providing a derived class that is exactly the same except 8# for a __setattr__ that raises an error when called. this beats having 9# a field for frozenness and an unconditional __setattr__ that checks this 10# field because it does not insert anything into the class dict. 11class Freezeable: 12 def freeze(self) -> None: 13 cls = type(self) 14 if not (frozen := _frozen_classes.get(cls)): 15 def __setattr__(instance: Any, n: str, v: Any) -> None: 16 raise TypeError(f'{cls.__name__} is frozen') 17 frozen = type(cls.__name__, (cls,), { 18 '__setattr__': __setattr__, 19 }) 20 _frozen_classes[cls] = frozen 21 self.__class__ = frozen