Skip to content

Commit ff4715b

Browse files
committed
Avoid pip install --dry-run downloading full wheels
* Removed "more preparation" (downloading) from the resolver to prevent downloading before dry-run validation * Added distribution caching to `InstallRequirement` with `set_dist()` and `get_dist()` methods to preserve metadata-only distributions * Set `download_info` during metadata-only fetching to ensure it's available for commands like `pip lock` and `--report` without requiring full downloads Closes #12603.
1 parent 18694f6 commit ff4715b

File tree

6 files changed

+26
-8
lines changed

6 files changed

+26
-8
lines changed

news/12603.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
When PEP-658 metadata is available, full distribution download no longer occurs when using dry-run mode on install.

src/pip/_internal/commands/download.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,15 @@ def run(self, options: Values, args: list[str]) -> int:
130130

131131
requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
132132

133+
preparer.prepare_linked_requirements_more(requirement_set.requirements.values())
134+
133135
downloaded: list[str] = []
134136
for req in requirement_set.requirements.values():
135137
if req.satisfied_by is None:
136138
assert req.name is not None
137139
preparer.save_linked_requirement(req)
138140
downloaded.append(req.name)
139141

140-
preparer.prepare_linked_requirements_more(requirement_set.requirements.values())
141-
142142
if downloaded:
143143
write_output("Successfully downloaded %s", " ".join(downloaded))
144144

src/pip/_internal/commands/install.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,13 @@ def run(self, options: Values, args: list[str]) -> int:
413413
)
414414
return SUCCESS
415415

416+
# If there is any more preparation to do for the actual installation, do
417+
# so now. This includes actually downloading the files in the case that
418+
# we have been using PEP-658 metadata so far.
419+
preparer.prepare_linked_requirements_more(
420+
requirement_set.requirements.values()
421+
)
422+
416423
try:
417424
pip_req = requirement_set.get_requirement("pip")
418425
except KeyError:

src/pip/_internal/operations/prepare.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,12 @@ def prepare_linked_requirement(
527527
metadata_dist = self._fetch_metadata_only(req)
528528
if metadata_dist is not None:
529529
req.needs_more_preparation = True
530+
req.set_dist(metadata_dist)
531+
# Ensure download_info is available even in dry-run mode
532+
if req.download_info is None:
533+
req.download_info = direct_url_from_link(
534+
req.link, req.source_dir
535+
)
530536
return metadata_dist
531537

532538
# None of the optimizations worked, fully prepare the requirement

src/pip/_internal/req/req_install.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ def __init__(
168168
# details).
169169
self.metadata_directory: str | None = None
170170

171+
# The cached metadata distribution that this requirement represents.
172+
# See get_dist / set_dist.
173+
self._distribution: BaseDistribution | None = None
174+
171175
# The static build requirements (from pyproject.toml)
172176
self.pyproject_requires: list[str] | None = None
173177

@@ -604,8 +608,13 @@ def metadata(self) -> Any:
604608

605609
return self._metadata
606610

611+
def set_dist(self, distribution: BaseDistribution) -> None:
612+
self._distribution = distribution
613+
607614
def get_dist(self) -> BaseDistribution:
608-
if self.metadata_directory:
615+
if self._distribution is not None:
616+
return self._distribution
617+
elif self.metadata_directory:
609618
return get_directory_distribution(self.metadata_directory)
610619
elif self.local_file_path and self.is_wheel:
611620
assert self.req is not None

src/pip/_internal/resolution/resolvelib/resolver.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,6 @@ def resolve(
180180

181181
req_set.add_named_requirement(ireq)
182182

183-
reqs = req_set.all_requirements
184-
self.factory.preparer.prepare_linked_requirements_more(reqs)
185-
for req in reqs:
186-
req.prepared = True
187-
req.needs_more_preparation = False
188183
return req_set
189184

190185
def get_installation_order(

0 commit comments

Comments
 (0)