Skip to content

Add PyQt6 and PySide6 CI workflows (and fix the hanging SnapshotTool test) - #87

Merged
PierreRaybaut merged 3 commits into
developfrom
ci/69-qt6-workflows
Oct 29, 2026
Merged

PierreRaybaut merged 3 commits into
developfrom
ci/69-qt6-workflows

Conversation

@PierreRaybaut

@PierreRaybaut PierreRaybaut commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Closes #69

What

  • New CI workflows test_pyqt6.yml and test_pyside6.yml, based on test_pyqt5.yml as in guidata:
    • Python 3.10 and 3.14;
    • additional system libraries required by Qt 6 (libegl1 libgl1 libopengl0 libxcb-cursor0);
    • timeout-minutes: 20, so that a hanging test cannot hold a runner for 6 hours;
    • PySide6: same pin as guidata and PythonQwt (PySide6!=6.12.0 before Python 3.12, where 6.12.0 crashes), so Python 3.10 tests PySide6 6.11.x and Python 3.14 tests 6.12.0.
  • Dependency selection for pull requests (all three test workflows): branches are now selected with github.base_ref || github.ref_name. Before, github.ref_name is <number>/merge on pull requests, so PRs to develop or release were tested against guidata and PythonQwt from PyPI instead of their development branches.
  • PyQt5 workflow: matrix reduced to Python 3.10 and 3.14 (lowest and highest supported versions), as the new workflows.
  • Release gate: build_deploy.yml now also requires the PyQt6 tests before uploading to PyPI (as guidata does; PySide6 is not required, as in guidata).
  • Test fix: test_annotation_tools.py::test_tool[SnapshotTool] no longer hangs.
  • Release notes: two entries in release_2.12.md (🔧 Other changes).

Why the test fix is needed

Adding the Qt 6 workflows as is makes the CI hang. Since the fix of #58, SnapshotTool defers its action with QTimer.singleShot(0, ...). In test_annotation_tools, this action ran during the processEvents() call of guidata's exec_dialog, before the automatic closing of dialogs was scheduled, so the modal ResizeDialog opened by save_snapshot waited forever.

The test only passed with PyQt5 on Linux, and only when run after another test, whose pending closing timer happened to close the dialog. It hung when run on its own (even with PyQt5 on Linux), on Windows with all bindings, and with PyQt6 or PySide6 on Linux.

The test now replaces the snapshot action by a recorder in unattended mode, runs the deferred actions within the test, and checks that the action was called once, with the plot and an oriented rectangle. The production code is unchanged, and the real snapshot dialogs are still used when the test is run interactively.

Local validation

Environment Result
Linux (WSL Ubuntu), replay of test_pyqt6.yml, Python 3.10 / 3.14 (PyQt6 6.12.0) 423 passed, 16 skipped / 423 passed, 16 skipped
Linux (WSL Ubuntu), replay of test_pyside6.yml, Python 3.10 (PySide6 6.11.3) / 3.14 (PySide6 6.12.0) 422 passed, 17 skipped / 422 passed, 17 skipped
Windows, Python 3.14, PyQt5 5.15 / PyQt6 6.10 / PySide6 6.10, full suite 423 / 423 / 422 passed (no hang)

The Linux replays ran the install script of each workflow (development dependencies: guidata develop, PythonQwt master), ruff check and the full pytest suite, without deselecting any test. The extra skipped test with PySide6 is test_qtdesigner.py (QtDesigner plugins are not supported by PySide6). actionlint reports no new issue.

After merging, #69 has to be closed manually, since this PR targets develop and the default branch is master.

Since the fix of #58, SnapshotTool defers its action with a zero-delay
timer. In test_annotation_tools, this action ran during the
processEvents() call of guidata's exec_dialog, before the automatic
closing of dialogs was scheduled, so the modal ResizeDialog opened by
save_snapshot was never closed and the test hung. The test only passed
under PyQt5 on Linux when run after another test, whose pending closing
timer closed the dialog; it hung when run on its own, on Windows, and
with PyQt6 or PySide6.

In unattended mode, replace the snapshot action by a recorder, run the
deferred actions within the test, and check that the action was called
once with the plot and an oriented rectangle.

Assisted-by: Claude Opus 5.5
Add test_pyqt6.yml and test_pyside6.yml, based on test_pyqt5.yml as in
guidata: Python 3.10 and 3.14, additional system libraries required by
Qt 6, and a 20-minute timeout so that a hanging test does not hold a
runner for hours. As in guidata and PythonQwt, PySide6 6.12.0 is
excluded before Python 3.12, where it crashes.

Pull requests now select the guidata and PythonQwt versions to test
against from their target branch (github.base_ref), as pushes do from
their own branch: pull requests to develop or release were tested
against the versions published on PyPI, since github.ref_name is
"<number>/merge" for them.

The PyQt6 tests are also required before uploading a release to PyPI,
as in guidata.

Closes #69

Assisted-by: Claude Opus 5.5
Test the lowest and highest supported Python versions, as the PyQt6 and PySide6 workflows do.

Assisted-by: Claude Opus 5.5
@PierreRaybaut
PierreRaybaut merged commit a893a5a into develop Oct 29, 2026
7 checks passed
@PierreRaybaut
PierreRaybaut deleted the ci/69-qt6-workflows branch October 29, 2026 15:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant