diff --git a/news/12603.feature.rst b/news/12603.feature.rst new file mode 100644 index 00000000000..da56b771820 --- /dev/null +++ b/news/12603.feature.rst @@ -0,0 +1 @@ +When PEP-658 metadata is available, full distribution download no longer occurs when using dry-run mode on install. diff --git a/src/pip/_internal/commands/download.py b/src/pip/_internal/commands/download.py index 900fb403d6f..2a65d2bdcb0 100644 --- a/src/pip/_internal/commands/download.py +++ b/src/pip/_internal/commands/download.py @@ -130,6 +130,8 @@ def run(self, options: Values, args: list[str]) -> int: requirement_set = resolver.resolve(reqs, check_supported_wheels=True) + preparer.prepare_linked_requirements_more(requirement_set.requirements.values()) + downloaded: list[str] = [] for req in requirement_set.requirements.values(): if req.satisfied_by is None: @@ -137,8 +139,6 @@ def run(self, options: Values, args: list[str]) -> int: preparer.save_linked_requirement(req) downloaded.append(req.name) - preparer.prepare_linked_requirements_more(requirement_set.requirements.values()) - if downloaded: write_output("Successfully downloaded %s", " ".join(downloaded)) diff --git a/src/pip/_internal/commands/install.py b/src/pip/_internal/commands/install.py index 056eb8866a0..2623ca98606 100644 --- a/src/pip/_internal/commands/install.py +++ b/src/pip/_internal/commands/install.py @@ -413,6 +413,13 @@ def run(self, options: Values, args: list[str]) -> int: ) return SUCCESS + # If there is any more preparation to do for the actual installation, do + # so now. This includes actually downloading the files in the case that + # we have been using PEP-658 metadata so far. + preparer.prepare_linked_requirements_more( + requirement_set.requirements.values() + ) + try: pip_req = requirement_set.get_requirement("pip") except KeyError: diff --git a/src/pip/_internal/commands/wheel.py b/src/pip/_internal/commands/wheel.py index 61be254912f..bf0409984c3 100644 --- a/src/pip/_internal/commands/wheel.py +++ b/src/pip/_internal/commands/wheel.py @@ -145,6 +145,8 @@ def run(self, options: Values, args: list[str]) -> int: requirement_set = resolver.resolve(reqs, check_supported_wheels=True) + preparer.prepare_linked_requirements_more(requirement_set.requirements.values()) + reqs_to_build: list[InstallRequirement] = [] for req in requirement_set.requirements.values(): if req.is_wheel: @@ -152,8 +154,6 @@ def run(self, options: Values, args: list[str]) -> int: else: reqs_to_build.append(req) - preparer.prepare_linked_requirements_more(requirement_set.requirements.values()) - # build wheels build_successes, build_failures = build( reqs_to_build, diff --git a/src/pip/_internal/operations/prepare.py b/src/pip/_internal/operations/prepare.py index 6c40643e138..f29be92584c 100644 --- a/src/pip/_internal/operations/prepare.py +++ b/src/pip/_internal/operations/prepare.py @@ -527,6 +527,12 @@ def prepare_linked_requirement( metadata_dist = self._fetch_metadata_only(req) if metadata_dist is not None: req.needs_more_preparation = True + req.set_dist(metadata_dist) + # Ensure download_info is available even in dry-run mode + if req.download_info is None: + req.download_info = direct_url_from_link( + req.link, req.source_dir + ) return metadata_dist # None of the optimizations worked, fully prepare the requirement diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index c9f6bff17e8..cdc71f738f0 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -168,6 +168,10 @@ def __init__( # details). self.metadata_directory: str | None = None + # The cached metadata distribution that this requirement represents. + # See get_dist / set_dist. + self._distribution: BaseDistribution | None = None + # The static build requirements (from pyproject.toml) self.pyproject_requires: list[str] | None = None @@ -604,8 +608,13 @@ def metadata(self) -> Any: return self._metadata + def set_dist(self, distribution: BaseDistribution) -> None: + self._distribution = distribution + def get_dist(self) -> BaseDistribution: - if self.metadata_directory: + if self._distribution is not None: + return self._distribution + elif self.metadata_directory: return get_directory_distribution(self.metadata_directory) elif self.local_file_path and self.is_wheel: assert self.req is not None diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py index 1ba70c2b39e..c4fd4e28f93 100644 --- a/src/pip/_internal/resolution/resolvelib/resolver.py +++ b/src/pip/_internal/resolution/resolvelib/resolver.py @@ -180,11 +180,6 @@ def resolve( req_set.add_named_requirement(ireq) - reqs = req_set.all_requirements - self.factory.preparer.prepare_linked_requirements_more(reqs) - for req in reqs: - req.prepared = True - req.needs_more_preparation = False return req_set def get_installation_order(