Skip to content

Commit 17ec0d8

Browse files
build: support python 3.14 (#721)
* update pyproject * fix magic-siganture * add py3.14 test * ignore matplotlib deprecations * pin numpy <2 for pyside2 * change numpy pins * change numpy pins * fix pyproject numpy * don't clean on CI * style(pre-commit.ci): auto fixes [...] * try tqdm tmon * style(pre-commit.ci): auto fixes [...] * try different os * build docs on 3.13 * drop windows version again --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent cf45221 commit 17ec0d8

File tree

5 files changed

+45
-18
lines changed

5 files changed

+45
-18
lines changed

.github/workflows/deploy_docs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ jobs:
1717
fetch-depth: 0
1818
- uses: astral-sh/setup-uv@v7
1919
with:
20+
python-version: "3.13"
2021
enable-cache: true
2122

2223
- name: Deploy docs to GitHub Pages

.github/workflows/test_and_deploy.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
strategy:
2424
fail-fast: false
2525
matrix:
26-
python-version: ["3.10", "3.11"]
26+
python-version: ["3.10", "3.13"]
2727
os: [ubuntu-latest, macos-latest, windows-latest]
2828
add-group: [pyqt6, pyside6]
2929
include:
@@ -37,22 +37,22 @@ jobs:
3737
- python-version: "3.9"
3838
os: ubuntu-latest
3939
add-group: pyside2
40-
- python-version: "3.11"
40+
- python-version: "3.12"
4141
os: windows-latest
4242
add-group: pyqt5
4343
- python-version: "3.10"
44-
os: ubuntu-latest
44+
os: windows-latest
4545
add-group: pyside2
4646
- python-version: "3.12"
4747
os: ubuntu-latest
4848
add-group: pyqt6
4949
- python-version: "3.12"
5050
os: ubuntu-latest
5151
add-group: pyside6
52-
- python-version: "3.13"
52+
- python-version: "3.14"
5353
os: ubuntu-latest
5454
add-group: pyside6
55-
- python-version: "3.13"
55+
- python-version: "3.14"
5656
os: windows-latest
5757
add-group: pyqt6
5858

pyproject.toml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ classifiers = [
2828
"Programming Language :: Python :: 3.11",
2929
"Programming Language :: Python :: 3.12",
3030
"Programming Language :: Python :: 3.13",
31+
"Programming Language :: Python :: 3.14",
3132
"Topic :: Desktop Environment",
3233
"Topic :: Software Development",
3334
"Topic :: Software Development :: User Interfaces",
@@ -47,7 +48,11 @@ dependencies = [
4748
# extras
4849
# https://peps.python.org/pep-0621/#dependencies-optional-dependencies
4950
[project.optional-dependencies]
50-
pyqt5 = ["PyQt5>=5.15.8", "pyqt5-qt5<=5.15.2; sys_platform == 'win32'"]
51+
pyqt5 = [
52+
"PyQt5>=5.15.8",
53+
"pyqt5-qt5<=5.15.2; sys_platform == 'win32'",
54+
"pyqt5-qt5>5.15.2; sys_platform != 'win32'",
55+
]
5156
pyqt6 = ["pyqt6>=6.4.0"]
5257
pyside2 = ["pyside2>=5.15"]
5358
pyside6 = ["pyside6>=6.4.0"]
@@ -62,6 +67,7 @@ third-party-support = [
6267
"attrs>=25.3.0",
6368
"ipykernel>=6.29.5",
6469
"matplotlib>=3.9.4",
70+
"numpy>=2.1.0; python_version >= '3.13'",
6571
"numpy>=1.26.4",
6672
"pandas>=2.2.3; python_version >= '3.11'",
6773
"pandas>=2.1",
@@ -77,7 +83,7 @@ test = [
7783
]
7884
pyqt5 = ["magicgui[pyqt5]", { include-group = "test-qt" }]
7985
pyqt6 = ["magicgui[pyqt6]", { include-group = "test-qt" }]
80-
pyside2 = ["magicgui[pyside2]", { include-group = "test-qt" }]
86+
pyside2 = ["magicgui[pyside2]", { include-group = "test-qt" }, "numpy<2; python_version < '3.13'"]
8187
pyside6 = ["magicgui[pyside6]", { include-group = "test-qt" }]
8288
dev = [
8389
{ include-group = "test" },
@@ -128,7 +134,7 @@ line-length = 88
128134
target-version = "py39"
129135
src = ["src", "tests"]
130136
fix = true
131-
# unsafe-fixes = true
137+
unsafe-fixes = true
132138

133139
[tool.ruff.lint]
134140
pydocstyle = { convention = "numpy" }
@@ -178,6 +184,7 @@ filterwarnings = [
178184
"ignore:.*read_binary is deprecated:",
179185
"ignore:Pickle, copy, and deepcopy support:DeprecationWarning",
180186
"ignore:'count' is passed as positional argument::vispy",
187+
"ignore::DeprecationWarning:matplotlib",
181188
]
182189

183190
# https://mypy.readthedocs.io/en/stable/config_file.html

src/magicgui/signature.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,19 @@ def __repr__(self) -> str:
182182
rep = rep.replace(": NoneType = ", "=")
183183
return rep
184184

185-
def __str__(self) -> str:
186-
"""Return string representation of the Parameter in a signature."""
185+
def _format(self, *, quote_annotation_strings: bool = True) -> str:
186+
"""Return formatted string for use in Signature.format() (Python 3.14+)."""
187187
hint, _ = get_args(self.annotation)
188-
return str(
189-
inspect.Parameter(
190-
self.name, self.kind, default=self.default, annotation=hint
191-
)
188+
param = inspect.Parameter(
189+
self.name, self.kind, default=self.default, annotation=hint
192190
)
191+
if hasattr(param, "_format"): # python 3.14+
192+
return param._format(quote_annotation_strings=quote_annotation_strings) # type: ignore[no-any-return]
193+
return str(param)
194+
195+
def __str__(self) -> str:
196+
"""Return string representation of the Parameter in a signature."""
197+
return self._format()
193198

194199
def to_widget(
195200
self,

tests/conftest.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1+
import os
2+
13
import pytest
24

35
from magicgui.application import use_app
46

7+
# Disable tqdm's TMonitor thread to prevent race conditions with Qt threading
8+
# that can cause intermittent segfaults on CI (especially with PySide6 on Linux).
9+
# See: https://github.com/tqdm/tqdm/issues/469
10+
try:
11+
from tqdm import tqdm as _tqdm_std
12+
13+
_tqdm_std.monitor_interval = 0
14+
except ImportError:
15+
pass
16+
517

618
@pytest.fixture(scope="session")
719
def qapp():
@@ -14,10 +26,12 @@ def qapp():
1426
@pytest.fixture(autouse=True, scope="function")
1527
def always_qapp(qapp):
1628
yield qapp
17-
for w in qapp.topLevelWidgets():
18-
w.close()
19-
w.deleteLater()
20-
qapp.processEvents()
29+
if not os.getenv("CI"):
30+
# I suspect, but can't prove, that this code causes occasional segfaults on CI.
31+
for w in qapp.topLevelWidgets():
32+
w.close()
33+
w.deleteLater()
34+
qapp.processEvents()
2135

2236

2337
@pytest.fixture(autouse=True, scope="function")

0 commit comments

Comments
 (0)