diff --git a/pyproject.toml b/pyproject.toml index 268302614..5502a05e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,7 @@ dependencies = [ "userpath~=1.7", "uv>=0.5.23", "virtualenv>=20.26.6", - "zstandard<1", + "backports.zstd>=0.4.0 ; python_version<'3.14'", ] dynamic = ["version"] diff --git a/src/hatch/python/resolve.py b/src/hatch/python/resolve.py index d893e6152..74f932248 100644 --- a/src/hatch/python/resolve.py +++ b/src/hatch/python/resolve.py @@ -5,7 +5,7 @@ import sys from abc import ABC, abstractmethod from functools import cached_property -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Literal from hatch.config.constants import PythonEnvVars from hatch.errors import PythonDistributionResolutionError, PythonDistributionUnknownError @@ -55,43 +55,36 @@ def archive_name(self) -> str: return self.source.rsplit('/', 1)[-1] def unpack(self, archive: Path, directory: Path) -> None: + # zip if self.source.endswith('.zip'): import zipfile with zipfile.ZipFile(archive, 'r') as zf: zf.extractall(directory) - elif self.source.endswith(('.tar.gz', '.tgz')): - import tarfile + return - with tarfile.open(archive, 'r:gz') as tf: - if sys.version_info[:2] >= (3, 12): - tf.extractall(directory, filter='data') - else: - tf.extractall(directory) # noqa: S202 - elif self.source.endswith(('.tar.bz2', '.bz2')): + # tar + if sys.version_info >= (3, 14): import tarfile + else: + # for zstd support (introduced in Python 3.14) + # and filter kwarg (introduced in Python 3.12) + from backports.zstd import tarfile - with tarfile.open(archive, 'r:bz2') as tf: - if sys.version_info[:2] >= (3, 12): - tf.extractall(directory, filter='data') - else: - tf.extractall(directory) # noqa: S202 + mode: Literal['r:gz', 'r:bz2', 'r:zst'] + if self.source.endswith(('.tar.gz', '.tgz')): + mode = 'r:gz' + elif self.source.endswith(('.tar.bz2', '.bz2')): + mode = 'r:bz2' elif self.source.endswith(('.tar.zst', '.tar.zstd')): - import tarfile - - import zstandard - - with open(archive, 'rb') as ifh: - dctx = zstandard.ZstdDecompressor() - with dctx.stream_reader(ifh) as reader, tarfile.open(mode='r|', fileobj=reader) as tf: - if sys.version_info[:2] >= (3, 12): - tf.extractall(directory, filter='data') - else: - tf.extractall(directory) # noqa: S202 + mode = 'r:zst' else: message = f'Unknown archive type: {archive}' raise ValueError(message) + with tarfile.open(archive, mode) as tf: + tf.extractall(directory, filter='data') + @property @abstractmethod def version(self) -> Version: