pex 2.62.1__py2.py3-none-any.whl → 2.64.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pex might be problematic. Click here for more details.

Files changed (50) hide show
  1. pex/cli/commands/lock.py +15 -0
  2. pex/cli/commands/venv.py +80 -15
  3. pex/dist_metadata.py +36 -9
  4. pex/docs/html/_pagefind/fragment/en_3046a3a.pf_fragment +0 -0
  5. pex/docs/html/_pagefind/fragment/en_3f5cca9.pf_fragment +0 -0
  6. pex/docs/html/_pagefind/fragment/en_5f2da5c.pf_fragment +0 -0
  7. pex/docs/html/_pagefind/fragment/en_7350892.pf_fragment +0 -0
  8. pex/docs/html/_pagefind/fragment/en_ac9b982.pf_fragment +0 -0
  9. pex/docs/html/_pagefind/fragment/en_d158da6.pf_fragment +0 -0
  10. pex/docs/html/_pagefind/fragment/en_e575d34.pf_fragment +0 -0
  11. pex/docs/html/_pagefind/fragment/en_fca878d.pf_fragment +0 -0
  12. pex/docs/html/_pagefind/index/en_23c894e.pf_index +0 -0
  13. pex/docs/html/_pagefind/pagefind-entry.json +1 -1
  14. pex/docs/html/_pagefind/pagefind.en_86ab41ad5d.pf_meta +0 -0
  15. pex/docs/html/_static/documentation_options.js +1 -1
  16. pex/docs/html/api/vars.html +5 -5
  17. pex/docs/html/buildingpex.html +5 -5
  18. pex/docs/html/genindex.html +5 -5
  19. pex/docs/html/index.html +5 -5
  20. pex/docs/html/recipes.html +5 -5
  21. pex/docs/html/scie.html +5 -5
  22. pex/docs/html/search.html +5 -5
  23. pex/docs/html/whatispex.html +5 -5
  24. pex/pip/tool.py +84 -11
  25. pex/pip/vcs.py +42 -25
  26. pex/resolve/lock_downloader.py +0 -1
  27. pex/resolve/locked_resolve.py +11 -11
  28. pex/resolve/locker.py +98 -18
  29. pex/resolve/lockfile/create.py +157 -27
  30. pex/resolve/lockfile/updater.py +11 -0
  31. pex/resolver.py +201 -4
  32. pex/venv/installer.py +37 -14
  33. pex/version.py +1 -1
  34. {pex-2.62.1.dist-info → pex-2.64.0.dist-info}/METADATA +4 -4
  35. {pex-2.62.1.dist-info → pex-2.64.0.dist-info}/RECORD +40 -40
  36. pex/docs/html/_pagefind/fragment/en_1bbeb07.pf_fragment +0 -0
  37. pex/docs/html/_pagefind/fragment/en_1befd43.pf_fragment +0 -0
  38. pex/docs/html/_pagefind/fragment/en_45eea4b.pf_fragment +0 -0
  39. pex/docs/html/_pagefind/fragment/en_7822de6.pf_fragment +0 -0
  40. pex/docs/html/_pagefind/fragment/en_87f76ba.pf_fragment +0 -0
  41. pex/docs/html/_pagefind/fragment/en_a89f2ec.pf_fragment +0 -0
  42. pex/docs/html/_pagefind/fragment/en_c2a647e.pf_fragment +0 -0
  43. pex/docs/html/_pagefind/fragment/en_d2f2c1b.pf_fragment +0 -0
  44. pex/docs/html/_pagefind/index/en_31a0754.pf_index +0 -0
  45. pex/docs/html/_pagefind/pagefind.en_32e8257caf.pf_meta +0 -0
  46. {pex-2.62.1.dist-info → pex-2.64.0.dist-info}/WHEEL +0 -0
  47. {pex-2.62.1.dist-info → pex-2.64.0.dist-info}/entry_points.txt +0 -0
  48. {pex-2.62.1.dist-info → pex-2.64.0.dist-info}/licenses/LICENSE +0 -0
  49. {pex-2.62.1.dist-info → pex-2.64.0.dist-info}/pylock/pylock.toml +0 -0
  50. {pex-2.62.1.dist-info → pex-2.64.0.dist-info}/top_level.txt +0 -0
