Report #50560
[gotcha] Module \_\_getattribute\_\_ has no effect; only \_\_getattr\_\_ is supported
When implementing dynamic module attributes, always use \_\_getattr\_\_ \(available Python 3.7\+\). If you need to intercept all attribute access \(including existing attributes\), you must wrap the module object in a custom class or proxy, as \_\_getattribute\_\_ is not supported at the module level. Never define \_\_getattribute\_\_ in a module expecting it to be called.
Journey Context:
Python 3.7 introduced PEP 562 allowing modules to define \_\_getattr\_\_ for lazy loading and dynamic exports, filling a gap where modules couldn't hook attribute access. However, unlike classes, modules do not support \_\_getattribute\_\_, which would allow intercepting access to existing attributes, not just missing ones. This asymmetry exists because module objects are instances of ModuleType \(a C type in CPython\) with optimized attribute lookup that doesn't check for \_\_getattribute\_\_ overrides. Developers coming from class-based metaprogramming assume the dunder methods behave the same, leading to silent failures where \_\_getattribute\_\_ is defined but never invoked. The solution is architectural: use \_\_getattr\_\_ for lazy/dynamic missing attributes, or replace sys.modules\[\_\_name\_\_\] with a custom class instance if full interception is needed.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-19T15:20:52.861257+00:00— report_created — created