Skip to content

Commit 059e195

Browse files
authored
chore: Drop test against pyside2, fix test for napari 0.7.0, add testing against python 3.13 (#1352)
Add testing napari repo against python 3.13 ## Summary by Sourcery Update napari testing configuration to separate repo-based tests from pinned napari versions and expand CI coverage to Python 3.13. Enhancements: - Split tox environments so napari widget tests run only against pinned napari versions while full test suite runs against the napari repo for selected Qt backends. - Stop running napari repo tests with the PySide2 backend in favor of PyQt5, PyQt6, and PySide6. CI: - Extend the napari repo GitHub Actions workflow matrix to include Python 3.13. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Expanded CI to include Python 3.13 and reorganized test environments to separate repo-based runs for clearer, targeted testing and headless-display setup. * **Bug Fixes** * Viewer windows are now initialized hidden and only shown after setup completes to prevent premature rendering. * **Tests** * Updated tests to instantiate viewers without immediate rendering and adjusted test invocations to align with the new CI/testenv layout. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 5f220a1 commit 059e195

5 files changed

Lines changed: 55 additions & 47 deletions

File tree

.github/workflows/base_test_workflow.yml

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ jobs:
5252
test:
5353
name: ${{ inputs.os }} py ${{ inputs.python_version }} ${{ inputs.napari }} ${{ inputs.qt_backend }} ${{ inputs.pydantic }}
5454
runs-on: ${{ inputs.os }}
55+
env:
56+
NAPARI: ${{ inputs.napari }}
57+
BACKEND: ${{ inputs.qt_backend }}
58+
PYVISTA_OFF_SCREEN: True # required for opengl on windows
59+
TOX_ARGS: ${{ inputs.tox_args }}
60+
PYTEST_ARGS: ${{ inputs.pytest_args }}
5561
steps:
5662
- uses: actions/checkout@v6
5763
- uses: actions/setup-python@v6
@@ -61,20 +67,11 @@ jobs:
6167
cache: 'pip'
6268
cache-dependency-path: 'pyproject.toml'
6369

64-
# - name: Install ubuntu libraries
65-
# if: runner.os == 'Linux'
66-
# run: |
67-
# sudo apt update
68-
# sudo apt-get install -y libegl1 libdbus-1-3 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xinput0 libxcb-xfixes0 x11-utils libxcb-cursor0
69-
70-
- uses: awalsh128/cache-apt-pkgs-action@latest
71-
if: runner.os == 'Linux'
70+
- name: Setup headless display
71+
uses: pyvista/setup-headless-display-action@7d84ae825e6d9297a8e99bdbbae20d1b919a0b19 # v4.2
7272
with:
73-
packages: libegl1 libdbus-1-3 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xinput0 libxcb-xfixes0 x11-utils libxcb-cursor0 libhdf5-dev
74-
version: 1.0
75-
76-
- name: Install Windows OpenGL
77-
uses: pyvista/setup-headless-display-action@v4
73+
qt: true
74+
wm: herbstluftwm
7875

7976
- name: Download test data
8077
if: ${{ inputs.test_data }}
@@ -90,47 +87,43 @@ jobs:
9087
9188
- name: Test with tox PartSegImage
9289
if: ${{ inputs.napari == 'latest' }}
93-
run: python -m tox ${{ inputs.tox_args }} -- package/tests/test_PartSegImage ${{ inputs.pytest_args }}
90+
shell: bash
91+
run: |
92+
# shellcheck disable=SC2086
93+
python -m tox $TOX_ARGS -- package/tests/test_PartSegImage $PYTEST_ARGS
9494
env:
95-
PYVISTA_OFF_SCREEN: True # required for opengl on windows
96-
NAPARI: ${{ inputs.napari }}
97-
BACKEND: ${{ inputs.qt_backend }}
9895
PIP_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }}
9996
UV_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }}
10097

10198
- name: Test with tox PartSegCore
10299
if: ${{ inputs.napari == 'latest' }}
103-
run: python -m tox ${{ inputs.tox_args }} -- package/tests/test_PartSegCore ${{ inputs.pytest_args }}
100+
shell: bash
101+
run: |
102+
# shellcheck disable=SC2086
103+
python -m tox $TOX_ARGS -- package/tests/test_PartSegCore $PYTEST_ARGS
104104
env:
105-
PYVISTA_OFF_SCREEN: True # required for opengl on windows
106-
NAPARI: ${{ inputs.napari }}
107-
BACKEND: ${{ inputs.qt_backend }}
108105
PIP_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }}
109106
UV_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }}
110107