@@ -23,7 +23,7 @@ from pex.pep_440 import Version
23
23
  from pex.pep_503 import ProjectName
24
24
  from pex.pip.download_observer import DownloadObserver
25
25
  from pex.pip.tool import PackageIndexConfiguration
26
- from pex.pip.version import PipVersionValue
26
+ from pex.pip.version import PipVersion, PipVersionValue
27
27
  from pex.resolve import lock_resolver, locker, resolvers
28
28
  from pex.resolve.configured_resolver import ConfiguredResolver
29
29
  from pex.resolve.downloads import ArtifactDownloader
@@ -41,15 +41,23 @@ from pex.resolve.locked_resolve import (
41
41
  from pex.resolve.locker import Locker
42
42
  from pex.resolve.lockfile.download_manager import DownloadManager
43
43
  from pex.resolve.lockfile.model import Lockfile
44
- from pex.resolve.lockfile.targets import calculate_download_input
44
+ from pex.resolve.lockfile.targets import DownloadInput, calculate_download_input
45
45
  from pex.resolve.package_repository import ReposConfiguration
46
46
  from pex.resolve.pep_691.fingerprint_service import FingerprintService
47
47
  from pex.resolve.requirement_configuration import RequirementConfiguration
48
48
  from pex.resolve.resolved_requirement import Pin, ResolvedRequirement
49
49
  from pex.resolve.resolver_configuration import BuildConfiguration, PipConfiguration, ResolverVersion
50
50
  from pex.resolve.resolvers import Resolver
51
- from pex.resolver import BuildRequest, Downloaded, DownloadTarget, ResolveObserver, WheelBuilder
51
+ from pex.resolver import (
52
+ BuildRequest,
53
+ Downloaded,
54
+ DownloadTarget,
55
+ Reports,
56
+ ResolveObserver,
57
+ WheelBuilder,
58
+ )
52
59
  from pex.resolver import download_requests as pip_download_requests
60
+ from pex.resolver import reports as pip_reports
53
61
  from pex.result import Error, try_
54
62
  from pex.targets import Target, Targets
55
63
  from pex.tracer import TRACER
@@ -277,6 +285,7 @@ class LockObserver(ResolveObserver):
277
285
  wheel_builder = attr.ib() # type: WheelBuilder
278
286
  package_index_configuration = attr.ib() # type: PackageIndexConfiguration
279
287
  max_parallel_jobs = attr.ib(default=None) # type: Optional[int]
288
+ lock_is_via_pip_download = attr.ib(default=False) # type: bool
280
289
  _analysis = attr.ib(factory=OrderedSet, eq=False) # type: OrderedSet[_LockAnalysis]
281
290
 
282
291
  def observe_download(
@@ -299,6 +308,7 @@ class LockObserver(ResolveObserver):
299
308
  ),
300
309
  max_parallel_jobs=self.max_parallel_jobs,
301
310
  ),
311
+ lock_is_via_pip_download=self.lock_is_via_pip_download,
302
312
  )
303
313
  patch_set = locker.patch(universal_target=download_target.universal_target)
304
314
  observer = DownloadObserver(analyzer=analyzer, patch_set=patch_set)
