pex 2.65.0__py2.py3-none-any.whl → 2.66.1__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 (47) hide show
  1. pex/docs/html/_pagefind/fragment/en_30a274f.pf_fragment +0 -0
  2. pex/docs/html/_pagefind/fragment/en_32bca6c.pf_fragment +0 -0
  3. pex/docs/html/_pagefind/fragment/en_415e2ea.pf_fragment +0 -0
  4. pex/docs/html/_pagefind/fragment/en_bcccea1.pf_fragment +0 -0
  5. pex/docs/html/_pagefind/fragment/en_ca898f2.pf_fragment +0 -0
  6. pex/docs/html/_pagefind/fragment/en_d4c744d.pf_fragment +0 -0
  7. pex/docs/html/_pagefind/fragment/en_f2ec11e.pf_fragment +0 -0
  8. pex/docs/html/_pagefind/fragment/en_f3e451d.pf_fragment +0 -0
  9. pex/docs/html/_pagefind/index/{en_71e2485.pf_index → en_5564892.pf_index} +0 -0
  10. pex/docs/html/_pagefind/pagefind-entry.json +1 -1
  11. pex/docs/html/_pagefind/pagefind.en_421bc6c608.pf_meta +0 -0
  12. pex/docs/html/_static/documentation_options.js +1 -1
  13. pex/docs/html/api/vars.html +5 -5
  14. pex/docs/html/buildingpex.html +5 -5
  15. pex/docs/html/genindex.html +5 -5
  16. pex/docs/html/index.html +5 -5
  17. pex/docs/html/recipes.html +5 -5
  18. pex/docs/html/scie.html +5 -5
  19. pex/docs/html/search.html +5 -5
  20. pex/docs/html/whatispex.html +5 -5
  21. pex/hashing.py +71 -9
  22. pex/pex_builder.py +1 -4
  23. pex/pip/tool.py +3 -3
  24. pex/pip/vcs.py +93 -44
  25. pex/pip/version.py +7 -0
  26. pex/resolve/lock_downloader.py +1 -0
  27. pex/resolve/locker.py +30 -14
  28. pex/resolve/lockfile/create.py +1 -6
  29. pex/resolve/pre_resolved_resolver.py +1 -7
  30. pex/resolver.py +40 -35
  31. pex/version.py +1 -1
  32. {pex-2.65.0.dist-info → pex-2.66.1.dist-info}/METADATA +4 -4
  33. {pex-2.65.0.dist-info → pex-2.66.1.dist-info}/RECORD +38 -38
  34. pex/docs/html/_pagefind/fragment/en_111cec6.pf_fragment +0 -0
  35. pex/docs/html/_pagefind/fragment/en_57535ac.pf_fragment +0 -0
  36. pex/docs/html/_pagefind/fragment/en_68579f8.pf_fragment +0 -0
  37. pex/docs/html/_pagefind/fragment/en_b915203.pf_fragment +0 -0
  38. pex/docs/html/_pagefind/fragment/en_d450b85.pf_fragment +0 -0
  39. pex/docs/html/_pagefind/fragment/en_eb89be5.pf_fragment +0 -0
  40. pex/docs/html/_pagefind/fragment/en_ff882aa.pf_fragment +0 -0
  41. pex/docs/html/_pagefind/fragment/en_ffc2e0e.pf_fragment +0 -0
  42. pex/docs/html/_pagefind/pagefind.en_f11d4148c9.pf_meta +0 -0
  43. {pex-2.65.0.dist-info → pex-2.66.1.dist-info}/WHEEL +0 -0
  44. {pex-2.65.0.dist-info → pex-2.66.1.dist-info}/entry_points.txt +0 -0
  45. {pex-2.65.0.dist-info → pex-2.66.1.dist-info}/licenses/LICENSE +0 -0
  46. {pex-2.65.0.dist-info → pex-2.66.1.dist-info}/pylock/pylock.toml +0 -0
  47. {pex-2.65.0.dist-info → pex-2.66.1.dist-info}/top_level.txt +0 -0
pex/pip/vcs.py CHANGED
@@ -3,26 +3,42 @@
3
3
 
4
4
  from __future__ import absolute_import
5
5
 
6
- import glob
7
6
  import os
8
7
  import re
9
8
 
10
9
  from pex import hashing
11
10
  from pex.artifact_url import VCS, Fingerprint
12
- from pex.common import is_pyc_dir, is_pyc_file, open_zip, temporary_dir
11
+ from pex.common import is_pyc_dir, is_pyc_file
12
+ from pex.exceptions import reportable_unexpected_error_msg
13
13
  from pex.hashing import Sha256
14
14
  from pex.pep_440 import Version
15
15
  from pex.pep_503 import ProjectName
16
16
  from pex.result import Error, try_
17
- from pex.tracer import TRACER
18
17
  from pex.typing import TYPE_CHECKING
19
18
 
20
19
  if TYPE_CHECKING:
21
- from typing import Optional, Tuple, Union
20
+ # N.B.: The `re.Pattern` type is not available in all Python versions Pex supports.
21
+ from re import Pattern # type: ignore[attr-defined]
22
+ from typing import Callable, Optional, Text, Tuple, Union
22
23
 
23
24
  from pex.hashing import HintedDigest
24
25
 
25
26
 
27
+ def _project_name_re(project_name):
28
+ # type: (ProjectName) -> str
29
+ return project_name.normalized.replace("-", "[-_.]+")
30
+
31
+
32
+ def _built_source_dist_pattern(project_name):
33
+ # type: (ProjectName) -> Pattern
34
+ return re.compile(
35
+ r"(?P<project_name>{project_name_re})-(?P<version>.+)\.zip".format(
36
+ project_name_re=_project_name_re(project_name)
37
+ ),
38
+ re.IGNORECASE,
39
+ )
40
+
41
+
26
42
  def _find_built_source_dist(
27
43
  build_dir, # type: str
28
44
  project_name, # type: ProjectName
@@ -34,12 +50,7 @@ def _find_built_source_dist(
34
50
  # encoded in: `pip._internal.req.req_install.InstallRequirement.archive`.
35
51
 
36
52
  listing = os.listdir(build_dir)
37
- pattern = re.compile(
38
- r"{project_name}-(?P<version>.+)\.zip".format(
39
- project_name=project_name.normalized.replace("-", "[-_.]+")
40
- ),
41
- re.IGNORECASE,
42
- )
53
+ pattern = _built_source_dist_pattern(project_name)
43
54
  for name in listing:
44
55
  match = pattern.match(name)
45
56
  if match and Version(match.group("version")) == version:
@@ -58,23 +69,66 @@ def _find_built_source_dist(
58
69
 
59
70
  def fingerprint_downloaded_vcs_archive(
60
71
  download_dir, # type: str
61
- project_name, # type: str
62
- version, # type: str
72
+ project_name, # type: ProjectName
73
+ version, # type: Version
63
74
  vcs, # type: VCS.Value
64
75
  ):
65
76
  # type: (...) -> Tuple[Fingerprint, str]
66
77
 
67
78
  archive_path = try_(
68
- _find_built_source_dist(
69
- build_dir=download_dir, project_name=ProjectName(project_name), version=Version(version)
70
- )
79
+ _find_built_source_dist(build_dir=download_dir, project_name=project_name, version=version)
71
80
  )
72
81
  digest = Sha256()
73
- digest_vcs_archive(archive_path=archive_path, vcs=vcs, digest=digest)
82
+ digest_vcs_archive(project_name=project_name, archive_path=archive_path, vcs=vcs, digest=digest)
74
83
  return Fingerprint.from_digest(digest), archive_path
75
84
 
76
85
 
86
+ def _vcs_dir_filter(
87
+ vcs, # type: VCS.Value
88
+ project_name, # type: ProjectName
89
+ ):
90
+ # type: (...) -> Callable[[Text], bool]
91
+
92
+ # Ignore VCS control directories for the purposes of fingerprinting the version controlled
93
+ # source tree. VCS control directories can contain non-reproducible content (Git at least
94
+ # has files that contain timestamps).
95
+ #
96
+ # We cannot prune these directories from the source archive directly unfortunately since
97
+ # some build processes use VCS version information to derive their version numbers (C.F.:
98
+ # https://pypi.org/project/setuptools-scm/). As such, we'll get a stable fingerprint, but be
99
+ # forced to re-build a wheel each time the VCS requirement is re-locked later, even when it
100
+ # hashes the same.
101
+ vcs_control_dir = ".{vcs}".format(vcs=vcs)
102
+
103
+ # N.B.: If the VCS project uses setuptools as its build backend, depending on the version of
104
+ # Pip used, the VCS checkout can have a `<project name>.egg-info/` directory littering its root
105
+ # left over from Pip generating project metadata to determine version and dependencies. No other
106
+ # well known build-backend has this problem at this time (checked hatchling, poetry-core,
107
+ # pdm-backend and uv_build).
108
+ # C.F.: https://github.com/pypa/pip/pull/13602
109
+ egg_info_dir_re = re.compile(
110
+ r"^{project_name_re}\.egg-info$".format(project_name_re=_project_name_re(project_name)),
111
+ re.IGNORECASE,
112
+ )
113
+
114
+ def vcs_dir_filter(dir_path):
115
+ # type: (Text) -> bool
116
+ if is_pyc_dir(dir_path):
117
+ return False
118
+
119
+ base_dir_name = dir_path.split(os.sep)[0]
120
+ return base_dir_name != vcs_control_dir and not egg_info_dir_re.match(base_dir_name)
121
+
122
+ return vcs_dir_filter
123
+
124
+
125
+ def _vcs_file_filter(vcs):
126
+ # type: (VCS.Value) -> Callable[[Text], bool]
127
+ return lambda f: not is_pyc_file(f)
128
+
129
+
77
130
  def digest_vcs_archive(
131
+ project_name, # type: ProjectName
78
132
  archive_path, # type: str
79
133
  vcs, # type: VCS.Value
80
134
  digest, # type: HintedDigest
@@ -84,22 +138,32 @@ def digest_vcs_archive(
84
138
  # All VCS requirements are prepared as zip archives as encoded in:
85
139
  # `pip._internal.req.req_install.InstallRequirement.archive` and the archive is already offset
86
140
  # by a subdirectory (if any).
87
- with TRACER.timed(
88
- "Digesting {archive} {vcs} archive".format(archive=os.path.basename(archive_path), vcs=vcs)
89
- ), temporary_dir() as chroot, open_zip(archive_path) as archive:
90
- # TODO(John Sirois): Consider implementing zip_hash to avoid the extractall.
91
- archive.extractall(chroot)
92
141
 
93
- # The zip archives created by Pip have a single project name top-level directory housing
94
- # the full clone. We look for that to get a consistent clone hash with a bare clone.
95
- listing = glob.glob(os.path.join(chroot, "*"))
96
- if len(listing) == 1 and os.path.isdir(listing[0]):
97
- chroot = listing[0]
142
+ # The zip archives created by Pip have a single project name top-level directory housing
143
+ # the full clone. We look for that to get a consistent clone hash with a bare clone.
144
+ match = _built_source_dist_pattern(project_name).match(os.path.basename(archive_path))
145
+ if match is None:
146
+ raise AssertionError(
147
+ reportable_unexpected_error_msg(
148
+ "Failed to determine the project name prefix for the VCS zip {zip} with expected "
149
+ "canonical project name {project_name}".format(
150
+ zip=archive_path, project_name=project_name
151
+ )
152
+ )
153
+ )
154
+ top_dir = match.group("project_name")
98
155
 
99
- digest_vcs_repo(repo_path=chroot, vcs=vcs, digest=digest)
156
+ hashing.zip_hash(
157
+ zip_path=archive_path,
158
+ digest=digest,
159
+ relpath=top_dir,
160
+ dir_filter=_vcs_dir_filter(vcs, project_name),
161
+ file_filter=_vcs_file_filter(vcs),
162
+ )
100
163
 
101
164
 
102
165
  def digest_vcs_repo(
166
+ project_name, # type: ProjectName
103
167
  repo_path, # type: str
104
168
  vcs, # type: VCS.Value
105
169
  digest, # type: HintedDigest
@@ -107,24 +171,9 @@ def digest_vcs_repo(
107
171
  ):
108
172
  # type: (...) -> None
109
173
 
110
- # Ignore VCS control directories for the purposes of fingerprinting the version controlled
111
- # source tree. VCS control directories can contain non-reproducible content (Git at least
112
- # has files that contain timestamps).
113
- #
114
- # We cannot prune these directories from the source archive directly unfortunately since
115
- # some build processes use VCS version information to derive their version numbers (C.F.:
116
- # https://pypi.org/project/setuptools-scm/). As such, we'll get a stable fingerprint, but be
117
- # forced to re-build a wheel each time the VCS requirement is re-locked later, even when it
118
- # hashes the same.
119
- vcs_control_dir = ".{vcs}".format(vcs=vcs)
120
-
121
174
  hashing.dir_hash(
122
175
  directory=os.path.join(repo_path, subdirectory) if subdirectory else repo_path,
123
176
  digest=digest,
124
- dir_filter=(
125
- lambda dir_path: (
126
- not is_pyc_dir(dir_path) and os.path.basename(dir_path) != vcs_control_dir
127
- )
128
- ),
129
- file_filter=lambda f: not is_pyc_file(f),
177
+ dir_filter=_vcs_dir_filter(vcs, project_name),
178
+ file_filter=_vcs_file_filter(vcs),
130
179
  )
pex/pip/version.py CHANGED
@@ -369,6 +369,13 @@ class PipVersion(Enum["PipVersionValue"]):
369
369
  requires_python=">=3.9,<3.16",
370
370
  )
371
371
 
372
+ v25_3 = PipVersionValue(
373
+ version="25.3",
374
+ setuptools_version="80.9.0",
375
+ wheel_version="0.45.1",
376
+ requires_python=">=3.9,<3.16",
377
+ )
378
+
372
379
  VENDORED = v20_3_4_patched
373
380
  LATEST = LatestPipVersion()
374
381
  LATEST_COMPATIBLE = LatestCompatiblePipVersion()
@@ -154,6 +154,7 @@ class VCSArtifactDownloadManager(DownloadManager[VCSArtifact]):
154
154
  local_distribution = downloaded_vcs.local_distributions[0]
155
155
  filename = os.path.basename(local_distribution.path)
156
156
  digest_vcs_archive(
157
+ project_name=project_name,
157
158
  archive_path=local_distribution.path,
158
159
  vcs=artifact.vcs,
159
160
  digest=digest,
pex/resolve/locker.py CHANGED
@@ -30,6 +30,7 @@ from pex.resolve.pep_691.model import Endpoint
30
30
  from pex.resolve.resolved_requirement import PartialArtifact, Pin, ResolvedRequirement
31
31
  from pex.resolve.resolvers import Resolver
32
32
  from pex.resolve.target_system import UniversalTarget
33
+ from pex.result import try_
33
34
  from pex.targets import Target
34
35
  from pex.typing import TYPE_CHECKING
35
36
 
@@ -291,6 +292,16 @@ class Locker(LogAnalyzer):
291
292
  def _maybe_record_wheel(self, url):
292
293
  # type: (str) -> ArtifactURL
293
294
  artifact_url = self.parse_url_and_maybe_record_fingerprint(url)
295
+
296
+ # N.B.: Lock resolves driven by `pip install --dry-run --report` will only consult PEP-658
297
+ # `.whl.metadata` side-car files in the happy path; so we must use these as a proxy for the
298
+ # `.whl` file they are paired with.
299
+ # See: https://peps.python.org/pep-0658/
300
+ if not self._lock_is_via_pip_download and artifact_url.url_info.path.endswith(".metadata"):
301
+ artifact_url = ArtifactURL.from_url_info(
302
+ artifact_url.url_info._replace(path=artifact_url.url_info.path[:-9])
303
+ )
304
+
294
305
  if artifact_url.is_wheel:
295
306
  pin, partial_artifact = self._extract_resolve_data(artifact_url)
296
307
 
@@ -371,8 +382,8 @@ class Locker(LogAnalyzer):
371
382
  if isinstance(artifact_url.scheme, VCSScheme):
372
383
  source_fingerprint, archive_path = fingerprint_downloaded_vcs_archive(
373
384
  download_dir=self._download_dir,
374
- project_name=str(build_result.pin.project_name),
375
- version=str(build_result.pin.version),
385
+ project_name=build_result.pin.project_name,
386
+ version=build_result.pin.version,
376
387
  vcs=artifact_url.scheme.vcs,
377
388
  )
378
389
  verified = True
@@ -407,12 +418,14 @@ class Locker(LogAnalyzer):
407
418
  os.path.basename(artifact_url.path)
408
419
  ] = build_result.pin
409
420
  else:
410
- digest_local_project(
411
- directory=artifact_url.path,
412
- digest=digest,
413
- pip_version=self._pip_version,
414
- target=self._target,
415
- resolver=self._resolver,
421
+ try_(
422
+ digest_local_project(
423
+ directory=artifact_url.path,
424
+ digest=digest,
425
+ pip_version=self._pip_version,
426
+ target=self._target,
427
+ resolver=self._resolver,
428
+ )
416
429
  )
417
430
  self._local_projects.add(artifact_url.path)
418
431
  self._saved.add(build_result.pin)
@@ -445,6 +458,7 @@ class Locker(LogAnalyzer):
445
458
  if isinstance(artifact_url.scheme, VCSScheme):
446
459
  digest = Sha256()
447
460
  digest_vcs_repo(
461
+ project_name=build_result.pin.project_name,
448
462
  repo_path=build_result.path,
449
463
  vcs=artifact_url.scheme.vcs,
450
464
  digest=digest,
@@ -479,12 +493,14 @@ class Locker(LogAnalyzer):
479
493
  os.path.basename(artifact_url.path)
480
494
  ] = build_result.pin
481
495
  else:
482
- digest_local_project(
483
- directory=artifact_url.path,
484
- digest=digest,
485
- pip_version=self._pip_version,
486
- target=self._target,
487
- resolver=self._resolver,
496
+ try_(
497
+ digest_local_project(
498
+ directory=artifact_url.path,
499
+ digest=digest,
500
+ pip_version=self._pip_version,
501
+ target=self._target,
502
+ resolver=self._resolver,
503
+ )
488
504
  )
489
505
  self._local_projects.add(artifact_url.path)
490
506
  self._saved.add(build_result.pin)
@@ -357,8 +357,6 @@ class LockObserver(ResolveObserver):
357
357
  target=local_distribution.download_target,
358
358
  source_path=local_distribution.path,
359
359
  subdirectory=local_distribution.subdirectory,
360
- resolver=self.resolver,
361
- pip_version=self.package_index_configuration.pip_version,
362
360
  )
363
361
  )
364
362
 
@@ -369,10 +367,7 @@ class LockObserver(ResolveObserver):
369
367
  lock_result = analysis.analyzer.lock_result
370
368
  build_requests.update(
371
369
  BuildRequest.for_directory(
372
- target=analysis.download_target,
373
- source_path=local_project,
374
- resolver=self.resolver,
375
- pip_version=self.package_index_configuration.pip_version,
370
+ target=analysis.download_target, source_path=local_project
376
371
  )
377
372
  for local_project in lock_result.local_projects
378
373
  )
@@ -101,19 +101,13 @@ def resolve_from_dists(
101
101
  extra_pip_requirements=pip_configuration.extra_requirements,
102
102
  keyring_provider=pip_configuration.keyring_provider,
103
103
  )
104
- resolver = ConfiguredResolver(pip_configuration=pip_configuration)
105
104
  build_requests = [
106
105
  BuildRequest.for_file(target=target, source_path=sdist)
107
106
  for sdist in sdists
108
107
  for target in unique_targets
109
108
  ]
110
109
  build_requests.extend(
111
- BuildRequest.for_directory(
112
- target=target,
113
- source_path=local_project.path,
114
- resolver=resolver,
115
- pip_version=pip_configuration.version,
116
- )
110
+ BuildRequest.for_directory(target=target, source_path=local_project.path)
117
111
  for local_project in local_projects
118
112
  for target in unique_targets
119
113
  )
pex/resolver.py CHANGED
@@ -65,6 +65,7 @@ from pex.resolve.resolvers import (
65
65
  check_resolve,
66
66
  )
67
67
  from pex.resolve.target_system import TargetSystem, UniversalTarget
68
+ from pex.result import Error
68
69
  from pex.targets import AbbreviatedPlatform, CompletePlatform, LocalInterpreter, Target, Targets
69
70
  from pex.third_party.packaging.specifiers import SpecifierSet
70
71
  from pex.third_party.packaging.tags import Tag
@@ -309,10 +310,7 @@ class _PipSession(object):
309
310
  if isinstance(requirement, LocalProjectRequirement):
310
311
  for request in self.requests:
311
312
  yield BuildRequest.for_directory(
312
- target=request.target,
313
- source_path=requirement.path,
314
- resolver=self.resolver,
315
- pip_version=self.pip_version,
313
+ target=request.target, source_path=requirement.path
316
314
  )
317
315
 
318
316
  def generate_reports(self, max_parallel_jobs=None):
@@ -594,12 +592,17 @@ def _fingerprint_directory(path):
594
592
  return CacheHelper.dir_hash(path, digest=_hasher())
595
593
 
596
594
 
595
+ class BuildError(Exception):
596
+ pass
597
+
598
+
597
599
  def _fingerprint_local_project(
598
600
  path, # type: str
599
601
  target, # type: Target
600
602
  resolver=None, # type: Optional[Resolver]
601
603
  pip_version=None, # type: Optional[PipVersionValue]
602
604
  ):
605
+ # type: (...) -> str
603
606
  if resolver:
604
607
  build_system_resolver = resolver
605
608
  else:
@@ -607,17 +610,20 @@ def _fingerprint_local_project(
607
610
 
608
611
  build_system_resolver = ConfiguredResolver.default()
609
612
 
610
- return digest_local_project(
613
+ hasher = _hasher()
614
+ result = digest_local_project(
611
615
  directory=path,
612
- digest=_hasher(),
616
+ digest=hasher,
613
617
  target=target,
614
618
  resolver=build_system_resolver,
615
619
  pip_version=pip_version,
616
620
  )
617
-
618
-
619
- class BuildError(Exception):
620
- pass
621
+ if isinstance(result, Error):
622
+ raise BuildError(
623
+ "Failed to create an sdist for hashing from the local project at {path}: "
624
+ "{err}".format(path=path, err=result)
625
+ )
626
+ return hasher.hexdigest()
621
627
 
622
628
 
623
629
  def _as_download_target(target):
@@ -649,27 +655,19 @@ class BuildRequest(object):
649
655
  target, # type: Union[DownloadTarget, Target]
650
656
  source_path, # type: str
651
657
  subdirectory=None, # type: Optional[str]
652
- resolver=None, # type: Optional[Resolver]
653
- pip_version=None, # type: Optional[PipVersionValue]
654
658
  ):
655
659
  # type: (...) -> BuildRequest
656
660
  download_target = _as_download_target(target)
657
- fingerprint = _fingerprint_local_project(
658
- path=source_path,
659
- target=download_target.target,
660
- resolver=resolver,
661
- pip_version=pip_version,
662
- )
663
661
  return cls(
664
662
  download_target=download_target,
665
663
  source_path=source_path,
666
- fingerprint=fingerprint,
664
+ fingerprint=None,
667
665
  subdirectory=subdirectory,
668
666
  )
669
667
 
670
668
  download_target = attr.ib(converter=_as_download_target) # type: DownloadTarget
671
669
  source_path = attr.ib() # type: str
672
- fingerprint = attr.ib() # type: str
670
+ fingerprint = attr.ib() # type: Optional[str]
673
671
  subdirectory = attr.ib() # type: Optional[str]
674
672
 
675
673
  @property
@@ -724,12 +722,16 @@ class BuildResult(object):
724
722
  source_path=None, # type: Optional[str]
725
723
  ):
726
724
  # type: (...) -> BuildResult
727
- built_wheel = BuiltWheelDir.create(
728
- sdist=source_path or build_request.source_path,
729
- fingerprint=build_request.fingerprint,
730
- target=build_request.target,
731
- )
732
- return cls(request=build_request, atomic_dir=AtomicDirectory(built_wheel.dist_dir))
725
+ if build_request.fingerprint:
726
+ built_wheel = BuiltWheelDir.create(
727
+ sdist=source_path or build_request.source_path,
728
+ fingerprint=build_request.fingerprint,
729
+ target=build_request.target,
730
+ )
731
+ target_dir = built_wheel.dist_dir
732
+ else:
733
+ target_dir = os.path.join(safe_mkdtemp(), "build")
734
+ return cls(request=build_request, atomic_dir=AtomicDirectory(target_dir))
733
735
 
734
736
  request = attr.ib() # type: BuildRequest
735
737
  _atomic_dir = attr.ib() # type: AtomicDirectory
@@ -1290,14 +1292,7 @@ class BuildAndInstallRequest(object):
1290
1292
  if is_wheel(dist_path):
1291
1293
  to_install.add(InstallRequest.create(install_request.target, dist_path))
1292
1294
  elif os.path.isdir(dist_path):
1293
- to_build.add(
1294
- BuildRequest.for_directory(
1295
- install_request.target,
1296
- dist_path,
1297
- resolver=self._resolver,
1298
- pip_version=self._pip_version,
1299
- )
1300
- )
1295
+ to_build.add(BuildRequest.for_directory(install_request.target, dist_path))
1301
1296
  else:
1302
1297
  to_build.add(BuildRequest.for_file(install_request.target, dist_path))
1303
1298
  already_analyzed.add(metadata.project_name)
@@ -1907,11 +1902,21 @@ def download_requests(
1907
1902
  def add_build_requests(requests):
1908
1903
  # type: (Iterable[BuildRequest]) -> None
1909
1904
  for request in requests:
1905
+ if request.fingerprint:
1906
+ fingerprint = request.fingerprint
1907
+ else:
1908
+ production_assert(os.path.isdir(request.source_path))
1909
+ fingerprint = _fingerprint_local_project(
1910
+ path=request.source_path,
1911
+ target=request.target,
1912
+ resolver=resolver,
1913
+ pip_version=pip_version,
1914
+ )
1910
1915
  local_distributions.append(
1911
1916
  LocalDistribution(
1912
1917
  download_target=request.download_target,
1913
1918
  path=request.source_path,
1914
- fingerprint=request.fingerprint,
1919
+ fingerprint=fingerprint,
1915
1920
  subdirectory=request.subdirectory,
1916
1921
  )
1917
1922
  )
pex/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # Copyright 2015 Pex project contributors.
2
2
  # Licensed under the Apache License, Version 2.0 (see LICENSE).
3
3
 
4
- __version__ = "2.65.0"
4
+ __version__ = "2.66.1"
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pex
3
- Version: 2.65.0
3
+ Version: 2.66.1
4
4
  Summary: The PEX packaging toolchain.
5
5
  Home-page: https://github.com/pex-tool/pex
6
- Download-URL: https://github.com/pex-tool/pex/releases/download/v2.65.0/pex
6
+ Download-URL: https://github.com/pex-tool/pex/releases/download/v2.66.1/pex
7
7
  Author: The PEX developers
8
8
  Author-email: developers@pex-tool.org
9
9
  License-Expression: Apache-2.0
10
- Project-URL: Changelog, https://github.com/pex-tool/pex/blob/v2.65.0/CHANGES.md
10
+ Project-URL: Changelog, https://github.com/pex-tool/pex/blob/v2.66.1/CHANGES.md
11
11
  Project-URL: Documentation, https://docs.pex-tool.org/
12
- Project-URL: Source, https://github.com/pex-tool/pex/tree/v2.65.0
12
+ Project-URL: Source, https://github.com/pex-tool/pex/tree/v2.66.1
13
13
  Keywords: package,executable,virtualenv,lock,freeze
14
14
  Classifier: Development Status :: 5 - Production/Stable
15
15
  Classifier: Intended Audience :: Developers