111108
- name: Test with tox PartSeg
112109
if: ${{ inputs.napari == 'latest' }}
113-
uses: aganders3/headless-gui@v2
114110
timeout-minutes: ${{ inputs.timeout }}
115-
with:
116-
run: python -m tox ${{ inputs.tox_args }} -- package/tests/test_PartSeg ${{ inputs.pytest_args }}
111+
shell: bash
112+
run: |
113+
# shellcheck disable=SC2086
114+
python -m tox $TOX_ARGS -- package/tests/test_PartSeg $PYTEST_ARGS
117115
env:
118-
PYVISTA_OFF_SCREEN: True # required for opengl on windows
119-
NAPARI: ${{ inputs.napari }}
120-
BACKEND: ${{ inputs.qt_backend }}
121116
PIP_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }}
122117
UV_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }}
123118

124119
- name: Test with tox all
125120
if: ${{ inputs.napari != 'latest' }}
126-
uses: aganders3/headless-gui@v2
127121
timeout-minutes: ${{ inputs.timeout }}
128-
with:
129-
run: python -m tox ${{ inputs.tox_args }} -- ${{ inputs.pytest_args }}
122+
shell: bash
123+
run: |
124+
# shellcheck disable=SC2086
125+
python -m tox $TOX_ARGS ${PYTEST_ARGS:+-- $PYTEST_ARGS}
130126
env:
131-
PYVISTA_OFF_SCREEN: True # required for opengl on windows
132-
NAPARI: ${{ inputs.napari }}
133-
BACKEND: ${{ inputs.qt_backend }}
134127
PIP_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }}
135128
UV_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }}
136129

.github/workflows/test_napari_repo.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
fail-fast: false
3535
matrix:
3636
platform: [ ubuntu-24.04 ]
37-
python: ['3.10', '3.11', '3.12']
37+
python: ['3.10', '3.11', '3.12', '3.13']
3838
napari_version: ['repo']
3939
steps:
4040
- uses: actions/checkout@v6

package/PartSeg/common_gui/main_window.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,20 +227,26 @@ def get_colormaps(self) -> list[Optional[colormap.Colormap]]:
227227
return [self.settings.colormap_dict[name][0] for name in colormaps_name]
228228

229229
def napari_viewer_show(self):
230-
viewer = Viewer(title="Additional output", settings=self.settings, partseg_viewer_name=self.channel_info)
230+
viewer = Viewer(
231+
title="Additional output", settings=self.settings, partseg_viewer_name=self.channel_info, show=False
232+
)
231233
viewer.scale_bar.unit = "nm"
232234
viewer.theme = self.settings.theme_name
233235
viewer.create_initial_layers(image=True, roi=True, additional_layers=False, points=True)
236+
viewer.show()
234237
self.viewer_list.append(viewer)
235238
viewer.window.qt_viewer.destroyed.connect(lambda _x: self.close_viewer(viewer))
236239

237240
def additional_layers_show(self, with_channels=False):
238241
if not self.settings.additional_layers:
239242
QMessageBox().information(self, "No data", "Last executed algorithm does not provide additional data")
240243
return
241-
viewer = Viewer(title="Additional output", settings=self.settings, partseg_viewer_name=self.channel_info)
244+
viewer = Viewer(
245+
title="Additional output", settings=self.settings, partseg_viewer_name=self.channel_info, show=False
246+
)
242247
viewer.theme = self.settings.theme_name
243248
viewer.create_initial_layers(image=with_channels, roi=False, additional_layers=True, points=False)
249+
viewer.show()
244250
self.viewer_list.append(viewer)
245251
viewer.window.qt_viewer.destroyed.connect(lambda _x: self.close_viewer(viewer))
246252

