diff --git a/piptools/build.py b/piptools/build.py index c19e206b..f6f8ff6f 100644 --- a/piptools/build.py +++ b/piptools/build.py @@ -2,6 +2,7 @@ import collections import contextlib +import os import pathlib import sys import tempfile @@ -119,6 +120,7 @@ def build_project_metadata( src_file: pathlib.Path, build_targets: tuple[str, ...], *, + upgrade_packages: tuple[str, ...] | None = None, attempt_static_parse: bool, isolated: bool, quiet: bool, @@ -159,7 +161,9 @@ def build_project_metadata( return project_metadata src_dir = src_file.parent - with _create_project_builder(src_dir, isolated=isolated, quiet=quiet) as builder: + with _create_project_builder( + src_dir, upgrade_packages=upgrade_packages, isolated=isolated, quiet=quiet + ) as builder: metadata = _build_project_wheel_metadata(builder) extras = tuple(metadata.get_all("Provides-Extra") or ()) requirements = tuple( @@ -182,7 +186,11 @@ def build_project_metadata( @contextlib.contextmanager def _create_project_builder( - src_dir: pathlib.Path, *, isolated: bool, quiet: bool + src_dir: pathlib.Path, + *, + upgrade_packages: tuple[str, ...] | None = None, + isolated: bool, + quiet: bool, ) -> Iterator[build.ProjectBuilder]: if quiet: runner = pyproject_hooks.quiet_subprocess_runner @@ -193,6 +201,15 @@ def _create_project_builder( yield build.ProjectBuilder(src_dir, runner=runner) return + if upgrade_packages is not None: + # Write packages to upgrade to a temporary file to set as + # constraints for the installation to the builder environment, + # in case build requirements are among them + tmpfile = tempfile.NamedTemporaryFile(mode="w+t", delete=False) + tmpfile.write("\n".join(package for package in upgrade_packages)) + tmpfile.flush() + os.environ["PIP_CONSTRAINT"] = tmpfile.name + with build.env.DefaultIsolatedEnv() as env: builder = build.ProjectBuilder.from_isolated_env(env, src_dir, runner) env.install(builder.build_system_requires) diff --git a/piptools/scripts/compile.py b/piptools/scripts/compile.py index 96aa9fa9..2b4522eb 100755 --- a/piptools/scripts/compile.py +++ b/piptools/scripts/compile.py @@ -370,6 +370,7 @@ def cli( metadata = build_project_metadata( src_file=Path(src_file), build_targets=build_deps_targets, + upgrade_packages=upgrade_packages, attempt_static_parse=not bool(build_deps_targets), isolated=build_isolation, quiet=log.verbosity <= 0, diff --git a/tests/test_cli_compile.py b/tests/test_cli_compile.py index c5031fc2..7e40ac0b 100644 --- a/tests/test_cli_compile.py +++ b/tests/test_cli_compile.py @@ -684,8 +684,8 @@ def test_url_package(runner, line, dependency, generate_hashes): ["-n", "--rebuild", "--no-build-isolation"] + (["--generate-hashes"] if generate_hashes else []), ) - assert out.exit_code == 0 assert dependency in out.stderr + assert out.exit_code == 0 @pytest.mark.parametrize( @@ -787,13 +787,13 @@ def test_direct_reference_with_extras(runner): "pip-tools[testing,coverage] @ git+https://github.com/jazzband/pip-tools@6.2.0" ) out = runner.invoke(cli, ["-n", "--rebuild", "--no-build-isolation"]) - assert out.exit_code == 0 assert ( "pip-tools[coverage,testing] @ git+https://github.com/jazzband/pip-tools@6.2.0" in out.stderr ) assert "pytest==" in out.stderr assert "pytest-cov==" in out.stderr + assert out.exit_code == 0 def test_input_file_without_extension(pip_conf, runner): @@ -3431,7 +3431,6 @@ def test_compile_recursive_extras_build_targets(runner, tmp_path, current_resolv """ ) ) - (tmp_path / "constraints.txt").write_text("wheel<0.43") out = runner.invoke( cli, [ @@ -3446,8 +3445,6 @@ def test_compile_recursive_extras_build_targets(runner, tmp_path, current_resolv "--find-links", os.fspath(MINIMAL_WHEELS_PATH), os.fspath(tmp_path / "pyproject.toml"), - "--constraint", - os.fspath(tmp_path / "constraints.txt"), "--output-file", "-", ], @@ -3455,6 +3452,60 @@ def test_compile_recursive_extras_build_targets(runner, tmp_path, current_resolv expected = rf"""foo[footest] @ {tmp_path.as_uri()} small-fake-a==0.2 small-fake-b==0.3 + +# The following packages are considered to be unsafe in a requirements file: +# setuptools +""" + try: + assert out.exit_code == 0 + assert expected == out.stdout + except Exception: # pragma: no cover + print(out.stdout) + print(out.stderr) + raise + + +@backtracking_resolver_only +def test_compile_build_targets_setuptools_no_wheel_dep( + runner, tmp_path, current_resolver +): + """ + Regression test for https://github.com/jazzband/pip-tools/pull/1681#issuecomment-2212541289 + """ + (tmp_path / "pyproject.toml").write_text( + dedent( + """ + [project] + name = "foo" + version = "0.0.1" + dependencies = ["small-fake-a"] + """ + ) + ) + (tmp_path / "constraints.txt").write_text("wheel<0.43") + out = runner.invoke( + cli, + [ + "--build-isolation", + "--no-header", + "--no-annotate", + "--no-emit-options", + "--extra", + "dev", + "--build-deps-for", + "wheel", + "--find-links", + os.fspath(MINIMAL_WHEELS_PATH), + os.fspath(tmp_path / "pyproject.toml"), + "--constraint", + os.fspath(tmp_path / "constraints.txt"), + "--upgrade-package", + "setuptools < 70.1.0", # setuptools>=70.1.0 doesn't require wheel any more + "--output-file", + "-", + ], + ) + expected = r"""small-fake-a==0.2 wheel==0.42.0 # The following packages are considered to be unsafe in a requirements file: