pex 2.54.2__py2.py3-none-any.whl → 2.69.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 (180) hide show
  1. pex/auth.py +1 -1
  2. pex/bin/pex.py +15 -2
  3. pex/build_backend/configuration.py +5 -5
  4. pex/build_backend/wrap.py +27 -23
  5. pex/build_system/pep_517.py +4 -1
  6. pex/cache/dirs.py +17 -12
  7. pex/cli/commands/lock.py +302 -165
  8. pex/cli/commands/pip/core.py +4 -12
  9. pex/cli/commands/pip/wheel.py +1 -1
  10. pex/cli/commands/run.py +13 -20
  11. pex/cli/commands/venv.py +85 -16
  12. pex/cli/pex.py +11 -4
  13. pex/common.py +57 -7
  14. pex/compatibility.py +1 -1
  15. pex/dependency_configuration.py +87 -15
  16. pex/dist_metadata.py +143 -25
  17. pex/docs/html/_pagefind/fragment/en_4250138.pf_fragment +0 -0
  18. pex/docs/html/_pagefind/fragment/en_7125dad.pf_fragment +0 -0
  19. pex/docs/html/_pagefind/fragment/en_785d562.pf_fragment +0 -0
  20. pex/docs/html/_pagefind/fragment/en_8e94bb8.pf_fragment +0 -0
  21. pex/docs/html/_pagefind/fragment/en_a0396bb.pf_fragment +0 -0
  22. pex/docs/html/_pagefind/fragment/en_a8a3588.pf_fragment +0 -0
  23. pex/docs/html/_pagefind/fragment/en_c07d988.pf_fragment +0 -0
  24. pex/docs/html/_pagefind/fragment/en_d718411.pf_fragment +0 -0
  25. pex/docs/html/_pagefind/index/en_a2e3c5e.pf_index +0 -0
  26. pex/docs/html/_pagefind/pagefind-entry.json +1 -1
  27. pex/docs/html/_pagefind/pagefind.en_4ce1afa9e3.pf_meta +0 -0
  28. pex/docs/html/_static/documentation_options.js +1 -1
  29. pex/docs/html/_static/pygments.css +164 -146
  30. pex/docs/html/_static/styles/furo.css +1 -1
  31. pex/docs/html/_static/styles/furo.css.map +1 -1
  32. pex/docs/html/api/vars.html +25 -34
  33. pex/docs/html/buildingpex.html +25 -34
  34. pex/docs/html/genindex.html +24 -33
  35. pex/docs/html/index.html +25 -34
  36. pex/docs/html/recipes.html +25 -34
  37. pex/docs/html/scie.html +25 -34
  38. pex/docs/html/search.html +24 -33
  39. pex/docs/html/whatispex.html +25 -34
  40. pex/entry_points_txt.py +98 -0
  41. pex/environment.py +54 -33
  42. pex/finders.py +1 -1
  43. pex/hashing.py +71 -9
  44. pex/installed_wheel.py +141 -0
  45. pex/interpreter.py +41 -38
  46. pex/interpreter_constraints.py +25 -25
  47. pex/interpreter_implementation.py +40 -0
  48. pex/jobs.py +13 -6
  49. pex/pep_376.py +68 -384
  50. pex/pep_425.py +11 -2
  51. pex/pep_427.py +937 -205
  52. pex/pep_508.py +4 -5
  53. pex/pex_builder.py +5 -8
  54. pex/pex_info.py +14 -9
  55. pex/pip/dependencies/__init__.py +85 -13
  56. pex/pip/dependencies/requires.py +38 -3
  57. pex/pip/foreign_platform/__init__.py +4 -3
  58. pex/pip/installation.py +2 -2
  59. pex/pip/local_project.py +6 -14
  60. pex/pip/package_repositories/__init__.py +78 -0
  61. pex/pip/package_repositories/link_collector.py +96 -0
  62. pex/pip/tool.py +139 -33
  63. pex/pip/vcs.py +109 -43
  64. pex/pip/version.py +8 -1
  65. pex/requirements.py +121 -16
  66. pex/resolve/config.py +5 -1
  67. pex/resolve/configured_resolve.py +32 -10
  68. pex/resolve/configured_resolver.py +10 -39
  69. pex/resolve/downloads.py +4 -3
  70. pex/resolve/lock_downloader.py +16 -23
  71. pex/resolve/lock_resolver.py +41 -51
  72. pex/resolve/locked_resolve.py +89 -32
  73. pex/resolve/locker.py +145 -101
  74. pex/resolve/locker_patches.py +123 -197
  75. pex/resolve/lockfile/create.py +232 -87
  76. pex/resolve/lockfile/download_manager.py +5 -1
  77. pex/resolve/lockfile/json_codec.py +103 -28
  78. pex/resolve/lockfile/model.py +13 -35
  79. pex/resolve/lockfile/pep_751.py +117 -98
  80. pex/resolve/lockfile/requires_dist.py +17 -262
  81. pex/resolve/lockfile/subset.py +11 -0
  82. pex/resolve/lockfile/targets.py +445 -0
  83. pex/resolve/lockfile/updater.py +22 -10
  84. pex/resolve/package_repository.py +406 -0
  85. pex/resolve/pex_repository_resolver.py +1 -1
  86. pex/resolve/pre_resolved_resolver.py +19 -16
  87. pex/resolve/project.py +233 -47
  88. pex/resolve/requirement_configuration.py +28 -10
  89. pex/resolve/resolver_configuration.py +18 -32
  90. pex/resolve/resolver_options.py +234 -28
  91. pex/resolve/resolvers.py +3 -12
  92. pex/resolve/target_options.py +18 -2
  93. pex/resolve/target_system.py +908 -0
  94. pex/resolve/venv_resolver.py +670 -0
  95. pex/resolver.py +673 -209
  96. pex/scie/__init__.py +40 -1
  97. pex/scie/model.py +2 -0
  98. pex/scie/science.py +25 -3
  99. pex/sdist.py +219 -0
  100. pex/sh_boot.py +24 -21
  101. pex/sysconfig.py +5 -3
  102. pex/targets.py +31 -10
  103. pex/third_party/__init__.py +1 -1
  104. pex/tools/commands/repository.py +48 -25
  105. pex/vendor/__init__.py +4 -9
  106. pex/vendor/__main__.py +65 -41
  107. pex/vendor/_vendored/ansicolors/.layout.json +1 -1
  108. pex/vendor/_vendored/ansicolors/ansicolors-1.1.8.dist-info/RECORD +11 -0
  109. pex/vendor/_vendored/ansicolors/ansicolors-1.1.8.pex-info/original-whl-info.json +1 -0
  110. pex/vendor/_vendored/appdirs/.layout.json +1 -1
  111. pex/vendor/_vendored/appdirs/appdirs-1.4.4.dist-info/RECORD +7 -0
  112. pex/vendor/_vendored/appdirs/appdirs-1.4.4.pex-info/original-whl-info.json +1 -0
  113. pex/vendor/_vendored/attrs/.layout.json +1 -1
  114. pex/vendor/_vendored/attrs/attrs-21.5.0.dev0.dist-info/RECORD +37 -0
  115. pex/vendor/_vendored/attrs/attrs-21.5.0.dev0.pex-info/original-whl-info.json +1 -0
  116. pex/vendor/_vendored/packaging_20_9/.layout.json +1 -1
  117. pex/vendor/_vendored/packaging_20_9/packaging-20.9.dist-info/RECORD +20 -0
  118. pex/vendor/_vendored/packaging_20_9/packaging-20.9.pex-info/original-whl-info.json +1 -0
  119. pex/vendor/_vendored/packaging_20_9/pyparsing-2.4.7.dist-info/RECORD +7 -0
  120. pex/vendor/_vendored/packaging_20_9/pyparsing-2.4.7.pex-info/original-whl-info.json +1 -0
  121. pex/vendor/_vendored/packaging_21_3/.layout.json +1 -1
  122. pex/vendor/_vendored/packaging_21_3/packaging-21.3.dist-info/RECORD +20 -0
  123. pex/vendor/_vendored/packaging_21_3/packaging-21.3.pex-info/original-whl-info.json +1 -0
  124. pex/vendor/_vendored/packaging_21_3/pyparsing-3.0.7.dist-info/RECORD +18 -0
  125. pex/vendor/_vendored/packaging_21_3/pyparsing-3.0.7.pex-info/original-whl-info.json +1 -0
  126. pex/vendor/_vendored/packaging_24_0/.layout.json +1 -1
  127. pex/vendor/_vendored/packaging_24_0/packaging-24.0.dist-info/RECORD +22 -0
  128. pex/vendor/_vendored/packaging_24_0/packaging-24.0.pex-info/original-whl-info.json +1 -0
  129. pex/vendor/_vendored/packaging_25_0/.layout.json +1 -1
  130. pex/vendor/_vendored/packaging_25_0/packaging-25.0.dist-info/RECORD +24 -0
  131. pex/vendor/_vendored/packaging_25_0/packaging-25.0.pex-info/original-whl-info.json +1 -0
  132. pex/vendor/_vendored/pip/.layout.json +1 -1
  133. pex/vendor/_vendored/pip/pip/_vendor/certifi/cacert.pem +63 -1
  134. pex/vendor/_vendored/pip/pip-20.3.4.dist-info/RECORD +388 -0
  135. pex/vendor/_vendored/pip/pip-20.3.4.pex-info/original-whl-info.json +1 -0
  136. pex/vendor/_vendored/setuptools/.layout.json +1 -1
  137. pex/vendor/_vendored/setuptools/setuptools-44.0.0+3acb925dd708430aeaf197ea53ac8a752f7c1863.dist-info/RECORD +107 -0
  138. pex/vendor/_vendored/setuptools/setuptools-44.0.0+3acb925dd708430aeaf197ea53ac8a752f7c1863.pex-info/original-whl-info.json +1 -0
  139. pex/vendor/_vendored/toml/.layout.json +1 -1
  140. pex/vendor/_vendored/toml/toml-0.10.2.dist-info/RECORD +11 -0
  141. pex/vendor/_vendored/toml/toml-0.10.2.pex-info/original-whl-info.json +1 -0
  142. pex/vendor/_vendored/tomli/.layout.json +1 -1
  143. pex/vendor/_vendored/tomli/tomli-2.0.1.dist-info/RECORD +10 -0
  144. pex/vendor/_vendored/tomli/tomli-2.0.1.pex-info/original-whl-info.json +1 -0
  145. pex/venv/installer.py +46 -19
  146. pex/venv/venv_pex.py +6 -3
  147. pex/version.py +1 -1
  148. pex/wheel.py +188 -40
  149. pex/whl.py +67 -0
  150. pex/windows/__init__.py +14 -11
  151. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/METADATA +6 -5
  152. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/RECORD +157 -133
  153. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/entry_points.txt +1 -0
  154. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/pylock/pylock.toml +1 -1
  155. pex/docs/html/_pagefind/fragment/en_42c9d8c.pf_fragment +0 -0
  156. pex/docs/html/_pagefind/fragment/en_45dd5a2.pf_fragment +0 -0
  157. pex/docs/html/_pagefind/fragment/en_4ca74d2.pf_fragment +0 -0
  158. pex/docs/html/_pagefind/fragment/en_77273d5.pf_fragment +0 -0
  159. pex/docs/html/_pagefind/fragment/en_87a59c5.pf_fragment +0 -0
  160. pex/docs/html/_pagefind/fragment/en_8dc89b5.pf_fragment +0 -0
  161. pex/docs/html/_pagefind/fragment/en_9d1319b.pf_fragment +0 -0
  162. pex/docs/html/_pagefind/fragment/en_e55df9d.pf_fragment +0 -0
  163. pex/docs/html/_pagefind/index/en_1e98c6f.pf_index +0 -0
  164. pex/docs/html/_pagefind/pagefind.en_d1c488ecae.pf_meta +0 -0
  165. pex/vendor/_vendored/ansicolors/ansicolors-1.1.8.dist-info/INSTALLER +0 -1
  166. pex/vendor/_vendored/appdirs/appdirs-1.4.4.dist-info/INSTALLER +0 -1
  167. pex/vendor/_vendored/attrs/attrs-21.5.0.dev0.dist-info/INSTALLER +0 -1
  168. pex/vendor/_vendored/packaging_20_9/packaging-20.9.dist-info/INSTALLER +0 -1
  169. pex/vendor/_vendored/packaging_20_9/pyparsing-2.4.7.dist-info/INSTALLER +0 -1
  170. pex/vendor/_vendored/packaging_21_3/packaging-21.3.dist-info/INSTALLER +0 -1
  171. pex/vendor/_vendored/packaging_21_3/pyparsing-3.0.7.dist-info/INSTALLER +0 -1
  172. pex/vendor/_vendored/packaging_24_0/packaging-24.0.dist-info/INSTALLER +0 -1
  173. pex/vendor/_vendored/packaging_25_0/packaging-25.0.dist-info/INSTALLER +0 -1
  174. pex/vendor/_vendored/pip/pip-20.3.4.dist-info/INSTALLER +0 -1
  175. pex/vendor/_vendored/setuptools/setuptools-44.0.0+3acb925dd708430aeaf197ea53ac8a752f7c1863.dist-info/INSTALLER +0 -1
  176. pex/vendor/_vendored/toml/toml-0.10.2.dist-info/INSTALLER +0 -1
  177. pex/vendor/_vendored/tomli/tomli-2.0.1.dist-info/INSTALLER +0 -1
  178. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/WHEEL +0 -0
  179. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/licenses/LICENSE +0 -0
  180. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,406 @@
