I have setup a custom __getattr__()
method on a module which works perfectly when called from outside the module. However, it doesn't work from within the module itself (since that uses the globals()
dict
which doesn't redirect to __getattr__
on failure). How would I work around that?
I was thinking about encapsulating the globals()
into a custom dict
where I can then modify the __getitem__
method, but that seems very dirty and error prone. Is there another way to do that?
MWE:
def __getattr__(name):
f'custom {name}'
print(a) # should print 'custom a', but raises NameError
When a name is looked up in the global namespace, it is first looked up in the the globals()
dict, and then the __builtins__
dict if that fails, so to customize the behavior of a missing name, you really want to customize the __builtins__
dict rather than the globals()
dict.
But the __builtins__
dict of the current running frame can't be reassigned, so one workaround would be to re-execute the current frame's code object after customizing __builtins__
with a dict with a __missing__
method defined to delegate access to the module's __getattr__
function.
To avoid triggering the __builtins__
customization logics upon re-execution, you can check if __builtins__
is of the custom dict class before deciding whether to run the original intended code instead.
This approach works both as a main program and as an imported module:
if type(__builtins__).__name__ == '_DefaultDict':
# your original code here
def __getattr__(name):
return f'custom {name}'
print(a)
else:
import sys
import builtins
class _DefaultDict(dict):
def __missing__(self, name):
return __getattr__(name)
__builtins__ = _DefaultDict(vars(builtins))
del _DefaultDict # avoid namespace pollution
exec(sys._getframe(0).f_code, globals())
This outputs:
custom a