Skip to content

Commit 2eda62b

Browse files
authored
Drop support for PySide2 (#583)
PySide2 is no longer maintained, with the last release being made in 2022.
1 parent 47cd6bf commit 2eda62b

File tree

11 files changed

+31
-84
lines changed

11 files changed

+31
-84
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,8 @@ jobs:
3030

3131
matrix:
3232
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
33-
qt-lib: [pyqt5, pyqt6, pyside2, pyside6]
33+
qt-lib: [pyqt5, pyqt6, pyside6]
3434
os: [ubuntu-latest, windows-latest, macos-latest]
35-
exclude:
36-
# Not installable:
37-
# ERROR: Could not find a version that satisfies the requirement pyside2 (from versions: none)
38-
- python-version: "3.11"
39-
qt-lib: pyside2
40-
os: windows-latest
41-
- python-version: "3.12"
42-
qt-lib: pyside2
43-
- python-version: "3.13"
44-
qt-lib: pyside2
45-
- qt-lib: pyside2
46-
os: macos-latest
4735

4836
steps:
4937
- uses: actions/checkout@v4

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ UNRELEASED
33

44
* Added official support for Python 3.13.
55
* Dropped support for EOL Python 3.8.
6+
* Dropped support for EOL PySide 2.
67

78
4.4.0 (2024-02-07)
89
------------------

README.rst

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pytest-qt
33
=========
44

55
pytest-qt is a `pytest`_ plugin that allows programmers to write tests
6-
for `PyQt5`_, `PyQt6`_, `PySide2`_ and `PySide6`_ applications.
6+
for `PyQt5`_, `PyQt6`_, and `PySide6`_ applications.
77

88
The main usage is to use the ``qtbot`` fixture, responsible for handling ``qApp``
99
creation as needed and provides methods to simulate user interaction,
@@ -22,7 +22,6 @@ like key presses and mouse clicks:
2222
assert widget.greet_label.text() == "Hello!"
2323
2424
25-
.. _PySide2: https://pypi.org/project/PySide2/
2625
.. _PySide6: https://pypi.org/project/PySide6/
2726
.. _PyQt5: https://pypi.org/project/PyQt5/
2827
.. _PyQt6: https://pypi.org/project/PyQt6/
@@ -74,24 +73,23 @@ Features
7473
Requirements
7574
============
7675

77-
Works with either PySide6_, PySide2_, PyQt6_ or PyQt5_.
76+
Works with either PySide6_, PyQt6_ or PyQt5_.
7877

7978
If any of the above libraries is already imported by the time the tests execute, that library will be used.
8079

8180
If not, pytest-qt will try to import and use the Qt APIs, in this order:
8281

8382
- ``PySide6``
84-
- ``PySide2``
8583
- ``PyQt6``
8684
- ``PyQt5``
8785

8886
To force a particular API, set the configuration variable ``qt_api`` in your ``pytest.ini`` file to
89-
``pyside6``, ``pyside2``, ``pyqt6`` or ``pyqt5``:
87+
``pyside6``, ``pyqt6`` or ``pyqt5``:
9088

9189
.. code-block:: ini
9290
9391
[pytest]
94-
qt_api=pyqt5
92+
qt_api=pyqt6
9593
9694
9795
Alternatively, you can set the ``PYTEST_QT_API`` environment
@@ -144,7 +142,7 @@ Running tests
144142

145143
Tests are run using `tox`_::
146144

147-
$ tox -e py37-pyside2,py37-pyqt5
145+
$ tox -e py-pyside6,py-pyqt5
148146

149147
``pytest-qt`` is formatted using `black <https://github.com/ambv/black>`_ and uses
150148
`pre-commit <https://github.com/pre-commit/pre-commit>`_ for linting checks before commits. You

docs/intro.rst

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pytest-qt
33
=========
44

55
pytest-qt is a `pytest`_ plugin that allows programmers to write tests
6-
for `PyQt5`_, `PyQt6`_, `PySide2`_ and `PySide6`_ applications.
6+
for `PyQt5`_, `PyQt6`_, and `PySide6`_ applications.
77

88
The main usage is to use the ``qtbot`` fixture, responsible for handling ``qApp``
99
creation as needed, and registering widgets for testing:
@@ -21,7 +21,6 @@ creation as needed, and registering widgets for testing:
2121
assert widget.greet_label.text() == "Hello!"
2222
2323
24-
.. _PySide2: https://pypi.org/project/PySide2/
2524
.. _PySide6: https://pypi.org/project/PySide6/
2625
.. _PyQt5: https://pypi.org/project/PyQt5/
2726
.. _PyQt6: https://pypi.org/project/PyQt6/
@@ -75,17 +74,16 @@ Requirements
7574

7675
``pytest-qt`` requires Python 3.7+.
7776

78-
Works with either PySide6_, PySide2_, PyQt6_ or PyQt5_, picking whichever
77+
Works with either PySide6_, PyQt6_ or PyQt5_, picking whichever
7978
is available on the system, giving preference to the first one installed in
8079
this order:
8180

8281
- ``PySide6``
83-
- ``PySide2``
8482
- ``PyQt6``
8583
- ``PyQt5``
8684

8785
To force a particular API, set the configuration variable ``qt_api`` in your ``pytest.ini`` file to
88-
``pyside6``, ``pyside2``, ``pyqt6`` or ``pyqt5``:
86+
``pyside6``, ``pyqt6`` or ``pyqt5``:
8987

9088
.. code-block:: ini
9189

src/pytestqt/plugin.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,7 @@ def qtmodeltester(request):
125125

126126

127127
def pytest_addoption(parser):
128-
parser.addini(
129-
"qt_api", 'Qt api version to use: "pyside6" , "pyside2", "pyqt6", "pyqt5"'
130-
)
128+
parser.addini("qt_api", 'Qt api version to use: "pyside6" , "pyqt6", "pyqt5"')
131129
parser.addini("qt_no_exception_capture", "disable automatic exception capture")
132130
parser.addini(
133131
"qt_default_raising",

src/pytestqt/qt_compat.py

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
22
Provide a common way to import Qt classes used by pytest-qt in a unique manner,
3-
abstracting API differences between PyQt5/6 and PySide2/6.
3+
abstracting API differences between PyQt5/6 and PySide6.
44
55
.. note:: This module is not part of pytest-qt public API, hence its interface
66
may change between releases and users should not rely on it.
@@ -19,7 +19,6 @@
1919

2020
QT_APIS = OrderedDict()
2121
QT_APIS["pyside6"] = "PySide6"
22-
QT_APIS["pyside2"] = "PySide2"
2322
QT_APIS["pyqt6"] = "PyQt6"
2423
QT_APIS["pyqt5"] = "PyQt5"
2524

@@ -85,7 +84,7 @@ def set_qt_api(self, api):
8584
or self._guess_qt_api()
8685
)
8786

88-
self.is_pyside = self.pytest_qt_api in ["pyside2", "pyside6"]
87+
self.is_pyside = self.pytest_qt_api in ["pyside6"]
8988
self.is_pyqt = self.pytest_qt_api in ["pyqt5", "pyqt6"]
9089

9190
if not self.pytest_qt_api: # pragma: no cover
@@ -94,7 +93,7 @@ def set_qt_api(self, api):
9493
for module, reason in sorted(self._import_errors.items())
9594
)
9695
msg = (
97-
"pytest-qt requires either PySide2, PySide6, PyQt5 or PyQt6 installed.\n"
96+
"pytest-qt requires either PySide6, PyQt5 or PyQt6 installed.\n"
9897
+ errors
9998
)
10099
raise pytest.UsageError(msg)
@@ -112,7 +111,7 @@ def _import_module(module_name):
112111

