diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9e5ddd516..a741a4071 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -113,17 +113,17 @@ jobs: fail-fast: false matrix: os: ['ubuntu-latest', 'windows-latest', 'macos-13', 'macos-latest'] - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12"] architecture: ['x64', 'x86', 'arm64'] dependencies: ['full', 'pre'] include: # Basic dependencies only - os: ubuntu-latest - python-version: 3.8 + python-version: 3.9 dependencies: 'none' # Absolute minimum dependencies - os: ubuntu-latest - python-version: 3.8 + python-version: 3.9 dependencies: 'min' # NoGIL - os: ubuntu-latest @@ -153,10 +153,10 @@ jobs: - os: macos-13 dependencies: pre # Drop pre tests for SPEC-0-unsupported Python versions - - python-version: '3.8' - dependencies: pre - python-version: '3.9' dependencies: pre + - python-version: '3.10' + dependencies: pre env: DEPENDS: ${{ matrix.dependencies }} diff --git a/.mailmap b/.mailmap index 7b5dfa0d4..43932c865 100644 --- a/.mailmap +++ b/.mailmap @@ -75,6 +75,7 @@ Oliver P. Hinds Or Duek Oscar Esteban Paul McCarthy +Paul McCarthy Reinder Vos de Wael Roberto Guidotti Roberto Guidotti diff --git a/.zenodo.json b/.zenodo.json index 553aba054..250611d54 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -25,6 +25,11 @@ "name": "Cipollini, Ben", "orcid": "0000-0002-7782-0790" }, + { + "affiliation": "CEA", + "name": "Papadopoulos Orfanos, Dimitri", + "orcid": "0000-0002-1242-8990" + }, { "name": "McCarthy, Paul" }, @@ -78,13 +83,11 @@ "orcid": "0000-0001-7159-1387" }, { - "name": "Wang, Hao-Ting", - "orcid": "0000-0003-4078-2038" + "name": "Moloney, Brendan" }, { - "affiliation": "CEA", - "name": "Papadopoulos Orfanos, Dimitri", - "orcid": "0000-0002-1242-8990" + "name": "Wang, Hao-Ting", + "orcid": "0000-0003-4078-2038" }, { "affiliation": "Harvard University - Psychology", @@ -123,9 +126,6 @@ { "name": "S\u00f3lon, Anibal" }, - { - "name": "Moloney, Brendan" - }, { "name": "Morency, F\u00e9lix C." }, @@ -177,6 +177,11 @@ { "name": "Van, Andrew" }, + { + "affiliation": "Brigham and Women's Hospital, Mass General Brigham/Harvard Medical School", + "name": "Legarreta, Jon Haitz", + "orcid": "0000-0002-9661-1396" + }, { "affiliation": "Google", "name": "Gorgolewski, Krzysztof J.", @@ -203,6 +208,9 @@ { "name": "Baker, Eric M." }, + { + "name": "Koudoro, Serge" + }, { "name": "Hayashi, Soichi" }, @@ -220,14 +228,14 @@ "name": "Esteban, Oscar", "orcid": "0000-0001-8435-6191" }, - { - "name": "Koudoro, Serge" - }, { "affiliation": "University College London", "name": "P\u00e9rez-Garc\u00eda, Fernando", "orcid": "0000-0001-9090-3024" }, + { + "name": "Becq, Guillaume" + }, { "name": "Dock\u00e8s, J\u00e9r\u00f4me" }, @@ -270,9 +278,9 @@ "orcid": "0000-0003-1076-5122" }, { - "affiliation": "Brigham and Women's Hospital, Mass General Brigham/Harvard Medical School", - "name": "Legarreta, Jon Haitz", - "orcid": "0000-0002-9661-1396" + "affiliation": "Polytechnique Montr\u00e9al, Montr\u00e9al, CA", + "name": "Newton, Joshua", + "orcid": "0009-0005-6963-3812" }, { "name": "Hahn, Kevin S." @@ -285,6 +293,9 @@ { "name": "Hinds, Oliver P." }, + { + "name": "Sandro" + }, { "name": "Fauber, Bennet" }, @@ -391,11 +402,6 @@ }, { "name": "freec84" - }, - { - "affiliation": "Polytechnique Montréal, Montréal, CA", - "name": "Newton, Joshua", - "orcid": "0009-0005-6963-3812" } ], "keywords": [ diff --git a/Changelog b/Changelog index 24e89095f..f72a6a887 100644 --- a/Changelog +++ b/Changelog @@ -25,31 +25,72 @@ Eric Larson (EL), Demian Wassermann, Stephan Gerhard and Ross Markello (RM). References like "pr/298" refer to github pull request numbers. -Upcoming release (To be determined) -=================================== +5.3.0 (Tuesday 8 October 2024) +============================== + +This release primarily adds support for Python 3.13 and Numpy 2.0. + +NiBabel 6.0 will drop support for Numpy 1.x. New features ------------ +* Update NIfTI extension protocol to include ``.content : bytes``, ``.text : str`` and ``.json : dict`` + properties for accessing extension contents. Exceptions will be raised on ``.text`` and ``.json`` if + conversion fails. (pr/1336) (CM) Enhancements ------------ - * Ability to read data from many multiframe DICOM files that previously generated errors +* Ability to read data from many multiframe DICOM files that previously generated errors (pr/1340) + (Brendan Moloney, reviewed by CM) +* ``nib-nifti-dx`` now supports NIfTI-2 files with a ``--nifti2`` flag (pr/1323) (CM) +* Update :mod:`nibabel.streamlines.tractogram` to support ragged arrays. (pr/1291) + (Serge Koudoro, reviewed by CM) +* Filter numpy ``UserWarning`` on ``np.finfo(np.longdouble)``. This can occur on + Windows systems, but it's done in the context of checking for the problem that + is being warned against, so there's no need to be noisy. (pr/1310) + (Joshua Newton, reviewed by CM) +* Improve error message for for dicomwrapper errors in shape calculation (pr/1302) + (YOH, reviewed by CM) +* Support "flat" ASCII-encoded GIFTI DataArrays (pr/1298) (PM, reviewed by CM) Bug fixes --------- - * Fixed multiframe DICOM issue where data could be flipped along slice dimension relative to the - affine - * Fixed multiframe DICOM issue where ``image_position`` and the translation component in the - ``affine`` could be incorrect - -Documentation -------------- +* Fix location initialization/update in OrthoSlicer3D for permuted axes (pr/1319, pr/1350) + (Guillaume Becq, reviewed by CM) +* Fix DICOM scaling, making frame filtering explicit (pr/1342) (Brendan Moloney, reviewed by CM) +* Fixed multiframe DICOM issue where data could be flipped along slice dimension relative to the + affine (pr/1340) (Brendan Moloney, reviewed by CM) +* Fixed multiframe DICOM issue where ``image_position`` and the translation component in the + ``affine`` could be incorrect (pr/1340) (Brendan Moloney, reviewed by CM) Maintenance ----------- +* Numpy 2.0 compatibility and addressing deprecations in numpy API + (pr/1304, pr/1330, pr/1331, pr/1334, pr/1337) (Jon Haitz Legarreta Gorroño, CM) +* Python 3.13 compatibility (pr/1315) (Sandro from the Fedora Project, reviewed by CM) +* Testing on Python 3.13 with free-threading (pr/1339) (CM) +* Testing on ARM64 Mac OS runners (pr/1320) (CM) +* Proactively address deprecations in coming Python versions (pr/1329, pr/1332, pr/1333) + (Jon Haitz Legarreta Gorroño, reviewed by CM) +* Replace nose-era ``setup()`` and ``teardown()`` functions with pytest equivalents + (pr/1325) (Sandro from the Fedora Project, reviewed by Étienne Mollier and CM) +* Transitioned from blue/isort/flake8 to `ruff `__. (pr/1289) + (Dimitri Papadopoulos, reviewed by CM) +* Vetted and added various rules to the ruff configuration for auto-formatting and style + guide enforcement. (pr/1321, pr/1351, pr/1352, pr/1353, pr/1354, pr/1355, pr/1357, pr/1358, + pr/1359, pr/1360, pr/1361, pr/1362, pr/1363, pr/1364, pr/1368, pr/1369) + (Dimitri Papadopoulos, reviewed by CM) +* Fixing typos when found. (pr/1313, pr/1370) (MB, Dimitri Papadopoulos) +* Applied Repo-Review suggestions (Dimitri Papadopoulos, reviewed by CM) API changes and deprecations ---------------------------- +* Raise :class:`~nibabel.spatialimages.HeaderDataError` from + :func:`~nibabel.nifti1.Nifti1Header.set_qform` if the affine fails to decompose. + This would previously result in :class:`numpy.linalg.LinAlgError`. (pr/1227) (CM) +* The :func:`nibabel.onetime.auto_attr` module can be replaced by :func:`functools.cached_property` + in all supported versions of Python. This alias may be removed in future versions. (pr/1341) (CM) +* Removed the deprecated ``nisext`` (setuptools extensions) package. (pr/1290) (CM, reviewed by MB) 5.2.1 (Monday 26 February 2024) diff --git a/doc/source/conf.py b/doc/source/conf.py index 4255ff184..981165122 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -102,7 +102,7 @@ # General information about the project. project = 'NiBabel' -copyright = f"2006-2023, {authors['name']} <{authors['email']}>" +copyright = f"2006, {authors['name']} <{authors['email']}>" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/doc/source/index.rst b/doc/source/index.rst index 72c731d25..677e81b33 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -130,6 +130,9 @@ contributed code and discussion (in rough order of appearance): * Reinder Vos de Wael * Peter Suter * Blake Dewey +* Guillaume Becq +* Joshua Newton +* Sandro from the Fedora Project License reprise =============== diff --git a/doc/source/installation.rst b/doc/source/installation.rst index 4f747e7fe..983968c50 100644 --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -81,16 +81,16 @@ is for you. Requirements ------------ -.. check these against pyproject.toml - -* Python_ 3.8 or greater -* NumPy_ 1.20 or greater -* Packaging_ 17.0 or greater -* importlib-resources_ 1.3 or greater (or Python 3.9+) -* SciPy_ (optional, for full SPM-ANALYZE support) -* h5py_ (optional, for MINC2 support) -* PyDICOM_ 1.0.0 or greater (optional, for DICOM support) -* `Python Imaging Library`_ (optional, for PNG conversion in DICOMFS) +.. check these against pyproject.toml / tox.ini + +* Python_ 3.9 or greater +* NumPy_ 1.22 or greater +* Packaging_ 20.0 or greater +* importlib-resources_ 5.12 or greater (or Python 3.12+) +* SciPy_ 1.8 or greater (optional, for full SPM-ANALYZE support) +* h5py_ 3.5 or greater (optional, for MINC2 support) +* PyDICOM_ 2.3.0 or greater (optional, for DICOM support) +* `Python Imaging Library`_ 8.4 or greater (optional, for PNG conversion in DICOMFS) * pytest_ (optional, to run the tests) * sphinx_ (optional, to build the documentation) diff --git a/nibabel/__init__.py b/nibabel/__init__.py index aa90540b8..c389c603f 100644 --- a/nibabel/__init__.py +++ b/nibabel/__init__.py @@ -170,10 +170,7 @@ def bench(label=None, verbose=1, extra_argv=None): code : ExitCode Returns the result of running the tests as a ``pytest.ExitCode`` enum """ - try: - from importlib.resources import as_file, files - except ImportError: - from importlib_resources import as_file, files + from importlib.resources import as_file, files args = [] if extra_argv is not None: diff --git a/nibabel/conftest.py b/nibabel/conftest.py index b16a832f2..1d7389e86 100644 --- a/nibabel/conftest.py +++ b/nibabel/conftest.py @@ -10,10 +10,7 @@ @pytest.fixture(scope='session', autouse=True) def legacy_printoptions(): - from packaging.version import Version - - if Version(np.__version__) >= Version('1.22'): - np.set_printoptions(legacy='1.21') + np.set_printoptions(legacy='1.21') @pytest.fixture @@ -24,7 +21,7 @@ def max_digits(): orig_max_str_digits = sys.get_int_max_str_digits() yield sys.set_int_max_str_digits sys.set_int_max_str_digits(orig_max_str_digits) - except AttributeError: # pragma: no cover + except AttributeError: # PY310 # pragma: no cover # Nothing to do for versions of Python that lack these methods # They were added as DoS protection in Python 3.11 and backported to # some other versions. diff --git a/nibabel/filebasedimages.py b/nibabel/filebasedimages.py index c12644a2b..086e31f12 100644 --- a/nibabel/filebasedimages.py +++ b/nibabel/filebasedimages.py @@ -23,7 +23,7 @@ if ty.TYPE_CHECKING: from .filename_parser import ExtensionSpec, FileSpec -FileSniff = ty.Tuple[bytes, str] +FileSniff = tuple[bytes, str] ImgT = ty.TypeVar('ImgT', bound='FileBasedImage') HdrT = ty.TypeVar('HdrT', bound='FileBasedHeader') diff --git a/nibabel/nicom/ascconv.py b/nibabel/nicom/ascconv.py index 6d7243603..2eca5a157 100644 --- a/nibabel/nicom/ascconv.py +++ b/nibabel/nicom/ascconv.py @@ -90,10 +90,7 @@ def assign2atoms(assign_ast, default_class=int): target = target.value prev_target_type = OrderedDict elif isinstance(target, ast.Subscript): - if isinstance(target.slice, ast.Constant): # PY39 - index = target.slice.value - else: # PY38 - index = target.slice.value.n + index = target.slice.value atoms.append(Atom(target, prev_target_type, index)) target = target.value prev_target_type = list diff --git a/nibabel/nifti1.py b/nibabel/nifti1.py index b9c78c81b..f0bd91fc4 100644 --- a/nibabel/nifti1.py +++ b/nibabel/nifti1.py @@ -671,7 +671,7 @@ def _mangle(self, dataset: DicomDataset) -> bytes: (38, 'eval', NiftiExtension), (40, 'matlab', NiftiExtension), (42, 'quantiphyse', NiftiExtension), - (44, 'mrs', NiftiExtension[ty.Dict[str, ty.Any]]), + (44, 'mrs', NiftiExtension[dict[str, ty.Any]]), ), fields=('code', 'label', 'handler'), ) diff --git a/nibabel/spatialimages.py b/nibabel/spatialimages.py index 19677c1a7..a8e899359 100644 --- a/nibabel/spatialimages.py +++ b/nibabel/spatialimages.py @@ -133,6 +133,7 @@ from __future__ import annotations import typing as ty +from functools import cache from typing import Literal import numpy as np @@ -145,11 +146,6 @@ from .viewers import OrthoSlicer3D from .volumeutils import shape_zoom_affine -try: - from functools import cache -except ImportError: # PY38 - from functools import lru_cache as cache - if ty.TYPE_CHECKING: import io from collections.abc import Sequence diff --git a/nibabel/testing/__init__.py b/nibabel/testing/__init__.py index be111747b..b42baf295 100644 --- a/nibabel/testing/__init__.py +++ b/nibabel/testing/__init__.py @@ -17,6 +17,7 @@ import unittest import warnings from contextlib import nullcontext +from importlib.resources import as_file, files from itertools import zip_longest import numpy as np @@ -29,11 +30,6 @@ if ty.TYPE_CHECKING: from importlib.resources.abc import Traversable -try: - from importlib.resources import as_file, files -except ImportError: # PY38 - from importlib_resources import as_file, files - def get_test_data( subdir: ty.Literal['gifti', 'nicom', 'externals'] | None = None, diff --git a/nibabel/testing/np_features.py b/nibabel/testing/np_features.py index 226df6484..dd21aac2c 100644 --- a/nibabel/testing/np_features.py +++ b/nibabel/testing/np_features.py @@ -1,11 +1,11 @@ """Look for changes in numpy behavior over versions""" -from functools import lru_cache +from functools import cache import numpy as np -@lru_cache(maxsize=None) +@cache def memmap_after_ufunc() -> bool: """Return True if ufuncs on memmap arrays always return memmap arrays diff --git a/nibabel/tests/test_arrayproxy.py b/nibabel/tests/test_arrayproxy.py index a79f63bc7..65b913190 100644 --- a/nibabel/tests/test_arrayproxy.py +++ b/nibabel/tests/test_arrayproxy.py @@ -482,9 +482,11 @@ def test_keep_file_open_true_false_invalid(): for test in tests: filetype, kfo, have_igzip, exp_persist, exp_kfo = test - with InTemporaryDirectory(), mock.patch( - 'nibabel.openers.ImageOpener', CountingImageOpener - ), patch_indexed_gzip(have_igzip): + with ( + InTemporaryDirectory(), + mock.patch('nibabel.openers.ImageOpener', CountingImageOpener), + patch_indexed_gzip(have_igzip), + ): fname = f'testdata.{filetype}' # create the test data file if filetype == 'gz': diff --git a/nibabel/tests/test_init.py b/nibabel/tests/test_init.py index d54f55053..d339c4e26 100644 --- a/nibabel/tests/test_init.py +++ b/nibabel/tests/test_init.py @@ -1,14 +1,10 @@ import pathlib import unittest +from importlib.resources import files from unittest import mock import pytest -try: - from importlib.resources import files -except ImportError: - from importlib_resources import files - import nibabel as nib diff --git a/nibabel/tests/test_openers.py b/nibabel/tests/test_openers.py index 0b5879433..05d0e04cd 100644 --- a/nibabel/tests/test_openers.py +++ b/nibabel/tests/test_openers.py @@ -121,8 +121,9 @@ def patch_indexed_gzip(state): values = (True, MockIndexedGzipFile) else: values = (False, GzipFile) - with mock.patch('nibabel.openers.HAVE_INDEXED_GZIP', values[0]), mock.patch( - 'nibabel.openers.IndexedGzipFile', values[1], create=True + with ( + mock.patch('nibabel.openers.HAVE_INDEXED_GZIP', values[0]), + mock.patch('nibabel.openers.IndexedGzipFile', values[1], create=True), ): yield diff --git a/nibabel/tests/test_tripwire.py b/nibabel/tests/test_tripwire.py index d7daefe0b..4bf91923f 100644 --- a/nibabel/tests/test_tripwire.py +++ b/nibabel/tests/test_tripwire.py @@ -16,5 +16,5 @@ def test_tripwire(): with pytest.raises(TripWireError): silly_module_name.do_silly_thing # Check AttributeError can be checked too - with pytest.raises(AttributeError) as err: + with pytest.raises(AttributeError): silly_module_name.__wrapped__ diff --git a/nibabel/volumeutils.py b/nibabel/volumeutils.py index 6e43f7918..d0ebb46a7 100644 --- a/nibabel/volumeutils.py +++ b/nibabel/volumeutils.py @@ -235,7 +235,7 @@ def value_set(self, name: str | None = None) -> OrderedSet: endian_codes = Recoder(_endian_codes) -class DtypeMapper(ty.Dict[ty.Hashable, ty.Hashable]): +class DtypeMapper(dict[ty.Hashable, ty.Hashable]): """Specialized mapper for numpy dtypes We pass this mapper into the Recoder class to deal with numpy dtype diff --git a/pyproject.toml b/pyproject.toml index 18883b90e..b62c0048a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,10 +9,10 @@ authors = [{ name = "NiBabel developers", email = "neuroimaging@python.org" }] maintainers = [{ name = "Christopher Markiewicz" }] readme = "README.rst" license = { text = "MIT License" } -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ - "numpy >=1.20", - "packaging >=17", + "numpy >=1.22", + "packaging >=20", "importlib_resources >=5.12; python_version < '3.12'", "typing_extensions >=4.6; python_version < '3.13'", ] @@ -23,11 +23,11 @@ classifiers = [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering", ] # Version from setuptools_scm @@ -53,7 +53,7 @@ parrec2nii = "nibabel.cmdline.parrec2nii:main" [project.optional-dependencies] all = ["nibabel[dicomfs,minc2,spm,zstd]"] # Features -dicom = ["pydicom >=1.0.0"] +dicom = ["pydicom >=2.3"] dicomfs = ["nibabel[dicom]", "pillow"] minc2 = ["h5py"] spm = ["scipy"] @@ -62,7 +62,7 @@ zstd = ["pyzstd >= 0.14.3"] # tox should use these with extras instead of duplicating doc = [ "sphinx", - "matplotlib>=1.5.3", + "matplotlib>=3.5", "numpydoc", "texext", "tomli; python_version < '3.11'", diff --git a/tools/markdown_release_notes.py b/tools/markdown_release_notes.py index 73bdbf775..cdae474f5 100644 --- a/tools/markdown_release_notes.py +++ b/tools/markdown_release_notes.py @@ -1,14 +1,53 @@ #!/usr/bin/env python import re import sys +from collections import defaultdict +from functools import cache +from operator import call from pathlib import Path +from sphinx.ext.intersphinx import fetch_inventory + CHANGELOG = Path(__file__).parent.parent / 'Changelog' # Match release lines like "5.2.0 (Monday 11 December 2023)" RELEASE_REGEX = re.compile(r"""((?:\d+)\.(?:\d+)\.(?:\d+)) \(\w+ \d{1,2} \w+ \d{4}\)$""") +class MockConfig: + intersphinx_timeout: int | None = None + tls_verify = False + tls_cacerts: str | dict[str, str] | None = None + user_agent: str = '' + + +@call +class MockApp: + srcdir = '' + config = MockConfig() + + +fetch_inv = cache(fetch_inventory) + + +def get_intersphinx(obj): + module = obj.split('.', 1)[0] + + registry = defaultdict(lambda: 'https://docs.python.org/3') + registry.update( + numpy='https://numpy.org/doc/stable', + ) + + base_url = registry[module] + + inventory = fetch_inv(MockApp, '', f'{base_url}/objects.inv') + # Check py: first, then whatever + for objclass in sorted(inventory, key=lambda x: not x.startswith('py:')): + if obj in inventory[objclass]: + return f'{base_url}/{inventory[objclass][obj][2]}' + raise ValueError("Couldn't lookup {obj}") + + def main(): version = sys.argv[1] output = sys.argv[2] @@ -46,7 +85,7 @@ def main(): release_notes = re.sub(r'\n +', ' ', release_notes) # Replace pr/ with # for GitHub - release_notes = re.sub(r'\(pr/(\d+)\)', r'(#\1)', release_notes) + release_notes = re.sub(r'pr/(\d+)', r'#\1', release_notes) # Replace :mod:`package.X` with [package.X](...) release_notes = re.sub( @@ -76,6 +115,14 @@ def main(): r'[\3](https://nipy.org/nibabel/reference/\1.html#\1.\2.\3)', release_notes, ) + # Replace ::`` with intersphinx lookup + for ref in re.findall(r'(:[^:]*:`~?\w[\w.]+\w`)', release_notes): + objclass, tilde, module, obj = re.match(r':([^:]*):`(~?)([\w.]+)\.(\w+)`', ref).groups() + url = get_intersphinx(f'{module}.{obj}') + mdlink = f'[{"" if tilde else module}{obj}]({url})' + release_notes = release_notes.replace(ref, mdlink) + # Replace RST links with Markdown links + release_notes = re.sub(r'`([^<`]*) <([^>]*)>`_+', r'[\1](\2)', release_notes) def python_doc(match): module = match.group(1) @@ -84,10 +131,9 @@ def python_doc(match): release_notes = re.sub(r':meth:`~([\w.]+)\.(\w+)`', python_doc, release_notes) - output.write('## Release notes\n\n') - output.write(release_notes) - - output.close() + with output: + output.write('## Release notes\n\n') + output.write(release_notes) if __name__ == '__main__': diff --git a/tox.ini b/tox.ini index 0e0f81a7a..82c13debc 100644 --- a/tox.ini +++ b/tox.ini @@ -7,14 +7,14 @@ requires = tox>=4 envlist = # No preinstallations - py3{8,9,10,11,12}-none + py3{9,10,11,12,13}-none # Minimum Python - py38-{min,full} + py39-{min,full} # x86 support range py3{9,10,11}-{full,pre}-{x86,x64} py3{9,10,11}-pre-{x86,x64} # x64-only range - py312-{full,pre}-x64 + py3{12,13}-{full,pre}-x64 # Special environment for numpy 2.0-dev testing py313-dev-x64 install @@ -26,7 +26,6 @@ skip_missing_interpreters = true # Configuration that allows us to split tests across GitHub runners effectively [gh-actions] python = - 3.8: py38 3.9: py39 3.10: py310 3.11: py311 @@ -76,35 +75,35 @@ set_env = extras = test deps = # General minimum dependencies: pin based on API usage - min: packaging ==17 + # matplotlib 3.5 requires packaging 20 + min: packaging ==20 min: importlib_resources ==5.12; python_version < '3.12' min: typing_extensions ==4.6; python_version < '3.13' # NEP29/SPEC0 + 1yr: Test on minor release series within the last 3 years # We're extending this to all optional dependencies # This only affects the range that we test on; numpy is the only non-optional # dependency, and will be the only one to affect pip environment resolution. - min: numpy ==1.20 - min: h5py ==2.10 - min: indexed_gzip ==1.4 - min: matplotlib ==3.4 - min: pillow ==8.1 - min: pydicom ==2.1 - min: pyzstd ==0.14.3 - min: scipy ==1.6 + min: numpy ==1.22 + min: h5py ==3.5 + min: indexed_gzip ==1.6 + min: matplotlib ==3.5 + min: pillow ==8.4 + min: pydicom ==2.3 + min: pyzstd ==0.15.2 + min: scipy ==1.8 # Numpy 2.0 is a major breaking release; we cannot put much effort into # supporting until it's at least RC stable - pre: numpy <2.0.dev0 dev: numpy >=2.1.dev0 # Scipy stopped producing win32 wheels at py310 - py3{8,9}-full-x86,x64,arm64: scipy >=1.6 + py39-full-x86,x64,arm64: scipy >=1.8 # Matplotlib depends on scipy, so cannot be built for py310 on x86 - py3{8,9}-full-x86,x64,arm64: matplotlib >=3.4 + py39-full-x86,x64,arm64: matplotlib >=3.5 # h5py stopped producing win32 wheels at py39 - py38-full-x86,{full,pre}-{x64,arm64}: h5py >=2.10 - full,pre,dev: pillow >=8.1 - full,pre: indexed_gzip >=1.4 - full,pre,dev: pyzstd >=0.14.3 - full,pre: pydicom >=2.1 + {full,pre}-{x64,arm64}: h5py >=3.5 + full,pre,dev: pillow >=8.4 + full,pre: indexed_gzip >=1.6 + full,pre,dev: pyzstd >=0.15.2 + full,pre: pydicom >=2.3 dev: pydicom @ git+https://github.com/pydicom/pydicom.git@main commands =