Skip to content

Commit de4f45c

Browse files
authored
Merge pull request #17 from dhomeier/qt65-6-py312-ci
Add CI envs for PyQt 6.5-6.7, PySide 6.5-6.7 & Python 3.12; fix Readthedocs and matplotlib 3.9, Qt6 failures
2 parents 37dd079 + 92f7247 commit de4f45c

File tree

8 files changed

+84
-30
lines changed

8 files changed

+84
-30
lines changed

.github/workflows/ci_workflows.yml

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ jobs:
4343
- linux: py38-test-pyqt514-all
4444
- linux: py39-test-pyqt515
4545
- linux: py310-test-pyqt63-all
46-
- linux: py310-test-pyqt64-all
46+
- linux: py311-test-pyqt64
47+
- linux: py312-test-pyqt65
48+
- linux: py311-test-pyqt66-all
4749
- linux: py311-test-pyqt514
4850
- linux: py311-test-pyqt515-lts-all
4951
@@ -53,19 +55,22 @@ jobs:
5355
- macos: py311-docs-pyqt64
5456
coverage: false
5557
56-
# Test a few configurations on macOS
58+
# Test a few configurations on macOS 12 (Intel) and 14 (ARM)
5759
- macos: py38-test-pyqt514-all
58-
- macos: py310-test-pyqt515
59-
- macos: py310-test-pyqt64
60-
- macos: py311-test-pyqt515
60+
- macos: py311-test-pyqt66
61+
- macos: py312-test-pyqt67
6162
6263
# Test some configurations on Windows
6364
- windows: py38-test-pyqt514
6465
- windows: py310-test-pyqt63
66+
- windows: py311-test-pyqt65
67+
- windows: py312-test-pyqt66
6568
6669
# Test against latest developer versions of some packages
6770
- linux: py310-test-pyqt515-dev-all
6871
- linux: py311-test-pyqt64-dev
72+
- linux: py312-test-pyqt515-dev
73+
- linux: py312-test-pyqt67-dev-all
6974
7075
allowed_failures:
7176
needs: initial_checks
@@ -92,13 +97,18 @@ jobs:
9297
9398
# PySide6 6.4 failures due to https://github.com/spyder-ide/qtpy/issues/373
9499
# and https://github.com/matplotlib/matplotlib/issues/24155
100+
# PyQt5 / < 6.6 segfaulting under macOS 14 (on arm64) in TestGlueDataDialog or TestLinkEditor
95101
# Python 3.11.0 failing on Windows in test_image.py on
96102
# > assert df.find_factory(fname) is df.img_data
97103
- linux: py310-test-pyside64-skipexitcode
98-
- linux: py311-test-pyside64-skipexitcode
104+
- linux: py311-test-pyside65-skipexitcode
105+
- linux: py312-test-pyside67-skipexitcode
99106
- macos: py310-test-pyside63-skipexitcode
100107
- macos: py311-test-pyside64-skipexitcode
108+
- macos: py312-test-pyside67-skipexitcode
109+
- macos: py310-test-pyqt515
101110
- windows: py310-test-pyside64
111+
- windows: py312-test-pyside66
102112
- windows: py311-test-pyqt515
103113
104114
# Windows docs build

.readthedocs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ version: 2
33
build:
44
os: "ubuntu-22.04"
55
tools:
6-
python: "3"
6+
python: "3.12"
77

88
sphinx:
99
builder: html

glue_qt/conftest.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
try:
88
from qtpy import PYSIDE2, PYSIDE6
99
except Exception:
10-
PYSIDE2 = False
10+
PYSIDE2 = PYSIDE6 = False
1111

1212
from glue.config import CFG_DIR as CFG_DIR_ORIG
1313

@@ -50,11 +50,15 @@ def pytest_configure(config):
5050

5151
if config.getoption('no_optional_skip'):
5252
from glue.tests import helpers
53+
from glue_qt.tests import helpers as qt_helpers
5354
for attr in helpers.__dict__:
5455
if attr.startswith('requires_'):
5556
# The following line replaces the decorators with a function
56-
# that does noting, effectively disabling it.
57+
# that does nothing, effectively disabling it.
5758
setattr(helpers, attr, lambda f: f)
59+
for attr in qt_helpers.__dict__:
60+
if attr.startswith('requires_'):
61+
setattr(qt_helpers, attr, lambda f: f)
5862

5963
# Make sure we don't affect the real glue config dir
6064
import tempfile
@@ -105,7 +109,7 @@ def pytest_unconfigure(config):
105109
# objgraph.show_most_common_types(limit=100)
106110