113112
self._check_qt_api_version()
114113

115-
# qInfo is not exposed in PySide2/6 (#232)
114+
# qInfo is not exposed in PySide6 (#232)
116115
if hasattr(QtCore, "QMessageLogger"):
117116
self.qInfo = lambda msg: QtCore.QMessageLogger().info(msg)
118117
elif hasattr(QtCore, "qInfo"):
@@ -151,8 +150,8 @@ def _check_qt_api_version(self):
151150
)
152151

153152
def exec(self, obj, *args, **kwargs):
154-
# exec was a keyword in Python 2, so PySide2 (and also PySide6 6.0)
155-
# name the corresponding method "exec_" instead.
153+
# exec was a keyword in Python 2, so PySide6 6.0
154+
# names the corresponding method "exec_" instead.
156155
#
157156
# The old _exec() alias is removed in PyQt6 and also deprecated as of
158157
# PySide 6.1:
@@ -170,14 +169,6 @@ def get_versions(self):
170169
return VersionTuple(
171170
"PySide6", version, self.QtCore.qVersion(), self.QtCore.__version__
172171
)
173-
elif self.pytest_qt_api == "pyside2":
174-
import PySide2
175-
176-
version = PySide2.__version__
177-
178-
return VersionTuple(
179-
"PySide2", version, self.QtCore.qVersion(), self.QtCore.__version__
180-
)
181172
elif self.pytest_qt_api == "pyqt6":
182173
return VersionTuple(
183174
"PyQt6",

tests/test_basics.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ def test_parse_ini_boolean_invalid():
438438
pytestqt.qtbot._parse_ini_boolean("foo")
439439

440440

441-
@pytest.mark.parametrize("option_api", ["pyqt5", "pyqt6", "pyside2", "pyside6"])
441+
@pytest.mark.parametrize("option_api", ["pyqt5", "pyqt6", "pyside6"])
442442
def test_qt_api_ini_config(testdir, monkeypatch, option_api):
443443
"""
444444
Test qt_api ini option handling.
@@ -479,7 +479,7 @@ def test_foo(qtbot):
479479
result.stderr.fnmatch_lines(["*ModuleNotFoundError:*"])
480480

481481

482-
@pytest.mark.parametrize("envvar", ["pyqt5", "pyqt6", "pyside2", "pyside6"])
482+
@pytest.mark.parametrize("envvar", ["pyqt5", "pyqt6", "pyside6"])
483483
def test_qt_api_ini_config_with_envvar(testdir, monkeypatch, envvar):
484484
"""ensure environment variable wins over config value if both are present"""
485485
testdir.makeini(
@@ -586,10 +586,9 @@ def _fake_is_library_loaded(name, *args):
586586
monkeypatch.setattr(qt_compat, "_is_library_loaded", _fake_is_library_loaded)
587587

588588
expected = (
589-
"pytest-qt requires either PySide2, PySide6, PyQt5 or PyQt6 installed.\n"
589+
"pytest-qt requires either PySide6, PyQt5 or PyQt6 installed.\n"
590590
" PyQt5.QtCore: Failed to import PyQt5.QtCore\n"
591591
" PyQt6.QtCore: Failed to import PyQt6.QtCore\n"
592-
" PySide2.QtCore: Failed to import PySide2.QtCore\n"
593592
" PySide6.QtCore: Failed to import PySide6.QtCore"
594593
)
595594

@@ -602,7 +601,6 @@ def _fake_is_library_loaded(name, *args):
602601
[
603602
("pyqt5", "PyQt5"),
604603
("pyqt6", "PyQt6"),
605-
("pyside2", "PySide2"),
606604
("pyside6", "PySide6"),
607605
],
608606
)

tests/test_logging.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def test_qinfo(qtlog):
7575
if qt_api.is_pyside:
7676
assert (
7777
qt_api.qInfo is None
78-
), "pyside2/6 does not expose qInfo. If it does, update this test."
78+
), "pyside6 does not expose qInfo. If it does, update this test."
7979
return
8080

8181
qt_api.qInfo("this is an INFO message")

tests/test_modeltest.py

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import sys
2-
31
import pytest
42

53
from pytestqt.qt_compat import qt_api
@@ -113,33 +111,12 @@ def data(
113111
check_model(BrokenTypeModel(), should_pass=False)
114112

115113

116-
def check_broken_flag_or():
117-
flag = qt_api.QtCore.Qt.AlignmentFlag
118-
try:
119-
int(flag.AlignHorizontal_Mask | flag.AlignVertical_Mask)
120-
except SystemError:
121-
# Should not be happening anywhere else
122-
assert sys.version_info[:2] == (3, 11) and qt_api.pytest_qt_api == "pyside2"
123-
return True
124-
return False
125-
126-
127-
xfail_py311_pyside2 = pytest.mark.xfail(
128-
check_broken_flag_or(),
129-
reason="Fails to OR mask flags",
130-
)
131-
132-
133114
@pytest.mark.parametrize(
134115
"role_value, should_pass",
135116
[
136-
pytest.param(
137-
qt_api.QtCore.Qt.AlignmentFlag.AlignLeft, True, marks=xfail_py311_pyside2
138-
),
139-
pytest.param(
140-
qt_api.QtCore.Qt.AlignmentFlag.AlignRight, True, marks=xfail_py311_pyside2
141-
),
142-
pytest.param(0xFFFFFF, False, marks=xfail_py311_pyside2),
117+
pytest.param(qt_api.QtCore.Qt.AlignmentFlag.AlignLeft, True),
118+
pytest.param(qt_api.QtCore.Qt.AlignmentFlag.AlignRight, True),
119+
pytest.param(0xFFFFFF, False),
143120
("foo", False),
144121
(object(), False),
145122
],

tests/test_wait_signal.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,7 @@ def cb(str_param, int_param):
881881
def get_mixed_signals_with_guaranteed_name(signaller):
882882
"""
883883
Returns a list of signals with the guarantee that the signals have names (i.e. the names are
884-
manually provided in case of using PySide2, where the signal names cannot be determined at run-time).
884+
manually provided in case of using PySide6, where the signal names cannot be determined at run-time).
885885
"""
886886
if qt_api.is_pyside:
887887
signals = [
@@ -918,9 +918,9 @@ def test_empty_when_no_signal_name_available(self, qtbot, signaller):
918918
Tests that all_signals_and_args is empty even though expected signals are emitted, but signal names aren't
919919
available.
920920
"""
921-
if qt_api.pytest_qt_api != "pyside2":
921+
if qt_api.pytest_qt_api != "pyside6":
922922
pytest.skip(
923-
"test only makes sense for PySide2, whose signals don't contain a name!"
923+
"test only makes sense for PySide6, whose signals don't contain a name"
924924
)
925925

