Skip to content

[Python 3.12] SystemError and other undesirable behavior #10928

@jaraco

Description

@jaraco

Consider the following test:

import pytest


class SpecialException(Exception):
    pass


class Base:
    def __getattr__(self, name):
        raise AttributeError(name)


class Propertious(Base):
    @property
    def foo_special(self):
        raise SpecialException


def test_attr():
    p = Propertious()
    with pytest.raises(SpecialException):
        p.foo_special

The test passes on Python 3.11, but on Python 3.12.0a7, fails thus:

 $ py -3.12 -m pip-run pytest -- -m pytest attr.py
============================================================= test session starts ==============================================================
platform darwin -- Python 3.12.0a7, pytest-7.3.1, pluggy-1.0.0
rootdir: /Users/jaraco/draft
plugins: anyio-3.6.2
collected 1 item                                                                                                                               

attr.py F                                                                                                                                [100%]

=================================================================== FAILURES ===================================================================
__________________________________________________________________ test_attr ___________________________________________________________________

self = <attr.Propertious object at 0x101aa9580>

    @property
    def foo_special(self):
>       raise SpecialException
E       attr.SpecialException

attr.py:16: SpecialException

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

    def test_attr():
        p = Propertious()
        with pytest.raises(SpecialException):
>           p.foo_special

attr.py:22: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <attr.Propertious object at 0x101aa9580>, name = 'foo_special'

    def __getattr__(self, name):
>       raise AttributeError(name)
E       SystemError: <class 'AttributeError'> returned a result with an exception set

attr.py:10: SystemError
=========================================================== short test summary info ============================================================
FAILED attr.py::test_attr - SystemError: <class 'AttributeError'> returned a result with an exception set
============================================================== 1 failed in 0.02s ===============================================================

I discovered the issue when troubleshooting a strange failure of jaraco.abode on Python 3.12. In that real-world example, tests fail when tests check for an expected exception during set_status, during which _control_url is accessed, but instead of being handled solely by the property on the class, somehow the superclass' getattr is evoked:


  stream = open(filename)
tests/mock/devices/secure_barrier.py ..
/home/runner/work/jaraco.abode/jaraco.abode/.tox/python/lib/python3.12/site-packages/blib2to3/pgen2/pgen.py:42: EncodingWarning: 'encoding' argument not specified
  stream = open(filename)
tests/mock/devices/siren.py ..
/home/runner/work/jaraco.abode/jaraco.abode/.tox/python/lib/python3.12/site-packages/blib2to3/pgen2/pgen.py:42: EncodingWarning: 'encoding' argument not specified
  stream = open(filename)
tests/mock/devices/status_display.py ..
/home/runner/work/jaraco.abode/jaraco.abode/.tox/python/lib/python3.12/site-packages/blib2to3/pgen2/pgen.py:42: EncodingWarning: 'encoding' argument not specified
  stream = open(filename)
tests/mock/devices/unknown.py ..
/home/runner/work/jaraco.abode/jaraco.abode/.tox/python/lib/python3.12/site-packages/blib2to3/pgen2/pgen.py:42: EncodingWarning: 'encoding' argument not specified
  stream = open(filename)
tests/mock/devices/valve.py ..
/home/runner/work/jaraco.abode/jaraco.abode/.tox/python/lib/python3.12/site-packages/blib2to3/pgen2/pgen.py:42: EncodingWarning: 'encoding' argument not specified
  stream = open(filename)
tests/mock/devices/water_sensor.py ..

=================================== FAILURES ===================================
________________________ TestDevice.test_no_control_url ________________________

self = <jaraco.abode.devices.binary_sensor.Connectivity object at 0x7f4f1a304950>
name = '_control_url'

    def __getattr__(self, name):
        try:
>           return self._state[name]
E           KeyError: '_control_url'

jaraco/abode/state.py:19: KeyError

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

self = <tests.test_device.TestDevice object at 0x7f4f218a35c0>
m = <requests_mock.mocker.Mocker object at 0x7f4f1b7d3a40>

    def test_no_control_url(self, m):
        """Check that devices return false without control url's."""
        # Set up URLs
        m.post(urls.LOGIN, json=LOGIN.post_response_ok())
        m.get(urls.OAUTH_TOKEN, json=OAUTH_CLAIMS.get_response_ok())
        m.post(urls.LOGOUT, json=LOGOUT.post_response_ok())
        m.get(urls.PANEL, json=PANEL.get_response_ok())
    
        m.get(urls.DEVICES, json=GLASS.device(status=STATUS.ONLINE))
    
        # Logout to reset everything
        self.client.logout()
    
        # Get device
        device = self.client.get_device(GLASS.DEVICE_ID)
    
        assert device is not None
        with pytest.raises(jaraco.abode.Exception):
>           device.set_status(1)

tests/test_device.py:254: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jaraco/abode/devices/base.py:34: in set_status
    path=self._control_url,
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <jaraco.abode.devices.binary_sensor.Connectivity object at 0x7f4f1a304950>
name = '_control_url'

    def __getattr__(self, name):
        try:
            return self._state[name]
        except KeyError as exc:
>           raise AttributeError(name) from exc
E           AttributeError: _control_url

jaraco/abode/state.py:21: AttributeError

The failure mode in the repro isn't precisely the same, but it's close, and I suspect it will trace to the same cause.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions