Exceptions in virtual methods

New in version 1.1.

Note

PySide6 6.5.2+ automatically captures exceptions that happen during the Qt event loop and re-raises them when control is moved back to Python, so the functionality described here does not work with PySide6 (nor is necessary).

It is common in Qt programming to override virtual C++ methods to customize behavior, like listening for mouse events, implement drawing routines, etc.

Fortunately, all Python bindings for Qt support overriding these virtual methods naturally in your Python code:

class MyWidget(QWidget):
    # mouseReleaseEvent
    def mouseReleaseEvent(self, ev):
        print(f"mouse released at: {ev.pos()}")

In PyQt5 and PyQt6, exceptions in virtual methods will by default call abort(), which will crash the interpreter. All other Qt wrappers will print the exception stacktrace and return a default value back to C++/Qt (if a return value is required).

This might be surprising for Python users which are used to exceptions being raised at the calling point: For example, the following code will just print a stack trace without raising any exception:

class MyWidget(QWidget):
    def mouseReleaseEvent(self, ev):
        raise RuntimeError("unexpected error")


w = MyWidget()
QTest.mouseClick(w, QtCore.Qt.LeftButton)

To make testing Qt code less surprising, pytest-qt automatically installs an exception hook which captures errors and fails tests when exceptions are raised inside virtual methods, like this:

E           Failed: Qt exceptions in virtual methods:
E           ________________________________________________________________________________
E             File "x:\pytest-qt\pytestqt\_tests\test_exceptions.py", line 14, in event
E               raise RuntimeError('unexpected error')
E
E           RuntimeError: unexpected error

Disabling the automatic exception hook

You can disable the automatic exception hook on individual tests by using a qt_no_exception_capture marker:

@pytest.mark.qt_no_exception_capture
def test_buttons(qtbot):
    ...

Or even disable it for your entire project in your pytest.ini file:

[pytest]
qt_no_exception_capture = 1

This might be desirable if you plan to install a custom exception hook.

Note

Starting with PyQt5.5, exceptions raised during virtual methods will actually trigger an abort(), crashing the Python interpreter. For this reason, disabling exception capture in PyQt5.5+ and PyQt6 is not recommended unless you install your own exception hook.

Note

As explained in the note at the top of the page, PySide6 6.5.2+ has its own exception capture mechanism, so this option has no effect when using this library.