Skip to content

Commit 00447dd

Browse files
committed
dependencies: sever the dependency on packaging.version
Make qtpy more self-contained by implementing our own version `parse()` function instead of relying on `packaging.version`. This makes it `qtpy` completely independent so that it can be trivially vendored and used in environments where `packaging` is not installed on the user's machine.
1 parent bf77179 commit 00447dd

File tree

12 files changed

+76
-49
lines changed

12 files changed

+76
-49
lines changed

qtpy/QtCore.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
import contextlib
1111
from typing import TYPE_CHECKING
1212

13-
from packaging.version import parse
14-
1513
from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6
1614
from . import QT_VERSION as _qt_version
15+
from . import _parse_version
1716
from ._utils import possibly_static_exec, possibly_static_exec_
1817

1918
if PYQT5:
@@ -159,7 +158,7 @@
159158
)
160159

161160
# Passing as default value 0 in the same way PySide6 6.3.2 does for the `Qt.ItemFlags` definition.
162-
if parse(_qt_version) > parse("6.3"):
161+
if _parse_version(_qt_version) > _parse_version("6.3"):
163162
Qt.ItemFlags = lambda value=0: Qt.ItemFlag(value)
164163

165164
# For issue #153 and updated for issue #305

qtpy/QtGui.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010

1111
from functools import partialmethod
1212

