From 694d28993a4ca00555530947a273223638fa269a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 18 Jul 2026 14:31:22 +0200 Subject: [PATCH] Add support for --no-binary with -r pylock.toml --- news/14187.feature.rst | 3 ++ src/pip/_internal/cli/req_command.py | 2 +- src/pip/_internal/utils/pylock.py | 15 ++++++++-- tests/data/lockfiles/pylock.onepackage.toml | 19 ++++++++++++ tests/data/packages/simplewheel-2.0.tar.gz | Bin 0 -> 786 bytes tests/functional/test_install_pylock_reqs.py | 29 +++++++++++++++++++ 6 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 news/14187.feature.rst create mode 100644 tests/data/lockfiles/pylock.onepackage.toml create mode 100644 tests/data/packages/simplewheel-2.0.tar.gz diff --git a/news/14187.feature.rst b/news/14187.feature.rst new file mode 100644 index 0000000000..1471d35c6b --- /dev/null +++ b/news/14187.feature.rst @@ -0,0 +1,3 @@ +Support ``--no-binary`` with ``-r pylock.toml``. Before, pip would get the +sdist from the index instead of the locked URL, or fail if no wheel were +available available in the lock file. diff --git a/src/pip/_internal/cli/req_command.py b/src/pip/_internal/cli/req_command.py index f58a970613..0e218ab5f2 100644 --- a/src/pip/_internal/cli/req_command.py +++ b/src/pip/_internal/cli/req_command.py @@ -368,7 +368,7 @@ def get_requirements( "without prior warning." ) for package, package_dist in select_from_pylock_path_or_url( - filename, session=session + filename, session=session, format_control=options.format_control ): req_to_add, locked_link = install_req_from_pylock_package( package, diff --git a/src/pip/_internal/utils/pylock.py b/src/pip/_internal/utils/pylock.py index 70df98693b..ab1937efde 100644 --- a/src/pip/_internal/utils/pylock.py +++ b/src/pip/_internal/utils/pylock.py @@ -17,6 +17,7 @@ Pylock, is_valid_pylock_path, ) +from pip._vendor.packaging.utils import NormalizedName from pip._vendor.packaging.version import Version from pip._internal.exceptions import DiagnosticPipError, InstallationError @@ -25,6 +26,7 @@ from pip._internal.utils.urls import path_to_url, url_to_path if TYPE_CHECKING: + from pip._internal.index.package_finder import FormatControl from pip._internal.network.session import PipSession from pip._internal.req.req_install import InstallRequirement @@ -267,6 +269,7 @@ def _get_pylock_path_or_url_content(path_or_url: str, session: PipSession) -> st def select_from_pylock_path_or_url( pylock_path_or_url: str, session: PipSession, + format_control: FormatControl | None, ) -> Iterator[ tuple[ Package, @@ -289,10 +292,16 @@ def select_from_pylock_path_or_url( f"Invalid pylock file {pylock_path_or_url!r}: {exc}" ) from exc + def prefer_sdist_predicate(name: NormalizedName) -> bool: + if format_control is None: + return False + allowed_formats = format_control.get_allowed_formats(name) + if "source" in allowed_formats and "binary" not in allowed_formats: + return True + return False + try: - # TODO: for completeness, pylock.select should support preferring sdist - # over wheels to support --no-binary - yield from lock.select() + yield from lock.select(prefer_sdist_predicate=prefer_sdist_predicate) except Exception as exc: raise InstallationError( f"Cannot select requirements from pylock file {pylock_path_or_url!r}: {exc}" diff --git a/tests/data/lockfiles/pylock.onepackage.toml b/tests/data/lockfiles/pylock.onepackage.toml new file mode 100644 index 0000000000..d75d0d4a09 --- /dev/null +++ b/tests/data/lockfiles/pylock.onepackage.toml @@ -0,0 +1,19 @@ +lock-version = "1.0" +created-by = "pip" + +[[packages]] +name = "simplewheel" +version = "2.0" + +[packages.sdist] +path = "../packages/simplewheel-2.0.tar.gz" + +[packages.sdist.hashes] +sha256 = "d1e1fceaf0b5eac764843e4a16ba62394a4fd85896c87a4439aab0af087af1ed" + +[[packages.wheels]] +name = "simplewheel-2.0-1-py2.py3-none-any.whl" +path = "../packages/simplewheel-2.0-1-py2.py3-none-any.whl" + +[packages.wheels.hashes] +sha256 = "71e1ca6b16ae3382a698c284013f66504f2581099b2ce4801f60e9536236ceee" diff --git a/tests/data/packages/simplewheel-2.0.tar.gz b/tests/data/packages/simplewheel-2.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..a19138d193566ac416394e7e69760bc194e54e9f GIT binary patch literal 786 zcmV+t1MU1DiwFq8Z(C{t|8r?=aBO9FXk}$=Eix`JE_7jX0PUMyPunmQhI8#-;dM8g zL=)SIoerteq-kH8254xzK`08;gorkY8W+mHpW8x7JKCzyW`e%YO&U8Y7xj6coW!`E z>wP?mhu?JE(S2ttunCKfjQPRC=g~N4f(wTZof;UFChprm@9ZX`MSCXg={65rfggsV z#a+MEQv8D;4tVvHX|L$&n@g>`P2nHP1ZPW3Iy)V%AGb2a>_RuAO5d}w{N^b+4s)U%+uL5EpE+a zo{@`w-lOBCVDh|MQoBg1PELTFcJ+$g#m zjiSj#MSOl0C%SB(%zVS?9(j*cLjMo{qyGon^#8-Tx5@fn@BMGUB**(-^nb{I^nc)g z<$n~VS!$x_x$WS}=f7gU|5H3b|5po@28_08!!CgasvrFy`akr4$bsGVzpIm^IeInd zZJ_?w>;I$-u>TkRAM(G>{%@N9=>L%aGx`6_O~LE$|FxYU_W#xb_Wz>)ga6@w_#ggX z=>Oxx-@iV6K6cH}JlX%t_W!8){l8%_`u|V(zlPuce!n-l-}yxryaFIgZw<++h4c6Y z9bM?GqqFndsGDY2<>D(%UPN7eZHJz?3IY5N|HJ=aZT~-((i`-@J)QNx;EHqjzYb99 z@IU+y|HJ?A|3d#iv-eu*e;&;FpNWvc|26;qsCobZ0000000000000000000000000 Q0N{o27q~Gr!2oyw01vIQyZ`_I literal 0 HcmV?d00001 diff --git a/tests/functional/test_install_pylock_reqs.py b/tests/functional/test_install_pylock_reqs.py index 1f94754cd5..9b6bb5c2b9 100644 --- a/tests/functional/test_install_pylock_reqs.py +++ b/tests/functional/test_install_pylock_reqs.py @@ -208,6 +208,35 @@ def test_install_pylock_select_error( assert "Cannot select requirements from pylock file" in result.stderr +def test_install_pylock_no_binary_selects_sdist( + script: PipTestEnvironment, + data: TestData, + tmp_path: Path, +) -> None: + pylock_path = data.lockfiles.joinpath("pylock.onepackage.toml") + report = tmp_path / "report.json" + result = script.pip( + "install", + "--no-index", + "--find-links", + data.common_wheels, # to obtain build backend to build sdist + "--dry-run", + "-r", + pylock_path, + "--no-binary=simplewheel", + "--report", + report, + allow_stderr_warning=True, + ) + assert "experimental" in result.stderr + assert "Would install simplewheel-2.0" in result.stdout + # check that the sdist was selected + report_json = json.loads(report.read_text()) + installed = report_json["install"] + assert installed[0]["metadata"]["name"] == "simplewheel" + assert installed[0]["download_info"]["url"].endswith(".tar.gz") + + def test_install_pylock_no_binary( script: PipTestEnvironment, data: TestData,