@@ -480,19 +480,7 @@ def _complete_partial_requirements(
480480 logger .debug ("Downloading link %s to %s" , link , filepath )
481481 req = links_to_fully_download [link ]
482482 req .local_file_path = filepath
483- # TODO: This needs fixing for sdists
484- # This is an emergency fix for #11847, which reports that
485- # distributions get downloaded twice when metadata is loaded
486- # from a PEP 658 standalone metadata file. Setting _downloaded
487- # fixes this for wheels, but breaks the sdist case (tests
488- # test_download_metadata). As PyPI is currently only serving
489- # metadata for wheels, this is not an immediate issue.
490- # Fixing the problem properly looks like it will require a
491- # complete refactoring of the `prepare_linked_requirements_more`
492- # logic, and I haven't a clue where to start on that, so for now
493- # I have fixed the issue *just* for wheels.
494- if req .is_wheel :
495- self ._downloaded [req .link .url ] = filepath
483+ self ._downloaded [req .link .url ] = filepath
496484
497485 # This step is necessary to ensure all lazy wheels are processed
498486 # successfully by the 'download', 'wheel', and 'install' commands.
@@ -531,16 +519,67 @@ def prepare_linked_requirement(
531519 # The file is not available, attempt to fetch only metadata
532520 metadata_dist = self ._fetch_metadata_only (req )
533521 if metadata_dist is not None :
522+ # These reqs now have the dependency information from the downloaded
523+ # metadata, without having downloaded the actual dist at all.
524+ req .dist_from_metadata = metadata_dist
534525 req .needs_more_preparation = True
535526 return metadata_dist
536527
537528 # None of the optimizations worked, fully prepare the requirement
538529 return self ._prepare_linked_requirement (req , parallel_builds )
539530
540- def prepare_linked_requirements_more (
541- self , reqs : Iterable [InstallRequirement ], parallel_builds : bool = False
531+ def _ensure_download_info (self , reqs : Iterable [InstallRequirement ]) -> None :
532+ """
533+ `pip install --report` extracts the download info from each requirement for its
534+ JSON output, so we need to make sure every requirement has this before finishing
535+ the resolve. But .download_info will only be populated by the point this method
536+ is called for requirements already found in the wheel cache, so we need to
537+ synthesize it for uncached results. Luckily, a DirectUrl can be parsed directly
538+ from a url without any other context. However, this also means the download info
539+ will only contain a hash if the link itself declares the hash.
540+ """
541+ for req in reqs :
542+ # If download_info is set, we got it from the wheel cache.
543+ if req .download_info is None :
544+ req .download_info = direct_url_from_link (req .link , req .source_dir )
545+
546+ def _force_fully_prepared (self , reqs : Iterable [InstallRequirement ]) -> None :
547+ """
548+ The legacy resolver seems to prepare requirements differently that can leave
549+ them half-done in certain code paths. I'm not quite sure how it's doing things,
550+ but at least we can do this to make sure they do things right.
551+ """
552+ for req in reqs :
553+ req .prepared = True
554+ req .needs_more_preparation = False
555+
556+ def finalize_linked_requirements (
557+ self ,
558+ reqs : Iterable [InstallRequirement ],
559+ hydrate_virtual_reqs : bool ,
560+ parallel_builds : bool = False ,
542561 ) -> None :
543- """Prepare linked requirements more, if needed."""
562+ """Prepare linked requirements more, if needed.
563+
564+ Neighboring .metadata files as per PEP 658 or lazy wheels via fast-deps will be
565+ preferred to extract metadata from any concrete requirement (one that has been
566+ mapped to a Link) without downloading the underlying wheel or sdist. When ``pip
567+ install --dry-run`` is called, we want to avoid ever downloading the underlying
568+ dist, but we still need to provide all of the results that pip commands expect
569+ from the typical resolve process.
570+
571+ Those expectations vary, but one distinction lies in whether the command needs
572+ an actual physical dist somewhere on the filesystem, or just the metadata about
573+ it from the resolver (as in ``pip install --report``). If the command requires
574+ actual physical filesystem locations for the resolved dists, it must call this
575+ method with ``hydrate_virtual_reqs=True`` to fully download anything
576+ that remains.
577+ """
578+ if not hydrate_virtual_reqs :
579+ self ._ensure_download_info (reqs )
580+ self ._force_fully_prepared (reqs )
581+ return
582+
544583 reqs = [req for req in reqs if req .needs_more_preparation ]
545584 for req in reqs :
546585 # Determine if any of these requirements were already downloaded.
@@ -549,6 +588,8 @@ def prepare_linked_requirements_more(
549588 file_path = _check_download_dir (req .link , self .download_dir , hashes )
550589 if file_path is not None :
551590 self ._downloaded [req .link .url ] = file_path
591+ # This is a wheel, so we know there's nothing more we need to do to
592+ # prepare it.
552593 req .needs_more_preparation = False
553594
554595 # Prepare requirements we found were already downloaded for some
@@ -566,6 +607,8 @@ def prepare_linked_requirements_more(
566607 partially_downloaded_reqs ,
567608 parallel_builds = parallel_builds ,
568609 )
610+ # NB: Must call this method before returning!
611+ self ._force_fully_prepared (reqs )
569612
570613 def _prepare_linked_requirement (
571614 self , req : InstallRequirement , parallel_builds : bool
0 commit comments