diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index bd658b7b9..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,7 +0,0 @@ -include CHANGES.md dtox.sh mypy.ini tox.ini -graft docker -graft docs -graft scripts -graft testing -graft tests -global-exclude *.pyc *.pyo diff --git a/build-backend/pex_build.dist-info/entry_points.txt b/build-backend/pex_build.dist-info/entry_points.txt new file mode 100644 index 000000000..7e0169d7f --- /dev/null +++ b/build-backend/pex_build.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[hatch] +pex-dynamic-requires-python = pex_build.hatchling.hooks diff --git a/build-backend/pex_build/__init__.py b/build-backend/pex_build/__init__.py new file mode 100644 index 000000000..8f221708c --- /dev/null +++ b/build-backend/pex_build/__init__.py @@ -0,0 +1,2 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). diff --git a/build-backend/pex_build/hatchling/__init__.py b/build-backend/pex_build/hatchling/__init__.py new file mode 100644 index 000000000..8f221708c --- /dev/null +++ b/build-backend/pex_build/hatchling/__init__.py @@ -0,0 +1,2 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). diff --git a/build-backend/pex_build/hatchling/build.py b/build-backend/pex_build/hatchling/build.py new file mode 100644 index 000000000..e1be4392b --- /dev/null +++ b/build-backend/pex_build/hatchling/build.py @@ -0,0 +1,7 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import absolute_import + +# We re-export all hatchling's PEP-517 build backend hooks here for the build frontend to call. +from hatchling.build import * # NOQA diff --git a/build-backend/pex_build/hatchling/dynamic_requires_python.py b/build-backend/pex_build/hatchling/dynamic_requires_python.py new file mode 100644 index 000000000..d65941fb8 --- /dev/null +++ b/build-backend/pex_build/hatchling/dynamic_requires_python.py @@ -0,0 +1,27 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import absolute_import, print_function + +import os +import sys +from typing import Any, Dict + +from hatchling.metadata.plugin.interface import MetadataHookInterface + + +class DynamicRequiresPythonHook(MetadataHookInterface): + """Allows dynamically specifying requires-python metadata via _PEX_REQUIRES_PYTHON env var.""" + + PLUGIN_NAME = "pex-dynamic-requires-python" + + def update(self, metadata): + # type: (Dict[str, Any]) -> None + requires_python = os.environ.get("_PEX_REQUIRES_PYTHON") + if requires_python: + print( + "pex_build: Dynamically modifying pyproject.toml requires-python of {original} to " + "{dynamic}".format(original=metadata["requires-python"], dynamic=requires_python), + file=sys.stderr, + ) + metadata["requires-python"] = requires_python diff --git a/build-backend/pex_build/hatchling/hooks.py b/build-backend/pex_build/hatchling/hooks.py new file mode 100644 index 000000000..b11ec0891 --- /dev/null +++ b/build-backend/pex_build/hatchling/hooks.py @@ -0,0 +1,15 @@ +# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import absolute_import + +from typing import Type + +from hatchling.plugin import hookimpl +from pex_build.hatchling.dynamic_requires_python import DynamicRequiresPythonHook + + +@hookimpl +def hatch_register_metadata_hook(): + # type: () -> Type[DynamicRequiresPythonHook] + return DynamicRequiresPythonHook diff --git a/mypy.ini b/mypy.ini index 76b0c73fd..a73b47703 100644 --- a/mypy.ini +++ b/mypy.ini @@ -45,3 +45,10 @@ ignore_missing_imports = True [mypy-pkg_resources] ignore_missing_imports = True + +[mypy-hatchling.*] +# Currently we use one venv for MyPy checks and the hatchling requirements are not compatible with +# the rest; so we ignore. Ideally pex.hatchling build backend type-checking would be segregated as +# scripts are today and each with its own venv. +ignore_missing_imports = True + diff --git a/pex/pip/tool.py b/pex/pip/tool.py index 2c189224a..90be6a305 100644 --- a/pex/pip/tool.py +++ b/pex/pip/tool.py @@ -436,6 +436,7 @@ def spawn_download_distributions( download_cmd = ["download", "--dest", download_dir] extra_env = {} # type: Dict[str, str] + pex_extra_sys_path = [] # type: List[str] if not build: download_cmd.extend(["--only-binary", ":all:"]) @@ -451,7 +452,7 @@ def spawn_download_distributions( if not build_isolation: download_cmd.append("--no-build-isolation") - extra_env.update(PEP517_BACKEND_PATH=os.pathsep.join(sys.path)) + pex_extra_sys_path.extend(sys.path) if allow_prereleases: download_cmd.append("--pre") @@ -483,7 +484,6 @@ def spawn_download_distributions( ) log_analyzers = [] # type: List[LogAnalyzer] - pex_extra_sys_path = [] # type: List[str] for obs in (foreign_platform_observer, observer): if obs: if obs.analyzer: @@ -492,10 +492,10 @@ def spawn_download_distributions( extra_sys_path = obs.patch_set.emit_patches(package=self._PATCHES_PACKAGE_NAME) if extra_sys_path: pex_extra_sys_path.append(extra_sys_path) + extra_env[self._PATCHES_PACKAGE_ENV_VAR_NAME] = self._PATCHES_PACKAGE_NAME if pex_extra_sys_path: extra_env["PEX_EXTRA_SYS_PATH"] = os.pathsep.join(pex_extra_sys_path) - extra_env[self._PATCHES_PACKAGE_ENV_VAR_NAME] = self._PATCHES_PACKAGE_NAME # The Pip 2020 resolver hides useful dependency conflict information in stdout interspersed # with other information we want to suppress. We jump though some hoops here to get at that @@ -624,7 +624,7 @@ def spawn_build_wheels( if not build_isolation: wheel_cmd.append("--no-build-isolation") interpreter = interpreter or PythonInterpreter.get() - extra_env.update(PEP517_BACKEND_PATH=os.pathsep.join(interpreter.sys_path)) + extra_env.update(PEX_EXTRA_SYS_PATH=os.pathsep.join(interpreter.sys_path)) if not verify: wheel_cmd.append("--no-verify") diff --git a/pex/vendor/__init__.py b/pex/vendor/__init__.py index 35131734d..caf648681 100644 --- a/pex/vendor/__init__.py +++ b/pex/vendor/__init__.py @@ -151,14 +151,7 @@ def create_packages(self): if not self.rewrite: # The extra package structure is only required by Pex for vendored code used via import # rewrites. - - # N.B.: Although we've historically early-returned here, the switch from flit to - # setuptools for our build backend necessitates all vendored dists are seen as part of - # the `pex` package tree by setuptools to get all vendored code properly included in - # our distribution. - # TODO(John Sirois): re-introduce early return once it is no longer foils our build - # process. - pass + return for index, _ in enumerate(self._subpath_components): relpath = _PACKAGE_COMPONENTS + self._subpath_components[: index + 1] + ["__init__.py"] diff --git a/pex/vendor/_vendored/pip/.layout.json b/pex/vendor/_vendored/pip/.layout.json index f58938501..9c0d6c183 100644 --- a/pex/vendor/_vendored/pip/.layout.json +++ b/pex/vendor/_vendored/pip/.layout.json @@ -1 +1 @@ -{"fingerprint": "69c1ee1f3c238f9f7a88645a63f68c3c5802b6e3d349612fde5fe2b8c692a668", "record_relpath": "pip-20.3.4.dist-info/RECORD", "stash_dir": ".prefix"} \ No newline at end of file +{"fingerprint": "120267325b80f5c4b4adac019eb6617ab3319395c043d2871eedf70dd6ae2954", "record_relpath": "pip-20.3.4.dist-info/RECORD", "stash_dir": ".prefix"} \ No newline at end of file diff --git a/pex/vendor/_vendored/pip/__init__.py b/pex/vendor/_vendored/pip/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pyproject.toml b/pyproject.toml index 1b734fee2..29889539c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,76 @@ [build-system] -# N.B.: We use `setup.cfg` for declarative metadata instead of `[project]` here since support for -# pyproject.toml projects was introduced in setuptools 61.0.0 which only supports Python>=3.7. We -# need to also support 2.7, 3.5 and 3.6. -requires = ["setuptools"] -build-backend = "setuptools.build_meta" +# N.B.: We use an in-tree backend just to get our hatchling in-tree plugins visible without having +# to publish a plugin distribution. The real backend is `hatchling.build` and the +# `pex_build.hatchling.build` backend is a very thin wrapper that just serves to install our hooks +# just in time. +backend-path = ["build-backend"] +build-backend = "pex_build.hatchling.build" +requires = ["hatchling"] + +[tool.hatch.metadata.hooks.pex-dynamic-requires-python] +# We need this empty table to enable our hook. + +[project] +name = "pex" +dynamic = ["version"] +requires-python = ">=2.7,<3.13,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" +authors = [ + {name = "The PEX developers", email="pantsbuild@gmail.com"} +] +description = "The PEX packaging toolchain." +readme = "README.rst" +license-files = { paths = ["LICENSE"] } +keywords = ["package", "executable", "virtualenv", "lock", "freeze"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Operating System :: Unix", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS :: MacOS X", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Software Development :: Build Tools", + "Topic :: System :: Archiving :: Packaging", + "Topic :: System :: Software Distribution", + "Topic :: Utilities", +] + +[project.optional-dependencies] +subprocess = [ + "subprocess32>=3.2.7; python_version < '3'" +] + +[project.scripts] +pex = "pex.bin.pex:main" +pex3 = "pex.cli.pex:main" +pex-tools = "pex.tools.main:main" + +[project.entry-points."distutils.commands"] +bdist_pex = "pex.distutils.commands.bdist_pex:bdist_pex" + +[project.urls] +Homepage = "https://github.com/pantsbuild/pex" +Download = "https://github.com/pantsbuild/pex/releases/latest/download/pex" +Changelog = "https://github.com/pantsbuild/pex/blob/main/CHANGES.md" +Documentation = "https://pex.readthedocs.io/en/latest/" +Source = "https://github.com/pantsbuild/pex" + +[tool.hatch.version] +path = "pex/version.py" +pattern = '__version__ = "(?P[^"]+)"' [tool.black] line-length = 100 diff --git a/scripts/format.py b/scripts/format.py index f71abf756..ba7629894 100755 --- a/scripts/format.py +++ b/scripts/format.py @@ -35,7 +35,7 @@ def run_black(*args: str) -> None: dest=sys.stdout, ) as out_fd: subprocess.run( - args=["black", "--color", *args, "setup.py", "pex", "scripts", "testing", "tests"], + args=["black", "--color", *args, "build-backend", "pex", "scripts", "testing", "tests"], stdout=out_fd, stderr=subprocess.STDOUT, check=True, @@ -44,7 +44,7 @@ def run_black(*args: str) -> None: def run_isort(*args: str) -> None: subprocess.run( - args=["isort", *args, "setup.py", "pex", "scripts", "testing", "tests"], check=True + args=["isort", *args, "build-backend", "pex", "scripts", "testing", "tests"], check=True ) diff --git a/scripts/lint.py b/scripts/lint.py index 794643fc3..41e01ec20 100644 --- a/scripts/lint.py +++ b/scripts/lint.py @@ -24,7 +24,7 @@ def run_autoflake(*args: str) -> None: "--exclude", ",".join(excludes), "--recursive", - "setup.py", + "build-backend", "pex", "scripts", "testing", diff --git a/scripts/package.py b/scripts/package.py index 50f55076e..b160f78bc 100755 --- a/scripts/package.py +++ b/scripts/package.py @@ -6,6 +6,7 @@ import subprocess import sys from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser +from email.parser import Parser from enum import Enum, unique from http.server import HTTPServer, SimpleHTTPRequestHandler from pathlib import Path, PurePath @@ -46,7 +47,12 @@ def build_pex_pex(output_file: PurePath, verbosity: int = 0) -> None: subprocess.run(args, check=True) -def describe_git_rev() -> str: +def describe_rev() -> str: + if not os.path.isdir(".git") and os.path.isfile("PKG-INFO"): + # We're being build from an unpacked sdist. + with open("PKG-INFO") as fp: + return Parser().parse(fp).get("Version", "Unknown Version") + git_describe = subprocess.run( ["git", "describe"], check=True, stdout=subprocess.PIPE, encoding="utf-8" ) @@ -103,9 +109,9 @@ def main( print(f"Building Pex PEX to `{pex_output_file}` ...") build_pex_pex(pex_output_file, verbosity) - git_rev = describe_git_rev() + rev = describe_rev() sha256, size = describe_file(pex_output_file) - print(f"Built Pex PEX @ {git_rev}:") + print(f"Built Pex PEX @ {rev}:") print(f"sha256: {sha256}") print(f" size: {size}") diff --git a/scripts/typecheck.py b/scripts/typecheck.py index fd2d33c13..f3e2ad93c 100755 --- a/scripts/typecheck.py +++ b/scripts/typecheck.py @@ -38,6 +38,9 @@ def run_mypy(python_version: str, files: Sequence[str], subject: str = "files") def main() -> None: + run_mypy( + "2.7", files=sorted(find_files_to_check(include=["build-backend"])), subject="build-backend" + ) run_mypy("3.8", files=sorted(find_files_to_check(include=["scripts"])), subject="scripts") source_and_tests = sorted( diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 37f9ecc4f..000000000 --- a/setup.cfg +++ /dev/null @@ -1,91 +0,0 @@ -[metadata] -name = pex -version = attr: pex.version.__version__ -author = The PEX developers -author_email = pantsbuild@gmail.com -description = The PEX packaging toolchain. -long_description = file: README.rst -keywords = package, executable, virtualenv, lock, freeze -url = https://github.com/pantsbuild/pex -download_url = https://github.com/pantsbuild/pex/releases/latest/download/pex -project_urls = - Changelog = https://github.com/pantsbuild/pex/blob/main/CHANGES.md - Documentation = https://pex.readthedocs.io/en/latest/ -license_file = LICENSE -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Operating System :: Unix - Operating System :: POSIX :: Linux - Operating System :: MacOS :: MacOS X - Programming Language :: Python - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3.12 - Programming Language :: Python :: Implementation :: CPython - Programming Language :: Python :: Implementation :: PyPy - Topic :: Software Development :: Build Tools - Topic :: System :: Archiving :: Packaging - Topic :: System :: Software Distribution - Topic :: Utilities - -[options] -zip_safe = False -include_package_data = False -packages = find: -python_requires = >=2.7,<3.13,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.* - -[options.entry_points] -console_scripts = - pex = pex.bin.pex:main - pex3 = pex.cli.pex:main - pex-tools = pex.tools.main:main - -distutils.commands = - bdist_pex = pex.distutils.commands.bdist_pex:bdist_pex - -[options.extras_require] -# For improved subprocess robustness under python2.7. -subprocess = - subprocess32>=3.2.7; python_version < "3" - -[options.packages.find] -include = - pex - pex.* - -# We use `[options] include_package_data = False` above and manually include package data here to -# avoid picking up `pex.egg-info/*` temporary build metadata in the final sdist, which leads to -# non-reproducible builds and resulting test failures. When using -# `[options] include_package_data = True`, older setuptools (python 2.7, 3.5 and 3.6 are affected) -# demands the build dirs we pass in `setup.py` be relative instead of absolute, but this comes with -# no way to turn off inclusion of the `pex.egg-info/*` which then is in-tree with the `pex` package -# source root. The relative path restriction in old setuptools is lifted though when -# `[options] include_package_data = False` at the cost of manually needing to specify package data -# includes here. An important detail in specifying those includes is that `**` syntax supported by -# newer setuptools also cannot be used (it does not cause an error, but also does not pick up the -# right files when building the sdist). As a result we use a clunkier, more manual, set of include -# patterns here than we would otherwise need if we could leverage `**` support. -[options.package_data] -* = - .layout.json - .prefix/bin/* - * - *.dist-info/* - -[options.exclude_package_data] -* = - *.pyc - *.pyo - -[bdist_wheel] -universal = True diff --git a/setup.py b/setup.py deleted file mode 100644 index d0bcf30a4..000000000 --- a/setup.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md). -# Licensed under the Apache License, Version 2.0 (see LICENSE). - -import atexit -import os -import shutil -import tempfile - -from setuptools import setup # type: ignore[import] # The Python 2.7 type check can't see this. - -_BUILD_DIR = None - - -def ensure_unique_build_dir(): - # type: () -> str - global _BUILD_DIR - if _BUILD_DIR is None: - _build_dir = tempfile.mkdtemp(prefix="pex-dist-build.") - atexit.register(shutil.rmtree, _build_dir, ignore_errors=True) - _BUILD_DIR = _build_dir - return _BUILD_DIR - - -def unique_build_dir(name): - # type: (str) -> str - path = os.path.join(ensure_unique_build_dir(), name) - os.mkdir(path) - return path - - -if __name__ == "__main__": - setup( - # The `egg_info --egg-base`, `build --build-base` and `bdist_wheel --bdist-dir` setup.py - # sub-command options we pass below work around the otherwise default `/build/` - # directory for all three which defeats concurrency in tests. - options={ - "egg_info": {"egg_base": unique_build_dir("egg_base")}, - "build": {"build_base": unique_build_dir("build_base")}, - "bdist_wheel": {"bdist_dir": unique_build_dir("bdist_dir")}, - }, - # This supports expanding the supported Python range via the _PEX_REQUIRES_PYTHON env var - # for testing unreleased Pythons. - python_requires=os.environ.get("_PEX_REQUIRES_PYTHON"), - ) diff --git a/tests/bin/test_sh_boot.py b/tests/bin/test_sh_boot.py index e191416ae..fa5b3d08b 100644 --- a/tests/bin/test_sh_boot.py +++ b/tests/bin/test_sh_boot.py @@ -6,7 +6,6 @@ import pytest from pex import sh_boot -from pex.compatibility import ConfigParser from pex.interpreter import PythonInterpreter from pex.interpreter_constraints import InterpreterConstraints, iter_compatible_versions from pex.orderedset import OrderedSet @@ -20,6 +19,10 @@ if TYPE_CHECKING: from typing import Iterable, List + import toml # vendor:skip +else: + from pex.third_party import toml + def calculate_binary_names( targets=Targets(), # type: Targets @@ -39,9 +42,10 @@ def requires_python(pex_project_dir): requires_python = os.environ.get("_PEX_REQUIRES_PYTHON") if requires_python: return requires_python - parser = ConfigParser() - parser.read(os.path.join(pex_project_dir, "setup.cfg")) - return cast(str, parser.get("options", "python_requires")).strip() + return cast( + str, + toml.load(os.path.join(pex_project_dir, "pyproject.toml"))["project"]["requires-python"], + ) def expected( diff --git a/tests/build_system/test_pep_518.py b/tests/build_system/test_pep_518.py index 19f01f8f6..0a129f61a 100644 --- a/tests/build_system/test_pep_518.py +++ b/tests/build_system/test_pep_518.py @@ -68,14 +68,15 @@ def test_load_build_system_pyproject( build_system = load_build_system(pex_project_dir) assert isinstance(build_system, BuildSystem) - assert "setuptools.build_meta" == build_system.build_backend + assert "pex_build.hatchling.build" == build_system.build_backend dists = { dist.metadata.project_name for dist in Virtualenv(build_system.venv_pex.venv_dir).iter_distributions() } - assert ProjectName("setuptools") in dists + assert ProjectName("hatchling") in dists subprocess.check_call( - args=[build_system.venv_pex.pex, "-c", "import {}".format(build_system.build_backend)] + args=[build_system.venv_pex.pex, "-c", "import {}".format(build_system.build_backend)], + env=build_system.env, ) diff --git a/tests/integration/build_system/test_pep_518.py b/tests/integration/build_system/test_pep_518.py index d538f43ae..c90199802 100644 --- a/tests/integration/build_system/test_pep_518.py +++ b/tests/integration/build_system/test_pep_518.py @@ -55,5 +55,6 @@ def test_load_build_system_pyproject_custom_repos( build_system = load_build_system(current_target, custom_resolver, pex_project_dir) assert isinstance(build_system, BuildSystem) subprocess.check_call( - args=[build_system.venv_pex.pex, "-c", "import {}".format(build_system.build_backend)] + args=[build_system.venv_pex.pex, "-c", "import {}".format(build_system.build_backend)], + env=build_system.env, ) diff --git a/tests/integration/test_issue_1232.py b/tests/integration/test_issue_1232.py index 9a13280e4..0521db026 100644 --- a/tests/integration/test_issue_1232.py +++ b/tests/integration/test_issue_1232.py @@ -79,16 +79,11 @@ def vendored_toplevel(isolated_dir): shutil.copytree("pex", os.path.join(modified_pex_src, "pex")) with open(os.path.join(modified_pex_src, "pex", "version.py"), "a") as fp: fp.write("# modified\n") - # N.B.: README.rst and LICENSE are needed by the build process: we configure our PEP-517 project - # to use these files for the project distribution and license respectively. - for build_file in ( - "pyproject.toml", - "setup.cfg", - "setup.py", - "MANIFEST.in", - "README.rst", - "LICENSE", - ): + # N.B.: build-backend/, README.rst and LICENSE are needed by the build process: we configure + # our PEP-517 project to use these paths for the in-tree build backend, distribution readme + # and distribution license respectively. + shutil.copytree("build-backend", os.path.join(modified_pex_src, "build-backend")) + for build_file in ("pyproject.toml", "README.rst", "LICENSE"): shutil.copy(build_file, os.path.join(modified_pex_src, build_file)) modified_pex = os.path.join(str(tmpdir), "modified.pex") diff --git a/tests/integration/test_issue_1560.py b/tests/integration/test_issue_1560.py index f9ece4900..70a4cb16c 100644 --- a/tests/integration/test_issue_1560.py +++ b/tests/integration/test_issue_1560.py @@ -40,13 +40,13 @@ def test_build_isolation( subprocess.check_call(args=[pip, "uninstall", "-y"] + build_requirements) result = run_pex_command(args=[pex_project_dir, "--no-build-isolation"], python=python) result.assert_failure() - assert "raise BackendInvalid(" in result.error, ( + assert "ModuleNotFoundError: " in result.error, ( "With build isolation turned off, it's expected that any build requirements " - "(setuptools for Pex) are pre-installed. They are not; so we expect a failure here." + "(setuptools for Pex) are pre-installed. They are not; so we expect a failure here. Got:\n" + "{error}".format(error=result.error) ) - # N.B.: We need wheel so that setuptools.build_meta can execute bdist_wheel successfully. - subprocess.check_call(args=[pip, "install", "wheel"] + build_requirements) + subprocess.check_call(args=[pip, "install"] + build_requirements) pex = os.path.join(str(tmpdir), "pex") run_pex_command( diff --git a/tox.ini b/tox.ini index 516a3ff3e..5cdd7455e 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,13 @@ requires = tox<4 virtualenv<20.16 +[tox:.package] +# N.B.: tox will use the same python version as under what tox is installed to package, so unless +# this is python 3 you can require a given python version for the packaging environment via the +# basepython key. We need this for hatchling which only runs under python3 but works for python2 +# projects. +basepython = python3 + [testenv] # N.B.: We need modern setuptools downloaded out of band by virtualenv to work with Python>=3.12. # Trying to upgrade via Pip is too late and Pip blows up. @@ -32,6 +39,8 @@ passenv = _PEX_TEST_DEV_ROOT # This allows increasing pexpect read timeouts in CI. _PEX_PEXPECT_TIMEOUT + # This allows experimenting with Requires-Python metadata adjustment. + _PEX_REQUIRES_PYTHON # These are to support directing test environments to the correct headers on OSX. CPATH CPPFLAGS