1
+ # Copyright 2025 Pex project contributors.
2
+ # Licensed under the Apache License, Version 2.0 (see LICENSE).
3
+
4
+ from __future__ import absolute_import
5
+
6
+ import itertools
7
+ import os
8
+ import re
9
+ from collections import OrderedDict, defaultdict
10
+
11
+ from pex.auth import PasswordEntry
12
+ from pex.compatibility import string
13
+ from pex.dist_metadata import Requirement, RequirementParseError
14
+ from pex.exceptions import production_assert, reportable_unexpected_error_msg
15
+ from pex.fetcher import URLFetcher
16
+ from pex.orderedset import OrderedSet
17
+ from pex.pep_503 import ProjectName
18
+ from pex.requirements import (
19
+ FindLinks,
20
+ Index,
21
+ PyPIRequirement,
22
+ URLRequirement,
23
+ VCSRequirement,
24
+ parse_requirement_file,
25
+ )
26
+ from pex.resolve.target_system import MarkerEnv
27
+ from pex.third_party.packaging.markers import InvalidMarker, Marker
28
+ from pex.typing import TYPE_CHECKING, cast
29
+
30
+ if TYPE_CHECKING:
31
+ # N.B.: The `re.Pattern` type is not available in all Python versions Pex supports.
32
+ from re import Pattern # type: ignore[attr-defined]
33
+ from typing import Any, DefaultDict, Dict, Iterable, List, Mapping, Optional, Text, Tuple, Union
34
+
35
+ import attr # vendor:skip
36
+
37
+ else:
38
+ from pex.third_party import attr
39
+
40
+
41
+ @attr.s(frozen=True, eq=False, hash=False)
42
+ class Scope(object):
43
+ @classmethod
44
+ def _parse_regex_forms(cls, value):
45
+ # type: (str) -> Optional[Scope]
46
+
47
+ project_spec, sep, marker_value = value.partition(";")
48
+ try:
49
+ project = ProjectName(
50
+ project_spec, validated=True
51
+ ) # type: Union[ProjectName, Pattern[str]]
52
+ except ProjectName.InvalidError:
53
+ try:
54
+ project = re.compile(project_spec)
55
+ except re.error:
56
+ return None
57
+
58
+ marker = None # type: Optional[Marker]
59
+ if marker_value:
60
+ try:
61
+ marker = Marker(marker_value)
62
+ except InvalidMarker:
63
+ return None
64
+ return cls(project=project, marker=marker)
65
+
66
+ @classmethod
67
+ def parse(cls, value):
68
+ # type: (str) -> Scope
69
+
70
+ if not value:
71
+ return Scope()
72
+
73
+ def create_invalid_error(footer=None):
74
+ # type: (Optional[str]) -> Exception
75
+ error_msg_lines = [
76
+ "The given scope is invalid: {scope}".format(scope=value),
77
+ "Expected a bare project name-or-regex, a bare marker or a project name-or-regex "
78
+ "and marker; e.g.: `torch; sys_platform != 'darwin'`.",
79
+ ]
80
+ if footer:
81
+ error_msg_lines.append(footer)
82
+ return ValueError(os.linesep.join(error_msg_lines))
83
+
84
+ try:
85
+ return cls(marker=Marker(value))
86
+ except InvalidMarker:
87
+ scope = cls._parse_regex_forms(value)
88
+ if scope:
89
+ return scope
90
+
91
+ try:
92
+ requirement = Requirement.parse(value)
93
+ except RequirementParseError:
94
+ raise create_invalid_error()
95
+ if requirement.extras:
96
+ raise create_invalid_error(
97
+ "The specified project name {project_name} has extras that should be removed: "
98
+ "{extras}".format(
99
+ project_name=requirement.project_name.raw,
100
+ extras=", ".join(sorted(requirement.extras)),
101
+ )
102
+ )
103
+ if requirement.specifier:
104
+ raise create_invalid_error(
105
+ "The specified project name {project_name} has a version specifier that should "
106
+ "be removed: {specifier}".format(
107
+ project_name=requirement.project_name.raw, specifier=requirement.specifier
108
+ )
109
+ )
110
+ return cls(project=requirement.project_name, marker=requirement.marker)
111
+
112
+ project = attr.ib(default=None) # type: Optional[Union[ProjectName, Pattern[str]]]
113
+ marker = attr.ib(default=None) # type: Optional[Marker]
114
+
115
+ def in_scope(
116
+ self,
117
+ target_env, # type: Union[Dict[str, str], MarkerEnv]
118
+ project_name=None, # type: Optional[ProjectName]
119
+ ):
120
+ # type: (...) -> bool
121
+
122
+ if self.marker:
123
+ if isinstance(target_env, dict) and not self.marker.evaluate(target_env):
124
+ return False
125
+ elif isinstance(target_env, MarkerEnv) and not target_env.evaluate(self.marker):
126
+ return False
127
+
128
+ if not self.project or not project_name:
129
+ return True
130
+
131
+ if isinstance(self.project, ProjectName):
132
+ return self.project == project_name
133
+
134
+ return cast("Pattern", self.project).match(project_name.normalized) is not None
135
+
136
+ def _tup(self):
137
+ # type: () -> Tuple[Optional[str], Optional[str]]
138
+
139
+ project = None # type: Optional[str]
140
+ if isinstance(self.project, ProjectName):
141
+ project = self.project.normalized
142
+ elif self.project:
143
+ # N.B.: The object returned from older versions of Python's `re.compile` do not
144
+ # implement __eq__; so we use the input pattern string as a proxy.
145
+ project = self.project.pattern
146
+
147
+ marker = None # type: Optional[str]
148
+ if self.marker:
149
+ # N.B.: Older versions of `Marker` do not implement __eq__; so we use str(...) as a
150
+ # proxy.
151
+ marker = str(self.marker)
152
+
153
+ return project, marker
154
+
155
+ def __hash__(self):
156
+ # type: () -> int
157
+ return hash(self._tup())
158
+
159
+ def __eq__(self, other):
160
+ # type: (Any) -> bool
161
+
162
+ if not isinstance(other, Scope):
163
+ return NotImplemented
164
+
165
+ return self._tup() == other._tup()
166
+
167
+ def __ne__(self, other):
168
+ return not self == other
169
+
170
+ def __str__(self):
171
+ # type: () -> str
172
+
173
+ project_as_str = None # type: Optional[str]
174
+ if isinstance(self.project, ProjectName):
175
+ project_as_str = self.project.raw
176
+ elif self.project:
177
+ project_as_str = self.project.pattern
178
+
179
+ if project_as_str and self.marker:
180
+ return "{project}; {marker}".format(project=project_as_str, marker=self.marker)
181
+ if project_as_str:
182
+ return project_as_str
183
+ if self.marker:
184
+ return str(self.marker)
185
+ return ""
186
+
187
+
188
+ @attr.s(frozen=True)
189
+ class Repo(object):
190
+ @classmethod
191
+ def from_dict(cls, data):
192
+ # type: (Dict[str, Any]) -> Repo
193
+ return cls(
194
+ location=data["location"], scopes=tuple(Scope.parse(scope) for scope in data["scopes"])
195
+ )
196
+
197
+ location = attr.ib() # type: Text
198
+ scopes = attr.ib(default=()) # type: Tuple[Scope, ...]
199
+
200
+ def as_dict(self):
201
+ # type: () -> Dict[str, Any]
202
+ return {"location": self.location, "scopes": [str(scope) for scope in self.scopes]}
203
+
204
+ def in_scope(
205
+ self,
206
+ target_env, # type: Union[Dict[str, str], MarkerEnv]
207
+ project_name=None, # type: Optional[ProjectName]
208
+ ):
209
+ # type: (...) -> bool
210
+ if not self.scopes:
211
+ return True
212
+ return any(scope.in_scope(target_env, project_name=project_name) for scope in self.scopes)
213
+
214
+
215
+ PYPI = "https://pypi.org/simple"
216
+
217
+
218
+ @attr.s(frozen=True)
219
+ class PackageRepositories(object):
220
+ @classmethod
221
+ def from_dict(cls, data):
222
+ # type: (Dict[str, Any]) -> PackageRepositories
223
+
224
+ markers = data.get("markers")
225
+ universal_markers_data = data.get("universal_markers")
226
+ production_assert(bool(markers) ^ bool(universal_markers_data))
227
+ if markers:
228
+ if not isinstance(markers, dict) or not all(
229
+ isinstance(key, string) and isinstance(value, string)
230
+ for key, value in markers.items()
231
+ ):
232
+ raise AssertionError(reportable_unexpected_error_msg())
233
+ target_env = markers # type: Union[Dict[str, str], MarkerEnv]
234
+ else:
235
+ if not isinstance(universal_markers_data, dict) or not all(
236
+ isinstance(key, string) for key in universal_markers_data
237
+ ):
238
+ raise AssertionError(reportable_unexpected_error_msg())
239
+ target_env = MarkerEnv.from_dict(universal_markers_data)
240
+
241
+ return cls(
242
+ target_env=target_env,
243
+ global_indexes=tuple(data["global_indexes"]),
244
+ global_find_links=tuple(data["global_find_links"]),
245
+ scoped_indexes=tuple(Repo.from_dict(index) for index in data["scoped_indexes"]),
246
+ scoped_find_links=tuple(
247
+ Repo.from_dict(find_links) for find_links in data["scoped_find_links"]
248
+ ),
249
+ )
250
+
251
+ _target_env = attr.ib() # type: Union[Dict[str, str], MarkerEnv]
252
+ _scoped_indexes = attr.ib(default=()) # type: Tuple[Repo, ...]
253
+ _scoped_find_links = attr.ib(default=()) # type: Tuple[Repo, ...]
254
+ global_indexes = attr.ib(default=(Repo(PYPI),)) # type: Tuple[Text, ...]
255
+ global_find_links = attr.ib(default=()) # type: Tuple[Text, ...]
256
+
257
+ @property
258
+ def has_scoped_repositories(self):
259
+ # type: () -> bool
260
+ return len(self._scoped_indexes) > 0 or len(self._scoped_find_links) > 0
261
+
262
+ def as_dict(self):
263
+ # type: () -> Dict[str, Any]
264
+ return {
265
+ "markers": self._target_env if isinstance(self._target_env, dict) else None,
266
+ "universal_markers": (
267
+ self._target_env.as_dict() if isinstance(self._target_env, MarkerEnv) else None
268
+ ),
269
+ "global_indexes": list(self.global_indexes),
270
+ "global_find_links": list(self.global_find_links),
271
+ "scoped_indexes": [index.as_dict() for index in self._scoped_indexes],
272
+ "scoped_find_links": [find_links.as_dict() for find_links in self._scoped_find_links],
273
+ }
274
+
275
+ def _in_scope_repos(
276
+ self,
277
+ scoped_repos, # type: Iterable[Repo]
278
+ project_name, # type: ProjectName
279
+ ):
280
+ # type: (...) -> List[Text]
281
+ return [
282
+ repo.location
283
+ for repo in scoped_repos
284
+ if repo.in_scope(target_env=self._target_env, project_name=project_name)
285
+ ]
286
+
287
+ def in_scope_indexes(self, project_name):
288
+ # type: (ProjectName) -> List[Text]
289
+ return self._in_scope_repos(scoped_repos=self._scoped_indexes, project_name=project_name)
290
+
291
+ def in_scope_find_links(self, project_name):
292
+ # type: (ProjectName) -> List[Text]
293
+ return self._in_scope_repos(scoped_repos=self._scoped_find_links, project_name=project_name)
294
+
295
+
296
+ @attr.s(frozen=True)
297
+ class ReposConfiguration(object):
298
+ @classmethod
299
+ def create(
300
+ cls,
301
+ indexes=(), # type: Iterable[Repo]
302
+ find_links=(), # type: Iterable[Repo]
303
+ derive_scopes_from_requirements_files=False, # type: bool
304
+ ):
305
+ # type: (...) -> ReposConfiguration
306
+ password_entries = []
307
+ for repo in itertools.chain(indexes, find_links):
308
+ password_entry = PasswordEntry.maybe_extract_from_url(repo.location)
309
+ if password_entry:
310
+ password_entries.append(password_entry)
311
+
312
+ return cls(
313
+ index_repos=tuple(indexes),
314
+ find_links_repos=tuple(find_links),
315
+ password_entries=tuple(password_entries),
316
+ derive_scopes_from_requirements_files=derive_scopes_from_requirements_files,
317
+ )
318
+
319
+ index_repos = attr.ib(default=(Repo(PYPI),)) # type: Tuple[Repo, ...]
320
+ find_links_repos = attr.ib(default=()) # type: Tuple[Repo, ...]
321
+ password_entries = attr.ib(default=()) # type: Tuple[PasswordEntry, ...]
322
+ derive_scopes_from_requirements_files = attr.ib(default=False) # type: bool
323
+
324
+ def with_contained_repos(
325
+ self,
326
+ requirement_files=None, # type: Optional[Iterable[Text]]
327
+ fetcher=None, # type: Optional[URLFetcher]
328
+ ):
329
+ # type: (...) -> ReposConfiguration
330
+
331
+ if not requirement_files:
332
+ return self
333
+
334
+ indexes_by_source = OrderedDict() # type: OrderedDict[Text, OrderedSet[Text]]
335
+ find_links_by_source = OrderedDict() # type: OrderedDict[Text, OrderedSet[Text]]
336
+ scopes_by_source = defaultdict(OrderedSet) # type: DefaultDict[Text, OrderedSet[Scope]]
337
+ for item in itertools.chain.from_iterable(
338
+ parse_requirement_file(requirement_file, fetcher=fetcher)
339
+ for requirement_file in requirement_files
340
+ ):
341
+ if self.derive_scopes_from_requirements_files and isinstance(
342
+ item, (PyPIRequirement, URLRequirement, VCSRequirement)
343
+ ):
344
+ scopes_by_source[item.line.source].add(
345
+ Scope(project=item.requirement.project_name, marker=item.requirement.marker)
346
+ )
347
+ elif isinstance(item, FindLinks):
348
+ find_links_by_source.setdefault(item.line.source, OrderedSet()).add(item.location)
349
+ elif isinstance(item, Index):
350
+ indexes_by_source.setdefault(item.line.source, OrderedSet()).add(item.location)
351
+
352
+ if not indexes_by_source and not find_links_by_source:
353
+ return self
354
+
355
+ def merge_scopes(
356
+ repos, # type: Iterable[Repo]
357
+ locations_by_source, # type: Mapping[Text, Iterable[Text]]
358
+ ):
359
+ scopes_by_location = OrderedDict(
360
+ (repo.location, OrderedSet(repo.scopes)) for repo in repos
361
+ )
362
+ for source, locations in locations_by_source.items():
363
+ for location in locations:
364
+ scopes_by_location.setdefault(location, OrderedSet()).update(
365
+ scopes_by_source[source]
366
+ )
367
+ return tuple(
368
+ Repo(location=location, scopes=tuple(scopes))
369
+ for location, scopes in scopes_by_location.items()
370
+ )
371
+
372
+ return attr.evolve(
373
+ self,
374
+ index_repos=merge_scopes(repos=self.index_repos, locations_by_source=indexes_by_source),
375
+ find_links_repos=merge_scopes(
376
+ repos=self.find_links_repos, locations_by_source=find_links_by_source
377
+ ),
378
+ )
379
+
380
+ @property
381
+ def indexes(self):
382
+ # type: () -> Tuple[Text, ...]
383
+ return tuple(repo.location for repo in self.index_repos if not repo.scopes)
384
+
385
+ @property
386
+ def find_links(self):
387
+ # type: () -> Tuple[Text, ...]
388
+ return tuple(repo.location for repo in self.find_links_repos if not repo.scopes)
389
+
390
+ def scoped(self, target_env):
391
+ # type: (Union[Dict[str, str], MarkerEnv]) -> PackageRepositories
392
+ return PackageRepositories(
393
+ target_env=target_env,
394
+ global_indexes=self.indexes,
395
+ global_find_links=self.find_links,
396
+ scoped_indexes=tuple(
397
+ index
398
+ for index in self.index_repos
399
+ if index.scopes and index.in_scope(target_env=target_env)
400
+ ),
401
+ scoped_find_links=tuple(
402
+ find_links
403
+ for find_links in self.find_links_repos
404
+ if find_links.scopes and find_links.in_scope(target_env=target_env)
405
+ ),
406
+ )
@@ -80,7 +80,7 @@ def resolve_from_pex(
80
80
  distributions = OrderedSet() # type: OrderedSet[ResolvedDistribution]
81
81
  for target in targets.unique_targets():
82
82
  # TODO(John Sirois): Handling of result type should be centralized. As it stands, it's
83
- # currently critical to _not_ PEXEnvironment.mount(...) if you want to resolve whell files
83
+ # currently critical to _not_ PEXEnvironment.mount(...) if you want to resolve wheel files
84
84
  # instead of installed wheel chroots.
85
85
  pex_env = (
86
86
  PEXEnvironment(pex, target=target)
@@ -39,7 +39,7 @@ from pex.typing import TYPE_CHECKING, cast
39
39
  from pex.util import CacheHelper
40
40
 
41
41
  if TYPE_CHECKING:
42
- from typing import DefaultDict, Dict, Iterable, List, Tuple
42
+ from typing import DefaultDict, Dict, List, Sequence, Tuple
43
43
 
44
44
 
45
45
  def _fingerprint_dist(dist_path):
@@ -49,8 +49,8 @@ def _fingerprint_dist(dist_path):
49
49
 
50
50
  def resolve_from_dists(
51
51
  targets, # type: Targets
52
- sdists, # type: Iterable[str]
53
- wheels, # type: Iterable[str]
52
+ sdists, # type: Sequence[str]
53
+ wheels, # type: Sequence[str]
54
54
  requirement_configuration, # type: RequirementConfiguration
55
55
  pip_configuration=PipConfiguration(), # type: PipConfiguration
56
56
  compile=False, # type: bool
@@ -70,9 +70,6 @@ def resolve_from_dists(
70
70
  if isinstance(direct_requirement, LocalProjectRequirement):
71
71
  local_projects.append(direct_requirement)
72
72
 
73
- source_paths = [local_project.path for local_project in local_projects] + list(
74
- sdists
75
- ) # type: List[str]
76
73
  with TRACER.timed("Fingerprinting pre-resolved wheels"):
77
74
  fingerprinted_wheels = tuple(
78
75
  FingerprintedDistribution(
@@ -94,27 +91,33 @@ def resolve_from_dists(
94
91
  fingerprinted_wheels and InstallableType.INSTALLED_WHEEL_CHROOT is result_type
95
92
  )
96
93
  with TRACER.timed("Preparing pre-resolved distributions"):
97
- if source_paths or resolve_installed_wheel_chroots:
94
+ if sdists or local_projects or resolve_installed_wheel_chroots:
98
95
  package_index_configuration = PackageIndexConfiguration.create(
99
96
  pip_version=pip_configuration.version,
100
97
  resolver_version=pip_configuration.resolver_version,
101
- indexes=pip_configuration.repos_configuration.indexes,
102
- find_links=pip_configuration.repos_configuration.find_links,
98
+ repos_configuration=pip_configuration.repos_configuration,
103
99
  network_configuration=pip_configuration.network_configuration,
104
- password_entries=pip_configuration.repos_configuration.password_entries,
105
100
  use_pip_config=pip_configuration.use_pip_config,
106
101
  extra_pip_requirements=pip_configuration.extra_requirements,
107
102
  keyring_provider=pip_configuration.keyring_provider,
108
103
  )
104
+ build_requests = [
105
+ BuildRequest.for_file(target=target, source_path=sdist)
106
+ for sdist in sdists
107
+ for target in unique_targets
108
+ ]
109
+ build_requests.extend(
110
+ BuildRequest.for_directory(target=target, source_path=local_project.path)
111
+ for local_project in local_projects
112
+ for target in unique_targets
113
+ )
109
114
  build_and_install = BuildAndInstallRequest(
110
- build_requests=[
111
- BuildRequest.create(target=target, source_path=source_path)
112
- for source_path in source_paths
113
- for target in unique_targets
114
- ],
115
+ build_requests=build_requests,
115
116
  install_requests=[
116
117
  InstallRequest(
117
- target=target, wheel_path=wheel.location, fingerprint=wheel.fingerprint
118
+ download_target=target,
119
+ wheel_path=wheel.location,
120
+ fingerprint=wheel.fingerprint,
118
121
  )
119
122
  for wheel in fingerprinted_wheels
120
123
  for target in unique_targets