Skip to content

Commit d7ff724

Browse files
authored
PR: Test using PyQt extra packages (#446)
2 parents 354461c + f81a0b3 commit d7ff724

File tree

5 files changed

+170
-57
lines changed

5 files changed

+170
-57
lines changed

.github/workflows/ci.yml

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ jobs:
3838
PYSIDE6_VERSION: ${{ matrix.pyside6-version || matrix.qt6-version-default }}
3939
PYSIDE6_QT_VERSION: ${{ matrix.pyside6-qt-version || matrix.pyside6-version || matrix.qt6-version-default }}
4040
QSCINTILLA_VERSION: ${{ matrix.qscintilla-version || matrix.qscintilla-version-default }}
41+
PYQT_EXTRAS: ${{ matrix.pyqt-extras || matrix.pyqt-extras-default }}
4142
SKIP_PIP_CHECK: ${{ matrix.skip-pip-check }}
4243
strategy:
4344
fail-fast: false
@@ -48,31 +49,34 @@ jobs:
4849
qt5-version-default: ['5.12']
4950
qt6-version-default: ['6.3']
5051
qscintilla-version-default: ['2.13']
52+
pyqt-extras-default: ['No']
5153
include:
5254
- os: ubuntu-latest
5355
special-invocation: 'xvfb-run --auto-servernum ' # Needed for GUI tests to work
5456
- python-version: '3.11'
5557
pyqt5-version: '5.15' # Python 3.11 needs 5.15+
5658
pyside2-version: '5.15' # Python 3.11 needs 5.15+
57-
pyside6-version: '6.4' # Python 3.11 needs 6.4+
59+
pyside6-version: '6.5' # Python 3.11 needs 6.4+. Test upper bound
5860
- use-conda: 'Yes'
5961
skip-pyqt6: true # No PyQt6 conda packages yet
60-
pyside6-version: '6.4' # Conda only has 6.4+ for Python <3.8
6162
- use-conda: 'No'
6263
pyqt5-version: '5.15' # Test with latest optional packages
6364
- python-version: '3.7'
6465
use-conda: 'Yes'
6566
pyside2-version: '5.13' # Conda needs 5.13+ to work reliably
6667
pyside2-qt-version: '5.12' # Conda only has 5.12 and 5.15, not 5.13
68+
pyside6-version: '6.4' # Conda only has 6.4 for Python <3.8
6769
- python-version: '3.11'
6870
use-conda: 'No'
71+
pyqt-extras: 'Yes' # Check PyQt extras
6972
skip-pyside2: true # Pyside2 wheels don't support Python 3.11+
73+
pyqt6-version: '6.5' # Test upper bound
7074
pyside6-version: '6.5' # Test upper bound
7175
- os: windows-latest
7276
python-version: '3.7'
7377
use-conda: 'Yes'
7478
pyqt5-version: '5.9' # Test lower bound
75-
skip-pyside6: true # Test hangs
79+
skip-pyside6: true # Test hangs with 6.4. 6.5 is not available for Python 3.7
7680
- os: windows-latest
7781
python-version: '3.7'
7882
use-conda: 'No'
@@ -81,13 +85,40 @@ jobs:
8185
- os: windows-latest
8286
python-version: '3.11'
8387
use-conda: 'Yes'
84-
skip-pyside6: true # Test hangs
88+
pyside6-version: 6.5 # Test upper bound
8589
- os: macos-latest
86-
python-version: '3.7'
90+
python-version: '3.11'
8791
use-conda: 'No'
8892
pyqt6-version: 6.5 # Test upper bound
8993
pyside2-version: 5.15 # Test upper bound
9094
steps:
95+
- name: Check job values
96+
run: |
97+
echo "---- General setup"
98+
echo "OS:" ${{ matrix.os }}
99+
echo "PYTHON_VERSION:" ${{ env.PYTHON_VERSION }}
100+
echo "USE_CONDA:" ${{ env.USE_CONDA }}
101+
echo "---- PyQt"
102+
echo "PYQT_EXTRAS:" ${{ env.PYQT_EXTRAS }}
103+
echo "---- PyQt5"
104+
echo "SKIP_PYQT5:" ${{ matrix.skip-pyqt5 }}
105+
echo "PYQT5_VERSION:" ${{ env.PYQT5_VERSION }}
106+
echo "PYQT5_QT_VERSION:" ${{ env.PYQT5_QT_VERSION }}
107+
echo "QSCINTILLA_VERSION:" ${{ env.QSCINTILLA_VERSION }}
108+
echo "---- PyQt6"
109+
echo "SKIP_PYQT6:" ${{ matrix.skip-pyqt6 }}
110+
echo "PYQT6_VERSION:" ${{ env.PYQT6_VERSION }}
111+
echo "PYQT6_QT_VERSION:" ${{ env.PYQT6_QT_VERSION }}
112+
echo "---- PySide2"
113+
echo "SKIP_PYSIDE2:" ${{ matrix.skip-pyside2 }}
114+
echo "PYSIDE2_VERSION:" ${{ env.PYSIDE2_VERSION }}
115+
echo "PYSIDE2_QT_VERSION:" ${{ env.PYSIDE2_QT_VERSION }}
116+
echo "---- PySide6"
117+
echo "SKIP_PYSIDE6:" ${{ matrix.skip-pyside6 }}
118+
echo "PYSIDE6_VERSION:" ${{ env.PYSIDE6_VERSION }}
119+
echo "PYSIDE6_QT_VERSION:" ${{ env.PYSIDE6_QT_VERSION }}
120+
echo "---- Other"
121+
echo "SKIP_PIP_CHECK:" ${{ env.SKIP_PIP_CHECK }}
91122
- name: Checkout branch
92123
uses: actions/checkout@v3
93124
- name: Setup Python

.github/workflows/test.sh

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,39 @@ conda activate test-env-${BINDING}
3434
if [ "$USE_CONDA" = "No" ]; then
3535

3636
if [ "${1}" = "pyqt5" ]; then
37-
pip install pyqt5==${PYQT5_VERSION}.* PyQtWebEngine==${PYQT5_VERSION}.* QScintilla==${QSCINTILLA_VERSION}.*
37+
38+
if [ "$PYQT_EXTRAS" = "Yes" ]; then
39+
pip install pyqt5==${PYQT5_VERSION}.* \
40+
PyQtWebEngine==${PYQT5_VERSION}.* \
41+
QScintilla==${QSCINTILLA_VERSION}.* \
42+
PyQt3D==${PYQT5_VERSION}.* \
43+
PyQtChart==${PYQT5_VERSION}.* \
44+
PyQtDataVisualization==${PYQT5_VERSION}.* \
45+
PyQtNetworkAuth==${PYQT5_VERSION}.* \
46+
PyQtPurchasing==${PYQT5_VERSION}.*
47+
else
48+
pip install pyqt5==${PYQT5_VERSION}.* \
49+
PyQtWebEngine==${PYQT5_VERSION}.* \
50+
QScintilla==${QSCINTILLA_VERSION}.*
51+
fi
52+
3853
elif [ "${1}" = "pyqt6" ]; then
39-
pip install pyqt6==${PYQT6_VERSION}.* PyQt6-WebEngine==${PYQT6_VERSION}.* PyQt6-Qt6==${PYQT6_QT_VERSION}.* PyQt6-QScintilla==${QSCINTILLA_VERSION}.*
54+
55+
if [ "$PYQT_EXTRAS" = "Yes" ]; then
56+
pip install pyqt6==${PYQT6_VERSION}.* \
57+
PyQt6-WebEngine==${PYQT6_VERSION}.* \
58+
PyQt6-Qt6==${PYQT6_QT_VERSION}.* \
59+
PyQt6-QScintilla \
60+
PyQt6-3D==${PYQT6_VERSION}.* \
61+
PyQt6-Charts==${PYQT6_VERSION}.* \
62+
PyQt6-DataVisualization==${PYQT6_VERSION}.* \
63+
PyQt6-NetworkAuth==${PYQT6_VERSION}.*
64+
else
65+
pip install pyqt6==${PYQT6_VERSION}.* \
66+
PyQt6-WebEngine==${PYQT6_VERSION}.* \
67+
PyQt6-Qt6==${PYQT6_QT_VERSION}.*
68+
fi
69+
4070
elif [ "${1}" = "pyside2" ]; then
4171
pip install pyside2==${PYSIDE2_VERSION}.*
4272
elif [ "${1}" = "pyside6" ]; then

README.md

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010
[![Github build status](https://github.com/spyder-ide/qtpy/workflows/Tests/badge.svg)](https://github.com/spyder-ide/qtpy/actions)
1111
[![Coverage Status](https://coveralls.io/repos/github/spyder-ide/qtpy/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/qtpy?branch=master)
1212

13-
*Copyright © 2009–2022 The Spyder Development Team*
13+
*Copyright © 2009– The Spyder Development Team*
1414

1515

1616
## Description
1717

1818
**QtPy** is a small abstraction layer that lets you
1919
write applications using a single API call to either PyQt or PySide.
2020

21-
It provides support for PyQt5, PyQt6, PySide6, PySide2 using the Qt5 layout
21+
It provides support for PyQt5, PySide2, PyQt6 and PySide6 using the Qt5 layout
2222
(where the QtGui module has been split into QtGui and QtWidgets).
2323

2424
Basically, you can write your code as if you were using PyQt or PySide directly,
@@ -41,7 +41,7 @@ to a particular project or namespace.
4141

4242
### License
4343

44-
This project is released under the MIT license.
44+
This project is released under the [MIT license](LICENSE.txt).
4545

4646

4747
### Requirements
@@ -107,10 +107,12 @@ conda install qtpy
107107
Type checkers have no knowledge of installed packages, so these tools require
108108
additional configuration.
109109

110+
A Command Line Interface (CLI) is offered to help with usage of QtPy (to get MyPy
111+
and Pyright/Pylance args/configurations).
112+
110113
#### Mypy
111114

112-
A Command Line Interface (CLI) is offered to help with usage of QtPy.
113-
Presently, its only feature is to generate command line arguments for Mypy
115+
The `mypy-args` command helps you to generate command line arguments for Mypy
114116
that will enable it to process the QtPy source files with the same API
115117
as QtPy itself would have selected.
116118

@@ -139,8 +141,11 @@ mypy --package mypackage $(qtpy mypy-args)
139141

140142
#### Pyright/Pylance
141143

142-
Instead of runtime arguments, it is required to create a config file for the project,
143-
called `pyrightconfig.json` or a `pyright` section in `pyproject.toml`. See [here](https://github.com/microsoft/pyright/blob/main/docs/configuration.md) for reference.
144+
In the case of Pyright, instead of runtime arguments, it is required to create a
145+
config file for the project, called `pyrightconfig.json` or a `pyright` section
146+
in `pyproject.toml`. See [here](https://github.com/microsoft/pyright/blob/main/docs/configuration.md)
147+
for reference. In order to set this configuration, QtPy offers the `pyright-config`
148+
command for guidance.
144149

145150
If you run
146151

@@ -149,14 +154,63 @@ qtpy pyright-config
149154
```
150155

151156
you will get the necessary configs to be included in your project files. If you don't
152-
have them, it is recommended to create the latter.
157+
have them, it is recommended to create the latter. For example, in an environment where PyQt5
158+
is installed and selected (or the default fallback, if no binding can be found in the
159+
environment), this would output the following:
160+
161+
```text
162+
pyrightconfig.json:
163+
{"defineConstant": {"PYQT5": true, "PYSIDE2": false, "PYQT6": false, "PYSIDE6": false}}
164+
165+
pyproject.toml:
166+
[tool.pyright.defineConstant]
167+
PYQT5 = true
168+
PYSIDE2 = false
169+
PYQT6 = false
170+
PYSIDE6 = false
171+
```
172+
173+
**Note**: These configurations are necessary for the correct usage of the default VSCode's type
174+
checking feature while using QtPy in your source code.
175+
176+
177+
## Testing matrix
178+
179+
Currently, QtPy runs tests for different bindings on Linux, Windows and macOS, using
180+
Python 3.7 and 3.11, and installing those bindings with `conda` and `pip`. For the
181+
PyQt bindings, we also check the installation of extra packages via `pip`.
182+
183+
Following this, the current test matrix looks something like this:
184+
185+
| | Python | 3.7 | | 3.11 | |
186+
|---------|-----------------|--------------------------------------------|------|--------------------|----------------------------|
187+
| OS | Binding / manager | conda | pip | conda | pip |
188+
| Linux | PyQt5 | 5.12 | 5.15 | 5.15 | 5.15 (with extras) |
189+
| | PyQt6 | skip (unavailable) | 6.3 | skip (unavailable) | 6.5 (with extras) |
190+
| | PySide2 | 5.13 | 5.12 | 5.15 | skip (no wheels available) |
191+
| | PySide6 | 6.4 | 6.3 | 6.5 | 6.5 |
192+
| Windows | PyQt5 | 5.9 | 5.15 | 5.15 | 5.15 (with extras) |
193+
| | PyQt6 | skip (unavailable) | 6.2 | skip (unavailable) | 6.5 (with extras) |
194+
| | PySide2 | 5.13 | 5.12 | 5.15 | skip (no wheels available) |
195+
| | PySide6 | skip (test hang with 6.4. 6.5 unavailable) | 6.2 | 6.5 | 6.5 |
196+
| MacOS | PyQt5 | 5.12 | 5.15 | 5.15 | 5.15 (with extras) |
197+
| | PyQt6 | skip (unavailable) | 6.3 | skip (unavailable) | 6.5 (with extras) |
198+
| | PySide2 | 5.13 | 5.12 | 5.15 | skip (no wheels available) |
199+
| | PySide6 | 6.4 | 6.3 | 6.5 | 6.5 |
153200

154-
These steps are necessary for running the default VSCode's type checking.
201+
**Note**: The mentioned extra packages for the PyQt bindings are the following:
155202

203+
* `PyQt3D` and `PyQt6-3D`
204+
* `PyQtChart` and `PyQt6-Charts`
205+
* `PyQtDataVisualization` and `PyQt6-DataVisualization`
206+
* `PyQtNetworkAuth` and `PyQt6-NetworkAuth`
207+
* `PyQtPurchasing`
208+
* `PyQtWebEngine` and `PyQt6-WebEngine`
209+
* `QScintilla` and `PyQt6-QScintilla`
156210

157211
## Contributing
158212

159-
Everyone is welcome to contribute!
213+
Everyone is welcome to contribute! See our [Contributing guide](CONTRIBUTING.md) for more details.
160214

161215

162216
## Sponsors

qtpy/tests/test_qtdatavisualization.py

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -46,40 +46,41 @@ def test_qtdatavisualization():
4646
assert QtDataVisualization.QLogValue3DAxisFormatter is not None
4747

4848
# QtDatavisualization
49-
QtDatavisualization = pytest.importorskip("qtpy.QtDatavisualization")
49+
# import qtpy to get alias for `QtDataVisualization` with lower `v`
50+
qtpy = pytest.importorskip("qtpy")
5051

51-
assert QtDatavisualization.QScatter3DSeries is not None
52-
assert QtDatavisualization.QSurfaceDataItem is not None
53-
assert QtDatavisualization.QSurface3DSeries is not None
54-
assert QtDatavisualization.QAbstract3DInputHandler is not None
55-
assert QtDatavisualization.QHeightMapSurfaceDataProxy is not None
56-
assert QtDatavisualization.QAbstractDataProxy is not None
57-
assert QtDatavisualization.Q3DCamera is not None
58-
assert QtDatavisualization.QAbstract3DGraph is not None
59-
assert QtDatavisualization.QCustom3DVolume is not None
60-
assert QtDatavisualization.Q3DInputHandler is not None
61-
assert QtDatavisualization.QBarDataProxy is not None
62-
assert QtDatavisualization.QSurfaceDataProxy is not None
63-
assert QtDatavisualization.QScatterDataItem is not None
64-
assert QtDatavisualization.Q3DLight is not None
65-
assert QtDatavisualization.QScatterDataProxy is not None
66-
assert QtDatavisualization.QValue3DAxis is not None
67-
assert QtDatavisualization.Q3DBars is not None
68-
assert QtDatavisualization.QBarDataItem is not None
69-
assert QtDatavisualization.QItemModelBarDataProxy is not None
70-
assert QtDatavisualization.Q3DTheme is not None
71-
assert QtDatavisualization.QCustom3DItem is not None
72-
assert QtDatavisualization.QItemModelScatterDataProxy is not None
73-
assert QtDatavisualization.QValue3DAxisFormatter is not None
74-
assert QtDatavisualization.QItemModelSurfaceDataProxy is not None
75-
assert QtDatavisualization.Q3DScatter is not None
76-
assert QtDatavisualization.QTouch3DInputHandler is not None
77-
assert QtDatavisualization.QBar3DSeries is not None
78-
assert QtDatavisualization.QAbstract3DAxis is not None
79-
assert QtDatavisualization.Q3DScene is not None
80-
assert QtDatavisualization.QCategory3DAxis is not None
81-
assert QtDatavisualization.QAbstract3DSeries is not None
82-
assert QtDatavisualization.Q3DObject is not None
83-
assert QtDatavisualization.QCustom3DLabel is not None
84-
assert QtDatavisualization.Q3DSurface is not None
85-
assert QtDatavisualization.QLogValue3DAxisFormatter is not None
52+
assert qtpy.QtDatavisualization.QScatter3DSeries is not None
53+
assert qtpy.QtDatavisualization.QSurfaceDataItem is not None
54+
assert qtpy.QtDatavisualization.QSurface3DSeries is not None
55+
assert qtpy.QtDatavisualization.QAbstract3DInputHandler is not None
56+
assert qtpy.QtDatavisualization.QHeightMapSurfaceDataProxy is not None
57+
assert qtpy.QtDatavisualization.QAbstractDataProxy is not None
58+
assert qtpy.QtDatavisualization.Q3DCamera is not None
59+
assert qtpy.QtDatavisualization.QAbstract3DGraph is not None
60+
assert qtpy.QtDatavisualization.QCustom3DVolume is not None
61+
assert qtpy.QtDatavisualization.Q3DInputHandler is not None
62+
assert qtpy.QtDatavisualization.QBarDataProxy is not None
63+
assert qtpy.QtDatavisualization.QSurfaceDataProxy is not None
64+
assert qtpy.QtDatavisualization.QScatterDataItem is not None
65+
assert qtpy.QtDatavisualization.Q3DLight is not None
66+
assert qtpy.QtDatavisualization.QScatterDataProxy is not None
67+
assert qtpy.QtDatavisualization.QValue3DAxis is not None
68+
assert qtpy.QtDatavisualization.Q3DBars is not None
69+
assert qtpy.QtDatavisualization.QBarDataItem is not None
70+
assert qtpy.QtDatavisualization.QItemModelBarDataProxy is not None
71+
assert qtpy.QtDatavisualization.Q3DTheme is not None
72+
assert qtpy.QtDatavisualization.QCustom3DItem is not None
73+
assert qtpy.QtDatavisualization.QItemModelScatterDataProxy is not None
74+
assert qtpy.QtDatavisualization.QValue3DAxisFormatter is not None
75+
assert qtpy.QtDatavisualization.QItemModelSurfaceDataProxy is not None
76+
assert qtpy.QtDatavisualization.Q3DScatter is not None
77+
assert qtpy.QtDatavisualization.QTouch3DInputHandler is not None
78+
assert qtpy.QtDatavisualization.QBar3DSeries is not None
79+
assert qtpy.QtDatavisualization.QAbstract3DAxis is not None
80+
assert qtpy.QtDatavisualization.Q3DScene is not None
81+
assert qtpy.QtDatavisualization.QCategory3DAxis is not None
82+
assert qtpy.QtDatavisualization.QAbstract3DSeries is not None
83+
assert qtpy.QtDatavisualization.Q3DObject is not None
84+
assert qtpy.QtDatavisualization.QCustom3DLabel is not None
85+
assert qtpy.QtDatavisualization.Q3DSurface is not None
86+
assert qtpy.QtDatavisualization.QLogValue3DAxisFormatter is not None

qtpy/tests/test_qtnetworkauth.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
from qtpy import PYQT5, PYQT6, PYSIDE2
44

55

6-
@pytest.mark.skipif(
7-
PYQT5 or PYQT6 or PYSIDE2,
8-
reason="Not available by default in PyQt. Not available for PySide2",
9-
)
6+
@pytest.mark.skipif(PYSIDE2, reason="Not available for PySide2")
107
def test_qtnetworkauth():
118
"""Test the qtpy.QtNetworkAuth namespace"""
129
QtNetworkAuth = pytest.importorskip("qtpy.QtNetworkAuth")

0 commit comments

Comments
 (0)