@@ -454,7 +464,7 @@ class LockObserver(ResolveObserver):
454
464
  return tuple(
455
465
  LockedResolve.create(
456
466
  resolved_requirements=resolved_requirements,
457
- dist_metadatas=dist_metadatas_by_download_target[download_target],
467
+ project_metadatas=dist_metadatas_by_download_target[download_target],
458
468
  fingerprinter=ArtifactDownloader(
459
469
  resolver=self.resolver,
460
470
  universal_target=download_target.universal_target,
@@ -476,6 +486,126 @@ class LockObserver(ResolveObserver):
476
486
  for download_target, resolved_requirements in resolved_requirements_by_download_target.items()
477
487
  )
478
488
 
489
+ def lock_reports(self, reports):
490
+ # type: (Reports) -> Tuple[LockedResolve, ...]
491
+
492
+ project_metadatas_by_download_target = {
493
+ report.download_target: tuple(report.metadata.values()) for report in reports
494
+ }
495
+ resolved_requirements_by_download_target = (
496
+ OrderedDict()
497
+ ) # type: OrderedDict[DownloadTarget, Tuple[ResolvedRequirement, ...]]
498
+ for analysis in self._analysis:
499
+ lock_result = analysis.analyzer.lock_result
500
+ resolved_requirements_by_download_target[
501
+ analysis.download_target
502
+ ] = lock_result.resolved_requirements
503
+
504
+ universal_targets = tuple(
505
+ download_target.universal_target
506
+ for download_target in resolved_requirements_by_download_target
507
+ if download_target.universal_target
508
+ )
509
+ return tuple(
510
+ LockedResolve.create(
511
+ resolved_requirements=resolved_requirements,
512
+ project_metadatas=project_metadatas_by_download_target[download_target],
513
+ fingerprinter=ArtifactDownloader(
514
+ resolver=self.resolver,
515
+ universal_target=download_target.universal_target,
516
+ target=download_target.target,
517
+ package_index_configuration=self.package_index_configuration,
518
+ max_parallel_jobs=self.max_parallel_jobs,
519
+ ),
520
+ platform_tag=(
521
+ None
522
+ if self.lock_style == LockStyle.UNIVERSAL
523
+ else download_target.target.platform.tag
524
+ ),
525
+ marker=(
526
+ download_target.universal_target.marker()
527
+ if download_target.universal_target and len(universal_targets) > 1
528
+ else None
529
+ ),
530
+ )
531
+ for download_target, resolved_requirements in resolved_requirements_by_download_target.items()
532
+ )
533
+
534
+
535
+ def _create_lock_pip_download(
536
+ download_input, # type: DownloadInput
537
+ pip_configuration, # type: PipConfiguration
538
+ network_configuration, # type: NetworkConfiguration
539
+ download_dir, # type: str
540
+ lock_observer, # type: LockObserver
541
+ configured_resolver, # type: ConfiguredResolver
542
+ dependency_configuration, # type: DependencyConfiguration
543
+ ):
544
+ # type: (...) -> Union[Tuple[LockedResolve, ...], Error]
545
+ try:
546
+ downloaded = pip_download_requests(
547
+ requests=download_input.download_requests,
548
+ direct_requirements=download_input.direct_requirements,
549
+ allow_prereleases=pip_configuration.allow_prereleases,
550
+ transitive=pip_configuration.transitive,
551
+ repos_configuration=pip_configuration.repos_configuration,
552
+ resolver_version=pip_configuration.resolver_version,
553
+ network_configuration=network_configuration,
554
+ build_configuration=pip_configuration.build_configuration,
555
+ max_parallel_jobs=pip_configuration.max_jobs,
556
+ observer=lock_observer,
557
+ dest=download_dir,
558
+ pip_log=pip_configuration.log,
559
+ pip_version=pip_configuration.version,
560
+ resolver=configured_resolver,
561
+ use_pip_config=pip_configuration.use_pip_config,
562
+ extra_pip_requirements=pip_configuration.extra_requirements,
563
+ keyring_provider=pip_configuration.keyring_provider,
564
+ dependency_configuration=dependency_configuration,
565
+ )
566
+ except resolvers.ResolveError as e:
567
+ return Error(str(e))
568
+
569
+ with TRACER.timed("Creating lock from resolve"):
570
+ return lock_observer.lock(downloaded)
571
+
572
+
573
+ def _create_lock_pip_reports(
574
+ download_input, # type: DownloadInput
575
+ pip_configuration, # type: PipConfiguration
576
+ network_configuration, # type: NetworkConfiguration
577
+ lock_observer, # type: LockObserver
578
+ configured_resolver, # type: ConfiguredResolver
579
+ dependency_configuration, # type: DependencyConfiguration
580
+ ):
581
+ # type: (...) -> Union[Tuple[LockedResolve, ...], Error]
582
+
583
+ try:
584
+ reports = pip_reports(
585
+ requests=download_input.download_requests,
586
+ direct_requirements=download_input.direct_requirements,
587
+ allow_prereleases=pip_configuration.allow_prereleases,
588
+ transitive=pip_configuration.transitive,
589
+ repos_configuration=pip_configuration.repos_configuration,
590
+ resolver_version=pip_configuration.resolver_version,
591
+ network_configuration=network_configuration,
592
+ build_configuration=pip_configuration.build_configuration,
593
+ max_parallel_jobs=pip_configuration.max_jobs,
594
+ observer=lock_observer,
595
+ pip_log=pip_configuration.log,
596
+ pip_version=pip_configuration.version,
597
+ resolver=configured_resolver,
598
+ use_pip_config=pip_configuration.use_pip_config,
599
+ extra_pip_requirements=pip_configuration.extra_requirements,
600
+ keyring_provider=pip_configuration.keyring_provider,
601
+ dependency_configuration=dependency_configuration,
602
+ )
603
+ except resolvers.ResolveError as e:
604
+ return Error(str(e))
605
+
606
+ with TRACER.timed("Creating lock from resolve"):
607
+ return lock_observer.lock_reports(reports)
608
+
479
609
 
480
610
  def create(
481
611
  lock_configuration, # type: LockConfiguration
@@ -483,6 +613,7 @@ def create(
483
613
  targets, # type: Targets
484
614
  pip_configuration, # type: PipConfiguration
485
615
  dependency_configuration=DependencyConfiguration(), # type: DependencyConfiguration
616
+ avoid_downloads=False, # type: bool
486
617
  ):
487
618
  # type: (...) -> Union[Lockfile, Error]
488
619
  """Create a lock file for the given resolve configurations."""
@@ -510,6 +641,9 @@ def create(
510
641
  )
511
642
 
512
643
  configured_resolver = ConfiguredResolver(pip_configuration=pip_configuration)
644
+ lock_is_via_pip_download = (
645
+ pip_configuration.version is PipVersion.VENDORED or not avoid_downloads
646
+ )
513
647
  lock_observer = LockObserver(
514
648
  root_requirements=parsed_requirements,
515
649
  lock_style=lock_configuration.style,
@@ -522,6 +656,7 @@ def create(
522
656
  ),
523
657
  package_index_configuration=package_index_configuration,
524
658
  max_parallel_jobs=pip_configuration.max_jobs,
659
+ lock_is_via_pip_download=lock_is_via_pip_download,
525
660
  )
526
661
 
527
662
  download_dir = safe_mkdtemp()
@@ -533,32 +668,27 @@ def create(
533
668
  repos_configuration=pip_configuration.repos_configuration,
534
669
  universal_target=lock_configuration.universal_target,
535
670
  )
536
- try:
537
- downloaded = pip_download_requests(
538
- requests=download_input.download_requests,
539
- direct_requirements=download_input.direct_requirements,
540
- allow_prereleases=pip_configuration.allow_prereleases,
541
- transitive=pip_configuration.transitive,
542
- repos_configuration=pip_configuration.repos_configuration,
543
- resolver_version=pip_configuration.resolver_version,
671
+
672
+ locked_resolves = try_(
673
+ _create_lock_pip_download(
674
+ download_input=download_input,
675
+ pip_configuration=pip_configuration,
544
676
  network_configuration=network_configuration,
545
- build_configuration=pip_configuration.build_configuration,
546
- max_parallel_jobs=pip_configuration.max_jobs,
547
- observer=lock_observer,
548
- dest=download_dir,
549
- pip_log=pip_configuration.log,
550
- pip_version=pip_configuration.version,
551
- resolver=configured_resolver,
552
- use_pip_config=pip_configuration.use_pip_config,
553
- extra_pip_requirements=pip_configuration.extra_requirements,
554
- keyring_provider=pip_configuration.keyring_provider,
677
+ download_dir=download_dir,
678
+ lock_observer=lock_observer,
679
+ configured_resolver=configured_resolver,
555
680
  dependency_configuration=dependency_configuration,
556
681
  )
557
- except resolvers.ResolveError as e:
558
- return Error(str(e))
559
-
560
- with TRACER.timed("Creating lock from resolve"):
561
- locked_resolves = lock_observer.lock(downloaded)
682
+ if lock_is_via_pip_download
683
+ else _create_lock_pip_reports(
684
+ download_input=download_input,
685
+ pip_configuration=pip_configuration,
686
+ network_configuration=network_configuration,
687
+ lock_observer=lock_observer,
688
+ configured_resolver=configured_resolver,
689
+ dependency_configuration=dependency_configuration,
690
+ )
691
+ )
562
692
 
563
693
  with TRACER.timed("Indexing downloads"):
564
694
  create_lock_download_manager = CreateLockDownloadManager.create(
@@ -252,6 +252,7 @@ class ResolveUpdater(object):
252
252
  lock_configuration, # type: LockConfiguration
253
253
  pip_configuration, # type: PipConfiguration
254
254
  dependency_configuration, # type: DependencyConfiguration
255
+ avoid_downloads, # type: bool
255
256
  ):
256
257
  # type: (...) -> Union[ResolveUpdater, Error]
257
258
 
@@ -310,6 +311,7 @@ class ResolveUpdater(object):
310
311
  lock_configuration=lock_configuration,
311
312
  pip_configuration=pip_configuration,
312
313
  dependency_configuration=dependency_configuration,
314
+ avoid_downloads=avoid_downloads,
313
315
  )
314
316
 
315
317
  @classmethod
@@ -322,6 +324,7 @@ class ResolveUpdater(object):
322
324
  lock_configuration, # type: LockConfiguration
323
325
  pip_configuration, # type: PipConfiguration
324
326
  dependency_configuration, # type: DependencyConfiguration
327
+ avoid_downloads, # type: bool
325
328
  ):
326
329
  # type: (...) -> ResolveUpdater
327
330
 
@@ -362,6 +365,7 @@ class ResolveUpdater(object):
362
365
  lock_configuration=lock_configuration,
363
366
  pip_configuration=pip_configuration,
364
367
  dependency_configuration=dependency_configuration,
368
+ avoid_downloads=avoid_downloads,
365
369
  )
366
370
 
367
371
  requirement_configuration = attr.ib() # type: RequirementConfiguration
@@ -377,6 +381,7 @@ class ResolveUpdater(object):
377
381
  update_requirements_by_project_name = attr.ib(
378
382
  factory=dict
379
383
  ) # type: Mapping[ProjectName, Requirement]
384
+ avoid_downloads = attr.ib(default=False)
380
385
 
381
386
  def iter_updated_requirements(self):
382
387
  # type: () -> Iterator[Requirement]
@@ -502,6 +507,7 @@ class ResolveUpdater(object):
502
507
  targets=Targets.from_target(target),
503
508
  pip_configuration=self.pip_configuration,
504
509
  dependency_configuration=self.dependency_configuration,
510
+ avoid_downloads=self.avoid_downloads,
505
511
  )
