PEP 726: Module __setattr__ and __delattr__

In my mental model of modules ModuleType was more of a metaclass and when I write the module I kinda of create a

class Foo(metaclass=types.ModuleType):
    def __dir__(self) ...
    ...

And only on import I create an instance that is cached in sys.modules, but I still can create another instance of the same module. And the way it is in CPython is more an implementation detail.

One of my use-cases is to invalidate cache when some module attribute is changed, for example

# module.py
default_arg2 = 1

_result_cache = {}
def foo(arg1, arg2=None):
    arg2 = default_arg2 if arg2 is None else arg2
    if (result := _result_cache.get((arg1, arg2))) is not None:
        return result
    # slow computation.
    return _result_cache.setdefault((arg1, arg2), result)

def __setattr__(self, name, value):
    if name == "default_arg2":
        _result_cache = {k: v for k, v in _result_cache.items() if k[1] == default_arg2}
    globals()[name] = value

Right now I have a module with a class that holds default_arg, cache and computation method and instance of it. And I also have foo = class_instance.foo to make computation, and to change default I expect users to do module.class_instance.default_arg = .
It was written that way before I knew about module.__class__ trick, and for my case I would be ok to pay 2x slowdown for attribute access (it shouldn’t happen often enough to be noticeable for normal workloads), I can imagine cases where it’s more a concern.

Well, I think cache is one of the cases of more general “module have some invariant and atrribute mutation should keep the invariant” and I think almost any invariant imaginabe for regular class can exists for module.