Repository: spyder-ide/qtpy (language: python)

Issue:
Add extra packages available for PyQt to CI for testing
Follow-up issue as discussed at https://github.com/spyder-ide/qtpy/pull/372 to add to the test suite setup extra packages.

Packages that could be added (when using `pip`):

* `PyQt3D` and `PyQt6-3D`
* `PyQtChart` and `PyQt6-Charts`
* `PyQtDataVisualization` and `PyQt6-DataVisualization`
* `PyQtNetworkAuth` and `PyQt6-NetworkAuth`
* `PyQtPurchasing`
* `PyQtWebEngine` and `PyQt6-WebEngine` 




Discussion / hints:

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4576b83d..761d60f7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -38,6 +38,7 @@ jobs:
       PYSIDE6_VERSION: ${{ matrix.pyside6-version || matrix.qt6-version-default }}
       PYSIDE6_QT_VERSION: ${{ matrix.pyside6-qt-version || matrix.pyside6-version || matrix.qt6-version-default }}
       QSCINTILLA_VERSION: ${{ matrix.qscintilla-version || matrix.qscintilla-version-default }}
+      PYQT_EXTRAS: ${{ matrix.pyqt-extras || matrix.pyqt-extras-default }}
       SKIP_PIP_CHECK: ${{ matrix.skip-pip-check }}
     strategy:
       fail-fast: false
@@ -48,31 +49,34 @@ jobs:
         qt5-version-default: ['5.12']
         qt6-version-default: ['6.3']
         qscintilla-version-default: ['2.13']
+        pyqt-extras-default: ['No']
         include:
         - os: ubuntu-latest
           special-invocation: 'xvfb-run --auto-servernum '  # Needed for GUI tests to work
         - python-version: '3.11'
           pyqt5-version: '5.15'  # Python 3.11 needs 5.15+
           pyside2-version: '5.15'  # Python 3.11 needs 5.15+
-          pyside6-version: '6.4'  # Python 3.11 needs 6.4+
+          pyside6-version: '6.5'  # Python 3.11 needs 6.4+. Test upper bound
         - use-conda: 'Yes'
           skip-pyqt6: true  # No PyQt6 conda packages yet
-          pyside6-version: '6.4'  # Conda only has 6.4+ for Python <3.8
         - use-conda: 'No'
           pyqt5-version: '5.15'  # Test with latest optional packages
         - python-version: '3.7'
           use-conda: 'Yes'
           pyside2-version: '5.13'  # Conda needs 5.13+ to work reliably
           pyside2-qt-version: '5.12'  # Conda only has 5.12 and 5.15, not 5.13
+          pyside6-version: '6.4'  # Conda only has 6.4 for Python <3.8
         - python-version: '3.11'
           use-conda: 'No'
+          pyqt-extras: 'Yes'  # Check PyQt extras
           skip-pyside2: true  # Pyside2 wheels don't support Python 3.11+
+          pyqt6-version: '6.5'  # Test upper bound
           pyside6-version: '6.5'  # Test upper bound
         - os: windows-latest
           python-version: '3.7'
           use-conda: 'Yes'
           pyqt5-version: '5.9'  # Test lower bound
-          skip-pyside6: true  # Test hangs
+          skip-pyside6: true  # Test hangs with 6.4. 6.5 is not available for Python 3.7
         - os: windows-latest
           python-version: '3.7'
           use-conda: 'No'
@@ -81,13 +85,40 @@ jobs:
         - os: windows-latest
           python-version: '3.11'
           use-conda: 'Yes'
-          skip-pyside6: true  # Test hangs
+          pyside6-version: 6.5  # Test upper bound
         - os: macos-latest
-          python-version: '3.7'
+          python-version: '3.11'
           use-conda: 'No'
           pyqt6-version: 6.5  # Test upper bound
           pyside2-version: 5.15  # Test upper bound
     steps:
