Skip to content

Commit f81f5a7

Browse files
authored
Merge pull request #1381 from effigies/py313
Add broad testing for Python 3.13
2 parents b6ca0d6 + 1e8043d commit f81f5a7

File tree

7 files changed

+144
-97
lines changed

7 files changed

+144
-97
lines changed

.github/workflows/test.yml

+52-20
Original file line numberDiff line numberDiff line change
@@ -113,31 +113,49 @@ jobs:
113113
fail-fast: false
114114
matrix:
115115
os: ['ubuntu-latest', 'windows-latest', 'macos-13', 'macos-latest']
116-
python-version: ["3.9", "3.10", "3.11", "3.12"]
117-
architecture: ['x64', 'x86', 'arm64']
116+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.13t"]
117+
architecture: ['x86', 'x64', 'arm64']
118118
dependencies: ['full', 'pre']
119119
include:
120120
# Basic dependencies only
121121
- os: ubuntu-latest
122-
python-version: 3.9
122+
python-version: "3.9"
123+
architecture: 'x64'
123124
dependencies: 'none'
124125
# Absolute minimum dependencies
125126
- os: ubuntu-latest
126-
python-version: 3.9
127+
python-version: "3.9"
128+
architecture: 'x64'
127129
dependencies: 'min'
128-
# NoGIL
129-
- os: ubuntu-latest
130-
python-version: '3.13-dev'
131-
dependencies: 'dev'
132130
exclude:
133-
# x86 for Windows + Python<3.12
134-
- os: ubuntu-latest
135-
architecture: x86
131+
# Use ubuntu-latest to cover the whole range of Python. For Windows
132+
# and OSX, checking oldest and newest should be sufficient.
133+
- os: windows-latest
134+
python-version: "3.10"
135+
- os: windows-latest
136+
python-version: "3.11"
137+
- os: windows-latest
138+
python-version: "3.12"
139+
- os: macos-13
140+
python-version: "3.10"
136141
- os: macos-13
142+
python-version: "3.11"
143+
- os: macos-13
144+
python-version: "3.12"
145+
- os: macos-latest
146+
python-version: "3.10"
147+
- os: macos-latest
148+
python-version: "3.11"
149+
- os: macos-latest
150+
python-version: "3.12"
151+
152+
## Unavailable architectures
153+
# x86 is available for Windows
154+
- os: ubuntu-latest
137155
architecture: x86
138156
- os: macos-latest
139157
architecture: x86
140-
- python-version: '3.12'
158+
- os: macos-13
141159
architecture: x86
142160
# arm64 is available for macos-14+
143161
- os: ubuntu-latest
@@ -149,6 +167,8 @@ jobs:
149167
# x64 is not available for macos-14+
150168
- os: macos-latest
151169
architecture: x64
170+
171+
## Reduced support
152172
# Drop pre tests for macos-13
153173
- os: macos-13
154174
dependencies: pre
@@ -167,25 +187,37 @@ jobs:
167187
with:
168188
submodules: recursive
169189
fetch-depth: 0
190+
- name: Install the latest version of uv
191+
uses: astral-sh/setup-uv@v4
170192
- name: Set up Python ${{ matrix.python-version }}
171-
if: "!endsWith(matrix.python-version, '-dev')"
193+
if: "!endsWith(matrix.python-version, 't')"
172194
uses: actions/setup-python@v5
173195
with:
174196
python-version: ${{ matrix.python-version }}
175197
architecture: ${{ matrix.architecture }}
176198
allow-prereleases: true
177199
- name: Set up Python ${{ matrix.python-version }}
178-
if: endsWith(matrix.python-version, '-dev')
179-
uses: deadsnakes/[email protected]
180-
with:
181-
python-version: ${{ matrix.python-version }}
182-
nogil: true
200+
if: endsWith(matrix.python-version, 't')
201+
run: |
202+
echo "UV_PYTHON=${IMPL}-${VERSION}-${OS%-*}-${ARCH}-${LIBC}" >> $GITHUB_ENV
203+
source $GITHUB_ENV
204+
uv python install $UV_PYTHON
205+
env:
206+
IMPL: cpython
207+
VERSION: ${{ matrix.python-version }}
208+
# uv expects linux|macos|windows, we can drop the -* but need to rename ubuntu
209+
OS: ${{ matrix.os == 'ubuntu-latest' && 'linux' || matrix.os }}
210+
# uv expects x86, x86_64, aarch64 (among others)
211+
ARCH: ${{ matrix.architecture == 'x64' && 'x86_64' ||
212+
matrix.architecture == 'arm64' && 'aarch64' ||
213+
matrix.architecture }}
214+
# windows and macos have no options, gnu is the only option for the archs
215+
LIBC: ${{ matrix.os == 'ubuntu-latest' && 'gnu' || 'none' }}
183216
- name: Display Python version
184217
run: python -c "import sys; print(sys.version)"
185218
- name: Install tox
186219
run: |
187-
python -m pip install --upgrade pip
188-
python -m pip install tox tox-gh-actions
220+
uv tool install -v tox --with=git+https://github.com/effigies/tox-gh-actions@abiflags --with=tox-uv
189221
- name: Show tox config
190222
run: tox c
191223
- name: Run tox

doc-requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Auto-generated by tools/update_requirements.py
22
-r requirements.txt
33
sphinx
4-
matplotlib>=1.5.3
4+
matplotlib>=3.5
55
numpydoc
66
texext
77
tomli; python_version < '3.11'

min-requirements.txt

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
# Auto-generated by tools/update_requirements.py
2-
numpy ==1.20
3-
packaging ==17
4-
importlib_resources ==1.3; python_version < '3.9'
1+
# This file was autogenerated by uv via the following command:
2+
# uv pip compile --resolution lowest-direct --python 3.9 -o min-requirements.txt pyproject.toml
3+
importlib-resources==5.12.0
4+
# via nibabel (pyproject.toml)
5+
numpy==1.22.0
6+
# via nibabel (pyproject.toml)
7+
packaging==20.0
8+
# via nibabel (pyproject.toml)
9+
pyparsing==3.2.0
10+
# via packaging
11+
six==1.16.0
12+
# via packaging
13+
typing-extensions==4.6.0
14+
# via nibabel (pyproject.toml)
15+
zipp==3.20.2
16+
# via importlib-resources

pyproject.toml

+16-11
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,15 @@ nib-roi = "nibabel.cmdline.roi:main"
5151
parrec2nii = "nibabel.cmdline.parrec2nii:main"
5252

5353
[project.optional-dependencies]
54-
all = ["nibabel[dicomfs,minc2,spm,zstd]"]
54+
all = ["nibabel[dicomfs,indexed_gzip,minc2,spm,zstd]"]
5555
# Features
56+
indexed_gzip = ["indexed_gzip >=1.6"]
5657
dicom = ["pydicom >=2.3"]
57-
dicomfs = ["nibabel[dicom]", "pillow"]
58-
minc2 = ["h5py"]
59-
spm = ["scipy"]
60-
zstd = ["pyzstd >= 0.14.3"]
58+
dicomfs = ["nibabel[dicom]", "pillow >=8.4"]
59+
minc2 = ["h5py >=3.5"]
60+
spm = ["scipy >=1.8"]
61+
viewers = ["matplotlib >=3.5"]
62+
zstd = ["pyzstd >=0.15.2"]
6163
# For doc and test, make easy to use outside of tox
6264
# tox should use these with extras instead of duplicating
6365
doc = [
@@ -68,12 +70,12 @@ doc = [
6870
"tomli; python_version < '3.11'",
6971
]
7072
test = [
71-
"pytest",
72-
"pytest-doctestplus",
73-
"pytest-cov",
74-
"pytest-httpserver",
75-
"pytest-xdist",
76-
"coverage>=7.2",
73+
"pytest >=6",
74+
"pytest-doctestplus >=1",
75+
"pytest-cov >=2.11",
76+
"pytest-httpserver >=1.0.7",
77+
"pytest-xdist >=3.5",
78+
"coverage[toml]>=7.2",
7779
]
7880
# Remaining: Simpler to centralize in tox
7981
dev = ["tox"]
@@ -200,3 +202,6 @@ enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
200202
[tool.codespell]
201203
skip = "*/data/*,./nibabel-data"
202204
ignore-words-list = "ans,te,ue,ist,nin,nd,ccompiler,ser"
205+
206+
[tool.uv.pip]
207+
only-binary = ["numpy", "scipy", "h5py"]

requirements.txt

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Auto-generated by tools/update_requirements.py
2-
numpy >=1.20
3-
packaging >=17
4-
importlib_resources >=1.3; python_version < '3.9'
2+
numpy >=1.22
3+
packaging >=20
4+
importlib_resources >=5.12; python_version < '3.12'
5+
typing_extensions >=4.6; python_version < '3.13'

tools/update_requirements.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
import sys
33
from pathlib import Path
44

5-
import tomli
5+
try:
6+
import tomllib
7+
except ImportError:
8+
import tomli as tomllib
69

710
if sys.version_info < (3, 6):
811
print('This script requires Python 3.6 to work correctly')
@@ -15,7 +18,7 @@
1518
doc_reqs = repo_root / 'doc-requirements.txt'
1619

1720
with open(pyproject_toml, 'rb') as fobj:
18-
config = tomli.load(fobj)
21+
config = tomllib.load(fobj)
1922
requirements = config['project']['dependencies']
2023
doc_requirements = config['project']['optional-dependencies']['doc']
2124

@@ -27,9 +30,10 @@
2730
lines[1:-1] = requirements
2831
reqs.write_text('\n'.join(lines))
2932

30-
# Write minimum requirements
31-
lines[1:-1] = [req.replace('>=', '==').replace('~=', '==') for req in requirements]
32-
min_reqs.write_text('\n'.join(lines))
33+
# # Write minimum requirements
34+
# lines[1:-1] = [req.replace('>=', '==').replace('~=', '==') for req in requirements]
35+
# min_reqs.write_text('\n'.join(lines))
36+
print(f"To update {min_reqs.name}, use `uv pip compile` (see comment at top of file).")
3337

3438
# Write documentation requirements
3539
lines[1:-1] = ['-r requirements.txt'] + doc_requirements

tox.ini

+46-53
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,14 @@
55
[tox]
66
requires =
77
tox>=4
8+
tox-uv
89
envlist =
910
# No preinstallations
10-
py3{9,10,11,12,13}-none
11-
# Minimum Python
12-
py39-{min,full}
13-
# x86 support range
14-
py3{9,10,11}-{full,pre}-{x86,x64}
15-
py3{9,10,11}-pre-{x86,x64}
16-
# x64-only range
17-
py3{12,13}-{full,pre}-x64
18-
# Special environment for numpy 2.0-dev testing
19-
py313-dev-x64
11+
py3{9,10,11,12,13,13t}-none
12+
# Minimum Python with minimum deps
13+
py39-min
14+
# Run full and pre dependencies against all archs
15+
py3{9,10,11,12,13,13t}-{full,pre}-{x86,x64,arm64}
2016
install
2117
doctest
2218
style
@@ -31,12 +27,12 @@ python =
3127
3.11: py311
3228
3.12: py312
3329
3.13: py313
30+
3.13t: py313t
3431

3532
[gh-actions:env]
3633
DEPENDS =
37-
none: none, install
34+
none: none
3835
pre: pre
39-
dev: dev
4036
full: full, install
4137
min: min
4238

@@ -48,14 +44,8 @@ ARCH =
4844
[testenv]
4945
description = Pytest with coverage
5046
labels = test
51-
install_command =
52-
python -I -m pip install -v \
53-
dev: --only-binary numpy,scipy,h5py \
54-
!dev: --only-binary numpy,scipy,h5py,pillow,matplotlib \
55-
pre,dev: --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple \
56-
{opts} {packages}
5747
pip_pre =
58-
pre,dev: true
48+
pre: true
5949
pass_env =
6050
# getpass.getuser() sources for Windows:
6151
LOGNAME
@@ -70,41 +60,45 @@ pass_env =
7060
NO_COLOR
7161
CLICOLOR
7262
CLICOLOR_FORCE
63+
# uv needs help in this case
64+
py313t-x86: UV_PYTHON
7365
set_env =
74-
py313: PYTHON_GIL=0
75-
extras = test
66+
pre: PIP_EXTRA_INDEX_URL=https://pypi.anaconda.org/scientific-python-nightly-wheels/simple
67+
pre: UV_EXTRA_INDEX_URL=https://pypi.anaconda.org/scientific-python-nightly-wheels/simple
68+
py313t: PYTHONGIL={env:PYTHONGIL:0}
69+
extras =
70+
test
71+
72+
# Simple, thanks Hugo and Paul
73+
!none: dicomfs
74+
!none: indexed_gzip
75+
76+
# Minimum dependencies
77+
min: minc2
78+
min: spm
79+
min: viewers
80+
min: zstd
81+
82+
# Matplotlib has wheels for everything except win32 (x86)
83+
{full,pre}-{x,arm}64: viewers
84+
85+
# Nightly, but not released cp313t wheels for: scipy
86+
# When released, remove the py3* line and add full to the pre line
87+
py3{9,10,11,12,13}-full-{x,arm}64: spm
88+
pre-{x,arm}64: spm
89+
90+
# No cp313t wheels for: h5py, pyzstd
91+
py3{9,10,11,12,13}-{full,pre}-{x,arm}64: minc2
92+
py3{9,10,11,12,13}-{full,pre}-{x,arm}64: zstd
93+
94+
# win32 (x86) wheels still exist for scipy+py39
95+
py39-full-x86: spm
96+
7697
deps =
77-
# General minimum dependencies: pin based on API usage
78-
# matplotlib 3.5 requires packaging 20
79-
min: packaging ==20
80-
min: importlib_resources ==5.12; python_version < '3.12'
81-
min: typing_extensions ==4.6; python_version < '3.13'
82-
# NEP29/SPEC0 + 1yr: Test on minor release series within the last 3 years
83-
# We're extending this to all optional dependencies
84-
# This only affects the range that we test on; numpy is the only non-optional
85-
# dependency, and will be the only one to affect pip environment resolution.
86-
min: numpy ==1.22
87-
min: h5py ==3.5
88-
min: indexed_gzip ==1.6
89-
min: matplotlib ==3.5
90-
min: pillow ==8.4
91-
min: pydicom ==2.3
92-
min: pyzstd ==0.15.2
93-
min: scipy ==1.8
94-
# Numpy 2.0 is a major breaking release; we cannot put much effort into
95-
# supporting until it's at least RC stable
96-
dev: numpy >=2.1.dev0
97-
# Scipy stopped producing win32 wheels at py310
98-
py39-full-x86,x64,arm64: scipy >=1.8
99-
# Matplotlib depends on scipy, so cannot be built for py310 on x86
100-
py39-full-x86,x64,arm64: matplotlib >=3.5
101-
# h5py stopped producing win32 wheels at py39
102-
{full,pre}-{x64,arm64}: h5py >=3.5
103-
full,pre,dev: pillow >=8.4
104-
full,pre: indexed_gzip >=1.6
105-
full,pre,dev: pyzstd >=0.15.2
106-
full,pre: pydicom >=2.3
107-
dev: pydicom @ git+https://github.com/pydicom/pydicom.git@main
98+
pre: pydicom @ git+https://github.com/pydicom/pydicom.git@main
99+
100+
uv_resolution =
101+
min: lowest-direct
108102

109103
commands =
110104
pytest --doctest-modules --doctest-plus \
@@ -118,7 +112,6 @@ description = Install and verify import succeeds
118112
labels = test
119113
deps =
120114
extras =
121-
install_command = python -I -m pip install {opts} {packages}
122115
commands =
123116
python -c "import nibabel; print(nibabel.__version__)"
124117

0 commit comments

Comments
 (0)