926926
with qtbot.waitSignals(
@@ -1198,13 +1198,13 @@ def test_strict_order_violation(self, qtbot, signaller):
11981198

11991199
def test_degenerate_error_msg(self, qtbot, signaller):
12001200
"""
1201-
Tests that the TimeoutError message is degenerate when using PySide2 signals for which no name is provided
1201+
Tests that the TimeoutError message is degenerate when using PySide6 signals for which no name is provided
12021202
by the user. This degenerate messages doesn't contain the signals' names, and includes a hint to the user how
12031203
to fix the situation.
12041204
"""
1205-
if qt_api.pytest_qt_api != "pyside2":
1205+
if qt_api.pytest_qt_api != "pyside6":
12061206
pytest.skip(
1207-
"test only makes sense for PySide, whose signals don't contain a name!"
1207+
"test only makes sense for PySide, whose signals don't contain a name"
12081208
)
12091209

12101210
with pytest.raises(TimeoutError) as excinfo:

tox.ini

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
[tox]
2-
envlist = py{39,310,311,312,313}-{pyqt5,pyside2,pyside6,pyqt6}
2+
envlist = py{39,310,311,312,313}-{pyqt5,pyside6,pyqt6}
33

44
[testenv]
55
deps=
66
pytest
77
pyside6: pyside6
8-
pyside2: pyside2
98
pyqt5: pyqt5
109
pyqt6: pyqt6
1110
commands=
1211
pytest --color=yes {posargs}
1312
setenv=
1413
pyside6: PYTEST_QT_API=pyside6
15-
pyside2: PYTEST_QT_API=pyside2
1614
pyqt5: PYTEST_QT_API=pyqt5
1715
pyqt6: PYTEST_QT_API=pyqt6
1816
QT_QPA_PLATFORM=offscreen

0 commit comments

Comments
 (0)