107111

108-
# With PySide2, tests can fail in a non-deterministic way on a teardown error
112+
# With PySide2/6, tests can fail in a non-deterministic way on a teardown error
109113
# or with the following error:
110114
#
111115
# AttributeError: 'PySide2.QtGui.QStandardItem' object has no attribute '...'

glue_qt/tests/helpers.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,29 @@
1+
# Define decorators that can be used for pytest tests
2+
13
import pytest
24

3-
from glue.tests.helpers import PYQT5_INSTALLED, PYQT6_INSTALLED, PYSIDE2_INSTALLED, PYSIDE6_INSTALLED
5+
from glue.tests.helpers import make_skipper
6+
7+
PYQT5_INSTALLED, requires_pyqt5 = make_skipper('PyQt5')
8+
PYQT6_INSTALLED, requires_pyqt6 = make_skipper('PyQt6')
9+
PYSIDE2_INSTALLED, requires_pyside2 = make_skipper('PySide2')
10+
PYSIDE6_INSTALLED, requires_pyside6 = make_skipper('PySide6')
11+
12+
PYQT_INSTALLED = PYQT5_INSTALLED or PYQT6_INSTALLED
13+
PYSIDE_INSTALLED = PYSIDE2_INSTALLED or PYSIDE6_INSTALLED
14+
QT_INSTALLED = PYQT_INSTALLED or PYSIDE_INSTALLED
415

5-
requires_pyqt = pytest.mark.skipif(str(not PYQT5_INSTALLED and not PYQT6_INSTALLED),
16+
requires_pyqt = pytest.mark.skipif(str(not PYQT_INSTALLED),
617
reason='An installation of PyQt is required')
718

8-
requires_pyside = pytest.mark.skipif(str(not PYSIDE2_INSTALLED and not PYSIDE6_INSTALLED),
19+
requires_pyside = pytest.mark.skipif(str(not PYSIDE_INSTALLED),
920
reason='An installation of PySide is required')
21+
22+
requires_qt = pytest.mark.skipif(str(not QT_INSTALLED),
23+
reason='An installation of Qt is required')
24+
25+
PYQT_GT_59, _ = make_skipper('PyQt5', version='5.10')
26+
27+
requires_pyqt_gt_59_or_pyside = pytest.mark.skipif(str(not (PYQT_GT_59 or PYQT6_INSTALLED or
28+
PYSIDE_INSTALLED)),
29+
reason='Requires PyQt > 5.9 or PySide2/6')

glue_qt/tests/test_session_back_compat.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import pytest
55
import numpy as np
66

7-
from glue.tests.helpers import requires_astropy, requires_h5py, requires_qt
8-
from glue_qt.tests.helpers import requires_pyqt
7+
from glue.tests.helpers import requires_astropy, requires_h5py
8+
from glue_qt.tests.helpers import requires_pyqt, requires_qt
99
from glue.core.component import CoordinateComponent, Component
1010
from glue.core.state import GlueUnSerializer
1111
from glue.core.component_id import PixelComponentID

glue_qt/viewers/profile/mouse_mode.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from echo import CallbackProperty, delay_callback
22
from glue.core.state_objects import State
33
from glue.viewers.matplotlib.mouse_mode import MouseMode
4+
from matplotlib.patches import Rectangle
45

56
__all__ = ['NavigateMouseMode', 'RangeMouseMode']
67

@@ -155,11 +156,12 @@ def _update_artist(self, *args):
155156
else:
156157
self._lines[0].set_data([self.state.x_min, self.state.x_min], [0, 1])
157158
self._lines[1].set_data([self.state.x_max, self.state.x_max], [0, 1])
158-
self._interval.set_xy([[self.state.x_min, 0],
159-
[self.state.x_min, 1],
160-
[self.state.x_max, 1],
161-
[self.state.x_max, 0],
162-
[self.state.x_min, 0]])
159+
if isinstance(self._interval, Rectangle):
160+
self._interval.set_xy([self.state.x_min, self.state.x_max])
161+
else:
162+
self._interval.set_xy([[self.state.x_min, 0], [self.state.x_min, 1],
163+
[self.state.x_max, 1], [self.state.x_max, 0],
164+
[self.state.x_min, 0]])
163165
else:
164166
if self.state.x_min is not None and self.state.x_max is not None:
165167
self._lines = (self._axes.axvline(self.state.x_min, color=COLOR),

glue_qt/viewers/table/tests/test_data_viewer.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import pytest
22
import numpy as np
3+
from packaging.version import Version
34
from unittest.mock import MagicMock, patch
45

5-
from qtpy import QtCore, QtGui
6+
from qtpy import QtCore, QtGui, QT_VERSION
67
from qtpy.QtCore import Qt
78

8-
from glue_qt.utils import get_qapp, process_events
9-
from glue.core import Data, DataCollection, BaseData
10-
from glue_qt.utils import qt_to_mpl_color
119
from glue_qt.app import GlueApplication
10+
from glue.core import Data, DataCollection, BaseData
11+
from glue_qt.tests.helpers import requires_pyqt_gt_59_or_pyside
12+
from glue_qt.utils import get_qapp, process_events, qt_to_mpl_color
1213

1314
from ..data_viewer import DataTableModel, TableViewer
1415

@@ -178,10 +179,15 @@ def press_key(key):
178179
press_key(Qt.Key_Down)
179180
press_key(Qt.Key_Down)
180181

181-
process_events()
182-
182+
process_events(0.5)
183183
indices = selection.selectedRows()
184184

185+
# On newer Qt6 down keys seem to be a bit "sticky"...
186+
187+
if len(indices) == 0 or indices[0].row() < 2:
188+
press_key(Qt.Key_Down)
189+
indices = selection.selectedRows()
190+
185191
# We make sure that the third row is selected
186192

187193
assert len(indices) == 1
@@ -460,6 +466,7 @@ def test_incompatible_subset():
460466
assert refresh2.call_count == 0
461467

462468

469+
@requires_pyqt_gt_59_or_pyside
463470
def test_table_incompatible_attribute():
464471
"""
465472
Regression test for a bug where the table viewer generates an
@@ -628,7 +635,7 @@ def press_key(key):
628635
press_key(Qt.Key_Down)
629636
press_key(Qt.Key_Enter)
630637

631-
process_events()
638+
process_events(0.5)
632639

633640
# Check that the table model is still the same, which it
634641
# should be since we aren't changing the viewer Data
@@ -641,7 +648,9 @@ def press_key(key):
641648
color = d.subsets[0].style.color
642649
colors[1] = color
643650

644-
check_values_and_color(post_model, data, colors)
651+
# Skip on higher versions, where `PyQt6.QtGui.QBrush` is not correctly cleared on 2nd pass
652+
if Version(QT_VERSION) < Version('6.6'):
653+
check_values_and_color(post_model, data, colors)
645654

646655

647656
def test_table_widget_filter(tmpdir):
@@ -799,7 +808,7 @@ def test_table_sorts_after_update_data():
799808

800809
d.update_components({d.components[2]: [3.2, 1.2, 4.5, 2.5, 3.4]})
801810

802-
process_events()
811+
process_events(0.5)
803812

804813
data = {'a': [2, 4, 1, 5, 3],
805814
'b': [1.2, 2.5, 3.2, 3.4, 4.5],

tox.ini

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tox]
22
envlist =
3-
py{38,39,310,311}-{codestyle,test,docs}-{pyqt514,pyqt515,pyside514,pyside515,pyqt63,pyside63}-all-{dev,legacy}
3+
py{38,39,310,311,312}-{codestyle,test,docs}-{pyqt514,pyqt515,pyside514,pyside515,pyqt63,pyqt64,pyqt66,pyqt67,pyqt65,pyqt63,pyside66,pyside67}-all-{dev,legacy}
44
requires = pip >= 18.0
55
setuptools >= 30.3.0
66

@@ -27,10 +27,19 @@ deps =
2727
pyqt63: PyQt6-Qt6==6.3.*
2828
pyqt64: PyQt6==6.4.*
2929
pyqt64: PyQt6-Qt6==6.4.*
30+
pyqt65: PyQt6-Qt6==6.5.*
31+
pyqt65: PyQt6==6.5.*
32+
pyqt66: PyQt6-Qt6==6.6.*
33+
pyqt66: PyQt6==6.6.*
34+
pyqt67: PyQt6-Qt6==6.7.*
35+
pyqt67: PyQt6==6.7.*
3036
pyside514: PySide2==5.14.*
3137
pyside515: PySide2==5.15.*
3238
pyside63: PySide6==6.3.*
3339
pyside64: PySide6==6.4.*
40+
pyside65: PySide6==6.5.*
41+
pyside66: PySide6==6.6.*
42+
pyside67: PySide6==6.7.*
3443
dev: git+https://github.com/numpy/numpy
3544
dev: git+https://github.com/astropy/astropy
3645
lts: astropy==5.0.*

0 commit comments

Comments
 (0)