package/tests/test_PartSeg/test_viewer.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class TestNapariViewer:
7373
def test_base(self, image, analysis_segmentation2, tmp_path):
7474
settings = BaseSettings(tmp_path)
7575
settings.image = image
76-
viewer = Viewer(settings, "")
76+
viewer = Viewer(settings, "", show=False)
7777
viewer.create_initial_layers(True, True, True, True)
7878
assert len(viewer.layers) == 2
7979
viewer.create_initial_layers(True, True, True, True)
@@ -92,7 +92,7 @@ def test_base(self, image, analysis_segmentation2, tmp_path):
9292
def test_points(self, image, tmp_path, qtbot):
9393
settings = BaseSettings(tmp_path)
9494
settings.image = image
95-
viewer = Viewer(settings, "")
95+
viewer = Viewer(settings, "", show=False)
9696
viewer.create_initial_layers(True, True, True, True)
9797
assert len(viewer.layers) == 2
9898
points = np.array([[0, 1, 1, 1], [0, 7, 10, 10]])
@@ -113,7 +113,7 @@ def test_points(self, image, tmp_path, qtbot):
113113
def test_image(self, image, image2, tmp_path, qtbot):
114114
settings = BaseSettings(tmp_path)
115115
settings.image = image
116-
viewer = Viewer(settings, "test")
116+
viewer = Viewer(settings, "test", show=False)
117117
with qtbot.waitSignal(viewer._sync_widget.sync_image_chk.stateChanged):
118118
viewer._sync_widget.sync_image_chk.setChecked(True)
119119
assert len(viewer.layers) == 2
@@ -125,7 +125,7 @@ def test_image(self, image, image2, tmp_path, qtbot):
125125
def test_roi(self, image, tmp_path, qtbot):
126126
settings = BaseSettings(tmp_path)
127127
settings.image = image
128-
viewer = Viewer(settings, "test")
128+
viewer = Viewer(settings, "test", show=False)
129129
viewer._sync_widget.sync_image()
130130
assert len(viewer.layers) == 2
131131
viewer._sync_widget.sync_ROI_chk.setChecked(True)
@@ -135,10 +135,11 @@ def test_roi(self, image, tmp_path, qtbot):
135135
assert len(viewer.layers) == 4
136136
viewer.close()
137137

138-
def test_additional(self, image, tmp_path, qtbot):
138+
@pytest.mark.usefixtures("qtbot")
139+
def test_additional(self, image, tmp_path):
139140
settings = BaseSettings(tmp_path)
140141
settings.image = image
141-
viewer = Viewer(settings, "test")
142+
viewer = Viewer(settings, "test", show=False)
142143
viewer._sync_widget.sync_image()
143144
assert len(viewer.layers) == 2
144145
settings._additional_layers = {

tox.ini

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# and then run "tox" from this directory.
55

66
[tox]
7-
envlist = py{39,310,311,312,313}-{PyQt5,PySide2,PyQt6,PySide6}-all, py{39,310,311,312,313}-{PyQt5,PyQt6}-napari_{419,54,repo}, py{39,310}-PySide2-napari_{419,54,repo}
7+
envlist = py{39,310,311,312,313}-{PyQt5,PySide2,PyQt6,PySide6}-all, py{310,311,312,313}-{PyQt5,PyQt6}-napari_repo,py{39,310,311,312,313}-{PyQt5,PyQt6,PySide2}-napari_{419,54}, py{312,313}-PySide6-napari_{419,54,repo}
88
toxworkdir=/tmp/tox
99

1010
[gh-actions]
@@ -71,15 +71,23 @@ deps=
7171
pytest-json-report
7272
lxml_html_clean
7373

74-
[testenv:py{39,310,311,312,313}-{PyQt5,PySide2,PyQt6,PySide6}-napari_{419,54,repo}]
74+
75+
[testenv:py{39,310,311,312,313}-{PyQt5,PySide2,PyQt6,PySide6}-napari_{419,54}]
7576
deps =
7677
{[testenv]deps}
7778
napari_419: napari==0.4.19.post1
7879
napari_54: napari==0.5.4
79-
napari_repo: git+https://github.com/napari/napari.git
8080
commands =
81-
!napari_repo: python -m pytest -v package/tests/test_PartSeg/test_napari_widgets.py --json-report --json-report-file={toxinidir}/report-{envname}-{sys_platform}.json {posargs}
82-
napari_repo: python -m pytest package/tests --json-report --json-report-file={toxinidir}/report-{envname}-{sys_platform}.json {posargs}
81+
python -m pytest -v --json-report --json-report-file={toxinidir}/report-{envname}-{sys_platform}.json {posargs:package/tests/test_PartSeg/test_napari_widgets.py}
82+
83+
84+
[testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo]
85+
deps =
86+
{[testenv]deps}
87+
git+https://github.com/napari/napari.git
88+
commands =
89+
python -m pytest --json-report --json-report-file={toxinidir}/report-{envname}-{sys_platform}.json {posargs:package/tests}
90+
8391

8492
[testenv:py{39,310,311,312,313}-PyQt5-coverage]
8593
deps =

0 commit comments

Comments
 (0)