This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Raising AttributeError in descriptor decorator causing searching attribute in __mro__
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.11
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: msolnica, rhettinger, uriyyo
Priority: normal Keywords:

Created on 2021-08-31 15:11 by msolnica, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
bug.py msolnica, 2021-08-31 15:11
Messages (3)
msg400743 - (view) Author: Mateusz Solnica (msolnica) Date: 2021-08-31 15:11
A descriptor that is raising AttributeError in __get__() causes that the Python's interpreter continues searching for attributes in __mro__ calling __getattr__() function in inherited classes.

Let's take a look for example script with this bug.


class A1:
    def __getattr__(self, name):
        print("A1 visited")
        raise AttributeError(f"{self.__class__.__name__}: {name} not found.")

class A2(A1):
    def __getattr__(self, name):
        print("A2 visited")
        super().__getattr__(name)


class A3(A2):
    def __getattr__(self, name):
        print("A3 visited")
        super().__getattr__(name)


class B:
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        raise AttributeError("Python bug?")

class C(A3):
    @B
    def test(self):
        return 25

B is a decorator attached to C.test() and it is throwing AttributeError. When c.test() is performed it starts for walking through C.__mro__ and calling __getattr__() function in objects.

>>> from bug import C
>>> c = C()
>>> c.test()
A3 visited
A2 visited
A1 visited
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/blooser/python-bug/bug.py", line 17, in __getattr__
    super().__getattr__(name)
  File "/home/blooser/python-bug/bug.py", line 11, in __getattr__
    super().__getattr__(name)
  File "/home/blooser/python-bug/bug.py", line 6, in __getattr__
    raise AttributeError(f"{self.__class__.__name__}: {name} not found.")
AttributeError: C: test not found.

Changing error in B.__get__() to NameError:

class B:
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        raise NameError("Python bug?")

causes it omits C.__mro__.

>>> from bug import C
>>> c = C()
>>> c.test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/blooser/python-bug/bug.py", line 26, in __get__
    raise NameError("Python bug?")
NameError: Python bug?

I'm thinking that it is expected behavior or is this a bug?
msg400789 - (view) Author: Yurii Karabas (uriyyo) * (Python triager) Date: 2021-08-31 22:03
Hi Mateusz,

It's not a bug, it's expected behavior.

__getattr__ called when the default attribute access fails with an AttributeError (either __getattribute__() raises an AttributeError because name is not an instance attribute or an attribute in the class tree for self; or __get__() of a name property raises AttributeError).

You can find more information here: 

https://docs.python.org/3/reference/datamodel.html?highlight=__getattr__#object.__getattr__
msg400792 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2021-08-31 22:46
To get a better insight into what is going on, see
https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance
  
The relevant text is, "So if __getattr__() exists, it is called whenever __getattribute__() raises AttributeError (either directly or in one of the descriptor calls)."

If needed, you can bypass the __getattr__ hook by calling object.__getattribute__ directly:

    >>> object.__getattribute__(C(), 'test')
    Traceback (most recent call last):
      File "<pyshell#6>", line 1, in <module>
      object.__getattribute__(c, 'test')
      File "/Users/raymond/Documents/tmp14.py", line 26, in __get__
        raise AttributeError("Python bug?")
    AttributeError: Python bug?
History
Date User Action Args
2022-04-11 14:59:49adminsetgithub: 89227
2021-08-31 22:46:04rhettingersetnosy: + rhettinger
messages: + msg400792
2021-08-31 22:03:11uriyyosetstatus: open -> closed

nosy: + uriyyo
messages: + msg400789

resolution: not a bug
stage: resolved
2021-08-31 15:11:37msolnicacreate