@@ -480,19 +480,7 @@ def _complete_partial_requirements(
480
480
logger .debug ("Downloading link %s to %s" , link , filepath )
481
481
req = links_to_fully_download [link ]
482
482
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
496
484
497
485
# This step is necessary to ensure all lazy wheels are processed
498
486
# successfully by the 'download', 'wheel', and 'install' commands.
@@ -531,16 +519,67 @@ def prepare_linked_requirement(
531
519
# The file is not available, attempt to fetch only metadata
532
520
metadata_dist = self ._fetch_metadata_only (req )
533
521
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
534
525
req .needs_more_preparation = True
535
526
return metadata_dist
536
527
537
528
# None of the optimizations worked, fully prepare the requirement
538
529
return self ._prepare_linked_requirement (req , parallel_builds )
539
530
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 ,
542
561
) -> 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
+
544
583
reqs = [req for req in reqs if req .needs_more_preparation ]
545
584
for req in reqs :
546
585
# Determine if any of these requirements were already downloaded.
@@ -549,6 +588,8 @@ def prepare_linked_requirements_more(
549
588
file_path = _check_download_dir (req .link , self .download_dir , hashes )
550
589
if file_path is not None :
551
590
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.
552
593
req .needs_more_preparation = False
553
594
554
595
# Prepare requirements we found were already downloaded for some
@@ -566,6 +607,8 @@ def prepare_linked_requirements_more(
566
607
partially_downloaded_reqs ,
567
608
parallel_builds = parallel_builds ,
568
609
)
610
+ # NB: Must call this method before returning!
611
+ self ._force_fully_prepared (reqs )
569
612
570
613
def _prepare_linked_requirement (
571
614
self , req : InstallRequirement , parallel_builds : bool
0 commit comments