506
512
  )
507
513
  assert 1 == len(updated_lock_file.locked_resolves)
@@ -691,6 +697,7 @@ class LockUpdater(object):
691
697
  use_pip_config, # type: bool
692
698
  dependency_configuration, # type: DependencyConfiguration
693
699
  pip_log, # type: Optional[PipLog]
700
+ avoid_downloads, # type: bool
694
701
  ):
695
702
  # type: (...) -> LockUpdater
696
703
 
@@ -711,12 +718,14 @@ class LockUpdater(object):
711
718
  lock_configuration=lock_file.configuration,
712
719
  pip_configuration=pip_configuration,
713
720
  dependency_configuration=dependency_configuration,
721
+ avoid_downloads=avoid_downloads,
714
722
  )
715
723
 
716
724
  lock_file = attr.ib() # type: Lockfile
717
725
  lock_configuration = attr.ib() # type: LockConfiguration
718
726
  pip_configuration = attr.ib() # type: PipConfiguration
719
727
  dependency_configuration = attr.ib() # type: DependencyConfiguration
728
+ avoid_downloads = attr.ib() # type: bool
720
729
 
721
730
  def sync(
722
731
  self,
@@ -738,6 +747,7 @@ class LockUpdater(object):
738
747
  lock_configuration=self.lock_configuration,
739
748
  pip_configuration=self.pip_configuration,
740
749
  dependency_configuration=self.dependency_configuration,
750
+ avoid_downloads=self.avoid_downloads,
741
751
  )
