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