Skip to content

[3.12] Behavior change: __getattr__ gets called after property raises NotImplementedError  #103551

Closed
@nicoddemus

Description

@nicoddemus

Hi,

Version: 3.12.0a7
OS: Windows

While testing pytest in 3.12 on Windows, we noticed an odd behavior that we believe might be a bug/regression.

Consider this minimal example:

class Stat:
    @property
    def owner(self):
        print("--> accessing owner")
        raise NotImplementedError("owner not implemented")

    def __getattr__(self, item):
        return getattr(1, "foobar")


s = Stat()
s.owner

(Using getattr(1, ...) there just to have an object in-place; in the real code 1 is actually another object, but the outcome is the same).

Running this in Python 3.11.2 (and previous versions), we get this output:

λ py -3.11 --version
Python 3.11.2

λ py -3.11 .tmp\reproducer.py
--> accessing owner
Traceback (most recent call last):
  File "e:\projects\pytest\.tmp\reproducer.py", line 12, in <module>
    s.owner
  File "e:\projects\pytest\.tmp\reproducer.py", line 5, in owner
    raise NotImplementedError("owner not implemented")
NotImplementedError: owner not implemented

NotImplementedError is raised from the property access, and __getattr__ is never called.

In Python 3.12.0a7 however we get:

λ py -3.12 --version
Python 3.12.0a7

λ py -3.12 .tmp\reproducer.py
--> accessing owner
Traceback (most recent call last):
  File "e:\projects\pytest\.tmp\reproducer.py", line 12, in <module>
    s.owner
  File "e:\projects\pytest\.tmp\reproducer.py", line 8, in __getattr__
    return getattr(1, "foobar")
           ^^^^^^^^^^^^^^^^^^^^
AttributeError: 'int' object has no attribute 'foobar'

The property is still being accessed (as shown by the print call), but NotImplementedError is being swallowed/ignored, and __getattr__ seems to be getting called as a fallback.


Changing the example slightly:

class Stat:
    @property
    def owner(self):
        print("--> accessing owner")
        raise NotImplementedError("owner not implemented")

    def __getattr__(self, item):
        raise AttributeError("foobar")


s = Stat()
s.owner

In Python 3.11 the behavior stays the same as before, however in Python 3.12 we now get:

λ py -3.12 .tmp\reproducer.py
--> accessing owner
Traceback (most recent call last):
  File "e:\projects\pytest\.tmp\reproducer.py", line 5, in owner
    raise NotImplementedError("owner not implemented")
NotImplementedError: owner not implemented

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "e:\projects\pytest\.tmp\reproducer.py", line 12, in <module>
    s.owner
  File "e:\projects\pytest\.tmp\reproducer.py", line 8, in __getattr__
    raise AttributeError("foobar")
          ^^^^^^^^^^^^^^^^^^^^^^^^
SystemError: <class 'AttributeError'> returned a result with an exception set

Please let us know if you need more information.

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions