From e9188e897ac0b40a9b05ac96166b66514b0dbc62 Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Wed, 26 Nov 2025 12:13:28 +0000 Subject: [PATCH 01/17] TEST: Adjust existing tests/benchmarks to import zstd from _compression, rather than calling optional_package again. Use "zstd" instead of "pyzstd" --- nibabel/benchmarks/bench_fileslice.py | 2 +- nibabel/tests/test_analyze.py | 3 +-- nibabel/tests/test_loadsave.py | 6 +++--- nibabel/tests/test_minc1.py | 5 ++--- nibabel/tests/test_openers.py | 5 ++--- nibabel/tests/test_volumeutils.py | 9 ++++----- 6 files changed, 13 insertions(+), 17 deletions(-) diff --git a/nibabel/benchmarks/bench_fileslice.py b/nibabel/benchmarks/bench_fileslice.py index cc3d837c2d..3b5d8691ec 100644 --- a/nibabel/benchmarks/bench_fileslice.py +++ b/nibabel/benchmarks/bench_fileslice.py @@ -19,11 +19,11 @@ from ..optpkg import optional_package from ..rstutils import rst_table from ..tmpdirs import InTemporaryDirectory +from .._compression import HAVE_ZSTD SHAPE = (64, 64, 32, 100) ROW_NAMES = [f'axis {i}, len {dim}' for i, dim in enumerate(SHAPE)] COL_NAMES = ['mid int', 'step 1', 'half step 1', 'step mid int'] -HAVE_ZSTD = optional_package('pyzstd')[1] def _slices_for_len(L): diff --git a/nibabel/tests/test_analyze.py b/nibabel/tests/test_analyze.py index 85669b3661..ee3c419502 100644 --- a/nibabel/tests/test_analyze.py +++ b/nibabel/tests/test_analyze.py @@ -38,11 +38,10 @@ suppress_warnings, ) from ..tmpdirs import InTemporaryDirectory +from .._compression import HAVE_ZSTD from . import test_spatialimages as tsi from . import test_wrapstruct as tws -HAVE_ZSTD = optional_package('pyzstd')[1] - header_file = os.path.join(data_path, 'analyze.hdr') PIXDIM0_MSG = 'pixdim[1,2,3] should be non-zero; setting 0 dims to 1' diff --git a/nibabel/tests/test_loadsave.py b/nibabel/tests/test_loadsave.py index 035cbb56c7..ae24ce30ff 100644 --- a/nibabel/tests/test_loadsave.py +++ b/nibabel/tests/test_loadsave.py @@ -22,9 +22,9 @@ from ..optpkg import optional_package from ..testing import deprecated_to, expires from ..tmpdirs import InTemporaryDirectory +from .._compression import HAVE_ZSTD _, have_scipy, _ = optional_package('scipy') -_, have_pyzstd, _ = optional_package('pyzstd') import pytest from numpy.testing import assert_almost_equal, assert_array_equal @@ -84,7 +84,7 @@ def test_load_empty_image(): @pytest.mark.parametrize('extension', ['.gz', '.bz2', '.zst']) def test_load_bad_compressed_extension(tmp_path, extension): - if extension == '.zst' and not have_pyzstd: + if extension == '.zst' and not HAVE_ZSTD: pytest.skip() file_path = tmp_path / f'img.nii{extension}' file_path.write_bytes(b'bad') @@ -94,7 +94,7 @@ def test_load_bad_compressed_extension(tmp_path, extension): @pytest.mark.parametrize('extension', ['.gz', '.bz2', '.zst']) def test_load_good_extension_with_bad_data(tmp_path, extension): - if extension == '.zst' and not have_pyzstd: + if extension == '.zst' and not HAVE_ZSTD: pytest.skip() file_path = tmp_path / f'img.nii{extension}' with Opener(file_path, 'wb') as fobj: diff --git a/nibabel/tests/test_minc1.py b/nibabel/tests/test_minc1.py index 8f88bf802d..911c9325c3 100644 --- a/nibabel/tests/test_minc1.py +++ b/nibabel/tests/test_minc1.py @@ -24,8 +24,7 @@ from ..tmpdirs import InTemporaryDirectory from . import test_spatialimages as tsi from .test_fileslice import slicer_samples - -pyzstd, HAVE_ZSTD, _ = optional_package('pyzstd') +from .._compression import zstd, HAVE_ZSTD EG_FNAME = pjoin(data_path, 'tiny.mnc') @@ -175,7 +174,7 @@ def test_compressed(self): content = open(tp['fname'], 'rb').read() openers_exts = [(gzip.open, '.gz'), (bz2.BZ2File, '.bz2')] if HAVE_ZSTD: # add .zst to test if installed - openers_exts += [(pyzstd.ZstdFile, '.zst')] + openers_exts += [(zstd.ZstdFile, '.zst')] with InTemporaryDirectory(): for opener, ext in openers_exts: fname = 'test.mnc' + ext diff --git a/nibabel/tests/test_openers.py b/nibabel/tests/test_openers.py index 05d0e04cd0..8d5e98619d 100644 --- a/nibabel/tests/test_openers.py +++ b/nibabel/tests/test_openers.py @@ -23,8 +23,7 @@ from ..openers import HAVE_INDEXED_GZIP, BZ2File, DeterministicGzipFile, ImageOpener, Opener from ..optpkg import optional_package from ..tmpdirs import InTemporaryDirectory - -pyzstd, HAVE_ZSTD, _ = optional_package('pyzstd') +from .._compression import zstd, HAVE_ZSTD class Lunk: @@ -271,7 +270,7 @@ class StrictOpener(Opener): IndexedGzipFile = GzipFile assert isinstance(fobj.fobj, (GzipFile, IndexedGzipFile)) elif lext == 'zst': - assert isinstance(fobj.fobj, pyzstd.ZstdFile) + assert isinstance(fobj.fobj, zstd.ZstdFile) else: assert isinstance(fobj.fobj, BZ2File) diff --git a/nibabel/tests/test_volumeutils.py b/nibabel/tests/test_volumeutils.py index c7b2b3e819..fd0b0cf8ce 100644 --- a/nibabel/tests/test_volumeutils.py +++ b/nibabel/tests/test_volumeutils.py @@ -56,8 +56,7 @@ working_type, write_zeros, ) - -pyzstd, HAVE_ZSTD, _ = optional_package('pyzstd') +from .._compression import zstd, HAVE_ZSTD # convenience variables for numpy types FLOAT_TYPES = sctypes['float'] @@ -81,7 +80,7 @@ def test__is_compressed_fobj(): with InTemporaryDirectory(): file_openers = [('', open, False), ('.gz', gzip.open, True), ('.bz2', BZ2File, True)] if HAVE_ZSTD: - file_openers += [('.zst', pyzstd.ZstdFile, True)] + file_openers += [('.zst', zstd.ZstdFile, True)] for ext, opener, compressed in file_openers: fname = 'test.bin' + ext for mode in ('wb', 'rb'): @@ -105,7 +104,7 @@ def make_array(n, bytes): with InTemporaryDirectory(): openers = [open, gzip.open, BZ2File] if HAVE_ZSTD: - openers += [pyzstd.ZstdFile] + openers += [zstd.ZstdFile] for n, opener in itertools.product((256, 1024, 2560, 25600), openers): in_arr = np.arange(n, dtype=dtype) # Write array to file @@ -262,7 +261,7 @@ def test_array_from_file_reread(): with InTemporaryDirectory(): openers = [open, gzip.open, bz2.BZ2File, BytesIO] if HAVE_ZSTD: - openers += [pyzstd.ZstdFile] + openers += [zstd.ZstdFile] for shape, opener, dtt, order in itertools.product( ((64,), (64, 65), (64, 65, 66)), openers, (np.int16, np.float32), ('F', 'C') ): From 20df2cf90d8f4d10d3d12e3eca542e62a57ac0eb Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Wed, 26 Nov 2025 12:16:49 +0000 Subject: [PATCH 02/17] RF: Try to import zstd from compression.zstd, backports.zstd, or pyzstd in that order. Use module name "zstd" instead of "pyzstd" --- nibabel/_compression.py | 16 +++++++++++----- nibabel/openers.py | 8 ++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/nibabel/_compression.py b/nibabel/_compression.py index 871be2629f..c6163b611d 100644 --- a/nibabel/_compression.py +++ b/nibabel/_compression.py @@ -20,14 +20,20 @@ import io import indexed_gzip # type: ignore[import] - import pyzstd + + # >= py314 + try: + from compression import zstd # type: ignore[import] + # < py314 + except ImportError: + from backports import zstd # type: ignore[import] HAVE_INDEXED_GZIP = True HAVE_ZSTD = True else: indexed_gzip, HAVE_INDEXED_GZIP, _ = optional_package('indexed_gzip') - pyzstd, HAVE_ZSTD, _ = optional_package('pyzstd') - + zstd, HAVE_ZSTD, _ = optional_package(('compression.zstd', + 'backports.zstd', 'pyzstd')) # Collections of types for isinstance or exception matching COMPRESSED_FILE_LIKES: tuple[type[io.IOBase], ...] = ( @@ -47,5 +53,5 @@ IndexedGzipFile = gzip.GzipFile if HAVE_ZSTD: - COMPRESSED_FILE_LIKES += (pyzstd.ZstdFile,) - COMPRESSION_ERRORS += (pyzstd.ZstdError,) + COMPRESSED_FILE_LIKES += (zstd.ZstdFile,) + COMPRESSION_ERRORS += (zstd.ZstdError,) diff --git a/nibabel/openers.py b/nibabel/openers.py index 3ad9b6c222..b21333542d 100644 --- a/nibabel/openers.py +++ b/nibabel/openers.py @@ -16,7 +16,7 @@ from bz2 import BZ2File from os.path import splitext -from ._compression import HAVE_INDEXED_GZIP, IndexedGzipFile, pyzstd +from ._compression import HAVE_INDEXED_GZIP, IndexedGzipFile, zstd if ty.TYPE_CHECKING: from types import TracebackType @@ -103,9 +103,9 @@ def _zstd_open( mode: Mode = 'r', *, level_or_option: int | dict | None = None, - zstd_dict: pyzstd.ZstdDict | None = None, -) -> pyzstd.ZstdFile: - return pyzstd.ZstdFile(filename, mode, level_or_option=level_or_option, zstd_dict=zstd_dict) + zstd_dict: zstd.ZstdDict | None = None, +) -> zstd.ZstdFile: + return zstd.ZstdFile(filename, mode, level_or_option=level_or_option, zstd_dict=zstd_dict) class Opener: From cbb1a89b3757fc483c40a25b1a1879f6aaf802f7 Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Wed, 26 Nov 2025 12:30:09 +0000 Subject: [PATCH 03/17] RF: Move gzip/zstd specific opening logic from openers into _compression module --- nibabel/_compression.py | 66 +++++++++++++++++++++++++++++++++++ nibabel/openers.py | 77 +++++------------------------------------ 2 files changed, 74 insertions(+), 69 deletions(-) diff --git a/nibabel/_compression.py b/nibabel/_compression.py index c6163b611d..537dec72b3 100644 --- a/nibabel/_compression.py +++ b/nibabel/_compression.py @@ -55,3 +55,69 @@ if HAVE_ZSTD: COMPRESSED_FILE_LIKES += (zstd.ZstdFile,) COMPRESSION_ERRORS += (zstd.ZstdError,) + + + +class DeterministicGzipFile(gzip.GzipFile): + """Deterministic variant of GzipFile + + This writer does not add filename information to the header, and defaults + to a modification time (``mtime``) of 0 seconds. + """ + + def __init__( + self, + filename: str | None = None, + mode: Mode | None = None, + compresslevel: int = 9, + fileobj: io.FileIO | None = None, + mtime: int = 0, + ): + if mode is None: + mode = 'rb' + modestr: str = mode + + # These two guards are adapted from + # https://github.com/python/cpython/blob/6ab65c6/Lib/gzip.py#L171-L174 + if 'b' not in modestr: + modestr = f'{mode}b' + if fileobj is None: + if filename is None: + raise TypeError('Must define either fileobj or filename') + # Cast because GzipFile.myfileobj has type io.FileIO while open returns ty.IO + fileobj = self.myfileobj = ty.cast('io.FileIO', open(filename, modestr)) + super().__init__( + filename='', + mode=modestr, + compresslevel=compresslevel, + fileobj=fileobj, + mtime=mtime, + ) + +def gzip_open( + filename: str, + mode: Mode = 'rb', + compresslevel: int = 9, + mtime: int = 0, + keep_open: bool = False, +) -> gzip.GzipFile: + if not HAVE_INDEXED_GZIP or mode != 'rb': + gzip_file = DeterministicGzipFile(filename, mode, compresslevel, mtime=mtime) + + # use indexed_gzip if possible for faster read access. If keep_open == + # True, we tell IndexedGzipFile to keep the file handle open. Otherwise + # the IndexedGzipFile will close/open the file on each read. + else: + gzip_file = IndexedGzipFile(filename, drop_handles=not keep_open) + + return gzip_file + + +def zstd_open( + filename: str, + mode: Mode = 'r', + *, + level_or_option: int | dict | None = None, + zstd_dict: zstd.ZstdDict | None = None, +) -> zstd.ZstdFile: + return zstd.ZstdFile(filename, mode, level_or_option=level_or_option, zstd_dict=zstd_dict) diff --git a/nibabel/openers.py b/nibabel/openers.py index b21333542d..38d0630568 100644 --- a/nibabel/openers.py +++ b/nibabel/openers.py @@ -16,7 +16,12 @@ from bz2 import BZ2File from os.path import splitext -from ._compression import HAVE_INDEXED_GZIP, IndexedGzipFile, zstd +from ._compression import (HAVE_INDEXED_GZIP, + IndexedGzipFile, + DeterministicGzipFile, + zstd, + gzip_open, + zstd_open) if ty.TYPE_CHECKING: from types import TracebackType @@ -42,72 +47,6 @@ def read(self, size: int = -1, /) -> bytes: ... def write(self, b: bytes, /) -> int | None: ... -class DeterministicGzipFile(gzip.GzipFile): - """Deterministic variant of GzipFile - - This writer does not add filename information to the header, and defaults - to a modification time (``mtime``) of 0 seconds. - """ - - def __init__( - self, - filename: str | None = None, - mode: Mode | None = None, - compresslevel: int = 9, - fileobj: io.FileIO | None = None, - mtime: int = 0, - ): - if mode is None: - mode = 'rb' - modestr: str = mode - - # These two guards are adapted from - # https://github.com/python/cpython/blob/6ab65c6/Lib/gzip.py#L171-L174 - if 'b' not in modestr: - modestr = f'{mode}b' - if fileobj is None: - if filename is None: - raise TypeError('Must define either fileobj or filename') - # Cast because GzipFile.myfileobj has type io.FileIO while open returns ty.IO - fileobj = self.myfileobj = ty.cast('io.FileIO', open(filename, modestr)) - super().__init__( - filename='', - mode=modestr, - compresslevel=compresslevel, - fileobj=fileobj, - mtime=mtime, - ) - - -def _gzip_open( - filename: str, - mode: Mode = 'rb', - compresslevel: int = 9, - mtime: int = 0, - keep_open: bool = False, -) -> gzip.GzipFile: - if not HAVE_INDEXED_GZIP or mode != 'rb': - gzip_file = DeterministicGzipFile(filename, mode, compresslevel, mtime=mtime) - - # use indexed_gzip if possible for faster read access. If keep_open == - # True, we tell IndexedGzipFile to keep the file handle open. Otherwise - # the IndexedGzipFile will close/open the file on each read. - else: - gzip_file = IndexedGzipFile(filename, drop_handles=not keep_open) - - return gzip_file - - -def _zstd_open( - filename: str, - mode: Mode = 'r', - *, - level_or_option: int | dict | None = None, - zstd_dict: zstd.ZstdDict | None = None, -) -> zstd.ZstdFile: - return zstd.ZstdFile(filename, mode, level_or_option=level_or_option, zstd_dict=zstd_dict) - - class Opener: r"""Class to accept, maybe open, and context-manage file-likes / filenames @@ -129,9 +68,9 @@ class Opener: for \*args """ - gz_def = (_gzip_open, ('mode', 'compresslevel', 'mtime', 'keep_open')) + gz_def = (gzip_open, ('mode', 'compresslevel', 'mtime', 'keep_open')) bz2_def = (BZ2File, ('mode', 'buffering', 'compresslevel')) - zstd_def = (_zstd_open, ('mode', 'level_or_option', 'zstd_dict')) + zstd_def = (zstd_open, ('mode', 'level_or_option', 'zstd_dict')) compress_ext_map: dict[str | None, OpenerDef] = { '.gz': gz_def, '.bz2': bz2_def, From 6cb175509bf8f59f3b619e46d2d7a350b45563b0 Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Wed, 26 Nov 2025 12:30:36 +0000 Subject: [PATCH 04/17] TEST: Update mocks in unit test for openers->_compression move --- nibabel/tests/test_openers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nibabel/tests/test_openers.py b/nibabel/tests/test_openers.py index 8d5e98619d..133ddf8dbc 100644 --- a/nibabel/tests/test_openers.py +++ b/nibabel/tests/test_openers.py @@ -121,8 +121,10 @@ def patch_indexed_gzip(state): else: values = (False, GzipFile) with ( + mock.patch('nibabel._compression.HAVE_INDEXED_GZIP', values[0]), mock.patch('nibabel.openers.HAVE_INDEXED_GZIP', values[0]), mock.patch('nibabel.openers.IndexedGzipFile', values[1], create=True), + mock.patch('nibabel._compression.IndexedGzipFile', values[1], create=True), ): yield From 38862750a79b9e5f7e5d2d229ccc1e7fe550f647 Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Wed, 26 Nov 2025 12:40:09 +0000 Subject: [PATCH 05/17] RF: Move type hints, add docs for gzip_open/zstd_open --- nibabel/_compression.py | 34 ++++++++++++++++++++++++++++++++++ nibabel/openers.py | 8 -------- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/nibabel/_compression.py b/nibabel/_compression.py index 537dec72b3..fc25aa2685 100644 --- a/nibabel/_compression.py +++ b/nibabel/_compression.py @@ -12,6 +12,7 @@ import bz2 import gzip +import io import typing as ty from .optpkg import optional_package @@ -30,6 +31,15 @@ HAVE_INDEXED_GZIP = True HAVE_ZSTD = True + + ModeRT = ty.Literal['r', 'rt'] + ModeRB = ty.Literal['rb'] + ModeWT = ty.Literal['w', 'wt'] + ModeWB = ty.Literal['wb'] + ModeR = ty.Union[ModeRT, ModeRB] + ModeW = ty.Union[ModeWT, ModeWB] + Mode = ty.Union[ModeR, ModeW] + else: indexed_gzip, HAVE_INDEXED_GZIP, _ = optional_package('indexed_gzip') zstd, HAVE_ZSTD, _ = optional_package(('compression.zstd', @@ -101,6 +111,30 @@ def gzip_open( mtime: int = 0, keep_open: bool = False, ) -> gzip.GzipFile: + """Open a gzip file for reading or writing. + + If opening a file for reading, and ``indexed_gzip`` is available, + an ``IndexedGzipFile`` is returned. + + Otherwise (opening for writing, or ``indexed_gzip`` not available), + a ``DeterministicGzipFile`` is returned. + + Parameters: + ----------- + + filename : str + Path of file to open. + mode : Mode + Opening mode - either ``rb`` or ``wb``. + compresslevel: int + Compression level when writing. + mtime: int + Modification time used when writing a file - passed to the + ``DetemrinisticGzipFile``. Ignored when reading. + keep_open: bool + Whether to keep the file handle open between reads. Ignored when writing, + or when ``indexed_gzip`` is not present. + """ if not HAVE_INDEXED_GZIP or mode != 'rb': gzip_file = DeterministicGzipFile(filename, mode, compresslevel, mtime=mtime) diff --git a/nibabel/openers.py b/nibabel/openers.py index 38d0630568..b356d8cbcd 100644 --- a/nibabel/openers.py +++ b/nibabel/openers.py @@ -30,14 +30,6 @@ from ._typing import Self - ModeRT = ty.Literal['r', 'rt'] - ModeRB = ty.Literal['rb'] - ModeWT = ty.Literal['w', 'wt'] - ModeWB = ty.Literal['wb'] - ModeR = ty.Union[ModeRT, ModeRB] - ModeW = ty.Union[ModeWT, ModeWB] - Mode = ty.Union[ModeR, ModeW] - OpenerDef = tuple[ty.Callable[..., io.IOBase], tuple[str, ...]] From f728911320c8ffeb82cc2941165a8045581bca43 Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Wed, 26 Nov 2025 12:57:15 +0000 Subject: [PATCH 06/17] RF: Adjust zstd_open function to accept level/option parameters, and to normalise level_or_option/level/option arguments - working around API difference between pyzstd and compression.zstd --- nibabel/_compression.py | 52 +++++++++++++++++++++++++++++++++++++++-- nibabel/openers.py | 3 ++- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/nibabel/_compression.py b/nibabel/_compression.py index fc25aa2685..4e6e1ab92c 100644 --- a/nibabel/_compression.py +++ b/nibabel/_compression.py @@ -124,7 +124,7 @@ def gzip_open( filename : str Path of file to open. - mode : Mode + mode : str Opening mode - either ``rb`` or ``wb``. compresslevel: int Compression level when writing. @@ -153,5 +153,53 @@ def zstd_open( *, level_or_option: int | dict | None = None, zstd_dict: zstd.ZstdDict | None = None, + level : int = None, + options : dict = None, ) -> zstd.ZstdFile: - return zstd.ZstdFile(filename, mode, level_or_option=level_or_option, zstd_dict=zstd_dict) + """Open a zstd file for reading or writing. + + The specific object type returned depends on which module out of + ``compression.zstd``, ``backports.zstd``, or ``pyzstd`` is available. + + At most one of the ``level_or_options``, ``level``, or + ``options`` parameters may be provided - a ``ValueError`` will be raised + if more than one is specified. + + Parameters + ---------- + + filename : str + Path of file to open. + mode : str + Opening mode. + level_or_option: int or dict + Compression level or dictionary containing options (see also the + level and options parameters). + zstd_dict : ZstdDict + Dictionary used for compression/decompression. + level : int + Compression level when writing. + options : dict + Dictionary of compression/decompression options. + """ + level_or_option_provided = sum((level_or_option is not None, + level is not None, + options is not None)) + if level_or_option_provided > 1: + raise ValueError( + 'Only one of level_or_option, level, or options may be specified') + # pyzstd accepts level_or_option, but compression.zstd/backports.zstd + # expects level or options + level_or_option_kwarg = {} + if level_or_option_provided == 1: + level_or_option_value = [v for v in [level_or_option, level, options] + if v is not None][0] + if zstd.__name__ == 'pyzstd': + level_or_option_kwarg['level_or_option'] = level_or_option_value + else: + if isinstance(level_or_option_value, int): + level_or_option_kwarg['level'] = level_or_option_value + else: + level_or_option_kwarg['options'] = level_or_option_value + return zstd.ZstdFile( + filename, mode, zstd_dict=zstd_dict, **level_or_option_kwarg) diff --git a/nibabel/openers.py b/nibabel/openers.py index b356d8cbcd..70653a5407 100644 --- a/nibabel/openers.py +++ b/nibabel/openers.py @@ -62,7 +62,8 @@ class Opener: gz_def = (gzip_open, ('mode', 'compresslevel', 'mtime', 'keep_open')) bz2_def = (BZ2File, ('mode', 'buffering', 'compresslevel')) - zstd_def = (zstd_open, ('mode', 'level_or_option', 'zstd_dict')) + zstd_def = (zstd_open, ('mode', 'level_or_option', 'zstd_dict', 'level', + 'option')) compress_ext_map: dict[str | None, OpenerDef] = { '.gz': gz_def, '.bz2': bz2_def, From 922be608941d9ac8d52af99bd6eba41f9b38c673 Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Wed, 26 Nov 2025 13:20:24 +0000 Subject: [PATCH 07/17] MNT: Use backports.zstd instead of pyzstd as primary dependency --- .pre-commit-config.yaml | 2 +- pyproject.toml | 2 +- tox.ini | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2e6c466f99..35d5d0e69b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,7 +31,7 @@ repos: - types-Pillow - pydicom - numpy - - pyzstd + - backports.zstd - importlib_resources args: ["nibabel"] pass_filenames: false diff --git a/pyproject.toml b/pyproject.toml index 4747acc97a..202781003d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ dicomfs = ["nibabel[dicom]", "pillow >=8.4"] minc2 = ["h5py >=3.5"] spm = ["scipy >=1.8"] viewers = ["matplotlib >=3.5"] -zstd = ["pyzstd >=0.15.2"] +zstd = ["backports.zstd; python_version<'3.14'"] # For doc and test, make easy to use outside of tox # tox should use these with extras instead of duplicating doc = [ diff --git a/tox.ini b/tox.ini index 9318de633e..6714ea4e56 100644 --- a/tox.ini +++ b/tox.ini @@ -77,17 +77,15 @@ extras = # Simple, thanks Hugo and Paul !none: dicomfs !none: indexed_gzip + !none: zstd # Minimum dependencies min: minc2 min: spm min: viewers - min: zstd # Matplotlib has wheels for everything except win32 (x86) {full,pre}-{x,arm}64: viewers - # No free-threaded wheels for: zstd - py3{9,10,11,12,13,14}-{full,pre}-{x,arm}64: zstd {full,pre}-{x,arm}64: spm @@ -179,7 +177,7 @@ deps = types-Pillow pydicom numpy - pyzstd + backports.zstd importlib_resources typing_extensions skip_install = true From 10aeeb504d40ac53c29b7d3ed880f12cba59af8d Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Wed, 26 Nov 2025 13:21:46 +0000 Subject: [PATCH 08/17] MNT: Update lockfile --- uv.lock | 222 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 114 insertions(+), 108 deletions(-) diff --git a/uv.lock b/uv.lock index e2dd1a8dff..a7007f0814 100644 --- a/uv.lock +++ b/uv.lock @@ -41,6 +41,116 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, ] +[[package]] +name = "backports-zstd" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/76/403d81c1b9264f6ee1c25b2f2ce892ed9930d1a0a1939ef26a01a95e644d/backports_zstd-1.1.0.tar.gz", hash = "sha256:8ce84125e3094ddefbaa25306e8949965ce092b400a1fd63b5e6a5e932d2994f", size = 996020, upload-time = "2025-11-23T12:51:19.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/bb/9f71f34b1c7ad957f36ca320bcdfef1836816af9a8f5d256debe5f12bad6/backports_zstd-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f02ab91901b637fae5d27cc26f02874db3e5f98af481afafb86d6487a4287897", size = 435750, upload-time = "2025-11-23T12:49:03.092Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9a/f064362e44419625b79e049dc532c7b7606bd072f230d2729460cf4f5da8/backports_zstd-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5805a215a3b048a10ca18dd5c525c737e004e59c92c21c44baa0c13083579dc1", size = 362145, upload-time = "2025-11-23T12:49:04.823Z" }, + { url = "https://files.pythonhosted.org/packages/69/c7/e163febeb3504a00f3a7625d40a2d1a120298762393caebe618e96c48594/backports_zstd-1.1.0-cp310-cp310-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:777ff9a60664abfa7e5cbcb676ecbedf49bd567d6184e538b9dee09d4bbee292", size = 506045, upload-time = "2025-11-23T12:49:05.951Z" }, + { url = "https://files.pythonhosted.org/packages/aa/a9/d28aa8f16a9fafd8e15220f3ac32fcd000589088f45c80df7f45839503cc/backports_zstd-1.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27a40d9518019a6b6ad2df5f6c2dd6ce627af9396c929556188a67607ce12d57", size = 475659, upload-time = "2025-11-23T12:49:07.463Z" }, + { url = "https://files.pythonhosted.org/packages/a3/94/9c9b2aa30d34e83f244057c6bf7b2a493c7d60df1caa999160bf83b5a525/backports_zstd-1.1.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a24e73d5d4b3c1b9c693f07081703db9e9398ce95576f1ed74892d6bd01adcb4", size = 581286, upload-time = "2025-11-23T12:49:09.095Z" }, + { url = "https://files.pythonhosted.org/packages/78/6e/09f21da99977d3b220c8a4642fa1218b2b5d854fb2f00eb22dc38b02be3e/backports_zstd-1.1.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:097ea6090dfb60f4af3c09d816c6d64175bd94ead930e5089acd00ea2e5b8de8", size = 640931, upload-time = "2025-11-23T12:49:10.341Z" }, + { url = "https://files.pythonhosted.org/packages/42/cb/d765b8f70e86e368d05632572e7eec3dd27d6bfb61c9c7867b3375b3f19e/backports_zstd-1.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0495547f5b727d798ff317b6cc6158655926b7149739ab9a193e184d65e4bc8f", size = 491149, upload-time = "2025-11-23T12:49:11.524Z" }, + { url = "https://files.pythonhosted.org/packages/f2/1f/6ad1c8af282dc6ad464ff58ecfb29abaeeae6f745c45e5500723f8a0c1e2/backports_zstd-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:544494f7f1ba88f6500a5e14219b19718cc2aa4bcc563ce78d67aef3b8e54ed5", size = 481607, upload-time = "2025-11-23T12:49:12.659Z" }, + { url = "https://files.pythonhosted.org/packages/d8/0a/6f82529599e92af47bd483bf3c318bd14c01e5a00b9a736d9bc7ab1db3b0/backports_zstd-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:559fe9384dbd43cdacc2b4aa7e19199f064da7aec947c6461d4a7aadfb614568", size = 509552, upload-time = "2025-11-23T12:49:14.166Z" }, + { url = "https://files.pythonhosted.org/packages/39/5a/87e4be4c18c920122949d45a9de7e253084af22ae50b3c0773e4d58aa30a/backports_zstd-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6e7ffa9747051fd994f8070aefc8317af3ce485d9dbd175593c6df9d795d24ff", size = 585650, upload-time = "2025-11-23T12:49:15.379Z" }, + { url = "https://files.pythonhosted.org/packages/a4/4c/4afff28eb575631882d564b6c8b93f4d43e53f99c62ceb03f51b15400f9a/backports_zstd-1.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:27858ea429914fc67446112fd7797e821d18e7ce3d6cfa7b2e124547c6a4ca83", size = 631508, upload-time = "2025-11-23T12:49:16.89Z" }, + { url = "https://files.pythonhosted.org/packages/d0/8d/f2564d8fe96398bc15ccd4db605d448a8dc4d6dc4e1e3bc613c6f2306b87/backports_zstd-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:233f35525940b5153223909a4c9f554e27ae7f5bff5c946b9fe28b633cdd463a", size = 495209, upload-time = "2025-11-23T12:49:18.083Z" }, + { url = "https://files.pythonhosted.org/packages/36/47/9eb4a27599af4501a79d9d80d6616a18b8cdf52683b1c711d403d247f6a2/backports_zstd-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be5723019ba1500cf727a6ba37a15e697a63f66314e3e7def52fd86c65249236", size = 288660, upload-time = "2025-11-23T12:49:19.553Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e8/e3f70c4835338167d7039152b8cb1c4adae8c14fed7a184f2d860b0bb952/backports_zstd-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c28905252c4273e7097baad10ca786ff8e5ab67807f428504c4fe3cbdecb135e", size = 313570, upload-time = "2025-11-23T12:49:20.706Z" }, + { url = "https://files.pythonhosted.org/packages/bc/bf/12f84454d23cd32a649adbc49983fed13ae2f60a75a6461e9db6e435d856/backports_zstd-1.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:bd2e69a69524161e20cad8245dcbacd9b825f83221c55b05bca0e506b7abddfd", size = 288773, upload-time = "2025-11-23T12:49:22.226Z" }, + { url = "https://files.pythonhosted.org/packages/73/81/47935ce4c225681a2a962c69cdc061f721bc02c6b2d9806d0ba4d29a1454/backports_zstd-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8b1f9ea3d2d3638af80fcd896dceba7ad5d902ce3e5b411a9b16e4cddd79c4ac", size = 435751, upload-time = "2025-11-23T12:49:23.738Z" }, + { url = "https://files.pythonhosted.org/packages/c1/36/85f89ea19338e7babfb91147ad3f4022c736c0bcbc1ebf83c90550f2722c/backports_zstd-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bfa36e208619fd152c20bd6c9d0febcb18ac90bdd811421c6a01e450f4b1dd6d", size = 362143, upload-time = "2025-11-23T12:49:25.245Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/f722122cbd44331245c674c9d3dac91df135d491348de7da5010980d346d/backports_zstd-1.1.0-cp311-cp311-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:9f43275263cfbd2e9096a8b333d15c2e96deb0a2df3751b15408155a03c57fab", size = 506044, upload-time = "2025-11-23T12:49:26.423Z" }, + { url = "https://files.pythonhosted.org/packages/e6/10/41d5088c35bbbd03a07e0f315566e06f915b0371700f568e8c3b26c68b40/backports_zstd-1.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b1c6ae75c09f211b4f5439b0984bfb8b8d15975585e50b1dc658e9f9b94bd305", size = 475657, upload-time = "2025-11-23T12:49:28.056Z" }, + { url = "https://files.pythonhosted.org/packages/d1/14/324476e8eef484779c390d0b9c83b7906c883ae82b7de4651a86463fbb71/backports_zstd-1.1.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2d38c6df19d5b828cb3d1c1ab62a2c61dd433ad00523d577e303f4552fa002b0", size = 581283, upload-time = "2025-11-23T12:49:29.536Z" }, + { url = "https://files.pythonhosted.org/packages/bd/01/4f61e96f52dd391834dd457ee970b57d2818609677a786f94840da359f02/backports_zstd-1.1.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:64d5c359a04c26a36eaad82386a873ff1af9e12f896af4748cff1f35e2550af3", size = 640975, upload-time = "2025-11-23T12:49:30.916Z" }, + { url = "https://files.pythonhosted.org/packages/57/79/69b97eea66a26f52975ae78d7413ca79efee51d2349a33ddfef9b196302c/backports_zstd-1.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:773cec5a3ff8c81e03f3db0c7df1d2f7c3cf820262138ec752c46fd25b532e85", size = 491188, upload-time = "2025-11-23T12:49:32.134Z" }, + { url = "https://files.pythonhosted.org/packages/9d/4d/5d402a41c29121f387be38e3a507ba6c3d9c5304356f659beba8bcdadf80/backports_zstd-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:be97026835fb5cf0cb96e8c1c6ee72e53816f0ff34c368546ea6e53962476216", size = 481610, upload-time = "2025-11-23T12:49:33.331Z" }, + { url = "https://files.pythonhosted.org/packages/85/9d/25dceef8e97fb815259b3c946236ff6e696bb5f461faa8303dca5d98e4e8/backports_zstd-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6fb2b381a404040c9dbfd258f3bcd740595e2aca088d822fb241942440fce509", size = 509555, upload-time = "2025-11-23T12:49:34.675Z" }, + { url = "https://files.pythonhosted.org/packages/bd/74/fb0883e9dcc6333f33f3f90e1101498800b82b6437cfa110244c59d6a03e/backports_zstd-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:adac539e6fbcf72d70cd9f354ab5689e080b8621b4d268f2ba0f486c25dfd3f3", size = 585639, upload-time = "2025-11-23T12:49:36.033Z" }, + { url = "https://files.pythonhosted.org/packages/88/ad/6db9224a34d0ef6ce553c9605649a522b485efc0aa098b1c78b4bcdaad5a/backports_zstd-1.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4298631dc91b4dafc20b6a7eb7edb028c341bc2e3b77a128c7ced5148aaa215c", size = 631550, upload-time = "2025-11-23T12:49:37.543Z" }, + { url = "https://files.pythonhosted.org/packages/87/a5/383d1d9b2281854b2809b83ec6dfe9092f3a8b9700055340176b29230b1c/backports_zstd-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62451464559525ff093f7d684bf88381722d7b3759693ea842e94b41c01f306b", size = 495212, upload-time = "2025-11-23T12:49:38.814Z" }, + { url = "https://files.pythonhosted.org/packages/cd/75/305ac0bf93de4a771d33926e27da401a807294c3858b133d3611d0b6be49/backports_zstd-1.1.0-cp311-cp311-win32.whl", hash = "sha256:b20309c15d4afa2919517669366da60dd30955dbc0784924ae79acb49f158ef6", size = 288753, upload-time = "2025-11-23T12:49:39.972Z" }, + { url = "https://files.pythonhosted.org/packages/50/f3/8887a706bef88307a55719b4d0cf73600c6703e3cfb5926f97441c8ffc3b/backports_zstd-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:9e3df9c43e9f4ed970cd8f48df41736c5697fed06523e463661788762c95d454", size = 313698, upload-time = "2025-11-23T12:49:41.202Z" }, + { url = "https://files.pythonhosted.org/packages/3c/ea/78f627df9b3765ac7036da11951db7c91eb77e6014f8819dbf575dc57f8e/backports_zstd-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:b5c43022248bd8711f606a481547dbcf4541b617446a07a359aad28ebf77973b", size = 288897, upload-time = "2025-11-23T12:49:42.306Z" }, + { url = "https://files.pythonhosted.org/packages/48/98/1426b121772c4f05735ad7c9542dd333c18720179a549dd2e22e6be386df/backports_zstd-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:63e1ba127129c7c4786147f873eef63f90694a13c5459ca0e970cad1bd52cf6c", size = 436062, upload-time = "2025-11-23T12:49:43.409Z" }, + { url = "https://files.pythonhosted.org/packages/4b/01/c851ce0cbe2611ce33232ecaa21f237b9b3c52964f01c9e96cabc7f60b0f/backports_zstd-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:37b30c0fb5d118344706ed9e26cb2f43584c0012565254a819485bab180f1652", size = 362213, upload-time = "2025-11-23T12:49:44.886Z" }, + { url = "https://files.pythonhosted.org/packages/af/c0/99596faafe038ba03695569adf0ec1f4e3f955c3156618b0f11deb3b9905/backports_zstd-1.1.0-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:b8b77fae55a245e5b61fc599b641343da2580ebc9fcb20a77822dcca9126bddc", size = 506466, upload-time = "2025-11-23T12:49:46.085Z" }, + { url = "https://files.pythonhosted.org/packages/eb/84/b6410324c8f495bd6fd1c8ab8b3fbb70120ea2b06ee004db4549e0eb58b0/backports_zstd-1.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34c9c425d113071c90a241f04fb12be46c83e7028a090aca4e72fe9f5fe11fb6", size = 476287, upload-time = "2025-11-23T12:49:47.327Z" }, + { url = "https://files.pythonhosted.org/packages/c1/7a/280f37869fb37dc16d2ca093734a6cac564a551d7fad8b55d0807d19f74c/backports_zstd-1.1.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33a7c9c2bd44b5bbfba2c78411feb2e0074406654f33e6b0317e307710c7fe57", size = 581744, upload-time = "2025-11-23T12:49:48.907Z" }, + { url = "https://files.pythonhosted.org/packages/72/32/828ce97c1c18d3735ca42b65819241702df101cfd13a4b88bd77ca1f03bd/backports_zstd-1.1.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3baf8b13aa90ed5a03503063be670d8cca22d5d81aab4cc499741f013dba4351", size = 640474, upload-time = "2025-11-23T12:49:50.184Z" }, + { url = "https://files.pythonhosted.org/packages/06/ad/5ba858ffc307ed2b51693d87642f9e9dd6013579840981ac364c6e24c38c/backports_zstd-1.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fe522558c4d56d9eb2294592fec5ac94b95c82ff22cf1f91d1bf589fe567f391", size = 494260, upload-time = "2025-11-23T12:49:51.765Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4c/3b2f67ae51d19716af5d9bd2c074b9a5ba03a7f6ef4c509adaee3e1f6958/backports_zstd-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4f459c3b43d382aa2ab44199b84ec86a33f79094701bd3bbf536d4b6e687e744", size = 482320, upload-time = "2025-11-23T12:49:52.943Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e0/05f1d8a8552254ab4ed447ad2950c079f15bd2c74125a8a15fa9f0eb28ef/backports_zstd-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b53841a136c326686d49f1b2b96dc5acdffb2e8e422a5298f36b95a7d927b69a", size = 509894, upload-time = "2025-11-23T12:49:54.149Z" }, + { url = "https://files.pythonhosted.org/packages/62/0d/15dc13edf653ba14f61f9ce0a4119f79647750a9525659b410be124b5d71/backports_zstd-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d64a172db16fa5e3aed875776537103cae0f2dd0e614c9658fce62ebd5211847", size = 586126, upload-time = "2025-11-23T12:49:55.363Z" }, + { url = "https://files.pythonhosted.org/packages/e8/c9/9ece6a0c684223e466188d7e8db74462075391f43be244f46ef46ce1f07a/backports_zstd-1.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:de5d7cdea31085b3d21c7f670f385ec666b8595c72c107efa50e29f3cc4e72a1", size = 630934, upload-time = "2025-11-23T12:49:57.115Z" }, + { url = "https://files.pythonhosted.org/packages/d9/da/9c704e8f11cc4cb17adae9693d97903fe9dc5ea7e8182d044c00cf70fb1d/backports_zstd-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8dc11ddef5529aff9fa18565a0f2a98f9f5cff83751e4357c219ae20c160bc1f", size = 498755, upload-time = "2025-11-23T12:49:58.313Z" }, + { url = "https://files.pythonhosted.org/packages/45/ac/843bc205463fce6aff56d2d6f8733635b0c8786c22d148978ecfed97645f/backports_zstd-1.1.0-cp312-cp312-win32.whl", hash = "sha256:8e0376da95159b39f538b6d7edafc211f4b9e7f5581ad4b2476b0f3c890bb25f", size = 288892, upload-time = "2025-11-23T12:49:59.569Z" }, + { url = "https://files.pythonhosted.org/packages/43/aa/dbee1f51bf3af6017e1b48b93edf9c60f74a57c7d34940a728789365355e/backports_zstd-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:8067a95f560c503d683b9e18de44749eefe0a4ad6da318ff9eabb4afe47e79c5", size = 313910, upload-time = "2025-11-23T12:50:00.678Z" }, + { url = "https://files.pythonhosted.org/packages/66/8f/cc99ca99a2e37a9a9f3c6053cd0a7bd255c465be24d1d931fe062dca55bd/backports_zstd-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:f8f2a975dd25b4b3ac901ce47d04881d43dcde18037d2d1513b8a5f4fededdd8", size = 289014, upload-time = "2025-11-23T12:50:02.226Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/2a50f48bd16a4677b1f17f0ab0d173f9225114100326e67fdd65b74f9586/backports_zstd-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a18668f1373fe9e5e0349e3fe5cc8148cbef29eccbb9998993eeb0e90d35638", size = 435551, upload-time = "2025-11-23T12:50:03.881Z" }, + { url = "https://files.pythonhosted.org/packages/0e/ee/10ddf48f87fe8aeedd4dca1a18061159eab006ede693ede00ba80946ba3a/backports_zstd-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3adf32f6e58323505c581a4d1e5b6404e98806c4cf01c54049c372642cf7ec6d", size = 361827, upload-time = "2025-11-23T12:50:04.999Z" }, + { url = "https://files.pythonhosted.org/packages/4c/56/5d98bd5a7b0a5d203f705f3200b467af6a6d34fa6d9aa2b9c9538369dae8/backports_zstd-1.1.0-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:b71f45d12274021e1d7b689ac98faff94874cf6c7bac2f0ab14a3714d4047a6c", size = 505739, upload-time = "2025-11-23T12:50:06.496Z" }, + { url = "https://files.pythonhosted.org/packages/22/16/5a3967e957fc5662eaaeca4ace5eb0646d1e174e70d149fe023092165047/backports_zstd-1.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd98ea2600335bd941bba347e168462c37a5ed1bdc21cbee2dce82871c3719b3", size = 475946, upload-time = "2025-11-23T12:50:07.737Z" }, + { url = "https://files.pythonhosted.org/packages/7d/f6/71c99d08987df24e09b93fde4a2d3aeee8075563374be88572a1f265f9a5/backports_zstd-1.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:83143a2a381d95276a8df5f0b6231f936595d3aeb15a55f91831a111d78297f4", size = 581425, upload-time = "2025-11-23T12:50:09.091Z" }, + { url = "https://files.pythonhosted.org/packages/79/a8/8788b74b53961b3d3dfb08eb4632d07c846afbc2f677752d9f2ae0d6b843/backports_zstd-1.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7caf0d5c2954eea1c160922207afd87cd83cef7f5081b5a7b12d4a646a78c1a2", size = 642271, upload-time = "2025-11-23T12:50:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/f1/08/211d882fb5fcee5014bac84c64acf4c6f5c6df3a10e3bb5ed20409b5e54c/backports_zstd-1.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:222fb59203bb7c8104d049c587cd60bee6e8c1cd6d34c4a87158b8393dc8113a", size = 490893, upload-time = "2025-11-23T12:50:11.623Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8b/8a2625657cc9965c62a56fb2d97ad4cdd31e8cbdb6aad493375c337ac3fe/backports_zstd-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d74065d6b151443b25f3005ae96dac1d896ce616c56595c18c7dfbea77b73a5a", size = 481704, upload-time = "2025-11-23T12:50:12.896Z" }, + { url = "https://files.pythonhosted.org/packages/56/ba/f3b225722e3f1544a1c8cdedc92a35650f6b4ce0ee1444db949240c40efa/backports_zstd-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bf5aa4431001eca56bac39e22e9c814075008d9be7317271e5f8bff21533086d", size = 509295, upload-time = "2025-11-23T12:50:14.409Z" }, + { url = "https://files.pythonhosted.org/packages/93/0e/2f1335f7cf06de3140612ac72bb0735c4d8e973f15678f5e62ba752a16b0/backports_zstd-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8c21cbeeba96d4b15e42ace21fd71f0bf2c431247ce66e82409c0f45dc7d7b4b", size = 585856, upload-time = "2025-11-23T12:50:15.595Z" }, + { url = "https://files.pythonhosted.org/packages/ac/6d/0001f8c654672d5ac309f315919b4d0be26c1687b46b2c9438bff4d16895/backports_zstd-1.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:79ab438599cedf19cce056147a26c64167bf83d05609b4481ffadf9330a322cf", size = 632636, upload-time = "2025-11-23T12:50:16.855Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/5f1835d53ce4b66e445aeca019c516c3989d74269e96c58295b8cd629421/backports_zstd-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a8389d1edc351af4c5e6cef9fcd41f0795d7e639becfb0f331cec67aef3eefa3", size = 495191, upload-time = "2025-11-23T12:50:19.015Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f0/82b912cd9bec18cec86466300372ad2913febe4c7066eb6a6721eada9777/backports_zstd-1.1.0-cp313-cp313-win32.whl", hash = "sha256:f862567e43a989be9c0d1a04c9d3068a8544a1a48c908635ecaffcb896f2c1b1", size = 288529, upload-time = "2025-11-23T12:50:20.234Z" }, + { url = "https://files.pythonhosted.org/packages/d7/07/182dc5e7c17206fc07f30c3786317fa82f18b124f037a5b8dbb73457a341/backports_zstd-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:2e93b0a04f624ebe5b4301ec4997b353db561b82e7e21b42c84c8b30c53218a8", size = 313745, upload-time = "2025-11-23T12:50:21.845Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f7/3418b0e3c15865c24fc390d57e33e141ea8f7347876e4a1feb2942e5abc7/backports_zstd-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:47ed787d85ea29776bc7a54825dd2bd6431a1497bb120d843f4dbacabb4f0d65", size = 288762, upload-time = "2025-11-23T12:50:23.114Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7c/7f62db771263bf5596489c2a5574278ebf4095980f6ee01305b7989ca78e/backports_zstd-1.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:01e488a5f1b008643a375936b655f383e92bd02b6b7830c4f4489eb9ede2661d", size = 436239, upload-time = "2025-11-23T12:50:24.651Z" }, + { url = "https://files.pythonhosted.org/packages/bd/1b/b336683e6e470ddc4f3665588327cef4c96da876469ad3640e3af6d0c502/backports_zstd-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:feef6c896d58f93e8fb9c900eb5b0813580a3a689a7bab53a82d7980599952bb", size = 362524, upload-time = "2025-11-23T12:50:25.892Z" }, + { url = "https://files.pythonhosted.org/packages/a0/23/e0497e863e37f7c659ee5037a743809d82270b84090fa4c7d3564312d4d4/backports_zstd-1.1.0-cp313-cp313t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:91c741b17240469f414786051dadc24372a2270c7664908159d19075a6e58455", size = 507685, upload-time = "2025-11-23T12:50:27.497Z" }, + { url = "https://files.pythonhosted.org/packages/be/cc/fa3d8656e0202030304c7d9de8da3e02157812ab04a5d726314073b04e61/backports_zstd-1.1.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef36d898fc5b592b9822a1b2c90d41c3fc4b684579ef558e6b08b53a747ee542", size = 475585, upload-time = "2025-11-23T12:50:28.834Z" }, + { url = "https://files.pythonhosted.org/packages/6c/21/c6effc30caef1dd8db49a962e554601f1ea5782218f38e16013f719a5711/backports_zstd-1.1.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:37cc049f855654f76fd145c32f3818848848c0128940f2e0a2e0f5597e3658a9", size = 581008, upload-time = "2025-11-23T12:50:30.052Z" }, + { url = "https://files.pythonhosted.org/packages/18/d2/bf2f74d68fde7884295138fe0ac8049031d8061346c395a61ebc256dd470/backports_zstd-1.1.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d30414d5d15ff358a1cbb88b293a108a2eebaed6ccf034176e2a4ffcc80672df", size = 639999, upload-time = "2025-11-23T12:50:31.29Z" }, + { url = "https://files.pythonhosted.org/packages/e9/63/ea63603b691c54a6af0259b4c74c218cc9ba14985bd18ecd629d735c81ec/backports_zstd-1.1.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:747c341023725ea83e5ab874325e85dc89d9008626bbbc329c40426bedd03ecb", size = 494888, upload-time = "2025-11-23T12:50:32.627Z" }, + { url = "https://files.pythonhosted.org/packages/55/c0/0a33c6036f8350954cb9f99363710c4e6589ba72e38dfe181a117c983fc2/backports_zstd-1.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:05b5d4f23b22f67b64825537e9c4d2e6a38914e924794803ee2c4f7a9715b46d", size = 482069, upload-time = "2025-11-23T12:50:33.862Z" }, + { url = "https://files.pythonhosted.org/packages/dc/40/4a955634f174f569aa8a03433148818528736ec66d5baa4bce2f535899ad/backports_zstd-1.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:284d91e68e67ee7b8d2c58f26032bb15e4ed6ec9f2b95121af0b9c260741e497", size = 511445, upload-time = "2025-11-23T12:50:35.09Z" }, + { url = "https://files.pythonhosted.org/packages/19/bf/8d99e2fb714e908b0282a432f9216968db46ed5b6ec42bdc9df5b0ffbea9/backports_zstd-1.1.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:9eb74ca6f9afa8d92eb268dff951d901ef3366a240ca358d1503da9a4355eab5", size = 585581, upload-time = "2025-11-23T12:50:36.745Z" }, + { url = "https://files.pythonhosted.org/packages/14/9f/768d64b30e80b32e262393caf2784f5fd43b45e6ab8e2a44c146a550c607/backports_zstd-1.1.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:b3bef8f715a4f8135709846179fb748d4ec5619282a8858cdeaf90ab9a3c1cd9", size = 630606, upload-time = "2025-11-23T12:50:37.937Z" }, + { url = "https://files.pythonhosted.org/packages/cd/69/41c3f1d2f97cb156e689e36f3ff8ddb9b1140f667394ce0b3d51f0a96012/backports_zstd-1.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:aba8cb745d48e66d5e9f007cbf6871e3a227b2812a81c99916de3f8edc2cfaa1", size = 499515, upload-time = "2025-11-23T12:50:39.16Z" }, + { url = "https://files.pythonhosted.org/packages/68/92/bb10b678aa8038172047adfd748b73b410c34aa8fefac4768128d79dbc2d/backports_zstd-1.1.0-cp313-cp313t-win32.whl", hash = "sha256:d13e6406a50051f2bdd5047dc68e79e398c4d4bf65de910a1cd02e2d93f6d346", size = 289476, upload-time = "2025-11-23T12:50:40.365Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/f1763f8f1cee20b2a836c1aa27a545cb1ae053bc1e282716f5062b9f6169/backports_zstd-1.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:029388257045a1bd091407ed4359899bb07ddac786ebd240ce6985b82da5729b", size = 314843, upload-time = "2025-11-23T12:50:41.519Z" }, + { url = "https://files.pythonhosted.org/packages/44/18/43e36a5a0313a932827ef7b601e673cfec64838458cbb44c452604ac71cc/backports_zstd-1.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:e005d77a72ba6cd1ad85f46dbc0620922e7b46cb90235c4874d16ab1839b4994", size = 289399, upload-time = "2025-11-23T12:50:42.629Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9b/60ee34d4ee7fe3da6c94b094a8b3b22573729157fe69abb7b4514b3b25c7/backports_zstd-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e8a25855ee621d2b59be10c0c71b98ef899fd9edb4872e2c90650495463f442", size = 435905, upload-time = "2025-11-23T12:50:43.802Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9a/002f4a6b808293f230474df7b90f84a51976a5ef3f60147817bf67e0b6cd/backports_zstd-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6504a2d14b350710ec984942602b7a324dcb064925c7a0ca9af1da74e44ba29", size = 362263, upload-time = "2025-11-23T12:50:45.178Z" }, + { url = "https://files.pythonhosted.org/packages/2e/a8/43f8fce9aa3d7091246aa136535aa95baef2d801847c542217d07fdccd08/backports_zstd-1.1.0-cp39-cp39-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:85b8f0af826a4cf5b009ade1d78f0fb8af0a70c09aaef9c81a9d908e12f219ba", size = 506142, upload-time = "2025-11-23T12:50:46.391Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a3/94ab276594fe47e822650f23270465ba21aae3986fbab618c761225a93fa/backports_zstd-1.1.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:328a22827429b225a99a6ca6b4fc67089a489af1d4fc4e78da1429b264fee407", size = 475786, upload-time = "2025-11-23T12:50:47.708Z" }, + { url = "https://files.pythonhosted.org/packages/92/7f/3774013b6897537b063652ce95438828346c1b010e4f10d0fd5bf50ecad3/backports_zstd-1.1.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0926c9e4f21337aeb8767e738672f8ba3f6a2e030f5d1d2170d7180e9a6e68d7", size = 581461, upload-time = "2025-11-23T12:50:48.946Z" }, + { url = "https://files.pythonhosted.org/packages/bf/a7/f5514701c4a84158774d49bb6b2103e3af995dd295f6c26b93879339431f/backports_zstd-1.1.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4fe8620b48e448369260be6fbcb366d6c6172e1afb9aa833b8235a600228aacc", size = 641118, upload-time = "2025-11-23T12:50:50.206Z" }, + { url = "https://files.pythonhosted.org/packages/65/66/c55a79b8d14ba2ed27c1ecdd9da1e7de9430c56ec5ce62cbaae87b544ca8/backports_zstd-1.1.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fca3c304c4b84efb4abce291f0e03c8b46bcc30edf2d314c00ab0a31cf956542", size = 491251, upload-time = "2025-11-23T12:50:51.734Z" }, + { url = "https://files.pythonhosted.org/packages/98/89/45031e26428258b74da127fc1096b43fff83e9f572fb49143e06a9088523/backports_zstd-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7d902c3526abf4cc822df8691ac456c22fa82d28585b15f92b7614a4f22e65e6", size = 481815, upload-time = "2025-11-23T12:50:53Z" }, + { url = "https://files.pythonhosted.org/packages/bf/44/aa4615dbda924f0af2422a82f84f48af9b4e3eb616197a2f7dd3a9f820fe/backports_zstd-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:8bb5714bd2738bc6327dd561e8f53fb9b279f5452261f3e0e21f95bfce076ed9", size = 509666, upload-time = "2025-11-23T12:50:54.187Z" }, + { url = "https://files.pythonhosted.org/packages/0f/5e/e925060bb2934336230f56833dcd0da056d7d4377061df3bf8bf23684df7/backports_zstd-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:52e47ad655550c37f6c5323d1d7f340a2b79429bf7a52dd27bee12cae85a4cca", size = 585809, upload-time = "2025-11-23T12:50:56.499Z" }, + { url = "https://files.pythonhosted.org/packages/1a/e7/a4d9945aaab5767270b92f4a1d6f1bbfe7fb3b606a3e504e4d3530aab866/backports_zstd-1.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:480dedd95d3d9f1274bf18b1db2bbb79f1da1ca6d0e37413498febbf3a1e1939", size = 631657, upload-time = "2025-11-23T12:50:57.756Z" }, + { url = "https://files.pythonhosted.org/packages/a2/38/5e55ab35e0fa330ac3c217e6fe996668a268cc0c3288e340f4c6c04c0a9d/backports_zstd-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bf9eec3b68914d2e157b8a37fe44bb7b1d2d74c29b7f1de30e906f3569df38f8", size = 495359, upload-time = "2025-11-23T12:50:59.034Z" }, + { url = "https://files.pythonhosted.org/packages/3d/3e/aa811c0fea6dc8e36f2086ac2972ccef3efe162ed584f5971497a76a8100/backports_zstd-1.1.0-cp39-cp39-win32.whl", hash = "sha256:b9a92024752786e44f796cfaf566d8c0618c08841becfa6f2ca0d87eac778943", size = 288711, upload-time = "2025-11-23T12:51:00.311Z" }, + { url = "https://files.pythonhosted.org/packages/a1/af/28f6c9928741d641224d89924937a2b5c0ed51010364bcb954ca313efe48/backports_zstd-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:5b80a8b0041a931a0c9ca218463eae7ba5d52fff501476aecc3669d7cbf4716c", size = 313600, upload-time = "2025-11-23T12:51:01.486Z" }, + { url = "https://files.pythonhosted.org/packages/e7/a6/10956eab990fa032afd10d651f32756ccdb57ce32b1648ee22ddb48cc3f9/backports_zstd-1.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:ba78e8c0b8c6605b69083ed3c6fa86af632989488e63bd7177a4f289bf8127c4", size = 288822, upload-time = "2025-11-23T12:51:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/6a/80/c08720ce3d7c446b5f91c2c894e677b673d87c4e9c7dd6f5c36303baaee8/backports_zstd-1.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d379e9c157e4b18df8852defe1d5da8888ba8ffb4d7d9410428b3245cf8216a", size = 409859, upload-time = "2025-11-23T12:51:04.504Z" }, + { url = "https://files.pythonhosted.org/packages/8f/f1/6220850b09a52cb07854d098307d71d9b330f4d9b49c2fda86c960c7df00/backports_zstd-1.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:79b29d1c66f9bd2bedb87551b303e53f96364a641d30251e9f4e15bf5d84699e", size = 339376, upload-time = "2025-11-23T12:51:05.677Z" }, + { url = "https://files.pythonhosted.org/packages/90/bf/0e6dfe1467dc228916a1715fc75421bafbb5dec4cf306c2395e45d8ef589/backports_zstd-1.1.0-pp310-pypy310_pp73-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:154870d0e0f817159671ffc8b0e39d70ca294542affe6df00d95ef135a2ceef5", size = 420422, upload-time = "2025-11-23T12:51:06.934Z" }, + { url = "https://files.pythonhosted.org/packages/68/9b/85dc1f5dc66c22f691bb60ec739e4be17c53dc670dcfabdf16ff194aec44/backports_zstd-1.1.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f174d8c93bd2682932ecb736e8f03612c162ec6a182c4293a663165f8ffbf5a", size = 393966, upload-time = "2025-11-23T12:51:08.247Z" }, + { url = "https://files.pythonhosted.org/packages/b6/23/1a375ea5e1e50741b80f9882c45a13c3b1b051985ae40eae44a34aa4e9e4/backports_zstd-1.1.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b1bf548087476e6ce997c2697072c67193b07e5a36d4eede2b582e7bc7324c2d", size = 413928, upload-time = "2025-11-23T12:51:09.353Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/687326aa8026b28251651add1e87931edbb0bc5bf563776cca6f81f35987/backports_zstd-1.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2a278c276e722399fbc769d1ae318a58578e20086bd09abcffe9af4b1daaf50c", size = 299788, upload-time = "2025-11-23T12:51:10.852Z" }, + { url = "https://files.pythonhosted.org/packages/f7/16/ce8d770a7129540520261694b0e83ec61684ab734f671e4eff652c040a9f/backports_zstd-1.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:06cef3f3b690221fd55d95cfb0316b914ce72063b99578b309205c9f73ce0bab", size = 409754, upload-time = "2025-11-23T12:51:12.084Z" }, + { url = "https://files.pythonhosted.org/packages/ef/c3/cbfdd61516d0a8448ad9194e34ffd434ac735ed1a89257f77407dbdbe0f1/backports_zstd-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:55446414f5deda38a4b8de22a0f3f93bacc1a8c4fe2205a0bace0dc55dbf9c90", size = 339289, upload-time = "2025-11-23T12:51:13.407Z" }, + { url = "https://files.pythonhosted.org/packages/97/7f/5e65b583abf56f7e3c54835a707cdd159e47b73f4b8548082debc630baae/backports_zstd-1.1.0-pp311-pypy311_pp73-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f1524c54ec491d80068598ba4328b82c403b99a04c0db6326a5cc8f05565de87", size = 420422, upload-time = "2025-11-23T12:51:14.553Z" }, + { url = "https://files.pythonhosted.org/packages/52/76/14349136c395f94fbd2af663301c2d661da9109fdf74692fe5095808cb9d/backports_zstd-1.1.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbcbb091fede530d096e32f3409056c59dc7b5e6cc9bf39131c7961eaaf8f475", size = 393965, upload-time = "2025-11-23T12:51:15.646Z" }, + { url = "https://files.pythonhosted.org/packages/c1/5b/fa580166cdf945be0b3ca1dbf8b23bc1370087707fa825b764d7e98b9a3c/backports_zstd-1.1.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5ac888007be1724efaa565f69ba64ce0c0c0e135d7f22830565399ff76465f1", size = 413929, upload-time = "2025-11-23T12:51:16.75Z" }, + { url = "https://files.pythonhosted.org/packages/aa/f3/650db1631d75c3f6ad46b09dfee2a761b2678b1e2e0004d90258020801c3/backports_zstd-1.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:08f66e228821a87ba7c74170b6b173cf249eeface98c0951c0da7db680b887d5", size = 299785, upload-time = "2025-11-23T12:51:17.969Z" }, +] + [[package]] name = "cachetools" version = "6.2.0" @@ -1210,12 +1320,12 @@ dependencies = [ [package.optional-dependencies] all = [ + { name = "backports-zstd", marker = "python_full_version < '3.14'" }, { name = "h5py" }, { name = "indexed-gzip" }, { name = "pillow" }, { name = "pydicom", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "pydicom", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "pyzstd" }, { name = "scipy", version = "1.13.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "scipy", version = "1.16.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -1275,11 +1385,13 @@ viewers = [ { name = "matplotlib", version = "3.10.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] zstd = [ - { name = "pyzstd" }, + { name = "backports-zstd", marker = "python_full_version < '3.14'" }, ] [package.metadata] requires-dist = [ + { name = "backports-zstd", marker = "python_full_version < '3.14' and extra == 'all'" }, + { name = "backports-zstd", marker = "python_full_version < '3.14' and extra == 'zstd'" }, { name = "coverage", extras = ["toml"], marker = "extra == 'test'", specifier = ">=7.2" }, { name = "h5py", marker = "extra == 'all'", specifier = ">=3.5" }, { name = "h5py", marker = "extra == 'minc2'", specifier = ">=3.5" }, @@ -1301,8 +1413,6 @@ requires-dist = [ { name = "pytest-doctestplus", marker = "extra == 'test'", specifier = ">=1.4" }, { name = "pytest-httpserver", marker = "extra == 'test'", specifier = ">=1.0.7" }, { name = "pytest-xdist", marker = "extra == 'test'", specifier = ">=3.5" }, - { name = "pyzstd", marker = "extra == 'all'", specifier = ">=0.15.2" }, - { name = "pyzstd", marker = "extra == 'zstd'", specifier = ">=0.15.2" }, { name = "scipy", marker = "extra == 'all'", specifier = ">=1.8" }, { name = "scipy", marker = "extra == 'spm'", specifier = ">=1.8" }, { name = "sphinx", marker = "extra == 'doc'" }, @@ -1813,110 +1923,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] -[[package]] -name = "pyzstd" -version = "0.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8f/a2/54d860ccbd07e3c67e4d0321d1c29fc7963ac82cf801a078debfc4ef7c15/pyzstd-0.17.0.tar.gz", hash = "sha256:d84271f8baa66c419204c1dd115a4dec8b266f8a2921da21b81764fa208c1db6", size = 1212160, upload-time = "2025-05-10T14:14:49.764Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/4f/fb1528fb8cc5c499d7d62953c6d0bce5e96260482abfba883f625c14d168/pyzstd-0.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ac857abb4c4daea71f134e74af7fe16bcfeec40911d13cf9128ddc600d46d92", size = 377826, upload-time = "2025-05-10T14:12:30.195Z" }, - { url = "https://files.pythonhosted.org/packages/f3/60/eedb75628f905263baf4c552dc8255912c43f70784c8b18ef9dd52b186f6/pyzstd-0.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2d84e8d1cbecd3b661febf5ca8ce12c5e112cfeb8401ceedfb84ab44365298ac", size = 297580, upload-time = "2025-05-10T14:12:32.254Z" }, - { url = "https://files.pythonhosted.org/packages/82/32/b7e776da4724c740e6a186e639b57ff0cd0ac23fac14e5c55cbd4bfcbd00/pyzstd-0.17.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f829fa1e7daac2e45b46656bdee13923150f329e53554aeaef75cceec706dd8c", size = 443135, upload-time = "2025-05-10T14:12:34.084Z" }, - { url = "https://files.pythonhosted.org/packages/4c/0b/3223f74d7b09122a695eebb861d7d7020f351b0610065db53d7c6981e592/pyzstd-0.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:994de7a13bb683c190a1b2a0fb99fe0c542126946f0345360582d7d5e8ce8cda", size = 390643, upload-time = "2025-05-10T14:12:36.052Z" }, - { url = "https://files.pythonhosted.org/packages/32/44/c98f10f62cf69d261ed796a2affe1c4ee5bedc05b9690a4c870bc2a74589/pyzstd-0.17.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3eb213a22823e2155aa252d9093c62ac12d7a9d698a4b37c5613f99cb9de327", size = 478067, upload-time = "2025-05-10T14:12:37.405Z" }, - { url = "https://files.pythonhosted.org/packages/5e/ec/78634376cec5de9e5648c92ca13efa350cab42acb48c72904652ac8a6b3e/pyzstd-0.17.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c451cfa31e70860334cc7dffe46e5178de1756642d972bc3a570fc6768673868", size = 421189, upload-time = "2025-05-10T14:12:38.728Z" }, - { url = "https://files.pythonhosted.org/packages/3f/d4/e7fd4b0bf3cb5d792e373c0002ac05b7b55ee8349dd80eb1c99c8d167973/pyzstd-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d66dc6f15249625e537ea4e5e64c195f50182556c3731f260b13c775b7888d6b", size = 412870, upload-time = "2025-05-10T14:12:40.038Z" }, - { url = "https://files.pythonhosted.org/packages/ea/65/1a5a8cb348349cef27326db169c61aa16f74cc8bc873b02ee1f8c0094b0e/pyzstd-0.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:308d4888083913fac2b7b6f4a88f67c0773d66db37e6060971c3f173cfa92d1e", size = 415555, upload-time = "2025-05-10T14:12:41.766Z" }, - { url = "https://files.pythonhosted.org/packages/8c/52/12c9402dce3dac85ae1e53bf5623deeb371221f1aa810c40f8b51f06ae40/pyzstd-0.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a3b636f37af9de52efb7dd2d2f15deaeabdeeacf8e69c29bf3e7e731931e6d66", size = 445346, upload-time = "2025-05-10T14:12:43.121Z" }, - { url = "https://files.pythonhosted.org/packages/fa/93/1d1bf5f73fc5b891d880ff96f6e266a1fe84c0be5beffe872afdd11a5e6a/pyzstd-0.17.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4c07391c67b496d851b18aa29ff552a552438187900965df57f64d5cf2100c40", size = 518741, upload-time = "2025-05-10T14:12:44.854Z" }, - { url = "https://files.pythonhosted.org/packages/fa/88/c9882b07c9010014161b39d28784f793219f89c86c4ba7748b6b71818f43/pyzstd-0.17.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e8bd12a13313ffa27347d7abe20840dcd2092852ab835a8e86008f38f11bd5ac", size = 562483, upload-time = "2025-05-10T14:12:46.508Z" }, - { url = "https://files.pythonhosted.org/packages/83/f7/8d34a9c424fed34353ebc9fcd93a42e9a289b13d651e9413ffd430d28874/pyzstd-0.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e27bfab45f9cdab0c336c747f493a00680a52a018a8bb7a1f787ddde4b29410", size = 432312, upload-time = "2025-05-10T14:12:48.248Z" }, - { url = "https://files.pythonhosted.org/packages/3f/0d/550003e5034383fa47741cb9991a0ec21fc373860eb4e145c6a2a4d06960/pyzstd-0.17.0-cp310-cp310-win32.whl", hash = "sha256:7370c0978edfcb679419f43ec504c128463858a7ea78cf6d0538c39dfb36fce3", size = 220017, upload-time = "2025-05-10T14:12:49.772Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9a/09cb36576f9ce0699bf271dd6a6d60afa1c79b67dc0f156e1c1dc479ba64/pyzstd-0.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:564f7aa66cda4acd9b2a8461ff0c6a6e39a977be3e2e7317411a9f7860d7338d", size = 246139, upload-time = "2025-05-10T14:12:51.529Z" }, - { url = "https://files.pythonhosted.org/packages/03/d4/ba87ffe5128e6c7d97bf99a9966bd9a76206b28c5d6c244b9697addbf3fc/pyzstd-0.17.0-cp310-cp310-win_arm64.whl", hash = "sha256:fccff3a37fa4c513fe1ebf94cb9dc0369c714da22b5671f78ddcbc7ec8f581cc", size = 223057, upload-time = "2025-05-10T14:12:52.879Z" }, - { url = "https://files.pythonhosted.org/packages/29/4a/81ca9a6a759ae10a51cb72f002c149b602ec81b3a568ca6292b117f6da0d/pyzstd-0.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06d1e7afafe86b90f3d763f83d2f6b6a437a8d75119fe1ff52b955eb9df04eaa", size = 377827, upload-time = "2025-05-10T14:12:54.102Z" }, - { url = "https://files.pythonhosted.org/packages/a1/09/584c12c8a918c9311a55be0c667e57a8ee73797367299e2a9f3fc3bf7a39/pyzstd-0.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc827657f644e4510211b49f5dab6b04913216bc316206d98f9a75214361f16e", size = 297579, upload-time = "2025-05-10T14:12:55.748Z" }, - { url = "https://files.pythonhosted.org/packages/e1/89/dc74cd83f30b97f95d42b028362e32032e61a8f8e6cc2a8e47b70976d99a/pyzstd-0.17.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecffadaa2ee516ecea3e432ebf45348fa8c360017f03b88800dd312d62ecb063", size = 443132, upload-time = "2025-05-10T14:12:57.098Z" }, - { url = "https://files.pythonhosted.org/packages/a8/12/fe93441228a324fe75d10f5f13d5e5d5ed028068810dfdf9505d89d704a0/pyzstd-0.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:596de361948d3aad98a837c98fcee4598e51b608f7e0912e0e725f82e013f00f", size = 390644, upload-time = "2025-05-10T14:12:58.379Z" }, - { url = "https://files.pythonhosted.org/packages/9d/d1/aa7cdeb9bf8995d9df9936c71151be5f4e7b231561d553e73bbf340c2281/pyzstd-0.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd3a8d0389c103e93853bf794b9a35ac5d0d11ca3e7e9f87e3305a10f6dfa6b2", size = 478070, upload-time = "2025-05-10T14:12:59.706Z" }, - { url = "https://files.pythonhosted.org/packages/95/62/7e5c450790bfd3db954694d4d877446d0b6d192aae9c73df44511f17b75c/pyzstd-0.17.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1356f72c7b8bb99b942d582b61d1a93c5065e66b6df3914dac9f2823136c3228", size = 421240, upload-time = "2025-05-10T14:13:01.151Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b5/d20c60678c0dfe2430f38241d118308f12516ccdb44f9edce27852ee2187/pyzstd-0.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f514c339b013b0b0a2ed8ea6e44684524223bd043267d7644d7c3a70e74a0dd", size = 412908, upload-time = "2025-05-10T14:13:02.904Z" }, - { url = "https://files.pythonhosted.org/packages/d2/a0/3ae0f1af2982b6cdeacc2a1e1cd20869d086d836ea43e0f14caee8664101/pyzstd-0.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d4de16306821021c2d82a45454b612e2a8683d99bfb98cff51a883af9334bea0", size = 415572, upload-time = "2025-05-10T14:13:04.828Z" }, - { url = "https://files.pythonhosted.org/packages/7d/84/cb0a10c3796f4cd5f09c112cbd72405ffd019f7c0d1e2e5e99ccc803c60c/pyzstd-0.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:aeb9759c04b6a45c1b56be21efb0a738e49b0b75c4d096a38707497a7ff2be82", size = 445334, upload-time = "2025-05-10T14:13:06.5Z" }, - { url = "https://files.pythonhosted.org/packages/d6/d6/8c5cf223067b69aa63f9ecf01846535d4ba82d98f8c9deadfc0092fa16ca/pyzstd-0.17.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a5b31ddeada0027e67464d99f09167cf08bab5f346c3c628b2d3c84e35e239a", size = 518748, upload-time = "2025-05-10T14:13:08.286Z" }, - { url = "https://files.pythonhosted.org/packages/bf/1c/dc7bab00a118d0ae931239b23e05bf703392005cf3bb16942b7b2286452a/pyzstd-0.17.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8338e4e91c52af839abcf32f1f65f3b21e2597ffe411609bdbdaf10274991bd0", size = 562487, upload-time = "2025-05-10T14:13:09.714Z" }, - { url = "https://files.pythonhosted.org/packages/e0/a4/fca96c0af643e4de38bce0dc25dab60ea558c49444c30b9dbe8b7a1714be/pyzstd-0.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:628e93862feb372b4700085ec4d1d389f1283ac31900af29591ae01019910ff3", size = 432319, upload-time = "2025-05-10T14:13:11.296Z" }, - { url = "https://files.pythonhosted.org/packages/f1/a3/7c924478f6c14b369fec8c5cd807b069439c6ecbf98c4783c5791036d3ad/pyzstd-0.17.0-cp311-cp311-win32.whl", hash = "sha256:c27773f9c95ebc891cfcf1ef282584d38cde0a96cb8d64127953ad752592d3d7", size = 220005, upload-time = "2025-05-10T14:13:13.188Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f6/d081b6b29cf00780c971b07f7889a19257dd884e64a842a5ebc406fd3992/pyzstd-0.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:c043a5766e00a2b7844705c8fa4563b7c195987120afee8f4cf594ecddf7e9ac", size = 246224, upload-time = "2025-05-10T14:13:14.478Z" }, - { url = "https://files.pythonhosted.org/packages/61/f3/f42c767cde8e3b94652baf85863c25476fd463f3bd61f73ed4a02c1db447/pyzstd-0.17.0-cp311-cp311-win_arm64.whl", hash = "sha256:efd371e41153ef55bf51f97e1ce4c1c0b05ceb59ed1d8972fc9aa1e9b20a790f", size = 223036, upload-time = "2025-05-10T14:13:15.752Z" }, - { url = "https://files.pythonhosted.org/packages/76/50/7fa47d0a13301b1ce20972aa0beb019c97f7ee8b0658d7ec66727b5967f9/pyzstd-0.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2ac330fc4f64f97a411b6f3fc179d2fe3050b86b79140e75a9a6dd9d6d82087f", size = 379056, upload-time = "2025-05-10T14:13:17.091Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f2/67b03b1fa4e2a0b05e147cc30ac6d271d3d11017b47b30084cb4699451f4/pyzstd-0.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:725180c0c4eb2e643b7048ebfb45ddf43585b740535907f70ff6088f5eda5096", size = 298381, upload-time = "2025-05-10T14:13:18.812Z" }, - { url = "https://files.pythonhosted.org/packages/01/8b/807ff0a13cf3790fe5de85e18e10c22b96d92107d2ce88699cefd3f890cb/pyzstd-0.17.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c20fe0a60019685fa1f7137cb284f09e3f64680a503d9c0d50be4dd0a3dc5ec", size = 443770, upload-time = "2025-05-10T14:13:20.495Z" }, - { url = "https://files.pythonhosted.org/packages/f0/88/832d8d8147691ee37736a89ea39eaf94ceac5f24a6ce2be316ff5276a1f8/pyzstd-0.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d97f7aaadc3b6e2f8e51bfa6aa203ead9c579db36d66602382534afaf296d0db", size = 391167, upload-time = "2025-05-10T14:13:22.236Z" }, - { url = "https://files.pythonhosted.org/packages/a8/a5/2e09bee398dfb0d94ca43f3655552a8770a6269881dc4710b8f29c7f71aa/pyzstd-0.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:42dcb34c5759b59721997036ff2d94210515d3ef47a9de84814f1c51a1e07e8a", size = 478960, upload-time = "2025-05-10T14:13:23.584Z" }, - { url = "https://files.pythonhosted.org/packages/da/b5/1f3b778ad1ccc395161fab7a3bf0dfbd85232234b6657c93213ed1ceda7e/pyzstd-0.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6bf05e18be6f6c003c7129e2878cffd76fcbebda4e7ebd7774e34ae140426cbf", size = 421891, upload-time = "2025-05-10T14:13:25.417Z" }, - { url = "https://files.pythonhosted.org/packages/83/c4/6bfb4725f4f38e9fe9735697060364fb36ee67546e7e8d78135044889619/pyzstd-0.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c40f7c3a5144aa4fbccf37c30411f6b1db4c0f2cb6ad4df470b37929bffe6ca0", size = 413608, upload-time = "2025-05-10T14:13:26.75Z" }, - { url = "https://files.pythonhosted.org/packages/95/a2/c48b543e3a482e758b648ea025b94efb1abe1f4859c5185ff02c29596035/pyzstd-0.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9efd4007f8369fd0890701a4fc77952a0a8c4cb3bd30f362a78a1adfb3c53c12", size = 416429, upload-time = "2025-05-10T14:13:28.096Z" }, - { url = "https://files.pythonhosted.org/packages/5c/62/2d039ee4dbc8116ca1f2a2729b88a1368f076f5dadad463f165993f7afa8/pyzstd-0.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5f8add139b5fd23b95daa844ca13118197f85bd35ce7507e92fcdce66286cc34", size = 446671, upload-time = "2025-05-10T14:13:29.772Z" }, - { url = "https://files.pythonhosted.org/packages/be/ec/9ec9f0957cf5b842c751103a2b75ecb0a73cf3d99fac57e0436aab6748e0/pyzstd-0.17.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:259a60e8ce9460367dcb4b34d8b66e44ca3d8c9c30d53ed59ae7037622b3bfc7", size = 520290, upload-time = "2025-05-10T14:13:31.585Z" }, - { url = "https://files.pythonhosted.org/packages/cc/42/2e2f4bb641c2a9ab693c31feebcffa1d7c24e946d8dde424bba371e4fcce/pyzstd-0.17.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:86011a93cc3455c5d2e35988feacffbf2fa106812a48e17eb32c2a52d25a95b3", size = 563785, upload-time = "2025-05-10T14:13:32.971Z" }, - { url = "https://files.pythonhosted.org/packages/4d/e4/25e198d382faa4d322f617d7a5ff82af4dc65749a10d90f1423af2d194f6/pyzstd-0.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:425c31bc3de80313054e600398e4f1bd229ee61327896d5d015e2cd0283c9012", size = 433390, upload-time = "2025-05-10T14:13:34.668Z" }, - { url = "https://files.pythonhosted.org/packages/ad/7c/1ab970f5404ace9d343a36a86f1bd0fcf2dc1adf1ef8886394cf0a58bd9e/pyzstd-0.17.0-cp312-cp312-win32.whl", hash = "sha256:7c4b88183bb36eb2cebbc0352e6e9fe8e2d594f15859ae1ef13b63ebc58be158", size = 220291, upload-time = "2025-05-10T14:13:36.005Z" }, - { url = "https://files.pythonhosted.org/packages/b2/52/d35bf3e4f0676a74359fccef015eabe3ceaba95da4ac2212f8be4dde16de/pyzstd-0.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c31947e0120468342d74e0fa936d43f7e1dad66a2262f939735715aa6c730e8", size = 246451, upload-time = "2025-05-10T14:13:37.712Z" }, - { url = "https://files.pythonhosted.org/packages/34/da/a44705fe44dd87e0f09861b062f93ebb114365640dbdd62cbe80da9b8306/pyzstd-0.17.0-cp312-cp312-win_arm64.whl", hash = "sha256:1d0346418abcef11507356a31bef5470520f6a5a786d4e2c69109408361b1020", size = 222967, upload-time = "2025-05-10T14:13:38.94Z" }, - { url = "https://files.pythonhosted.org/packages/7e/51/171f5aad999e3f99e664e8ef572bbf97cbd684c46891a99fe8767eb9b7f6/pyzstd-0.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6cd1a1d37a7abe9c01d180dad699e3ac3889e4f48ac5dcca145cc46b04e9abd2", size = 379051, upload-time = "2025-05-10T14:13:40.36Z" }, - { url = "https://files.pythonhosted.org/packages/83/1e/bdae9d1331a7fb60cdd9d3c75794ea4c0271d5e8408fbbe877353b730f99/pyzstd-0.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a44fd596eda06b6265dc0358d5b309715a93f8e96e8a4b5292c2fe0e14575b3", size = 298384, upload-time = "2025-05-10T14:13:41.728Z" }, - { url = "https://files.pythonhosted.org/packages/80/3d/c0b61fc7994254b369aa5e96fcd02dbb3f8964482d51e098640802dd35e8/pyzstd-0.17.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a99b37453f92f0691b2454d0905bbf2f430522612f6f12bbc81133ad947eb97", size = 445950, upload-time = "2025-05-10T14:13:43.034Z" }, - { url = "https://files.pythonhosted.org/packages/78/62/318de78124d49fe3f7ae2b44726bdb85eef63c3f3338ec3673665326df25/pyzstd-0.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63d864e9f9e624a466070a121ace9d9cbf579eac4ed575dee3b203ab1b3cbeee", size = 392923, upload-time = "2025-05-10T14:13:44.443Z" }, - { url = "https://files.pythonhosted.org/packages/7a/24/21541ee45cae4fd7e3d15d67f67ad3e96e41e0ee0a95653006f8a0df2349/pyzstd-0.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e58bc02b055f96d1f83c791dd197d8c80253275a56cd84f917a006e9f528420d", size = 480524, upload-time = "2025-05-10T14:13:45.798Z" }, - { url = "https://files.pythonhosted.org/packages/ed/fd/6659504588f4cb53ac5f347bd75206072c4969eacf3ae6925f46ddb6dadb/pyzstd-0.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e62df7c0ba74618481149c849bc3ed7d551b9147e1274b4b3170bbcc0bfcc0a", size = 423568, upload-time = "2025-05-10T14:13:47.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/50/1eefc03eb21745321893fbd52702245f85e9e1f7ad35411dff2606792100/pyzstd-0.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42ecdd7136294f1becb8e57441df00eaa6dfd7444a8b0c96a1dfba5c81b066e7", size = 415473, upload-time = "2025-05-10T14:13:48.994Z" }, - { url = "https://files.pythonhosted.org/packages/8a/27/f3da112795f9b9dc4db819f9f6e1b231a7adc03c609db1f2b33a4185be1d/pyzstd-0.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:be07a57af75f99fc39b8e2d35f8fb823ecd7ef099cd1f6203829a5094a991ae2", size = 418276, upload-time = "2025-05-10T14:13:50.316Z" }, - { url = "https://files.pythonhosted.org/packages/95/56/02b601d7198dc5138ceea6f2b978b3205b9fab05740957d1ef1c4ca59621/pyzstd-0.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0d41e6f7ec2a70dab4982157a099562de35a6735c890945b4cebb12fb7eb0be0", size = 449285, upload-time = "2025-05-10T14:13:51.759Z" }, - { url = "https://files.pythonhosted.org/packages/f4/79/8a4c352f9dd5728402318f324930250ad40df8fd27fea33818cf0c9ac171/pyzstd-0.17.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f482d906426756e7cc9a43f500fee907e1b3b4e9c04d42d58fb1918c6758759b", size = 522190, upload-time = "2025-05-10T14:13:53.075Z" }, - { url = "https://files.pythonhosted.org/packages/55/4a/51385325e7b816365292078449a8007bc3ab3e05b7b29ab91d9d519edb01/pyzstd-0.17.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:827327b35605265e1d05a2f6100244415e8f2728bb75c951736c9288415908d7", size = 566488, upload-time = "2025-05-10T14:13:54.484Z" }, - { url = "https://files.pythonhosted.org/packages/26/68/da37fb4e6a79a3cff7de4a3ee006fb5f981230c59de79f6c8c426392a265/pyzstd-0.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a55008f80e3390e4f37bd9353830f1675f271d13d6368d2f1dc413b7c6022b3", size = 432870, upload-time = "2025-05-10T14:13:55.86Z" }, - { url = "https://files.pythonhosted.org/packages/30/05/769d82f9708c4907512111a1de44bb77e5b08ad3862287c2e5fc5ead2df2/pyzstd-0.17.0-cp313-cp313-win32.whl", hash = "sha256:a4be186c0df86d4d95091c759a06582654f2b93690503b1c24d77f537d0cf5d0", size = 220290, upload-time = "2025-05-10T14:13:57.227Z" }, - { url = "https://files.pythonhosted.org/packages/62/92/f69eb8623f041c2656e27337ac08e69cd18a9eacb1557ab498d391f191bd/pyzstd-0.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:251a0b599bd224ec66f39165ddb2f959d0a523938e3bbfa82d8188dc03a271a2", size = 246450, upload-time = "2025-05-10T14:13:58.596Z" }, - { url = "https://files.pythonhosted.org/packages/ad/ef/5ae5445d5f675e9e8c868b2326597c5b396e41c5c9645daa45e8c1cd3d5c/pyzstd-0.17.0-cp313-cp313-win_arm64.whl", hash = "sha256:ce6d5fd908fd3ddec32d1c1a5a7a15b9d7737d0ef2ab20fe1e8261da61395017", size = 222966, upload-time = "2025-05-10T14:13:59.881Z" }, - { url = "https://files.pythonhosted.org/packages/59/0b/2a168c2e04a0fdb132544ad3ff181d70d1d04da86352a48110ed70fae4b4/pyzstd-0.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d5cb23c3c4ba4105a518cfbe8a566f9482da26f4bc8c1c865fd66e8e266be071", size = 377825, upload-time = "2025-05-10T14:14:01.631Z" }, - { url = "https://files.pythonhosted.org/packages/64/c8/9ccf1c2b7ff5feb292733bc2415a43a4508904df50041893a15af6785370/pyzstd-0.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:10b5d9215890a24f22505b68add26beeb2e3858bbe738a7ee339f0db8e29d033", size = 297574, upload-time = "2025-05-10T14:14:02.963Z" }, - { url = "https://files.pythonhosted.org/packages/b5/37/c2efb8c9fae97efc5d7e26e392c8e096f882415b5aa2d4ae2ca34a151222/pyzstd-0.17.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db1cff52fd24caf42a2cfb7e5d8dc822b93e9fac5dab505d0bd22e302061e2d2", size = 443131, upload-time = "2025-05-10T14:14:04.365Z" }, - { url = "https://files.pythonhosted.org/packages/aa/cf/8991ad14c18d385948ed353b55a10bb30fcaa0c9afdffd57b2ab2e47fae8/pyzstd-0.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3caad3106e0e80f76acbb19c15e1e834ba6fd44dd4c82719ef8e3374f7fafd3", size = 390638, upload-time = "2025-05-10T14:14:06.191Z" }, - { url = "https://files.pythonhosted.org/packages/f2/fd/ce69eaccf6e9839f5072f81bcf0f18cc840a7b6804b5857d750d104e24d7/pyzstd-0.17.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7e52e1de31b935e27568742145d8b4d0f204a1605e36f4e1e2846e0d39bed98", size = 478070, upload-time = "2025-05-10T14:14:07.548Z" }, - { url = "https://files.pythonhosted.org/packages/fe/b1/cb549c928394d75a6a32b5aefb6519a8bdbb6c946a87a9f0d78f1bd985ce/pyzstd-0.17.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eaa046bc9e751c4083102f3624a52bbb66e20e7aa3e28673543b22e69d9b57cd", size = 421188, upload-time = "2025-05-10T14:14:09.403Z" }, - { url = "https://files.pythonhosted.org/packages/58/00/556710feebcc4283740b553c2a9d6e5d8a5af01289ae034fe9b9f233a650/pyzstd-0.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cc9310bdb7cf2c70098aab40fb6bf68faaf0149110c6ef668996e7957e0147a", size = 412870, upload-time = "2025-05-10T14:14:10.767Z" }, - { url = "https://files.pythonhosted.org/packages/53/34/716504b8016e388519b9612261ec7038ca05da7ff2fbab5bbbf31529afcb/pyzstd-0.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3619075966456783818904f9d9e213c6fe2e583d5beb545fa1968b1848781e0f", size = 415558, upload-time = "2025-05-10T14:14:12.142Z" }, - { url = "https://files.pythonhosted.org/packages/d7/52/71e2ced6a30b55e4bb08c0ade8a8d1560ddcf8e9aeb81971b0232a364ab0/pyzstd-0.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3844f8c7d7850580423b1b33601b016b3b913d18deb6fe14a7641b9c2714275c", size = 445347, upload-time = "2025-05-10T14:14:14.214Z" }, - { url = "https://files.pythonhosted.org/packages/25/43/2ed031676f8b6427494213ff2d3a0dfd226d4b9c5bf53a39265652d9e77b/pyzstd-0.17.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab53f91280b7b639c47bb2048e01182230e7cf3f0f0980bdb405b4241cfb705e", size = 518739, upload-time = "2025-05-10T14:14:16.205Z" }, - { url = "https://files.pythonhosted.org/packages/88/a5/fc787ca6b47a286a6ed9b797836a094590d5266e850ead33184f0fc22869/pyzstd-0.17.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:75252ee53e53a819ea7ac4271f66686018bc8b98ef12628269f099c10d881077", size = 562482, upload-time = "2025-05-10T14:14:17.629Z" }, - { url = "https://files.pythonhosted.org/packages/75/c4/9b8c8a445b60d245d1bfe37de4914b5711885a870a15cbb97d1999461c03/pyzstd-0.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0795afdaa34e1ed7f3d7552100cd57a1cef9d7310b386a893e0890e9a585b427", size = 432311, upload-time = "2025-05-10T14:14:19.005Z" }, - { url = "https://files.pythonhosted.org/packages/79/a9/fb9dac75cfa2d0f616836ddcdc15bacfcebff3373ced70d17ff05ebf6aa9/pyzstd-0.17.0-cp39-cp39-win32.whl", hash = "sha256:f7316be5a5246b6bbdd807c7a4f10382b6b02c3afc5ae6acd2e266a84f715493", size = 220009, upload-time = "2025-05-10T14:14:20.361Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c4/dbc10af938777be8d23038372d7d3b030893b577b7d4103c0aeb8ee8e2b5/pyzstd-0.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:121e8fac3e24b881fed59d638100b80c34f6347c02d2f24580f633451939f2d7", size = 246137, upload-time = "2025-05-10T14:14:21.705Z" }, - { url = "https://files.pythonhosted.org/packages/87/cb/c05b66e51f0fd1b44cb197149e8e86388a58fee079d37aff88012321dfde/pyzstd-0.17.0-cp39-cp39-win_arm64.whl", hash = "sha256:fe36ccda67f73e909ac305984fe13b7b5a79296706d095a80472ada4413174c2", size = 223068, upload-time = "2025-05-10T14:14:23.041Z" }, - { url = "https://files.pythonhosted.org/packages/c9/32/97505422bd403a4207587fc454eaa6497d353e6110fce234e1d2be780279/pyzstd-0.17.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1c56f99c697130f39702e07ab9fa0bb4c929c7bfe47c0a488dea732bd8a8752a", size = 368393, upload-time = "2025-05-10T14:14:24.909Z" }, - { url = "https://files.pythonhosted.org/packages/1d/db/963dd8a5f9e29581097a4f3a9f0deaa8a2cd516b2ce945fcb489e3c19e2a/pyzstd-0.17.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:152bae1b2197bcd41fc143f93acd23d474f590162547484ca04ce5874c4847de", size = 283560, upload-time = "2025-05-10T14:14:26.171Z" }, - { url = "https://files.pythonhosted.org/packages/66/14/a8868202b896538f1f1ecbf13f226722426b6d44a11a8d6ce23ce57a4370/pyzstd-0.17.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2ddbbd7614922e52018ba3e7bb4cbe6f25b230096831d97916b8b89be8cd0cb", size = 356913, upload-time = "2025-05-10T14:14:27.519Z" }, - { url = "https://files.pythonhosted.org/packages/35/a6/7198ab6abd0604eb7d71a8a36b69b66441258d9216bc2fa5f181dcd47c7a/pyzstd-0.17.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f6f3f152888825f71fd2cf2499f093fac252a5c1fa15ab8747110b3dc095f6b", size = 329418, upload-time = "2025-05-10T14:14:28.897Z" }, - { url = "https://files.pythonhosted.org/packages/c3/6b/9901ea929ea481428113a16530b26873615ae2ed184897ec92e15004cc07/pyzstd-0.17.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d00a2d2bddf51c7bf32c17dc47f0f49f47ebae07c2528b9ee8abf1f318ac193", size = 349449, upload-time = "2025-05-10T14:14:30.247Z" }, - { url = "https://files.pythonhosted.org/packages/11/30/fc8258499b9a556eaadc61f542aa930d2046d96125454add97b2bc8fb052/pyzstd-0.17.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d79e3eff07217707a92c1a6a9841c2466bfcca4d00fea0bea968f4034c27a256", size = 241666, upload-time = "2025-05-10T14:14:31.712Z" }, - { url = "https://files.pythonhosted.org/packages/b8/95/b1ae395968efdba92704c23f2f8e027d08e00d1407671e42f65ac914d211/pyzstd-0.17.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3ce6bac0c4c032c5200647992a8efcb9801c918633ebe11cceba946afea152d9", size = 368391, upload-time = "2025-05-10T14:14:33.064Z" }, - { url = "https://files.pythonhosted.org/packages/c7/72/856831cacef58492878b8307353e28a3ba4326a85c3c82e4803a95ad0d14/pyzstd-0.17.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:a00998144b35be7c485a383f739fe0843a784cd96c3f1f2f53f1a249545ce49a", size = 283561, upload-time = "2025-05-10T14:14:34.469Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a7/a86e55cd9f3e630a71c0bf78ac6da0c6b50dc428ca81aa7c5adbc66eb880/pyzstd-0.17.0-pp311-pypy311_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8521d7bbd00e0e1c1fd222c1369a7600fba94d24ba380618f9f75ee0c375c277", size = 356912, upload-time = "2025-05-10T14:14:35.722Z" }, - { url = "https://files.pythonhosted.org/packages/ad/b7/de2b42dd96dfdb1c0feb5f43d53db2d3a060607f878da7576f35dff68789/pyzstd-0.17.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da65158c877eac78dcc108861d607c02fb3703195c3a177f2687e0bcdfd519d0", size = 329417, upload-time = "2025-05-10T14:14:37.487Z" }, - { url = "https://files.pythonhosted.org/packages/52/65/d4e8196e068e6b430499fb2a5092380eb2cb7eecf459b9d4316cff7ecf6c/pyzstd-0.17.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:226ca0430e2357abae1ade802585231a2959b010ec9865600e416652121ba80b", size = 349448, upload-time = "2025-05-10T14:14:38.797Z" }, - { url = "https://files.pythonhosted.org/packages/9e/15/b5ed5ad8c8d2d80c5f5d51e6c61b2cc05f93aaf171164f67ccc7ade815cd/pyzstd-0.17.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e3a19e8521c145a0e2cd87ca464bf83604000c5454f7e0746092834fd7de84d1", size = 241668, upload-time = "2025-05-10T14:14:40.18Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9c/f283a9918bb519b00703432ef0af1e79cabad9338e22d21897c621d919aa/pyzstd-0.17.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:56ed2de4717844ffdebb5c312ec7e7b8eb2b69eb72883bbfe472ba2c872419e6", size = 368390, upload-time = "2025-05-10T14:14:41.538Z" }, - { url = "https://files.pythonhosted.org/packages/e9/2c/8e3172ef6888b2549b51dab3378479b501123e46176227ddd7769031fff4/pyzstd-0.17.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc61c47ca631241081c0c99895a1feb56dab4beab37cac7d1f9f18aff06962eb", size = 283560, upload-time = "2025-05-10T14:14:42.879Z" }, - { url = "https://files.pythonhosted.org/packages/3d/6c/874399f3d342e6a54e12df3de4f2cc9ed2810aad27e2afd2317b7e71fcc4/pyzstd-0.17.0-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd61757a4020590dad6c20fdbf37c054ed9f349591a0d308c3c03c0303ce221", size = 356906, upload-time = "2025-05-10T14:14:44.233Z" }, - { url = "https://files.pythonhosted.org/packages/48/73/c4d8937b6eb755ff794ca695c5eaf369855dbab873ba44a76402100b3411/pyzstd-0.17.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d6cce91a8ac8ae1aab06684a8bf0dee088405de7f451e1e89776ddc1f40074", size = 329412, upload-time = "2025-05-10T14:14:45.608Z" }, - { url = "https://files.pythonhosted.org/packages/9b/d3/cac07c183ee124d809e025674e092b186f23083003339379a27708f8deff/pyzstd-0.17.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc668b67a13bf6213d0a9c09edc1f4842ed680b92fc3c9361f55a904903bfd1f", size = 349446, upload-time = "2025-05-10T14:14:47Z" }, - { url = "https://files.pythonhosted.org/packages/ee/21/c8726b1738d72c7f1602a6720996c4c227754b12335ad84e7db1300f8363/pyzstd-0.17.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a67d7ef18715875b31127eb90075c03ced722fd87902b34bca4b807a2ce1e4d9", size = 241664, upload-time = "2025-05-10T14:14:48.374Z" }, -] - [[package]] name = "requests" version = "2.32.5" From e9b87f081b7aa71115cb0cbd68c3e1a326c2b4c4 Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Wed, 26 Nov 2025 16:25:36 +0000 Subject: [PATCH 09/17] RF: Drop support for pyzstd - depend on compression.zstd, falling back to optional backports.zstd. Accept level_or_option parameter, but emit a deprecation warning --- nibabel/_compression.py | 75 ++++++++++++++++++++--------------------- nibabel/openers.py | 22 ++++++------ 2 files changed, 48 insertions(+), 49 deletions(-) diff --git a/nibabel/_compression.py b/nibabel/_compression.py index 4e6e1ab92c..4b079644f0 100644 --- a/nibabel/_compression.py +++ b/nibabel/_compression.py @@ -15,19 +15,22 @@ import io import typing as ty +try: + from compression import zstd + HAVE_ZSTD = True +except ImportError: # PY313 + HAVE_ZSTD = False + +from .deprecated import alert_future_error from .optpkg import optional_package if ty.TYPE_CHECKING: - import io import indexed_gzip # type: ignore[import] - # >= py314 - try: - from compression import zstd # type: ignore[import] - # < py314 - except ImportError: + if not HAVE_ZSTD: # PY313 from backports import zstd # type: ignore[import] + HAVE_ZSTD = True HAVE_INDEXED_GZIP = True HAVE_ZSTD = True @@ -42,8 +45,8 @@ else: indexed_gzip, HAVE_INDEXED_GZIP, _ = optional_package('indexed_gzip') - zstd, HAVE_ZSTD, _ = optional_package(('compression.zstd', - 'backports.zstd', 'pyzstd')) + if not HAVE_ZSTD: # PY313 + zstd, HAVE_ZSTD, _ = optional_package('backports.zstd') # Collections of types for isinstance or exception matching COMPRESSED_FILE_LIKES: tuple[type[io.IOBase], ...] = ( @@ -151,19 +154,15 @@ def zstd_open( filename: str, mode: Mode = 'r', *, - level_or_option: int | dict | None = None, - zstd_dict: zstd.ZstdDict | None = None, level : int = None, options : dict = None, + zstd_dict: zstd.ZstdDict | None = None, + level_or_option: int | dict | None = None ) -> zstd.ZstdFile: """Open a zstd file for reading or writing. - The specific object type returned depends on which module out of - ``compression.zstd``, ``backports.zstd``, or ``pyzstd`` is available. - - At most one of the ``level_or_options``, ``level``, or - ``options`` parameters may be provided - a ``ValueError`` will be raised - if more than one is specified. + The specific object returned will be a ``compression.zstd.ZstdFile`` or + a ``backports.zstd.ZstdFile``. Parameters ---------- @@ -172,9 +171,6 @@ def zstd_open( Path of file to open. mode : str Opening mode. - level_or_option: int or dict - Compression level or dictionary containing options (see also the - level and options parameters). zstd_dict : ZstdDict Dictionary used for compression/decompression. level : int @@ -182,24 +178,27 @@ def zstd_open( options : dict Dictionary of compression/decompression options. """ - level_or_option_provided = sum((level_or_option is not None, - level is not None, - options is not None)) - if level_or_option_provided > 1: - raise ValueError( - 'Only one of level_or_option, level, or options may be specified') - # pyzstd accepts level_or_option, but compression.zstd/backports.zstd - # expects level or options - level_or_option_kwarg = {} - if level_or_option_provided == 1: - level_or_option_value = [v for v in [level_or_option, level, options] - if v is not None][0] - if zstd.__name__ == 'pyzstd': - level_or_option_kwarg['level_or_option'] = level_or_option_value - else: - if isinstance(level_or_option_value, int): - level_or_option_kwarg['level'] = level_or_option_value + if level_or_option is not None: + alert_future_error( + 'The level_or_option parameter will be removed in a future ' + 'version of nibabel', + '7.0', + warning_rec='This warning can be silenced by using the separate ' + 'level/option parameters', + error_rec='Future errors can be avoided by using the separate ' + 'level/option parameters', + error_class=TypeError) + level_or_option_provided = sum((level_or_option is not None, + level is not None, + options is not None)) + if level_or_option_provided > 1: + raise ValueError( + 'Only one of level_or_option, level or options may be ' + 'specified') + if level_or_option is not None: + if isinstance(level_or_option, int): + level = level_or_option else: - level_or_option_kwarg['options'] = level_or_option_value + options = level_or_option return zstd.ZstdFile( - filename, mode, zstd_dict=zstd_dict, **level_or_option_kwarg) + filename, mode, level=level, options=options, zstd_dict=zstd_dict) diff --git a/nibabel/openers.py b/nibabel/openers.py index 70653a5407..9ef84c2ed2 100644 --- a/nibabel/openers.py +++ b/nibabel/openers.py @@ -62,8 +62,7 @@ class Opener: gz_def = (gzip_open, ('mode', 'compresslevel', 'mtime', 'keep_open')) bz2_def = (BZ2File, ('mode', 'buffering', 'compresslevel')) - zstd_def = (zstd_open, ('mode', 'level_or_option', 'zstd_dict', 'level', - 'option')) + zstd_def = (zstd_open, ('mode', 'level', 'option', 'zstd_dict')) compress_ext_map: dict[str | None, OpenerDef] = { '.gz': gz_def, '.bz2': bz2_def, @@ -72,13 +71,12 @@ class Opener: } #: default compression level when writing gz and bz2 files default_compresslevel = 1 - #: default option for zst files - default_zst_compresslevel = 3 - default_level_or_option = { + #: default compression level for zst files + default_zst_level = { 'rb': None, 'r': None, - 'wb': default_zst_compresslevel, - 'w': default_zst_compresslevel, + 'wb': 3, + 'w': 3, } #: whether to ignore case looking for compression extensions compress_ext_icase: bool = True @@ -92,7 +90,7 @@ def __init__(self, fileish: str | io.IOBase, *args, **kwargs): self._name = getattr(fileish, 'name', None) return opener, arg_names = self._get_opener_argnames(fileish) - # Get full arguments to check for mode and compresslevel + # Get full arguments to check for optional parameters full_kwargs = {**kwargs, **dict(zip(arg_names, args))} # Set default mode if 'mode' not in full_kwargs: @@ -100,11 +98,13 @@ def __init__(self, fileish: str | io.IOBase, *args, **kwargs): kwargs['mode'] = mode else: mode = full_kwargs['mode'] - # Default compression level + # Default gz/bz2 compression level if 'compresslevel' in arg_names and 'compresslevel' not in kwargs: kwargs['compresslevel'] = self.default_compresslevel - if 'level_or_option' in arg_names and 'level_or_option' not in kwargs: - kwargs['level_or_option'] = self.default_level_or_option[mode] + # Default zstd compression level + if ('level' in arg_names and 'level' not in kwargs and + 'option' not in kwargs): + kwargs['level'] = self.default_zst_level[mode] # Default keep_open hint if 'keep_open' in arg_names: kwargs.setdefault('keep_open', False) From c9fe6fcfacba0de50bf0f96a46ead7a5d2246c65 Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Wed, 26 Nov 2025 16:51:14 +0000 Subject: [PATCH 10/17] RF: Remove (HAVE_INDEXED_GZIP, IndexedGzipFile, DeterministicGzipFile, zstd) from openers module - now located in _compression. --- nibabel/arrayproxy.py | 5 +++-- nibabel/openers.py | 11 ++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/nibabel/arrayproxy.py b/nibabel/arrayproxy.py index 82713f639f..e31d63cde7 100644 --- a/nibabel/arrayproxy.py +++ b/nibabel/arrayproxy.py @@ -36,6 +36,7 @@ import numpy as np from . import openers +from ._compression import HAVE_INDEXED_GZIP from .fileslice import canonical_slicers, fileslice from .volumeutils import apply_read_scaling, array_from_file @@ -263,7 +264,7 @@ def _should_keep_file_open(self, keep_file_open): - whether ``self.file_like`` is an an open file handle, or a path to a ``'.gz'`` file, or a path to a non-gzip file. - whether ``indexed_gzip`` is present (see - :attr:`.openers.HAVE_INDEXED_GZIP`). + :attr:`._compression.HAVE_INDEXED_GZIP`). An ``ArrayProxy`` object uses two internal flags to manage ``ImageOpener`` instances and underlying file handles. @@ -330,7 +331,7 @@ def _should_keep_file_open(self, keep_file_open): if self._has_fh(): return False, False # if the file is a gzip file, and we have_indexed_gzip, - have_igzip = openers.HAVE_INDEXED_GZIP and self.file_like.endswith('.gz') + have_igzip = HAVE_INDEXED_GZIP and self.file_like.endswith('.gz') persist_opener = keep_file_open or have_igzip return keep_file_open, persist_opener diff --git a/nibabel/openers.py b/nibabel/openers.py index 9ef84c2ed2..e3b74bdf21 100644 --- a/nibabel/openers.py +++ b/nibabel/openers.py @@ -10,18 +10,15 @@ from __future__ import annotations -import gzip import io import typing as ty from bz2 import BZ2File from os.path import splitext -from ._compression import (HAVE_INDEXED_GZIP, - IndexedGzipFile, - DeterministicGzipFile, - zstd, - gzip_open, - zstd_open) +from ._compression import ( + gzip_open, + zstd_open, +) if ty.TYPE_CHECKING: from types import TracebackType From efdb160a9caa19c9097825589b3e9397e2d81384 Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Wed, 26 Nov 2025 16:52:14 +0000 Subject: [PATCH 11/17] TEST: Fix imports, update uses of HAVE_INDEXED_GZIP, IndexedGzipFile --- nibabel/benchmarks/bench_arrayproxy_slicing.py | 6 +++--- nibabel/benchmarks/bench_fileslice.py | 3 +-- nibabel/tests/test_analyze.py | 3 +-- nibabel/tests/test_loadsave.py | 2 +- nibabel/tests/test_minc1.py | 3 +-- nibabel/tests/test_openers.py | 9 +++------ nibabel/tests/test_volumeutils.py | 3 +-- 7 files changed, 11 insertions(+), 18 deletions(-) diff --git a/nibabel/benchmarks/bench_arrayproxy_slicing.py b/nibabel/benchmarks/bench_arrayproxy_slicing.py index 5da6c578f7..db902ce11b 100644 --- a/nibabel/benchmarks/bench_arrayproxy_slicing.py +++ b/nibabel/benchmarks/bench_arrayproxy_slicing.py @@ -18,7 +18,7 @@ import numpy as np import nibabel as nib -from nibabel.openers import HAVE_INDEXED_GZIP +from nibabel._compression import HAVE_INDEXED_GZIP from nibabel.tmpdirs import InTemporaryDirectory from ..rstutils import rst_table @@ -135,14 +135,14 @@ def fmt_sliceobj(sliceobj): # load uncompressed and compressed versions of the image img = nib.load(testfile, keep_file_open=keep_open) - with mock.patch('nibabel.openers.HAVE_INDEXED_GZIP', have_igzip): + with mock.patch('nibabel._compression.HAVE_INDEXED_GZIP', have_igzip): imggz = nib.load(testfilegz, keep_file_open=keep_open) def basefunc(): img.dataobj[fix_sliceobj(sliceobj)] def testfunc(): - with mock.patch('nibabel.openers.HAVE_INDEXED_GZIP', have_igzip): + with mock.patch('nibabel._compression.HAVE_INDEXED_GZIP', have_igzip): imggz.dataobj[fix_sliceobj(sliceobj)] # make sure nothing is floating around from the previous test diff --git a/nibabel/benchmarks/bench_fileslice.py b/nibabel/benchmarks/bench_fileslice.py index 3b5d8691ec..925df23db4 100644 --- a/nibabel/benchmarks/bench_fileslice.py +++ b/nibabel/benchmarks/bench_fileslice.py @@ -14,12 +14,11 @@ import numpy as np +from .._compression import HAVE_ZSTD from ..fileslice import fileslice from ..openers import ImageOpener -from ..optpkg import optional_package from ..rstutils import rst_table from ..tmpdirs import InTemporaryDirectory -from .._compression import HAVE_ZSTD SHAPE = (64, 64, 32, 100) ROW_NAMES = [f'axis {i}, len {dim}' for i, dim in enumerate(SHAPE)] diff --git a/nibabel/tests/test_analyze.py b/nibabel/tests/test_analyze.py index ee3c419502..5567029562 100644 --- a/nibabel/tests/test_analyze.py +++ b/nibabel/tests/test_analyze.py @@ -24,11 +24,11 @@ from numpy.testing import assert_array_almost_equal, assert_array_equal from .. import imageglobals +from .._compression import HAVE_ZSTD from ..analyze import AnalyzeHeader, AnalyzeImage from ..arraywriters import WriterError from ..casting import sctypes_aliases from ..nifti1 import Nifti1Header -from ..optpkg import optional_package from ..spatialimages import HeaderDataError, HeaderTypeError, supported_np_types from ..testing import ( assert_dt_equal, @@ -38,7 +38,6 @@ suppress_warnings, ) from ..tmpdirs import InTemporaryDirectory -from .._compression import HAVE_ZSTD from . import test_spatialimages as tsi from . import test_wrapstruct as tws diff --git a/nibabel/tests/test_loadsave.py b/nibabel/tests/test_loadsave.py index ae24ce30ff..505a4a2548 100644 --- a/nibabel/tests/test_loadsave.py +++ b/nibabel/tests/test_loadsave.py @@ -16,13 +16,13 @@ Spm2AnalyzeImage, Spm99AnalyzeImage, ) +from .._compression import HAVE_ZSTD from ..filebasedimages import ImageFileError from ..loadsave import _signature_matches_extension, load, read_img_data from ..openers import Opener from ..optpkg import optional_package from ..testing import deprecated_to, expires from ..tmpdirs import InTemporaryDirectory -from .._compression import HAVE_ZSTD _, have_scipy, _ = optional_package('scipy') diff --git a/nibabel/tests/test_minc1.py b/nibabel/tests/test_minc1.py index 911c9325c3..55832f73a9 100644 --- a/nibabel/tests/test_minc1.py +++ b/nibabel/tests/test_minc1.py @@ -17,14 +17,13 @@ from numpy.testing import assert_array_equal from .. import Nifti1Image, load, minc1 +from .._compression import HAVE_ZSTD, zstd from ..externals.netcdf import netcdf_file from ..minc1 import Minc1File, Minc1Image, MincHeader -from ..optpkg import optional_package from ..testing import assert_data_similar, data_path from ..tmpdirs import InTemporaryDirectory from . import test_spatialimages as tsi from .test_fileslice import slicer_samples -from .._compression import zstd, HAVE_ZSTD EG_FNAME = pjoin(data_path, 'tiny.mnc') diff --git a/nibabel/tests/test_openers.py b/nibabel/tests/test_openers.py index 133ddf8dbc..ad2b98b1eb 100644 --- a/nibabel/tests/test_openers.py +++ b/nibabel/tests/test_openers.py @@ -20,10 +20,9 @@ import pytest from packaging.version import Version -from ..openers import HAVE_INDEXED_GZIP, BZ2File, DeterministicGzipFile, ImageOpener, Opener -from ..optpkg import optional_package +from .._compression import HAVE_INDEXED_GZIP, HAVE_ZSTD, DeterministicGzipFile, zstd +from ..openers import BZ2File, ImageOpener, Opener from ..tmpdirs import InTemporaryDirectory -from .._compression import zstd, HAVE_ZSTD class Lunk: @@ -122,8 +121,6 @@ def patch_indexed_gzip(state): values = (False, GzipFile) with ( mock.patch('nibabel._compression.HAVE_INDEXED_GZIP', values[0]), - mock.patch('nibabel.openers.HAVE_INDEXED_GZIP', values[0]), - mock.patch('nibabel.openers.IndexedGzipFile', values[1], create=True), mock.patch('nibabel._compression.IndexedGzipFile', values[1], create=True), ): yield @@ -267,7 +264,7 @@ class StrictOpener(Opener): assert isinstance(fobj.fobj, file_class) elif lext == 'gz': try: - from ..openers import IndexedGzipFile + from .._compression import IndexedGzipFile except ImportError: IndexedGzipFile = GzipFile assert isinstance(fobj.fobj, (GzipFile, IndexedGzipFile)) diff --git a/nibabel/tests/test_volumeutils.py b/nibabel/tests/test_volumeutils.py index fd0b0cf8ce..9260310f6f 100644 --- a/nibabel/tests/test_volumeutils.py +++ b/nibabel/tests/test_volumeutils.py @@ -32,9 +32,9 @@ suppress_warnings, ) +from .._compression import HAVE_ZSTD, zstd from ..casting import OK_FLOATS, floor_log2, sctypes, shared_range, type_info from ..openers import BZ2File, ImageOpener, Opener -from ..optpkg import optional_package from ..tmpdirs import InTemporaryDirectory from ..volumeutils import ( _dt_min_max, @@ -56,7 +56,6 @@ working_type, write_zeros, ) -from .._compression import zstd, HAVE_ZSTD # convenience variables for numpy types FLOAT_TYPES = sctypes['float'] From 44745ac69b5cfac5d806b5cd0cb87173ddd1d3af Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Wed, 26 Nov 2025 16:55:17 +0000 Subject: [PATCH 12/17] STY: Address style warnings --- nibabel/_compression.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nibabel/_compression.py b/nibabel/_compression.py index 4b079644f0..b173422a3d 100644 --- a/nibabel/_compression.py +++ b/nibabel/_compression.py @@ -16,7 +16,7 @@ import typing as ty try: - from compression import zstd + from compression import zstd # type: ignore[import-not-found] HAVE_ZSTD = True except ImportError: # PY313 HAVE_ZSTD = False @@ -154,8 +154,8 @@ def zstd_open( filename: str, mode: Mode = 'r', *, - level : int = None, - options : dict = None, + level : int | None = None, + options : dict | None = None, zstd_dict: zstd.ZstdDict | None = None, level_or_option: int | dict | None = None ) -> zstd.ZstdFile: From b410d923b879f72670175cbb40b043d28452345c Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Thu, 27 Nov 2025 09:45:14 +0000 Subject: [PATCH 13/17] RF: Import _compression module rather than individual symbols to ease testing/mocking --- nibabel/arrayproxy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nibabel/arrayproxy.py b/nibabel/arrayproxy.py index e31d63cde7..627ac37c25 100644 --- a/nibabel/arrayproxy.py +++ b/nibabel/arrayproxy.py @@ -36,7 +36,7 @@ import numpy as np from . import openers -from ._compression import HAVE_INDEXED_GZIP +from . import _compression from .fileslice import canonical_slicers, fileslice from .volumeutils import apply_read_scaling, array_from_file @@ -331,7 +331,7 @@ def _should_keep_file_open(self, keep_file_open): if self._has_fh(): return False, False # if the file is a gzip file, and we have_indexed_gzip, - have_igzip = HAVE_INDEXED_GZIP and self.file_like.endswith('.gz') + have_igzip = _compression.HAVE_INDEXED_GZIP and self.file_like.endswith('.gz') persist_opener = keep_file_open or have_igzip return keep_file_open, persist_opener From d5c28b2a7d67c88f2fe44def27bb53103ab987b4 Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Thu, 27 Nov 2025 09:55:47 +0000 Subject: [PATCH 14/17] TEST: It appears that run_tests should be a set --- nibabel/tests/test_api_validators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nibabel/tests/test_api_validators.py b/nibabel/tests/test_api_validators.py index 2388089f2c..d559fd6328 100644 --- a/nibabel/tests/test_api_validators.py +++ b/nibabel/tests/test_api_validators.py @@ -99,7 +99,7 @@ class TestRunAllTests(ValidateAPI): We check this in the module teardown function """ - run_tests = {} + run_tests = set() def obj_params(self): yield 1, 2 From 24f8b52e17b894860dfd2a0a8566a8f5799745e6 Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Thu, 27 Nov 2025 10:00:27 +0000 Subject: [PATCH 15/17] STY: Style fixes --- nibabel/arrayproxy.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nibabel/arrayproxy.py b/nibabel/arrayproxy.py index 627ac37c25..e444853f97 100644 --- a/nibabel/arrayproxy.py +++ b/nibabel/arrayproxy.py @@ -35,8 +35,7 @@ import numpy as np -from . import openers -from . import _compression +from . import _compression, openers from .fileslice import canonical_slicers, fileslice from .volumeutils import apply_read_scaling, array_from_file From 2d0040df2bb42b058bbfbba2f9ffaa2060fded2b Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Thu, 27 Nov 2025 10:11:31 +0000 Subject: [PATCH 16/17] STY: More style fixes --- nibabel/_compression.py | 33 +++++++++++++++------------------ nibabel/openers.py | 3 +-- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/nibabel/_compression.py b/nibabel/_compression.py index b173422a3d..e436be4458 100644 --- a/nibabel/_compression.py +++ b/nibabel/_compression.py @@ -17,6 +17,7 @@ try: from compression import zstd # type: ignore[import-not-found] + HAVE_ZSTD = True except ImportError: # PY313 HAVE_ZSTD = False @@ -25,11 +26,11 @@ from .optpkg import optional_package if ty.TYPE_CHECKING: - import indexed_gzip # type: ignore[import] if not HAVE_ZSTD: # PY313 from backports import zstd # type: ignore[import] + HAVE_ZSTD = True HAVE_INDEXED_GZIP = True @@ -70,7 +71,6 @@ COMPRESSION_ERRORS += (zstd.ZstdError,) - class DeterministicGzipFile(gzip.GzipFile): """Deterministic variant of GzipFile @@ -107,6 +107,7 @@ def __init__( mtime=mtime, ) + def gzip_open( filename: str, mode: Mode = 'rb', @@ -154,10 +155,10 @@ def zstd_open( filename: str, mode: Mode = 'r', *, - level : int | None = None, - options : dict | None = None, + level: int | None = None, + options: dict | None = None, zstd_dict: zstd.ZstdDict | None = None, - level_or_option: int | dict | None = None + level_or_option: int | dict | None = None, ) -> zstd.ZstdFile: """Open a zstd file for reading or writing. @@ -180,25 +181,21 @@ def zstd_open( """ if level_or_option is not None: alert_future_error( - 'The level_or_option parameter will be removed in a future ' - 'version of nibabel', + 'The level_or_option parameter will be removed in a future version of nibabel', '7.0', warning_rec='This warning can be silenced by using the separate ' 'level/option parameters', - error_rec='Future errors can be avoided by using the separate ' - 'level/option parameters', - error_class=TypeError) - level_or_option_provided = sum((level_or_option is not None, - level is not None, - options is not None)) + error_rec='Future errors can be avoided by using the separate level/option parameters', + error_class=TypeError, + ) + level_or_option_provided = sum( + (level_or_option is not None, level is not None, options is not None) + ) if level_or_option_provided > 1: - raise ValueError( - 'Only one of level_or_option, level or options may be ' - 'specified') + raise ValueError('Only one of level_or_option, level or options may be specified') if level_or_option is not None: if isinstance(level_or_option, int): level = level_or_option else: options = level_or_option - return zstd.ZstdFile( - filename, mode, level=level, options=options, zstd_dict=zstd_dict) + return zstd.ZstdFile(filename, mode, level=level, options=options, zstd_dict=zstd_dict) diff --git a/nibabel/openers.py b/nibabel/openers.py index e3b74bdf21..8d958f7399 100644 --- a/nibabel/openers.py +++ b/nibabel/openers.py @@ -99,8 +99,7 @@ def __init__(self, fileish: str | io.IOBase, *args, **kwargs): if 'compresslevel' in arg_names and 'compresslevel' not in kwargs: kwargs['compresslevel'] = self.default_compresslevel # Default zstd compression level - if ('level' in arg_names and 'level' not in kwargs and - 'option' not in kwargs): + if 'level' in arg_names and 'level' not in kwargs and 'option' not in kwargs: kwargs['level'] = self.default_zst_level[mode] # Default keep_open hint if 'keep_open' in arg_names: From 4d2755f166a5e111e16227f1142a502e4d825a10 Mon Sep 17 00:00:00 2001 From: Paul McCarthy Date: Fri, 5 Dec 2025 18:22:01 +0000 Subject: [PATCH 17/17] RF: Simplify logic --- nibabel/_compression.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/nibabel/_compression.py b/nibabel/_compression.py index e436be4458..66a299b174 100644 --- a/nibabel/_compression.py +++ b/nibabel/_compression.py @@ -183,19 +183,14 @@ def zstd_open( alert_future_error( 'The level_or_option parameter will be removed in a future version of nibabel', '7.0', - warning_rec='This warning can be silenced by using the separate ' - 'level/option parameters', + warning_rec='This warning can be silenced by using the separate level/option parameters', error_rec='Future errors can be avoided by using the separate level/option parameters', error_class=TypeError, ) - level_or_option_provided = sum( - (level_or_option is not None, level is not None, options is not None) - ) - if level_or_option_provided > 1: + if level is not None or options is not None: raise ValueError('Only one of level_or_option, level or options may be specified') - if level_or_option is not None: - if isinstance(level_or_option, int): - level = level_or_option - else: - options = level_or_option + if isinstance(level_or_option, int): + level = level_or_option + else: + options = level_or_option return zstd.ZstdFile(filename, mode, level=level, options=options, zstd_dict=zstd_dict)