Hi,
I would like to propose adding a new __frozendict__ = True marker to type definition or module namespace to make them use a frozendict for their namespace. The type/module is created as usual, but once the initialization is complete, the namespace becomes a frozendict and no longer possible to modify the namespace. Only the namespace is read-only. If a type/module attribute is mutable (list, dict, whatever), it is still possible to modify the attribute value.
What do you think of this idea? Would it be useful for your use case?
Example on a class defined in Python. Trying to override an attribute or setting a new attributes raises an exception:
class A:
__frozendict__ = True
attr = 1
assert type(A.__dict__) == frozendict
A.attr = 2 # error
A.xyz = 3 # also an error
Note: A.__dict__ is not a types.MappingProxyType of a frozendict, but directly the frozendict.
Module example:
__frozendict__ = True
def func():
pass
attr = 1
It’s no longer possible to override a module attribute or define a new module attribute:
import mod
assert type(mod.__dict__) == frozendict
mod.func = str # error
mod.attr = 2 # error
mod.new_attr = 3 # error
For a class, it’s different than the Java final keyword, since it remains possible to inherit from the class and override methods and attributes in the child class. It’s also different than typing.Final which only applies to a single attribute (and not the whole class namespace).
I implemented my idea in my fork: pull request. To test my idea, I modified a bunch of stdlib modules and some stdlib classes to use __frozendict__ = True.
Example on the modified math extension:
>>> import math
>>> type(math.__dict__)
<class 'frozendict'>
>>> math.pi
3.141592653589793
>>> math.pi = 4
TypeError: frozendict object does not support item assignment
>>> math.floor
<built-in function floor>
>>> math.floor = math.ceil
TypeError: frozendict object does not support item assignment
>>> math.new_attr = 123
TypeError: frozendict object does not support item assignment
Multiple stdlib modules cannot be modified to use __frozendict__ = True, because their test modifies module attributes (for example, using unittest.mock). Tests is the major blocker point to use __frozendict__ = True more widely. Well, and backward compatibility obviously.
See also the rejected PEP 726 – Module __setattr__ and __delattr__.
Victor