Search code examples
pythonpython-modulegetattr

How to check if a Python module with a custom __getattr__ has a given attribute?


I implemented a custom __getattr__ on a module that retrieves a value and caches it with setattr. Here is the simplified version:

# mymodule.py
import sys

__this = sys.modules[__name__]


def __getattr__(name: str):
    print(f"Generating {name}")
    value = "some value"
    setattr(__this, name, value)
    return value

This works fine:

>>> import mymodule
>>> mymodule.foo
Generating foo
'some value'
>>> mymodule.bar
Generating bar
'some value'
>>> mymodule.bar
'some value'

Note how __getattr__ is called only on attributes that don’t already exist on the module. How can I identify these attributes? Python obviously knows about them, but I can’t get that information.

hasattr doesn’t work, because under the hood it calls getattr which calls my function:

>>> hasattr(mymodule, "foo")
True
>>> hasattr(mymodule, "something")
Generating something
True

"foo" in dir(mymodule) works, but it requires to enumerate all attributes with dir() to test for a single one. One can delete attributes with del mymodule.foo, so keeping track of them in __getattr__ using a global set doesn’t work (and having a custom __delattr__ is not supported yet).

How can I test if "something" is an attribute of mymodule without calling mymodule.__getattr__?


Solution

  • Modules are objects in Python, and just like most objects, they have a __dict__.

    >>> mymodule.__dict__
    {...}
    >>> "foo" in mymodule.__dict__
    True
    >>> "something" in mymodule.__dict__
    False
    

    Keep in mind that __dict__ also contains certain things that you don't explicitly define, like __name__ and __doc__ (which exist on all modules). Though attempting to access these would not invoke your __getattr__ anyway, so it's consistent with the behavior you're looking for.