File: virtual_methods.rst

package info (click to toggle)
pytest-qt 4.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 644 kB
  • sloc: python: 4,144; makefile: 139
file content (90 lines) | stat: -rw-r--r-- 3,016 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
Exceptions in virtual methods
=============================

.. versionadded:: 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:

.. code-block:: python

    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:

.. code-block:: python

    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:

.. code-block:: python

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

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

.. code-block:: ini

    [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.