13-
from packaging.version import parse
14-
15-
from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6
13+
from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, _parse_version
1614
from . import QT_VERSION as _qt_version
1715
from ._utils import (
1816
getattr_missing_optional_dep,
@@ -265,7 +263,7 @@ def movePositionPatched(
265263

266264

267265
# Make `QAction.setShortcut` and `QAction.setShortcuts` compatible with Qt>=6.4
268-
if PYQT5 or PYSIDE2 or parse(_qt_version) < parse("6.4"):
266+
if PYQT5 or PYSIDE2 or _parse_version(_qt_version) < _parse_version("6.4"):
269267

270268
class _QAction(QAction):
271269
old_set_shortcut = QAction.setShortcut

qtpy/QtWidgets.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@
99
"""Provides widget classes and functions."""
1010
from functools import partialmethod
1111

12-
from packaging.version import parse
13-
14-
from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6
12+
from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, _parse_version
1513
from . import QT_VERSION as _qt_version
1614
from ._utils import (
1715
add_action,
@@ -162,7 +160,7 @@ def __getattr__(name):
162160
)
163161

164162
# Passing as default value 0 in the same way PySide6 < 6.3.2 does for the `QFileDialog.Options` definition.
165-
if parse(_qt_version) > parse("6.3"):
163+
if _parse_version(_qt_version) > _parse_version("6.3"):
166164
QFileDialog.Options = lambda value=0: QFileDialog.Option(value)
167165

168166

@@ -212,7 +210,7 @@ def __getattr__(name):
212210
)
213211

214212
# Make `addAction` compatible with Qt6 >= 6.4
215-
if PYQT5 or PYSIDE2 or parse(_qt_version) < parse("6.4"):
213+
if PYQT5 or PYSIDE2 or _parse_version(_qt_version) < _parse_version("6.4"):
216214

217215
class _QMenu(QMenu):
218216
old_add_action = QMenu.addAction

qtpy/__init__.py

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@
6060
import sys
6161
import warnings
6262

63-
from packaging.version import parse
64-
6563
# Version of QtPy
6664
__version__ = "2.5.0.dev0"
6765

@@ -185,6 +183,20 @@ def __init__(self, *, missing_package=None, **superclass_kwargs):
185183
PYSIDE_VERSION = None
186184
QT_VERSION = None
187185

186+
187+
def _parse_int(value):
188+
"""Convert a value into an integer"""
189+
try:
190+
return int(value)
191+
except ValueError:
192+
return 0
193+
194+
195+
def _parse_version(version):
196+
"""Parse a version string into a tuple of ints"""
197+
return tuple(_parse_int(x) for x in version.split("."))
198+
199+
188200
# Unless `FORCE_QT_API` is set, use previously imported Qt Python bindings
189201
if not os.environ.get("FORCE_QT_API"):
190202
if "PyQt5" in sys.modules:
@@ -208,16 +220,20 @@ def __init__(self, *, missing_package=None, **superclass_kwargs):
208220
QT5 = PYQT5 = True
209221

210222
if sys.platform == "darwin":
211-
macos_version = parse(platform.mac_ver()[0])
212-
qt_ver = parse(QT_VERSION)
213-
if macos_version < parse("10.10") and qt_ver >= parse("5.9"):
223+
macos_version = _parse_version(platform.mac_ver()[0])
224+
qt_ver = _parse_version(QT_VERSION)
225+
if macos_version < _parse_version(
226+
"10.10"
227+
) and qt_ver >= _parse_version("5.9"):
214228
raise PythonQtError(
215229
"Qt 5.9 or higher only works in "
216230
"macOS 10.10 or higher. Your "
217231
"program will fail in this "
218232
"system.",
219233
)
220-
elif macos_version < parse("10.11") and qt_ver >= parse("5.11"):
234+
elif macos_version < _parse_version(
235+
"10.11"
236+
) and qt_ver >= _parse_version("5.11"):
221237
raise PythonQtError(
222238
"Qt 5.11 or higher only works in "
223239
"macOS 10.11 or higher. Your "
@@ -241,9 +257,11 @@ def __init__(self, *, missing_package=None, **superclass_kwargs):
241257
QT5 = PYSIDE2 = True
242258

243259
if sys.platform == "darwin":
244-
macos_version = parse(platform.mac_ver()[0])
245-
qt_ver = parse(QT_VERSION)
246-
if macos_version < parse("10.11") and qt_ver >= parse("5.11"):
260+
macos_version = _parse_version(platform.mac_ver()[0])
261+
qt_ver = _parse_version(QT_VERSION)
262+
if macos_version < _parse_version(
263+
"10.11"
264+
) and qt_ver >= _parse_version("5.11"):
247265
raise PythonQtError(
248266
"Qt 5.11 or higher only works in "
249267
"macOS 10.11 or higher. Your "
@@ -327,18 +345,28 @@ def _warn_old_minor_version(name, old_version, min_version):
327345

328346
# Warn if using an End of Life or unsupported Qt API/binding minor version
329347
if QT_VERSION:
330-
if QT5 and (parse(QT_VERSION) < parse(QT5_VERSION_MIN)):
348+
if QT5 and (_parse_version(QT_VERSION) < _parse_version(QT5_VERSION_MIN)):
331349
_warn_old_minor_version("Qt5", QT_VERSION, QT5_VERSION_MIN)
332-
elif QT6 and (parse(QT_VERSION) < parse(QT6_VERSION_MIN)):
350+
elif QT6 and (
351+
_parse_version(QT_VERSION) < _parse_version(QT6_VERSION_MIN)
352+
):
333353
_warn_old_minor_version("Qt6", QT_VERSION, QT6_VERSION_MIN)
334354

335355
if PYQT_VERSION:
336-
if PYQT5 and (parse(PYQT_VERSION) < parse(PYQT5_VERSION_MIN)):
356+
if PYQT5 and (
357+
_parse_version(PYQT_VERSION) < _parse_version(PYQT5_VERSION_MIN)
358+
):
337359
_warn_old_minor_version("PyQt5", PYQT_VERSION, PYQT5_VERSION_MIN)
338-
elif PYQT6 and (parse(PYQT_VERSION) < parse(PYQT6_VERSION_MIN)):
360+
elif PYQT6 and (
361+
_parse_version(PYQT_VERSION) < _parse_version(PYQT6_VERSION_MIN)
362+
):
339363
_warn_old_minor_version("PyQt6", PYQT_VERSION, PYQT6_VERSION_MIN)
340364
elif PYSIDE_VERSION:
341-
if PYSIDE2 and (parse(PYSIDE_VERSION) < parse(PYSIDE2_VERSION_MIN)):
365+
if PYSIDE2 and (
366+
_parse_version(PYSIDE_VERSION) < _parse_version(PYSIDE2_VERSION_MIN)
367+
):
342368
_warn_old_minor_version("PySide2", PYSIDE_VERSION, PYSIDE2_VERSION_MIN)
343-
elif PYSIDE6 and (parse(PYSIDE_VERSION) < parse(PYSIDE6_VERSION_MIN)):
369+
elif PYSIDE6 and (
370+
_parse_version(PYSIDE_VERSION) < _parse_version(PYSIDE6_VERSION_MIN)
371+
):
344372
_warn_old_minor_version("PySide6", PYSIDE_VERSION, PYSIDE6_VERSION_MIN)

qtpy/tests/test_qtconcurrent.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import pytest
2-
from packaging.version import parse
32

4-
from qtpy import PYSIDE2, PYSIDE_VERSION
3+
from qtpy import PYSIDE2, PYSIDE_VERSION, _parse_version
54
from qtpy.tests.utils import pytest_importorskip
65

76

@@ -11,7 +10,7 @@ def test_qtconcurrent():
1110

1211
assert QtConcurrent.QtConcurrent is not None
1312

14-
if PYSIDE2 and parse(PYSIDE_VERSION) >= parse("5.15.2"):
13+
if PYSIDE2 and _parse_version(PYSIDE_VERSION) >= _parse_version("5.15.2"):
1514
assert QtConcurrent.QFutureQString is not None
1615
assert QtConcurrent.QFutureVoid is not None
1716
assert QtConcurrent.QFutureWatcherQString is not None

qtpy/tests/test_qtcore.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from datetime import date, datetime, time
66

77
import pytest
8-
from packaging.version import parse
98

109
from qtpy import (
1110
PYQT5,
@@ -14,6 +13,7 @@
1413
PYSIDE2,
1514
PYSIDE_VERSION,
1615
QtCore,
16+
_parse_version,
1717
)
1818

1919
_now = datetime.now()
@@ -71,7 +71,7 @@ def test_qthread_exec():
7171

7272

7373
@pytest.mark.skipif(
74-
PYSIDE2 and parse(PYSIDE_VERSION) < parse("5.15"),
74+
PYSIDE2 and _parse_version(PYSIDE_VERSION) < _parse_version("5.15"),
7575
reason="QEnum macro doesn't seem to be present on PySide2 <5.15",
7676
)
7777
def test_qenum():

qtpy/tests/test_qtgui.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import sys
44

55
import pytest
6-
from packaging.version import parse
76

87
from qtpy import (
98
PYQT5,
@@ -14,6 +13,7 @@
1413
QtCore,
1514
QtGui,
1615
QtWidgets,
16+
_parse_version,
1717
)
1818
from qtpy.tests.utils import not_using_conda
1919

@@ -198,7 +198,7 @@ def test_QAction_functions(qtbot):
198198

199199

200200
@pytest.mark.skipif(
201-
parse(QT_VERSION) < parse("6.5.0"),
201+
_parse_version(QT_VERSION) < _parse_version("6.5.0"),
202202
reason="Qt6 >= 6.5 specific test",
203203
)
204204
@pytest.mark.skipif(

qtpy/tests/test_qttest.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import pytest
2-
from packaging import version
32

4-
from qtpy import PYQT5, PYQT6, PYQT_VERSION, PYSIDE6, QtTest
3+
from qtpy import PYQT5, PYQT6, PYQT_VERSION, PYSIDE6, QtTest, _parse_version
54

65

76
def test_qttest():
@@ -12,7 +11,7 @@ def test_qttest():
1211
assert QtTest.QSignalSpy is not None
1312

1413
if (
15-
(PYQT5 and version.parse(PYQT_VERSION) >= version.parse("5.11"))
14+
(PYQT5 and _parse_version(PYQT_VERSION) >= _parse_version("5.11"))
1615
or PYQT6
1716
or PYSIDE6
1817
):

qtpy/tests/test_qttexttospeech.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import pytest
2-
from packaging import version
32

4-
from qtpy import PYQT5, PYQT_VERSION, PYSIDE2
3+
from qtpy import PYQT5, PYQT_VERSION, PYSIDE2, _parse_version
54

65

76
@pytest.mark.skipif(
87
not (
9-
(PYQT5 and version.parse(PYQT_VERSION) >= version.parse("5.15.1"))
8+
(PYQT5 and _parse_version(PYQT_VERSION) >= _parse_version("5.15.1"))
109
or PYSIDE2
1110
),
1211
reason="Only available in Qt5 bindings (PyQt5 >= 5.15.1 or PySide2)",

qtpy/tests/test_qtwebenginewidgets.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
import pytest
2-
from packaging import version
32

4-
from qtpy import PYQT5, PYQT6, PYQT_VERSION, PYSIDE2, PYSIDE6, PYSIDE_VERSION
3+
from qtpy import (
4+
PYQT5,
5+
PYQT6,
6+
PYQT_VERSION,
7+
PYSIDE2,
8+
PYSIDE6,
9+
PYSIDE_VERSION,
10+
_parse_version,
11+
)
512
from qtpy.tests.utils import pytest_importorskip
613

714

815
@pytest.mark.skipif(
916
not (
10-
(PYQT6 and version.parse(PYQT_VERSION) >= version.parse("6.2"))
11-
or (PYSIDE6 and version.parse(PYSIDE_VERSION) >= version.parse("6.2"))
17+
(PYQT6 and _parse_version(PYQT_VERSION) >= _parse_version("6.2"))
18+
or (PYSIDE6 and _parse_version(PYSIDE_VERSION) >= _parse_version("6.2"))
1219
or PYQT5
1320
or PYSIDE2
1421
),

0 commit comments

Comments
 (0)