diff --git a/news/11512.bugfix.rst b/news/11512.bugfix.rst new file mode 100644 index 00000000000..a2169ec6e10 --- /dev/null +++ b/news/11512.bugfix.rst @@ -0,0 +1 @@ +Avoid downloading wheels when performing a ``--dry-run`` install when .metadata files are used. diff --git a/src/pip/_internal/commands/install.py b/src/pip/_internal/commands/install.py index e081c27d2d2..24b60b2fb6f 100644 --- a/src/pip/_internal/commands/install.py +++ b/src/pip/_internal/commands/install.py @@ -398,7 +398,9 @@ def run(self, options: Values, args: List[str]) -> int: self.trace_basic_info(finder) requirement_set = resolver.resolve( - reqs, check_supported_wheels=not options.target_dir + reqs, + check_supported_wheels=not options.target_dir, + dry_run=options.dry_run, ) if options.json_report_file: diff --git a/src/pip/_internal/operations/prepare.py b/src/pip/_internal/operations/prepare.py index 4bf414cb005..a1adee85b8f 100644 --- a/src/pip/_internal/operations/prepare.py +++ b/src/pip/_internal/operations/prepare.py @@ -400,6 +400,8 @@ def _fetch_metadata_using_link_data_attr( raise MetadataInconsistent( req, "Name", req.req.name, metadata_dist.raw_name ) + # (5) Store the dist in the install requirement for reporting + req.dist_from_metadata = metadata_dist return metadata_dist def _fetch_metadata_using_lazy_wheel( @@ -490,6 +492,16 @@ def prepare_linked_requirement( # None of the optimizations worked, fully prepare the requirement return self._prepare_linked_requirement(req, parallel_builds) + def prepare_download_info(self, reqs: Iterable[InstallRequirement]) -> None: + """Prepare linked requirements with download_info, if needed.""" + # During install --dry-run, .metadata files or lazy wheels may be used when determining + # distribution dependencies. The associated wheel does not need to be downloaded so the + # download_info need to be derived from the link. If the link does not contain a hash no + # hash will be included in the download_info. + for req in reqs: + if req.download_info is None: + req.download_info = direct_url_from_link(req.link, req.source_dir) + def prepare_linked_requirements_more( self, reqs: Iterable[InstallRequirement], parallel_builds: bool = False ) -> None: diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index 5f29261c252..6dd97feefc0 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -185,6 +185,10 @@ def __init__( # This requirement needs more preparation before it can be built self.needs_more_preparation = False + # Distribution from the .metadata file referenced by the PEP 658 + # data-dist-info-metadata attribute. + self.dist_from_metadata: Optional[BaseDistribution] = None + def __str__(self) -> str: if self.req: s = str(self.req) @@ -568,6 +572,8 @@ def get_dist(self) -> BaseDistribution: return get_wheel_distribution( FilesystemWheel(self.local_file_path), canonicalize_name(self.name) ) + elif self.is_wheel and self.dist_from_metadata: + return self.dist_from_metadata raise AssertionError( f"InstallRequirement {self} has no metadata directory and no wheel: " f"can't make a distribution." diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py index a605d6c254f..8717b436f37 100644 --- a/src/pip/_internal/resolution/resolvelib/resolver.py +++ b/src/pip/_internal/resolution/resolvelib/resolver.py @@ -68,7 +68,10 @@ def __init__( self._result: Optional[Result] = None def resolve( - self, root_reqs: List[InstallRequirement], check_supported_wheels: bool + self, + root_reqs: List[InstallRequirement], + check_supported_wheels: bool, + dry_run: bool = False, ) -> RequirementSet: collected = self.factory.collect_root_requirements(root_reqs) provider = PipProvider( @@ -158,7 +161,10 @@ def resolve( req_set.add_named_requirement(ireq) reqs = req_set.all_requirements - self.factory.preparer.prepare_linked_requirements_more(reqs) + if dry_run: + self.factory.preparer.prepare_download_info(reqs) + else: + self.factory.preparer.prepare_linked_requirements_more(reqs) return req_set def get_installation_order(