1

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
7
  • Where is the definition of a?
    – OldBoy
    Commented Feb 23 at 12:00
  • 2
    You can just make the module import itself. Like, if the module is named foo, write import foo; print(foo.a). (Feel free to turn this into an answer, I don't want to earn any more rep.)
    – Aran-Fey
    Commented Feb 23 at 12:38
  • 2
    a does not get an attribute. __getattr__() is use if you retrieve an attribute of an object via the . operator.
    – Klaus D.
    Commented Feb 23 at 12:53
  • @Aran-Fey I want to avoid going through all my code and adding foo. everywhere, do you see a way around that?
    – Xoriun
    Commented Feb 23 at 13:27
  • 2
    There are no hooks for ordinary name lookup: it's a fundamental operation in Python.
    – chepner
    Commented Feb 23 at 21:28

1 Answer 1

1

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

Demo: https://ideone.com/ThEpv6

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.