Skip to content

Commit f56c427

Browse files
authored
Merge pull request #13788 from notatallshaw/match-release-control-behavior-to-format-control-behavior-with-cli-and-requirements-file
Match release control behavior to the same as format control behavior
2 parents b78d808 + 4b2f2ff commit f56c427

File tree

5 files changed

+220
-1
lines changed

5 files changed

+220
-1
lines changed

news/13788.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix ``--pre`` not being respected when a requirement file
2+
includes an option e.g. ``-extra-index-url``.

src/pip/_internal/req/req_file.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,9 @@ def handle_option_line(
283283
opts.release_control.all_releases.add(":all:")
284284

285285
if opts.release_control:
286-
finder.set_release_control(opts.release_control)
286+
if not finder.release_control:
287+
# First time seeing release_control, set it on finder
288+
finder.set_release_control(opts.release_control)
287289

288290
if opts.prefer_binary:
289291
finder.set_prefer_binary()
@@ -440,6 +442,7 @@ def parse_line(line: str) -> tuple[str, Values]:
440442
defaults.index_url = None
441443
if finder:
442444
defaults.format_control = finder.format_control
445+
defaults.release_control = finder.release_control
443446

444447
args_str, options_str = break_args_options(line)
445448

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"""Tests for --only-binary and --no-binary format control flags.
2+
3+
These tests verify edge case CLI and requirements file interaction behavior,
4+
matching the pattern established by --all-releases and --only-final tests.
5+
"""
6+
7+
from __future__ import annotations
8+
9+
from tests.lib import (
10+
PipTestEnvironment,
11+
create_basic_sdist_for_package,
12+
create_basic_wheel_for_package,
13+
)
14+
15+
16+
def test_order_no_binary_then_only_binary(script: PipTestEnvironment) -> None:
17+
"""Test --no-binary=:all: --only-binary=<package>.
18+
19+
When the user specifies --no-binary=:all: --only-binary=simple, they
20+
expect 'simple' to allow wheels (later flag overrides).
21+
"""
22+
wheel_path = create_basic_wheel_for_package(script, "simple", "1.0")
23+
24+
# This should allow wheels for 'simple' because --only-binary comes after
25+
result = script.pip_install_local(
26+
"--no-binary=:all:",
27+
"--only-binary=simple",
28+
"simple==1.0",
29+
find_links=[wheel_path.parent],
30+
)
31+
script.assert_installed(simple="1.0")
32+
# Should NOT be building from source
33+
assert "Building wheel for simple" not in result.stdout
34+
35+
36+
def test_order_only_binary_then_no_binary(script: PipTestEnvironment) -> None:
37+
"""Test --only-binary=:all: --no-binary=<package>.
38+
39+
When the user specifies --only-binary=:all: --no-binary=simple,
40+
'simple' should be built from source (later flag overrides).
41+
"""
42+
wheel_path = create_basic_wheel_for_package(script, "simple", "1.0")
43+
create_basic_sdist_for_package(script, "simple", "1.0")
44+
45+
# This should build from source for 'simple' because --no-binary comes after
46+
result = script.pip_install_local(
47+
"--only-binary=:all:",
48+
"--no-binary=simple",
49+
"simple==1.0",
50+
find_links=[wheel_path.parent],
51+
)
52+
script.assert_installed(simple="1.0")
53+
assert "Building wheel for simple" in result.stdout
54+
55+
56+
def test_reqfile_no_binary_overrides_cmdline_only_binary(
57+
script: PipTestEnvironment,
58+
) -> None:
59+
"""Test requirements file --no-binary overrides command line --only-binary."""
60+
wheel_path = create_basic_wheel_for_package(script, "simple", "1.0")
61+
create_basic_sdist_for_package(script, "simple", "1.0")
62+
63+
req_file = script.temporary_file(
64+
"requirements.txt",
65+
f"--find-links {wheel_path.parent.as_posix()}\n"
66+
"--no-binary :all:\nsimple==1.0\n",
67+
)
68+
69+
result = script.pip_install_local(
70+
"--only-binary=:all:", "-r", req_file, find_links=[]
71+
)
72+
script.assert_installed(simple="1.0")
73+
# Requirements file --no-binary should override CLI --only-binary
74+
assert "Building wheel for simple" in result.stdout
75+
76+
77+
def test_reqfile_only_binary_overrides_cmdline_no_binary(
78+
script: PipTestEnvironment,
79+
) -> None:
80+
"""Test requirements file --only-binary overrides command line --no-binary."""
81+
# Create only a wheel, no sdist
82+
wheel_path = create_basic_wheel_for_package(script, "simple", "1.0")
83+
84+
req_file = script.temporary_file(
85+
"requirements.txt",
86+
f"--find-links {wheel_path.parent.as_posix()}\n"
87+
"--only-binary :all:\nsimple==1.0\n",
88+
)
89+
90+
result = script.pip_install_local(
91+
"--no-binary=:all:", "-r", req_file, find_links=[]
92+
)
93+
result.assert_installed("simple", editable=False)
94+
# Requirements file --only-binary should override CLI --no-binary
95+
assert "Building wheel for simple" not in result.stdout
96+
97+
98+
def test_package_specific_overrides_all_in_requirements_file(
99+
script: PipTestEnvironment,
100+
) -> None:
101+
"""Test package-specific setting overrides :all: in requirements file."""
102+
wheel_path = create_basic_wheel_for_package(script, "simple", "1.0")
103+
104+
req_file = script.temporary_file(
105+
"requirements.txt",
106+
f"--find-links {wheel_path.parent.as_posix()}\n--no-binary :all:\n"
107+
"--only-binary simple\nsimple==1.0\n",
108+
)
109+
110+
result = script.pip_install_local("-r", req_file, find_links=[])
111+
result.assert_installed("simple", editable=False)
112+
# Package-specific --only-binary should override --no-binary :all:
113+
assert "Building wheel for simple" not in result.stdout

tests/functional/test_install_release_control.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,83 @@ def test_no_matching_version_with_all_releases(
228228
expect_error=True,
229229
)
230230
assert "Could not find a version that satisfies the requirement" in result.stderr
231+
232+
233+
def test_pre_flag_with_requirements_file_containing_options(
234+
script: PipTestEnvironment,
235+
) -> None:
236+
"""Test --pre on command line works with requirements file options.
237+
238+
Requirements file options overwrote command-line release control.
239+
"""
240+
pre_pkg = create_basic_wheel_for_package(script, "simple", "2.0a1")
241+
create_basic_wheel_for_package(script, "simple", "1.0")
242+
243+
req_file = script.temporary_file(
244+
"requirements.txt",
245+
f"--find-links {pre_pkg.parent.as_posix()}\nsimple\n",
246+
)
247+
248+
report = script.pip_install_local_report("-r", req_file, find_links=[])
249+
assert len(report["install"]) == 1
250+
assert report["install"][0]["metadata"]["version"] == "1.0"
251+
252+
report = script.pip_install_local_report("--pre", "-r", req_file, find_links=[])
253+
assert len(report["install"]) == 1
254+
assert report["install"][0]["metadata"]["version"] == "2.0a1"
255+
256+
257+
def test_reqfile_all_releases_overrides_cmdline_only_final(
258+
script: PipTestEnvironment,
259+
) -> None:
260+
"""Test requirements file --all-releases overrides command line --only-final."""
261+
pre_pkg = create_basic_wheel_for_package(script, "simple", "2.0a1")
262+
create_basic_wheel_for_package(script, "simple", "1.0")
263+
264+
req_file = script.temporary_file(
265+
"requirements.txt",
266+
f"--find-links {pre_pkg.parent.as_posix()}\n--all-releases :all:\nsimple\n",
267+
)
268+
269+
report = script.pip_install_local_report(
270+
"--only-final=:all:", "-r", req_file, find_links=[]
271+
)
272+
assert len(report["install"]) == 1
273+
assert report["install"][0]["metadata"]["version"] == "2.0a1"
274+
275+
276+
def test_reqfile_only_final_overrides_cmdline_all_releases(
277+
script: PipTestEnvironment,
278+
) -> None:
279+
"""Test requirements file --only-final overrides command line --all-releases."""
280+
pre_pkg = create_basic_wheel_for_package(script, "simple", "2.0a1")
281+
create_basic_wheel_for_package(script, "simple", "1.0")
282+
283+
req_file = script.temporary_file(
284+
"requirements.txt",
285+
f"--find-links {pre_pkg.parent.as_posix()}\n--only-final :all:\nsimple\n",
286+
)
287+
288+
report = script.pip_install_local_report(
289+
"--all-releases=:all:", "-r", req_file, find_links=[]
290+
)
291+
assert len(report["install"]) == 1
292+
assert report["install"][0]["metadata"]["version"] == "1.0"
293+
294+
295+
def test_package_specific_overrides_all_in_requirements_file(
296+
script: PipTestEnvironment,
297+
) -> None:
298+
"""Test package-specific setting overrides :all: in requirements file."""
299+
pre_pkg = create_basic_wheel_for_package(script, "simple", "2.0a1")
300+
create_basic_wheel_for_package(script, "simple", "1.0")
301+
302+
req_file = script.temporary_file(
303+
"requirements.txt",
304+
f"--find-links {pre_pkg.parent.as_posix()}\n--all-releases :all:\n"
305+
"--only-final simple\nsimple\n",
306+
)
307+
308+
report = script.pip_install_local_report("-r", req_file, find_links=[])
309+
assert len(report["install"]) == 1
310+
assert report["install"][0]["metadata"]["version"] == "1.0"

tests/lib/__init__.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,27 @@ def pip_install_local(
736736
cmd.insert(1, "--no-build-isolation")
737737
return self.pip(*cmd, **kwargs)
738738

739+
def pip_install_local_report(
740+
self,
741+
*args: StrPath,
742+
find_links: StrPath | list[StrPath] = pathlib.Path(DATA_DIR, "packages"),
743+
**kwargs: Any,
744+
) -> dict[str, Any]:
745+
"""
746+
Invoke pip install with --dry-run --report and return parsed JSON report.
747+
Includes --no-index and --find-links like pip_install_local.
748+
"""
749+
result = self.pip_install_local(
750+
"--dry-run",
751+
"--report",
752+
"-",
753+
"--quiet",
754+
*args,
755+
find_links=find_links,
756+
**kwargs,
757+
)
758+
return json.loads(result.stdout)
759+
739760
def easy_install(self, *args: str, **kwargs: Any) -> TestPipResult:
740761
args = ("-m", "easy_install") + args
741762
return self.run("python", *args, **kwargs)

0 commit comments

Comments
 (0)