diff --git a/src/build/__init__.py b/src/build/__init__.py index 8b3b2681..c92daba7 100644 --- a/src/build/__init__.py +++ b/src/build/__init__.py @@ -24,6 +24,7 @@ import pyproject_hooks from . import env +from ._compat import tomllib from ._exceptions import ( BuildBackendException, BuildException, @@ -34,12 +35,6 @@ from ._util import check_dependency, parse_wheel_filename -if sys.version_info >= (3, 11): - import tomllib -else: - import tomli as tomllib - - RunnerType = Callable[[Sequence[str], Optional[str], Optional[Mapping[str, str]]], None] ConfigSettingsType = Mapping[str, Union[str, Sequence[str]]] PathType = Union[str, 'os.PathLike[str]'] diff --git a/src/build/__main__.py b/src/build/__main__.py index 985caf9a..03c3dcd8 100644 --- a/src/build/__main__.py +++ b/src/build/__main__.py @@ -230,7 +230,7 @@ def build_package_via_sdist( :param isolation: Isolate the build in a separate environment :param skip_dependency_check: Do not perform the dependency check """ - from ._util import TarFile + from ._compat import tarfile if 'sdist' in distributions: msg = 'Only binary distributions are allowed but sdist was specified' @@ -243,7 +243,7 @@ def build_package_via_sdist( built: list[str] = [] if distributions: # extract sdist - with TarFile.open(sdist) as t: + with tarfile.TarFile.open(sdist) as t: t.extractall(sdist_out) try: _ProjectBuilder.log(f'Building {_natural_language_list(distributions)} from sdist') diff --git a/src/build/_compat/__init__.py b/src/build/_compat/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/build/_importlib.py b/src/build/_compat/importlib.py similarity index 84% rename from src/build/_importlib.py rename to src/build/_compat/importlib.py index 0282729f..7487ddfd 100644 --- a/src/build/_importlib.py +++ b/src/build/_compat/importlib.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys @@ -12,4 +14,7 @@ # helps bootstrapping when dependencies aren't installed from importlib import metadata -__all__ = ['metadata'] + +__all__ = [ + 'metadata', +] diff --git a/src/build/_compat/tarfile.py b/src/build/_compat/tarfile.py new file mode 100644 index 00000000..2984a497 --- /dev/null +++ b/src/build/_compat/tarfile.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +import sys +import tarfile +import typing + + +if typing.TYPE_CHECKING: + TarFile = tarfile.TarFile + +else: + # Per https://peps.python.org/pep-0706/, the "data" filter will become + # the default in Python 3.14. The first series of releases with the filter + # had a broken filter that could not process symlinks correctly. + if ( + (3, 8, 18) <= sys.version_info < (3, 9) + or (3, 9, 18) <= sys.version_info < (3, 10) + or (3, 10, 13) <= sys.version_info < (3, 11) + or (3, 11, 5) <= sys.version_info < (3, 12) + or (3, 12) <= sys.version_info < (3, 14) + ): + + class TarFile(tarfile.TarFile): + extraction_filter = staticmethod(tarfile.data_filter) + + else: + TarFile = tarfile.TarFile + + +__all__ = [ + 'TarFile', +] diff --git a/src/build/_compat/tomllib.py b/src/build/_compat/tomllib.py new file mode 100644 index 00000000..51191efe --- /dev/null +++ b/src/build/_compat/tomllib.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +import sys + + +if sys.version_info >= (3, 11): + from tomllib import TOMLDecodeError, load, loads +else: + from tomli import TOMLDecodeError, load, loads + + +__all__ = [ + 'TOMLDecodeError', + 'load', + 'loads', +] diff --git a/src/build/_util.py b/src/build/_util.py index 691e15a7..a57c41eb 100644 --- a/src/build/_util.py +++ b/src/build/_util.py @@ -1,9 +1,6 @@ from __future__ import annotations import re -import sys -import tarfile -import typing from collections.abc import Iterator, Set @@ -27,7 +24,7 @@ def check_dependency( """ import packaging.requirements - from ._importlib import metadata + from ._compat import importlib req = packaging.requirements.Requirement(req_string) normalised_req_string = str(req) @@ -48,8 +45,8 @@ def check_dependency( return try: - dist = metadata.distribution(req.name) - except metadata.PackageNotFoundError: + dist = importlib.metadata.distribution(req.name) + except importlib.metadata.PackageNotFoundError: # dependency is not installed in the environment. yield (*ancestral_req_strings, normalised_req_string) else: @@ -64,25 +61,3 @@ def check_dependency( def parse_wheel_filename(filename: str) -> re.Match[str] | None: return _WHEEL_FILENAME_REGEX.match(filename) - - -if typing.TYPE_CHECKING: - TarFile = tarfile.TarFile - -else: - # Per https://peps.python.org/pep-0706/, the "data" filter will become - # the default in Python 3.14. The first series of releases with the filter - # had a broken filter that could not process symlinks correctly. - if ( - (3, 8, 18) <= sys.version_info < (3, 9) - or (3, 9, 18) <= sys.version_info < (3, 10) - or (3, 10, 13) <= sys.version_info < (3, 11) - or (3, 11, 5) <= sys.version_info < (3, 12) - or (3, 12) <= sys.version_info < (3, 14) - ): - - class TarFile(tarfile.TarFile): - extraction_filter = staticmethod(tarfile.data_filter) - - else: - TarFile = tarfile.TarFile diff --git a/src/build/env.py b/src/build/env.py index 95ba9d15..5cb7a3ae 100644 --- a/src/build/env.py +++ b/src/build/env.py @@ -79,12 +79,12 @@ def _has_valid_pip(**distargs: object) -> bool: import packaging.version - from ._importlib import metadata + from ._compat import importlib name = 'pip' try: - pip_distribution = next(iter(metadata.distributions(name=name, **distargs))) + pip_distribution = next(iter(importlib.metadata.distributions(name=name, **distargs))) except StopIteration: raise ModuleNotFoundError(name) from None diff --git a/src/build/util.py b/src/build/util.py index 9f204b85..671c1c62 100644 --- a/src/build/util.py +++ b/src/build/util.py @@ -8,14 +8,14 @@ import pyproject_hooks from . import PathType, ProjectBuilder, RunnerType -from ._importlib import metadata +from ._compat import importlib from .env import DefaultIsolatedEnv -def _project_wheel_metadata(builder: ProjectBuilder) -> metadata.PackageMetadata: +def _project_wheel_metadata(builder: ProjectBuilder) -> importlib.metadata.PackageMetadata: with tempfile.TemporaryDirectory() as tmpdir: path = pathlib.Path(builder.metadata_path(tmpdir)) - return metadata.PathDistribution(path).metadata + return importlib.metadata.PathDistribution(path).metadata def project_wheel_metadata( @@ -23,7 +23,7 @@ def project_wheel_metadata( isolated: bool = True, *, runner: RunnerType = pyproject_hooks.quiet_subprocess_runner, -) -> metadata.PackageMetadata: +) -> importlib.metadata.PackageMetadata: """ Return the wheel metadata for a project. diff --git a/tests/test_env.py b/tests/test_env.py index feaf042a..379d8e57 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -140,7 +140,7 @@ def test_pip_needs_upgrade_mac_os_11(mocker, pip_version, arch): mocker.patch('platform.system', return_value='Darwin') mocker.patch('platform.machine', return_value=arch) mocker.patch('platform.mac_ver', return_value=('11.0', ('', '', ''), '')) - mocker.patch('build._importlib.metadata.distributions', return_value=(SimpleNamespace(version=pip_version),)) + mocker.patch('build._compat.importlib.metadata.distributions', return_value=(SimpleNamespace(version=pip_version),)) min_version = Version('20.3' if arch == 'x86_64' else '21.0.1') with build.env.DefaultIsolatedEnv(): diff --git a/tests/test_projectbuilder.py b/tests/test_projectbuilder.py index 602ca06e..cccb3852 100644 --- a/tests/test_projectbuilder.py +++ b/tests/test_projectbuilder.py @@ -13,7 +13,7 @@ import build -from build import _importlib +from build._compat import importlib as _importlib build_open_owner = 'builtins' diff --git a/tests/test_self_packaging.py b/tests/test_self_packaging.py index 3a436ef4..9d515e5d 100644 --- a/tests/test_self_packaging.py +++ b/tests/test_self_packaging.py @@ -34,6 +34,7 @@ sdist_patterns = { 'docs/*.rst', 'src/build/*.py', + 'src/build/_compat/*.py', 'tests/*.py', 'tests/packages/*/*.py', 'tests/packages/*/*/*.py', @@ -46,8 +47,11 @@ wheel_files = { 'build/__init__.py', 'build/__main__.py', + 'build/_compat/__init__.py', + 'build/_compat/importlib.py', + 'build/_compat/tarfile.py', + 'build/_compat/tomllib.py', 'build/_exceptions.py', - 'build/_importlib.py', 'build/_util.py', 'build/env.py', 'build/py.typed',