+      - name: Check job values
+        run: |
+          echo "---- General setup"
+          echo "OS:" ${{ matrix.os }}
+          echo "PYTHON_VERSION:" ${{ env.PYTHON_VERSION }}
+          echo "USE_CONDA:" ${{ env.USE_CONDA }}
+          echo "---- PyQt"
+          echo "PYQT_EXTRAS:" ${{ env.PYQT_EXTRAS }}
+          echo "---- PyQt5"
+          echo "SKIP_PYQT5:" ${{ matrix.skip-pyqt5 }}
+          echo "PYQT5_VERSION:" ${{ env.PYQT5_VERSION }}
+          echo "PYQT5_QT_VERSION:" ${{ env.PYQT5_QT_VERSION }}
+          echo "QSCINTILLA_VERSION:" ${{ env.QSCINTILLA_VERSION }}
+          echo "---- PyQt6"
+          echo "SKIP_PYQT6:" ${{ matrix.skip-pyqt6 }}
+          echo "PYQT6_VERSION:" ${{ env.PYQT6_VERSION }}
+          echo "PYQT6_QT_VERSION:" ${{ env.PYQT6_QT_VERSION }}
+          echo "---- PySide2"
+          echo "SKIP_PYSIDE2:" ${{ matrix.skip-pyside2 }}
+          echo "PYSIDE2_VERSION:" ${{ env.PYSIDE2_VERSION }}
+          echo "PYSIDE2_QT_VERSION:" ${{ env.PYSIDE2_QT_VERSION }}
+          echo "---- PySide6"
+          echo "SKIP_PYSIDE6:" ${{ matrix.skip-pyside6 }}
+          echo "PYSIDE6_VERSION:" ${{ env.PYSIDE6_VERSION }}
+          echo "PYSIDE6_QT_VERSION:" ${{ env.PYSIDE6_QT_VERSION }}
+          echo "---- Other"
+          echo "SKIP_PIP_CHECK:" ${{ env.SKIP_PIP_CHECK }}
       - name: Checkout branch
         uses: actions/checkout@v3
       - name: Setup Python
diff --git a/README.md b/README.md
index c6fe1538..8663bb95 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
 [![Github build status](https://github.com/spyder-ide/qtpy/workflows/Tests/badge.svg)](https://github.com/spyder-ide/qtpy/actions)
 [![Coverage Status](https://coveralls.io/repos/github/spyder-ide/qtpy/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/qtpy?branch=master)
 
-*Copyright © 2009–2022 The Spyder Development Team*
+*Copyright © 2009– The Spyder Development Team*
 
 
 ## Description
@@ -18,7 +18,7 @@
 **QtPy** is a small abstraction layer that lets you
 write applications using a single API call to either PyQt or PySide.
 
-It provides support for PyQt5, PyQt6, PySide6, PySide2 using the Qt5 layout
+It provides support for PyQt5, PySide2, PyQt6 and PySide6 using the Qt5 layout
 (where the QtGui module has been split into QtGui and QtWidgets).
 
 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.
 
 ### License
 
-This project is released under the MIT license.
+This project is released under the [MIT license](LICENSE.txt).
 
 
 ### Requirements
@@ -107,10 +107,12 @@ conda install qtpy
 Type checkers have no knowledge of installed packages, so these tools require
 additional configuration.
 
+A Command Line Interface (CLI) is offered to help with usage of QtPy (to get MyPy
+and Pyright/Pylance args/configurations).
+
 #### Mypy
 
-A Command Line Interface (CLI) is offered to help with usage of QtPy.
-Presently, its only feature is to generate command line arguments for Mypy
+The `mypy-args` command helps you to generate command line arguments for Mypy
 that will enable it to process the QtPy source files with the same API
 as QtPy itself would have selected.
 
@@ -139,8 +141,11 @@ mypy --package mypackage $(qtpy mypy-args)
 
 #### Pyright/Pylance
 
-Instead of runtime arguments, it is required to create a config file for the project,
-called `pyrightconfig.json` or a `pyright` section in `pyproject.toml`. See [here](https://github.com/microsoft/pyright/blob/main/docs/configuration.md) for reference.
+In the case of Pyright, instead of runtime arguments, it is required to create a
+config file for the project, called `pyrightconfig.json` or a `pyright` section
+in `pyproject.toml`. See [here](https://github.com/microsoft/pyright/blob/main/docs/configuration.md)
+for reference. In order to set this configuration, QtPy offers the `pyright-config`
+command for guidance.
 
 If you run
 
@@ -149,14 +154,63 @@ qtpy pyright-config
 ```
 
 you will get the necessary configs to be included in your project files. If you don't
-have them, it is recommended to create the latter.
+have them, it is recommended to create the latter. For example, in an environment where PyQt5
+is installed and selected (or the default fallback, if no binding can be found in the
+environment), this would output the following:
+
+```text
+pyrightconfig.json:
+{"defineConstant": {"PYQT5": true, "PYSIDE2": false, "PYQT6": false, "PYSIDE6": false}}
+
+pyproject.toml:
+[tool.pyright.defineConstant]
+PYQT5 = true
+PYSIDE2 = false
+PYQT6 = false
+PYSIDE6 = false
+```
+
+**Note**: These configurations are necessary for the correct usage of the default VSCode's type
+checking feature while using QtPy in your source code.
+
+
+## Testing matrix
+
+Currently, QtPy runs tests for different bindings on Linux, Windows and macOS, using
+Python 3.7 and 3.11, and installing those bindings with `conda` and `pip`. For the
+PyQt bindings, we also check the installation of extra packages via `pip`.
+
+Following this, the current test matrix looks something like this:
+
+|         | Python          | 3.7                                        |      | 3.11               |                            |
+|---------|-----------------|--------------------------------------------|------|--------------------|----------------------------|
+| OS      | Binding / manager | conda                                      | pip  | conda              | pip                        |
+| Linux   | PyQt5           | 5.12                                       | 5.15 | 5.15               | 5.15 (with extras)         |
+|         | PyQt6           | skip (unavailable)                         | 6.3  | skip (unavailable) | 6.5 (with extras)          |
+|         | PySide2         | 5.13                                       | 5.12 | 5.15               | skip (no wheels available) |
+|         | PySide6         | 6.4                                        | 6.3  | 6.5                | 6.5                        |
+| Windows | PyQt5           | 5.9                                        | 5.15 | 5.15               | 5.15 (with extras)         |
+|         | PyQt6           | skip (unavailable)                         | 6.2  | skip (unavailable) | 6.5 (with extras)          |
+|         | PySide2         | 5.13                                       | 5.12 | 5.15               | skip (no wheels available) |
+|         | PySide6         | skip (test hang with 6.4. 6.5 unavailable) | 6.2  | 6.5                | 6.5                        |
+| MacOS   | PyQt5           | 5.12                                       | 5.15 | 5.15               | 5.15 (with extras)         |
+|         | PyQt6           | skip (unavailable)                         | 6.3  | skip (unavailable) | 6.5 (with extras)          |
+|         | PySide2         | 5.13                                       | 5.12 | 5.15               | skip (no wheels available) |
+|         | PySide6         | 6.4                                        | 6.3  | 6.5                | 6.5                        |
 
-These steps are necessary for running the default VSCode's type checking.
+**Note**: The mentioned extra packages for the PyQt bindings are the following:
 
+* `PyQt3D` and `PyQt6-3D`
+* `PyQtChart` and `PyQt6-Charts`
+* `PyQtDataVisualization` and `PyQt6-DataVisualization`
+* `PyQtNetworkAuth` and `PyQt6-NetworkAuth`
+* `PyQtPurchasing`
+* `PyQtWebEngine` and `PyQt6-WebEngine` 
+* `QScintilla` and `PyQt6-QScintilla`
 
 ## Contributing
 
-Everyone is welcome to contribute!
+Everyone is welcome to contribute! See our [Contributing guide](CONTRIBUTING.md) for more details.
 
 
 ## Sponsors


Test patch:
diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh
index 69267f57..5e87543e 100755
--- a/.github/workflows/test.sh
+++ b/.github/workflows/test.sh
@@ -34,9 +34,39 @@ conda activate test-env-${BINDING}
 if [ "$USE_CONDA" = "No" ]; then
 
     if [ "${1}" = "pyqt5" ]; then
-        pip install pyqt5==${PYQT5_VERSION}.* PyQtWebEngine==${PYQT5_VERSION}.* QScintilla==${QSCINTILLA_VERSION}.*
+
+        if [ "$PYQT_EXTRAS" = "Yes" ]; then
+            pip install pyqt5==${PYQT5_VERSION}.* \
+            PyQtWebEngine==${PYQT5_VERSION}.* \
+            QScintilla==${QSCINTILLA_VERSION}.* \
+            PyQt3D==${PYQT5_VERSION}.* \
+            PyQtChart==${PYQT5_VERSION}.* \
+            PyQtDataVisualization==${PYQT5_VERSION}.* \
+            PyQtNetworkAuth==${PYQT5_VERSION}.* \
+            PyQtPurchasing==${PYQT5_VERSION}.*
+        else
+            pip install pyqt5==${PYQT5_VERSION}.* \
+            PyQtWebEngine==${PYQT5_VERSION}.* \
+            QScintilla==${QSCINTILLA_VERSION}.*
+        fi
+
     elif [ "${1}" = "pyqt6" ]; then
-        pip install pyqt6==${PYQT6_VERSION}.* PyQt6-WebEngine==${PYQT6_VERSION}.* PyQt6-Qt6==${PYQT6_QT_VERSION}.* PyQt6-QScintilla==${QSCINTILLA_VERSION}.*
+
+        if [ "$PYQT_EXTRAS" = "Yes" ]; then
+            pip install pyqt6==${PYQT6_VERSION}.* \
+            PyQt6-WebEngine==${PYQT6_VERSION}.* \
+            PyQt6-Qt6==${PYQT6_QT_VERSION}.* \
+            PyQt6-QScintilla \
+            PyQt6-3D==${PYQT6_VERSION}.* \
+            PyQt6-Charts==${PYQT6_VERSION}.* \
+            PyQt6-DataVisualization==${PYQT6_VERSION}.* \
+            PyQt6-NetworkAuth==${PYQT6_VERSION}.*
+        else
+            pip install pyqt6==${PYQT6_VERSION}.* \
+            PyQt6-WebEngine==${PYQT6_VERSION}.* \
+            PyQt6-Qt6==${PYQT6_QT_VERSION}.*
+        fi
+
     elif [ "${1}" = "pyside2" ]; then
         pip install pyside2==${PYSIDE2_VERSION}.*
     elif [ "${1}" = "pyside6" ]; then
diff --git a/qtpy/tests/test_qtdatavisualization.py b/qtpy/tests/test_qtdatavisualization.py
index 09f19f31..5bbf7caf 100644
--- a/qtpy/tests/test_qtdatavisualization.py
+++ b/qtpy/tests/test_qtdatavisualization.py
@@ -46,40 +46,41 @@ def test_qtdatavisualization():
     assert QtDataVisualization.QLogValue3DAxisFormatter is not None
 
     # QtDatavisualization
-    QtDatavisualization = pytest.importorskip("qtpy.QtDatavisualization")
+    # import qtpy to get alias for `QtDataVisualization` with lower `v`
+    qtpy = pytest.importorskip("qtpy")
 
-    assert QtDatavisualization.QScatter3DSeries is not None
-    assert QtDatavisualization.QSurfaceDataItem is not None
-    assert QtDatavisualization.QSurface3DSeries is not None
-    assert QtDatavisualization.QAbstract3DInputHandler is not None
-    assert QtDatavisualization.QHeightMapSurfaceDataProxy is not None
-    assert QtDatavisualization.QAbstractDataProxy is not None
-    assert QtDatavisualization.Q3DCamera is not None
-    assert QtDatavisualization.QAbstract3DGraph is not None
-    assert QtDatavisualization.QCustom3DVolume is not None
-    assert QtDatavisualization.Q3DInputHandler is not None
-    assert QtDatavisualization.QBarDataProxy is not None
-    assert QtDatavisualization.QSurfaceDataProxy is not None
-    assert QtDatavisualization.QScatterDataItem is not None
-    assert QtDatavisualization.Q3DLight is not None
-    assert QtDatavisualization.QScatterDataProxy is not None
-    assert QtDatavisualization.QValue3DAxis is not None
-    assert QtDatavisualization.Q3DBars is not None
-    assert QtDatavisualization.QBarDataItem is not None
-    assert QtDatavisualization.QItemModelBarDataProxy is not None
-    assert QtDatavisualization.Q3DTheme is not None
-    assert QtDatavisualization.QCustom3DItem is not None
-    assert QtDatavisualization.QItemModelScatterDataProxy is not None
-    assert QtDatavisualization.QValue3DAxisFormatter is not None
-    assert QtDatavisualization.QItemModelSurfaceDataProxy is not None
-    assert QtDatavisualization.Q3DScatter is not None
-    assert QtDatavisualization.QTouch3DInputHandler is not None
-    assert QtDatavisualization.QBar3DSeries is not None
-    assert QtDatavisualization.QAbstract3DAxis is not None
-    assert QtDatavisualization.Q3DScene is not None
-    assert QtDatavisualization.QCategory3DAxis is not None
-    assert QtDatavisualization.QAbstract3DSeries is not None
-    assert QtDatavisualization.Q3DObject is not None
-    assert QtDatavisualization.QCustom3DLabel is not None
-    assert QtDatavisualization.Q3DSurface is not None
-    assert QtDatavisualization.QLogValue3DAxisFormatter is not None
+    assert qtpy.QtDatavisualization.QScatter3DSeries is not None
+    assert qtpy.QtDatavisualization.QSurfaceDataItem is not None
+    assert qtpy.QtDatavisualization.QSurface3DSeries is not None
+    assert qtpy.QtDatavisualization.QAbstract3DInputHandler is not None
+    assert qtpy.QtDatavisualization.QHeightMapSurfaceDataProxy is not None
+    assert qtpy.QtDatavisualization.QAbstractDataProxy is not None
+    assert qtpy.QtDatavisualization.Q3DCamera is not None
+    assert qtpy.QtDatavisualization.QAbstract3DGraph is not None
+    assert qtpy.QtDatavisualization.QCustom3DVolume is not None
+    assert qtpy.QtDatavisualization.Q3DInputHandler is not None
+    assert qtpy.QtDatavisualization.QBarDataProxy is not None
+    assert qtpy.QtDatavisualization.QSurfaceDataProxy is not None
+    assert qtpy.QtDatavisualization.QScatterDataItem is not None
+    assert qtpy.QtDatavisualization.Q3DLight is not None
+    assert qtpy.QtDatavisualization.QScatterDataProxy is not None
+    assert qtpy.QtDatavisualization.QValue3DAxis is not None
+    assert qtpy.QtDatavisualization.Q3DBars is not None
+    assert qtpy.QtDatavisualization.QBarDataItem is not None
+    assert qtpy.QtDatavisualization.QItemModelBarDataProxy is not None
+    assert qtpy.QtDatavisualization.Q3DTheme is not None
+    assert qtpy.QtDatavisualization.QCustom3DItem is not None
+    assert qtpy.QtDatavisualization.QItemModelScatterDataProxy is not None
+    assert qtpy.QtDatavisualization.QValue3DAxisFormatter is not None
+    assert qtpy.QtDatavisualization.QItemModelSurfaceDataProxy is not None
+    assert qtpy.QtDatavisualization.Q3DScatter is not None
+    assert qtpy.QtDatavisualization.QTouch3DInputHandler is not None
+    assert qtpy.QtDatavisualization.QBar3DSeries is not None
+    assert qtpy.QtDatavisualization.QAbstract3DAxis is not None
+    assert qtpy.QtDatavisualization.Q3DScene is not None
+    assert qtpy.QtDatavisualization.QCategory3DAxis is not None
+    assert qtpy.QtDatavisualization.QAbstract3DSeries is not None
+    assert qtpy.QtDatavisualization.Q3DObject is not None
+    assert qtpy.QtDatavisualization.QCustom3DLabel is not None
+    assert qtpy.QtDatavisualization.Q3DSurface is not None
+    assert qtpy.QtDatavisualization.QLogValue3DAxisFormatter is not None
diff --git a/qtpy/tests/test_qtnetworkauth.py b/qtpy/tests/test_qtnetworkauth.py
index ba71d0ab..ff9b9237 100644
--- a/qtpy/tests/test_qtnetworkauth.py
+++ b/qtpy/tests/test_qtnetworkauth.py
@@ -3,10 +3,7 @@
 from qtpy import PYQT5, PYQT6, PYSIDE2
 
 
-@pytest.mark.skipif(
-    PYQT5 or PYQT6 or PYSIDE2,
-    reason="Not available by default in PyQt. Not available for PySide2",
-)
+@pytest.mark.skipif(PYSIDE2, reason="Not available for PySide2")
 def test_qtnetworkauth():
     """Test the qtpy.QtNetworkAuth namespace"""
     QtNetworkAuth = pytest.importorskip("qtpy.QtNetworkAuth")