742
752
  )
743
753
  return self._perform_update(
@@ -773,6 +783,7 @@ class LockUpdater(object):
773
783
  lock_configuration=self.lock_configuration,
774
784
  pip_configuration=self.pip_configuration,
775
785
  dependency_configuration=self.dependency_configuration,
786
+ avoid_downloads=self.avoid_downloads,
776
787
  )
777
788
  return self._perform_update(
778
789
  update_requests=update_requests,
pex/resolver.py CHANGED
@@ -8,6 +8,7 @@ import functools
8
8
  import glob
9
9
  import hashlib
10
10
  import itertools
11
+ import json
11
12
  import os
12
13
  import tarfile
13
14
  import zipfile
@@ -30,6 +31,7 @@ from pex.dependency_configuration import DependencyConfiguration
30
31
  from pex.dist_metadata import (
31
32
  DistMetadata,
32
33
  Distribution,
34
+ ProjectMetadata,
33
35
  Requirement,
34
36
  is_tar_sdist,
35
37
  is_wheel,
@@ -43,6 +45,7 @@ from pex.network_configuration import NetworkConfiguration
43
45
  from pex.orderedset import OrderedSet
44
46
  from pex.pep_425 import CompatibilityTags
45
47
  from pex.pep_427 import InstallableType, WheelError, install_wheel_chroot
48
+ from pex.pep_440 import Version
46
49
  from pex.pep_503 import ProjectName
47
50
  from pex.pip.download_observer import DownloadObserver
48
51
  from pex.pip.installation import get_pip
@@ -63,6 +66,7 @@ from pex.resolve.resolvers import (
63
66
  )
64
67
  from pex.resolve.target_system import TargetSystem, UniversalTarget
65
68
  from pex.targets import AbbreviatedPlatform, CompletePlatform, LocalInterpreter, Target, Targets
69
+ from pex.third_party.packaging.specifiers import SpecifierSet
66
70
  from pex.third_party.packaging.tags import Tag
67
71
  from pex.tracer import TRACER
68
72
  from pex.typing import TYPE_CHECKING
@@ -239,7 +243,52 @@ class PipLogManager(object):
239
243
 
240
244
 
241
245
  @attr.s(frozen=True)
242
- class _DownloadSession(object):
246
+ class Report(object):
247
+ @classmethod
248
+ def parse(
249
+ cls,
250
+ download_target, # type: DownloadTarget
251
+ report, # type: str
252
+ ):
253
+ # type: (...) -> Report
254
+
255
+ with open(report) as fp:
256
+ data = json.load(fp)
257
+
258
+ project_metadata = [] # type: List[ProjectMetadata]
259
+ for distribution in data["install"]:
260
+ metadata = distribution["metadata"]
261
+ project_metadata.append(
262
+ ProjectMetadata(
263
+ project_name=ProjectName(metadata["name"]),
264
+ version=Version(metadata["version"]),
265
+ requires_python=SpecifierSet(metadata.get("requires_python", "")),
266
+ requires_dists=tuple(
267
+ Requirement.parse(requirement)
268
+ for requirement in metadata.get("requires_dist", ())
269
+ ),
270
+ )
271
+ )
272
+ return cls(
273
+ download_target=download_target,
274
+ metadata={metadata.project_name: metadata for metadata in project_metadata},
275
+ )
276
+
277
+ download_target = attr.ib() # type: DownloadTarget
278
+ metadata = attr.ib() # type: Mapping[ProjectName, ProjectMetadata]
279
+
280
+
281
+ @attr.s(frozen=True)
282
+ class Reports(object):
283
+ reports = attr.ib(default=()) # type: Tuple[Report, ...]
284
+
285
+ def __iter__(self):
286
+ # type: () -> Iterator[Report]
287
+ return iter(self.reports)
288
+
289
+
290
+ @attr.s(frozen=True)
291
+ class _PipSession(object):
243
292
  requests = attr.ib(converter=_uniqued_download_requests) # type: Tuple[DownloadRequest, ...]
244
293
  direct_requirements = attr.ib() # type: Iterable[ParsedRequirement]
245
294
  allow_prereleases = attr.ib(default=False) # type: bool
@@ -266,8 +315,108 @@ class _DownloadSession(object):
266
315
  pip_version=self.pip_version,
267
316
  )
268
317
 
318
+ def generate_reports(self, max_parallel_jobs=None):
319
+ # type: (Optional[int]) -> Reports
320
+
321
+ if not self.requests or not any(request.has_requirements for request in self.requests):
322
+ # Nothing to report.
323
+ return Reports()
324
+
325
+ dest = safe_mkdtemp(
326
+ prefix="resolver_report.", dir=safe_mkdir(CacheDir.DOWNLOADS.path(".tmp"))
327
+ )
328
+
329
+ log_manager = PipLogManager.create(
330
+ self.pip_log,
331
+ download_targets=tuple(request.download_target for request in self.requests),
332
+ )
333
+ if self.pip_log and not self.pip_log.user_specified:
334
+ TRACER.log(
335
+ "Preserving `pip install --dry-run` log at {log_path}".format(
336
+ log_path=self.pip_log.path
337
+ ),
338
+ V=ENV.PEX_VERBOSE,
339
+ )
340
+
341
+ spawn_report = functools.partial(
342
+ self._spawn_report, resolved_target_dir=dest, log_manager=log_manager
343
+ )
344
+ with TRACER.timed(
345
+ "Resolving for:\n {}".format(
346
+ "\n ".join(request.render_description() for request in self.requests)
347
+ )
348
+ ):
349
+ try:
350
+ return Reports(
351
+ reports=tuple(
352
+ Report.parse(download_request.download_target, report)
353
+ for download_request, report in zip(
354
+ self.requests,
355
+ execute_parallel(
356
+ inputs=self.requests,
357
+ spawn_func=spawn_report,
358
+ error_handler=Raise[DownloadRequest, str](Unsatisfiable),
359
+ max_jobs=max_parallel_jobs,
360
+ ),
361
+ )
362
+ )
363
+ )
364
+ finally:
365
+ log_manager.finalize_log()
366
+
367
+ def _spawn_report(
368
+ self,
369
+ request, # type: DownloadRequest
370
+ resolved_target_dir, # type: str
371
+ log_manager, # type: PipLogManager
372
+ ):
373
+ # type: (...) -> SpawnedJob[str]
374
+
375
+ report_dir = safe_mkdir(
376
+ os.path.join(resolved_target_dir, request.download_target.id(complete=True))
377
+ )
378
+ report = os.path.join(report_dir, "pip-report.json")
379
+ download_target = request.download_target
380
+ observer = (
381
+ self.observer.observe_download(
382
+ download_target=download_target, download_dir=resolved_target_dir
383
+ )
384
+ if self.observer
385
+ else None
386
+ )
387
+
388
+ target = download_target.target
389
+
390
+ download_job = get_pip(
391
+ interpreter=target.get_interpreter(),
392
+ version=self.pip_version,
393
+ resolver=self.resolver,
394
+ extra_requirements=(
395
+ self.package_index_configuration.extra_pip_requirements
396
+ if self.package_index_configuration
397
+ else ()
398
+ ),
399
+ ).spawn_report(
400
+ report_path=report,
401
+ requirements=request.requirements,
402
+ requirement_files=request.requirement_files,
403
+ constraint_files=request.constraint_files,
404
+ allow_prereleases=self.allow_prereleases,
405
+ transitive=self.transitive,
406
+ target=target,
407
+ package_index_configuration=self.package_index_configuration,
408
+ build_configuration=self.build_configuration,
409
+ observer=observer,
410
+ dependency_configuration=self.dependency_configuration,
411
+ universal_target=download_target.universal_target,
412
+ log=log_manager.get_log(download_target),
413
+ )
414
+
415
+ return SpawnedJob.wait(job=download_job, result=report)
416
+
269
417
  def download_distributions(self, dest=None, max_parallel_jobs=None):
270
418
  # type: (...) -> List[DownloadResult]
419
+
271
420
  if not self.requests or not any(request.has_requirements for request in self.requests):
272
421
  # Nothing to resolve.
273
422
  return []
@@ -1515,7 +1664,7 @@ def _download_internal(
1515
1664
  ):
1516
1665
  # type: (...) -> Tuple[List[BuildRequest], List[DownloadResult]]
1517
1666
 
1518
- download_session = _DownloadSession(
1667
+ pip_session = _PipSession(
1519
1668
  requests=requests,
1520
1669
  direct_requirements=direct_requirements,
1521
1670
  allow_prereleases=allow_prereleases,
@@ -1529,8 +1678,8 @@ def _download_internal(
1529
1678
  dependency_configuration=dependency_configuration,
1530
1679
  )
1531
1680
 
1532
- local_projects = list(download_session.iter_local_projects())
1533
- download_results = download_session.download_distributions(
1681
+ local_projects = list(pip_session.iter_local_projects())
1682
+ download_results = pip_session.download_distributions(
1534
1683
  dest=dest, max_parallel_jobs=max_parallel_jobs
1535
1684
  )
1536
1685
  return local_projects, download_results
@@ -1780,3 +1929,51 @@ def download_requests(
1780
1929
  )
1781
1930
 
1782
1931
  return Downloaded(local_distributions=tuple(local_distributions))
1932
+
1933
+
1934
+ def reports(
1935
+ requests, # type: Tuple[DownloadRequest, ...]
1936
+ direct_requirements, # type: Tuple[ParsedRequirement, ...]
1937
+ allow_prereleases=False, # type: bool
1938
+ transitive=True, # type: bool
1939
+ repos_configuration=ReposConfiguration(), # type: ReposConfiguration
1940
+ resolver_version=None, # type: Optional[ResolverVersion.Value]
1941
+ network_configuration=None, # type: Optional[NetworkConfiguration]
1942
+ build_configuration=BuildConfiguration(), # type: BuildConfiguration
1943
+ max_parallel_jobs=None, # type: Optional[int]
1944
+ observer=None, # type: Optional[ResolveObserver]
1945
+ pip_log=None, # type: Optional[PipLog]
1946
+ pip_version=None, # type: Optional[PipVersionValue]
1947
+ resolver=None, # type: Optional[Resolver]
1948
+ use_pip_config=False, # type: bool
1949
+ extra_pip_requirements=(), # type: Tuple[Requirement, ...]
1950
+ keyring_provider=None, # type: Optional[str]
1951
+ dependency_configuration=DependencyConfiguration(), # type: DependencyConfiguration
1952
+ ):
1953
+ # type: (...) -> Reports
1954
+
1955
+ package_index_configuration = PackageIndexConfiguration.create(
1956
+ pip_version=pip_version,
1957
+ resolver_version=resolver_version,
1958
+ repos_configuration=repos_configuration,
1959
+ network_configuration=network_configuration,
1960
+ use_pip_config=use_pip_config,
1961
+ extra_pip_requirements=extra_pip_requirements,
1962
+ keyring_provider=keyring_provider,
1963
+ )
1964
+
1965
+ pip_session = _PipSession(
1966
+ requests=requests,
1967
+ direct_requirements=direct_requirements,
1968
+ allow_prereleases=allow_prereleases,
1969
+ transitive=transitive,
1970
+ package_index_configuration=package_index_configuration,
1971
+ build_configuration=build_configuration,
1972
+ observer=observer,
1973
+ pip_log=pip_log,
1974
+ pip_version=pip_version,
1975
+ resolver=resolver,
1976
+ dependency_configuration=dependency_configuration,
1977
+ )
1978
+
1979
+ return pip_session.generate_reports(max_parallel_jobs=max_parallel_jobs)