pytest-dev / pytest-qt

pytest plugin for Qt (PyQt5/PyQt6 and PySide2/PySide6) application testing
https://pytest-qt.readthedocs.io
MIT License
401 stars 71 forks source link

SystemError on disconnect with pyside 6.7.1 #558

Closed tlambert03 closed 3 months ago

tlambert03 commented 3 months ago

Don't see an existing issue for this yet, so opening one. This seems related to https://github.com/pytest-dev/pytest-qt/issues/552, but has now turned into an exception with pyside 6.7.1:

from PySide6.QtWidgets import QLineEdit

def test_thing(qtbot):
    wdg = QLineEdit()
    qtbot.addWidget(wdg)
    with qtbot.waitSignal(wdg.textChanged):
        wdg.setText("hello")

running this test raises:

tests/test_thing.py F                                                    [100%]

=================================== FAILURES ===================================
__________________________________ test_thing __________________________________
CALL ERROR: Exceptions caught in Qt event loop:
________________________________________________________________________________
RuntimeWarning: Failed to disconnect (<bound method _AbstractSignalBlocker._quit_loop_by_timeout of <pytestqt.wait_signal.SignalBlocker object at 0x113115c50>>) from signal "timeout()".

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

Traceback (most recent call last):
  File "/Users/talley/miniforge3/envs/superqt/lib/python3.11/site-packages/pytestqt/wait_signal.py", line 219, in _quit_loop_by_signal
    self._cleanup()
  File "/Users/talley/miniforge3/envs/superqt/lib/python3.11/site-packages/pytestqt/wait_signal.py", line 224, in _cleanup
    super()._cleanup()
  File "/Users/talley/miniforge3/envs/superqt/lib/python3.11/site-packages/pytestqt/wait_signal.py", line 66, in _cleanup
    _silent_disconnect(self._timer.timeout, self._quit_loop_by_timeout)
  File "/Users/talley/miniforge3/envs/superqt/lib/python3.11/site-packages/pytestqt/wait_signal.py", line 741, in _silent_disconnect
    signal.disconnect(slot)
SystemError: <method 'disconnect' of 'PySide6.QtCore.SignalInstance' objects> returned a result with an exception set
________________________________________________________________________________
----------------------------- Captured stderr call -----------------------------
Exceptions caught in Qt event loop:
________________________________________________________________________________
RuntimeWarning: Failed to disconnect (<bound method _AbstractSignalBlocker._quit_loop_by_timeout of <pytestqt.wait_signal.SignalBlocker object at 0x113115c50>>) from signal "timeout()".

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

Traceback (most recent call last):
  File "/Users/talley/miniforge3/envs/superqt/lib/python3.11/site-packages/pytestqt/wait_signal.py", line 219, in _quit_loop_by_signal
    self._cleanup()
  File "/Users/talley/miniforge3/envs/superqt/lib/python3.11/site-packages/pytestqt/wait_signal.py", line 224, in _cleanup
    super()._cleanup()
  File "/Users/talley/miniforge3/envs/superqt/lib/python3.11/site-packages/pytestqt/wait_signal.py", line 66, in _cleanup
    _silent_disconnect(self._timer.timeout, self._quit_loop_by_timeout)
  File "/Users/talley/miniforge3/envs/superqt/lib/python3.11/site-packages/pytestqt/wait_signal.py", line 741, in _silent_disconnect
    signal.disconnect(slot)
SystemError: <method 'disconnect' of 'PySide6.QtCore.SignalInstance' objects> returned a result with an exception set
________________________________________________________________________________
=========================== short test summary info ============================
FAILED tests/test_thing.py::test_thing - Failed: CALL ERROR: Exceptions caught in Qt event loop:
============================== 1 failed in 0.17s ===============================

on Pyside 6.7.0, this worked but caused the RuntimeError warning as noted in #552.

tests/test_thing.py .                                                    [100%]

=============================== warnings summary ===============================
tests/test_thing.py::test_thing
  /Users/talley/miniforge3/envs/superqt/lib/python3.11/site-packages/pytestqt/wait_signal.py:741: RuntimeError: Failed to disconnect (<bound method _AbstractSignalBlocker._quit_loop_by_timeout of <pytestqt.wait_signal.SignalBlocker object at 0x108880d50>>) from signal "timeout()".
    signal.disconnect(slot)

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
========================= 1 passed, 1 warning in 0.52s =========================
nicoddemus commented 3 months ago

SystemError is often an internal error in the bindings... I would report this upstream, seems like a bug to me.

tlambert03 commented 3 months ago

k

tlambert03 commented 3 months ago

would it be trivial for you to suggest an MRE without using pytest-qt's _AbstractSignalBlocker? it's ok if not

nicoddemus commented 3 months ago

would it be trivial for you to suggest an MRE without using pytest-qt's _AbstractSignalBlocker? it's ok if not

I don't have the time to work on it right now, but I suspect if you just connect a signal and try to disconnect it twice you will get the SystemError.

nicoddemus commented 3 months ago

If you do report it upstream, please link it here for reference. :+1:

bersbersbers commented 3 months ago

PYSIDE-2705 suggests that the exception-like warning in 6.7.0 has been converted to an actual warning, compare https://codereview.qt-project.org/c/pyside/pyside-setup/+/558142/3/sources/pyside6/libpyside/pysidesignal.cpp. So there should be no more (unhandled) RuntimeError.

I was able to suppress these errors like this (pyproject.toml):

[tool.pytest.ini_options]
filterwarnings = [
    # https://github.com/pytest-dev/pytest-qt/issues/558
    "ignore:Failed to disconnect .* from signal:RuntimeWarning",
]

That gets rid of pytest reporting both the new RuntimeWarning as well as the underlying, and seemingly caught, SystemError.

tlambert03 commented 3 months ago

ahh, thank you for noting that. indeed I turn my warnings into exceptions, and I failed to note that this was just a warning and not an actual exception. adding that ignore works fine for my case as well. I'll close this issue, but @nicoddemus feel free to reopen if you want to track anything else related to this change