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,908 @@
1
+ # Copyright 2021 Pex project contributors.
2
+ # Licensed under the Apache License, Version 2.0 (see LICENSE).
3
+
4
+ from __future__ import absolute_import
5
+
6
+ import operator
7
+ import sys
8
+
9
+ from pex import pex_warnings
10
+ from pex.common import pluralize
11
+ from pex.compatibility import string
12
+ from pex.enum import Enum
13
+ from pex.exceptions import production_assert, reportable_unexpected_error_msg
14
+ from pex.interpreter_constraints import InterpreterConstraint, iter_compatible_versions
15
+ from pex.interpreter_implementation import InterpreterImplementation
16
+ from pex.orderedset import OrderedSet
17
+ from pex.os import LINUX, MAC, WINDOWS
18
+ from pex.pep_503 import ProjectName
19
+ from pex.sorted_tuple import SortedTuple
20
+ from pex.third_party.packaging.markers import Marker, Variable
21
+ from pex.third_party.packaging.specifiers import Specifier, SpecifierSet
22
+ from pex.typing import TYPE_CHECKING, Generic, cast
23
+
24
+ if TYPE_CHECKING:
25
+ from typing import (
26
+ Any,
27
+ Callable,
28
+ Dict,
29
+ Iterable,
30
+ Iterator,
31
+ List,
32
+ Mapping,
33
+ Optional,
34
+ Sequence,
35
+ Tuple,
36
+ TypeVar,
37
+ Union,
38
+ )
39
+
40
+ import attr # vendor:skip
41
+ else:
42
+ from pex.third_party import attr
43
+
44
+
45
+ class TargetSystem(Enum["TargetSystem.Value"]):
46
+ class Value(Enum.Value):
47
+ pass
48
+
49
+ LINUX = Value("linux")
50
+ MAC = Value("mac")
51
+ WINDOWS = Value("windows")
52
+
53
+ @classmethod
54
+ def current(cls):
55
+ # type: () -> TargetSystem.Value
56
+ if LINUX:
57
+ return TargetSystem.LINUX
58
+ elif MAC:
59
+ return TargetSystem.MAC
60
+ elif WINDOWS:
61
+ return TargetSystem.WINDOWS
62
+ raise AssertionError(reportable_unexpected_error_msg("Unexpected os {os}", os=sys.platform))
63
+
64
+
65
+ TargetSystem.seal()
66
+
67
+
68
+ def _marker_items(marker):
69
+ # type:(Marker) -> Iterable[Any]
70
+
71
+ marker_items = getattr(marker, "_markers", None)
72
+ if marker_items is None:
73
+ raise AssertionError(
74
+ reportable_unexpected_error_msg(
75
+ "Expected packaging.markers.Marker to have a _markers attribute; found none in "
76
+ "{marker} of type {type}",
77
+ marker=marker,
78
+ type=type(marker).__name__,
79
+ )
80
+ )
81
+ production_assert(
82
+ hasattr(marker_items, "__iter__"),
83
+ "Expected packaging.markers.Marker._markers to be iterable; found {marker_items} of type "
84
+ "{type}",
85
+ marker_items=marker_items,
86
+ type=type(marker_items).__name__,
87
+ )
88
+ return cast("Iterable[Any]", marker._markers)
89
+
90
+
91
+ class MarkerVisitor(Generic["_C"]):
92
+ def visit_and(
93
+ self,
94
+ marker, # type: Marker
95
+ context, # type: _C
96
+ ):
97
+ # type: (...) -> None
98
+ pass
99
+
100
+ def visit_or(
101
+ self,
102
+ marker, # type: Marker
103
+ context, # type: _C
104
+ ):
105
+ # type: (...) -> None
106
+ pass
107
+
108
+ def begin_visit_group(
109
+ self,
110
+ group, # type: List[Any]
111
+ marker, # type: Marker
112
+ context, # type: _C
113
+ ):
114
+ # type: (...) -> Optional[_C]
115
+ return None
116
+
117
+ def end_visit_group(
118
+ self,
119
+ group, # type: List[Any]
120
+ marker, # type: Marker
121
+ context, # type: _C
122
+ group_context, # type: Optional[_C]
123
+ ):
124
+ # type: (...) -> None
125
+ pass
126
+
127
+ def visit_op(
128
+ self,
129
+ lhs, # type: Any
130
+ op, # type: Any
131
+ rhs, # type: Any
132
+ marker, # type: Marker
133
+ context, # type: _C
134
+ ):
135
+ # type: (...) -> None
136
+ pass
137
+
138
+
139
+ if TYPE_CHECKING:
140
+ _C = TypeVar("_C")
141
+
142
+
143
+ class MarkerParser(Generic["_C"]):
144
+ def __init__(self, visitor):
145
+ # type: (MarkerVisitor["_C"]) -> None
146
+ self._visitor = visitor
147
+
148
+ def _parse_marker_item(
149
+ self,
150
+ item, # type: Union[str, List, Tuple]
151
+ marker, # type: Marker
152
+ context, # type: _C
153
+ ):
154
+ # type: (...) -> None
155
+
156
+ if item == "and":
157
+ self._visitor.visit_and(marker, context)
158
+ elif item == "or":
159
+ self._visitor.visit_or(marker, context)
160
+ elif isinstance(item, list):
161
+ group_context = self._visitor.begin_visit_group(item, marker, context)
162
+ element_context = group_context if group_context is not None else context
163
+ for element in item:
164
+ self._parse_marker_item(element, marker, element_context)
165
+ self._visitor.end_visit_group(item, marker, context, group_context)
166
+ elif isinstance(item, tuple):
167
+ lhs, op, rhs = item
168
+ self._visitor.visit_op(lhs, op, rhs, marker, context)
169
+ else:
170
+ raise ValueError("Marker is invalid: {marker}".format(marker=marker))
171
+
172
+ def parse(
173
+ self,
174
+ marker, # type: Marker
175
+ context, # type: _C
176
+ ):
177
+ # type: (...) -> _C
178
+
179
+ for item in _marker_items(marker):
180
+ self._parse_marker_item(item, marker, context)
181
+ return context
182
+
183
+
184
+ class HasMarkerVisitor(MarkerVisitor[None]):
185
+ def __init__(self, name):
186
+ # type: (str) -> None
187
+ self._name = name
188
+ self.has_marker = False
189
+
190
+ def visit_op(
191
+ self,
192
+ lhs, # type: Any
193
+ op, # type: Any
194
+ rhs, # type: Any
195
+ marker, # type: Marker
196
+ context, # type: None
197
+ ):
198
+ # type: (...) -> None
199
+ if self.has_marker:
200
+ return
201
+
202
+ for term in lhs, rhs:
203
+ if is_variable(term) and self._name == str(term):
204
+ self.has_marker = True
205
+ break
206
+
207
+
208
+ def has_marker(
209
+ marker, # type: Marker
210
+ name, # type: str
211
+ ):
212
+ # type: (...) -> bool
213
+
214
+ visitor = HasMarkerVisitor(name)
215
+ MarkerParser(visitor).parse(marker, None)
216
+ return visitor.has_marker
217
+
218
+
219
+ if TYPE_CHECKING:
220
+ EvalMarker = Callable[["MarkerEnv"], bool]
221
+
222
+
223
+ _OPERATORS = {
224
+ "in": lambda lhs, rhs: lhs in rhs,
225
+ "not in": lambda lhs, rhs: lhs not in rhs,
226
+ "<": operator.lt,
227
+ "<=": operator.le,
228
+ "==": operator.eq,
229
+ "!=": operator.ne,
230
+ ">=": operator.ge,
231
+ ">": operator.gt,
232
+ } # type: Mapping[str, Callable[[Any, Any], bool]]
233
+
234
+
235
+ _VERSION_MARKER_OP_FLIPPED = {
236
+ "<": ">",
237
+ "<=": ">=",
238
+ ">=": "<=",
239
+ ">": "<",
240
+ }
241
+
242
+ _VERSION_CMP_OPS = {
243
+ "<",
244
+ "<=",
245
+ "==",
246
+ "!=",
247
+ ">=",
248
+ ">",
249
+ "~=",
250
+ "===",
251
+ }
252
+
253
+
254
+ class _Op(object):
255
+ def __init__(self, lhs):
256
+ self.lhs = lhs # type: EvalMarker
257
+ self.rhs = None # type: Optional[EvalMarker]
258
+
259
+
260
+ class _And(_Op):
261
+ def __call__(self, marker_env):
262
+ # type: (MarkerEnv) -> bool
263
+ production_assert(self.rhs is not None)
264
+ return self.lhs(marker_env) and cast("EvalMarker", self.rhs)(marker_env)
265
+
266
+
267
+ class _Or(_Op):
268
+ def __call__(self, marker_env):
269
+ # type: (MarkerEnv) -> bool
270
+ production_assert(self.rhs is not None)
271
+ return self.lhs(marker_env) or cast("EvalMarker", self.rhs)(marker_env)
272
+
273
+
274
+ @attr.s(frozen=True)
275
+ class Values(object):
276
+ @classmethod
277
+ def from_dict(cls, data):
278
+ # type: (Dict[str, Any]) -> Values
279
+ return cls(
280
+ marker_name=data.pop("marker_name"),
281
+ values=tuple(data.pop("values", ())),
282
+ inclusive=data.pop("inclusive", True),
283
+ )
284
+
285
+ marker_name = attr.ib() # type: str
286
+ values = attr.ib(default=()) # type: Tuple[str, ...]
287
+ inclusive = attr.ib(default=True) # type: bool
288
+
289
+ def to_dict(self):
290
+ # type: () -> Dict[str, Any]
291
+ return {"marker_name": self.marker_name, "values": self.values, "inclusive": self.inclusive}
292
+
293
+ def apply(self, func):
294
+ # type: (Callable[[str], bool]) -> bool
295
+ if len(self.values) == 0:
296
+ return self.marker_name != "extra"
297
+ if self.inclusive:
298
+ return any(map(func, self.values))
299
+ return all((not result) for result in map(func, self.values))
300
+
301
+ def __len__(self):
302
+ # type: () -> int
303
+ return len(self.values)
304
+
305
+ def __iter__(self):
306
+ # type: () -> Iterator[str]
307
+ return iter(self.values)
308
+
309
+
310
+ def _get_values_func(marker_name):
311
+ # type: (str) -> Callable[[MarkerEnv], Values]
312
+
313
+ if marker_name == "extra":
314
+ return lambda marker_env: Values(marker_name, marker_env.extras)
315
+ elif marker_name == "os_name":
316
+ return lambda marker_env: Values(marker_name, marker_env.os_names)
317
+ elif marker_name == "platform_system":
318
+ return lambda marker_env: Values(marker_name, marker_env.platform_systems)
319
+ elif marker_name == "sys_platform":
320
+ return lambda marker_env: Values(marker_name, marker_env.sys_platforms)
321
+ elif marker_name == "platform_python_implementation":
322
+ return lambda marker_env: Values(marker_name, marker_env.platform_python_implementations)
323
+ elif marker_name == "python_version":
324
+ return lambda marker_env: Values(marker_name, marker_env.python_versions)
325
+ elif marker_name == "python_full_version":
326
+ return lambda marker_env: Values(marker_name, marker_env.python_full_versions)
327
+ return lambda marker_env: marker_env.extra_markers.get_values(marker_name)
328
+
329
+
330
+ class UniversalMarkerVisitor(MarkerVisitor["List[EvalMarker]"]):
331
+ @classmethod
332
+ def parse_marker(cls, marker):
333
+ # type: (Marker) -> EvalMarker
334
+
335
+ checks = MarkerParser(cls()).parse(marker, context=[])
336
+ production_assert(len(checks) == 1)
337
+ return checks[0]
338
+
339
+ def visit_and(
340
+ self,
341
+ marker, # type: Marker
342
+ context, # type: List[EvalMarker]
343
+ ):
344
+ # type: (...) -> None
345
+ context.append(_And(context.pop()))
346
+
347
+ def visit_or(
348
+ self,
349
+ marker, # type: Marker
350
+ context, # type: List[EvalMarker]
351
+ ):
352
+ # type: (...) -> None
353
+ context.append(_Or(context.pop()))
354
+
355
+ def begin_visit_group(
356
+ self,
357
+ group, # type: List[Any]
358
+ marker, # type: Marker
359
+ context, # type: List[EvalMarker]
360
+ ):
361
+ # type: (...) -> Optional[List[EvalMarker]]
362
+ return []
363
+
364
+ def end_visit_group(
365
+ self,
366
+ group, # type: List[Any]
367
+ marker, # type: Marker
368
+ context, # type: List[EvalMarker]
369
+ group_context, # type: Optional[List[EvalMarker]]
370
+ ):
371
+ # type: (...) -> None
372
+
373
+ if group_context is None or len(group_context) != 1:
374
+ raise AssertionError(reportable_unexpected_error_msg())
375
+
376
+ if context:
377
+ production_assert(isinstance(context[-1], _Op))
378
+ cast(_Op, context[-1]).rhs = group_context[0]
379
+ else:
380
+ context.extend(group_context)
381
+
382
+ def visit_op(
383
+ self,
384
+ lhs, # type: Any
385
+ op, # type: Any
386
+ rhs, # type: Any
387
+ marker, # type: Marker
388
+ context, # type: List[EvalMarker]
389
+ ):
390
+ # type: (...) -> None
391
+
392
+ check = EvalMarkerFunc.create(lhs, op, rhs)
393
+ if context:
394
+ production_assert(isinstance(context[-1], _Op))
395
+ cast(_Op, context[-1]).rhs = check
396
+ else:
397
+ context.append(check)
398
+
399
+
400
+ _MARKER_CHECKS = {} # type: Dict[Union[Marker, str], EvalMarker]
401
+
402
+
403
+ def _parse_marker(marker):
404
+ # type: (Marker) -> EvalMarker
405
+ eval_marker = _MARKER_CHECKS.get(marker)
406
+ if not eval_marker:
407
+ marker_str = str(marker)
408
+ eval_marker = _MARKER_CHECKS.get(marker_str)
409
+ if not eval_marker:
410
+ eval_marker = UniversalMarkerVisitor.parse_marker(marker)
411
+ _MARKER_CHECKS[marker] = eval_marker
412
+ _MARKER_CHECKS[marker_str] = eval_marker
413
+ return eval_marker
414
+
415
+
416
+ def is_variable(value):
417
+ # type: (Any) -> bool
418
+
419
+ if isinstance(value, Variable):
420
+ return True
421
+
422
+ # N.B.: This allows interop with Pip vendored packaging which has the same types in a different
423
+ # namespace.
424
+ return type(value).__name__ == "Variable"
425
+
426
+
427
+ class EvalMarkerFunc(object):
428
+ @classmethod
429
+ def create(
430
+ cls,
431
+ lhs, # type: Any
432
+ op, # type: Any
433
+ rhs, # type: Any
434
+ ):
435
+ # type: (...) -> Callable[[MarkerEnv], bool]
436
+
437
+ for var, operand, operand_side in ((lhs, rhs, "rhs"), (rhs, lhs, "lhs")):
438
+ if not is_variable(var):
439
+ continue
440
+ marker_name = str(var)
441
+ get_values = _get_values_func(marker_name)
442
+ value = str(operand)
443
+ if marker_name == "extra":
444
+ value = ProjectName(value).normalized
445
+ op_string = str(op)
446
+ operand_side_arg = {operand_side: value}
447
+ return cls(
448
+ get_values=get_values,
449
+ op=op_string,
450
+ is_version_comparison=(
451
+ marker_name
452
+ in ("python_version", "python_full_version", "implementation_version")
453
+ and op_string in _VERSION_CMP_OPS
454
+ ),
455
+ **operand_side_arg
456
+ )
457
+
458
+ return lambda _: True
459
+
460
+ def __init__(
461
+ self,
462
+ get_values, # type: Callable[[MarkerEnv], Values]
463
+ op, # type: str
464
+ lhs=None, # type: Optional[str]
465
+ rhs=None, # type: Optional[str]
466
+ is_version_comparison=False, # type: bool
467
+ ):
468
+ # type: (...) -> None
469
+
470
+ if lhs is not None:
471
+ if is_version_comparison:
472
+ flipped_op = _VERSION_MARKER_OP_FLIPPED.get(op, op)
473
+ version_specifier = Specifier(
474
+ "{flipped_op}{lhs}".format(lhs=lhs, flipped_op=flipped_op)
475
+ )
476
+ self._func = lambda value: cast(
477
+ bool,
478
+ version_specifier.contains(
479
+ # N.B.: This handles `implementation_version` for Python dev releases in the
480
+ # same way `packaging` does.
481
+ (value + "local") if value.endswith("+") else value,
482
+ prereleases=True,
483
+ ),
484
+ )
485
+ else:
486
+ oper = _OPERATORS[op]
487
+ self._func = lambda value: oper(lhs, value)
488
+ elif rhs is not None:
489
+ if is_version_comparison:
490
+ version_specifier = Specifier("{op}{rhs}".format(op=op, rhs=rhs))
491
+ self._func = lambda value: cast(
492
+ bool,
493
+ version_specifier.contains(
494
+ # N.B.: This handles `implementation_version` for Python dev releases in the
495
+ # same way `packaging` does.
496
+ (value + "local") if value.endswith("+") else value,
497
+ prereleases=True,
498
+ ),
499
+ )
500
+ else:
501
+ oper = _OPERATORS[op]
502
+ self._func = lambda value: oper(value, rhs)
503
+ else:
504
+ raise ValueError(
505
+ "Must be called with exactly one of lhs or rhs but not both. "
506
+ "Given lhs={lhs} and rhs={rhs}".format(lhs=lhs, rhs=rhs)
507
+ )
508
+ self._get_values = get_values
509
+
510
+ def __call__(self, marker_env):
511
+ # type: (MarkerEnv) -> bool
512
+
513
+ return self._get_values(marker_env).apply(self._func)
514
+
515
+
516
+ class ExtraMarkersVisitor(MarkerVisitor[str]):
517
+ def __init__(self):
518
+ # type: () -> None
519
+ self.conflicts = OrderedSet() # type: OrderedSet[str]
520
+ self._marker_values = {} # type: Dict[str, Tuple[str, OrderedSet[str], bool]]
521
+
522
+ def visit_op(
523
+ self,
524
+ lhs, # type: Any
525
+ op, # type: Any
526
+ rhs, # type: Any
527
+ marker, # type: Marker
528
+ context, # type: str
529
+ ):
530
+ # type: (...) -> None
531
+
532
+ op_symbol = str(op)
533
+ if is_variable(lhs):
534
+ name = str(lhs)
535
+ value = str(rhs)
536
+ elif is_variable(rhs):
537
+ name = str(rhs)
538
+ value = str(lhs)
539
+ else:
540
+ return
541
+
542
+ if name in (
543
+ "extra",
544
+ "os_name",
545
+ "platform_system",
546
+ "sys_platform",
547
+ "platform_python_implementation",
548
+ "python_version",
549
+ "python_full_version",
550
+ ):
551
+ return
552
+
553
+ if op_symbol == "==":
554
+ requirement, values, inclusive = self._marker_values.setdefault(
555
+ name, (context, OrderedSet(), True)
556
+ )
557
+ if not inclusive:
558
+ self.conflicts.add(
559
+ "The requirement {context} includes {value} for {name} but the requirement "
560
+ "{requirement} established {name} as an exclusive set with values: "
561
+ "{values}.".format(
562
+ context=context,
563
+ value=value,
564
+ name=name,
565
+ requirement=requirement,
566
+ values=" ".join(values),
567
+ )
568
+ )
569
+ else:
570
+ values.add(value)
571
+ elif op_symbol == "!=":
572
+ requirement, values, inclusive = self._marker_values.setdefault(
573
+ name, (context, OrderedSet(), False)
574
+ )
575
+ if inclusive:
576
+ self.conflicts.add(
577
+ "The requirement {context} excludes {value} for {name} but the requirement "
578
+ "{requirement} established {name} as an inclusive set with values: "
579
+ "{values}.".format(
580
+ context=context,
581
+ value=value,
582
+ name=name,
583
+ requirement=requirement,
584
+ values=" ".join(values),
585
+ )
586
+ )
587
+ else:
588
+ values.add(value)
589
+ else:
590
+ pex_warnings.warn(
591
+ "Cannot split universal lock on all clauses of the marker in `{requirement}`.\n"
592
+ "The clause `{lhs} {op} {rhs}` uses comparison `{op}` but only `==` and `!=` are "
593
+ "supported for splitting on '{name}'.\n"
594
+ "Ignoring this clause in split calculations; lock results may be "
595
+ "unexpected.".format(requirement=context, lhs=lhs, op=op, rhs=rhs, name=name)
596
+ )
597
+
598
+ def marker_values(self):
599
+ # type: () -> Tuple[Values, ...]
600
+ return tuple(
601
+ Values(marker_name=name, values=tuple(values), inclusive=inclusive)
602
+ for name, (_, values, inclusive) in self._marker_values.items()
603
+ )
604
+
605
+
606
+ @attr.s(frozen=True)
607
+ class ExtraMarkers(object):
608
+ @classmethod
609
+ def extract(cls, requirements):
610
+ # type: (Iterable[Tuple[Marker, str]]) -> Optional[ExtraMarkers]
611
+
612
+ visitor = ExtraMarkersVisitor()
613
+ marker_parser = MarkerParser(visitor)
614
+
615
+ markers = [] # type: List[Marker]
616
+ for marker, provenance in requirements:
617
+ marker_parser.parse(marker, context=provenance)
618
+ markers.append(marker)
619
+
620
+ if visitor.conflicts:
621
+ raise ValueError(
622
+ "Encountered {count} {conflicts} when extracting universal lock splits from "
623
+ "top-level requirement markers:\n{items}".format(
624
+ count=len(visitor.conflicts),
625
+ conflicts=pluralize(visitor.conflicts, "conflict"),
626
+ items="\n".join(
627
+ "{index}. {conflict}".format(index=index, conflict=conflict)
628
+ for index, conflict in enumerate(visitor.conflicts, start=1)
629
+ ),
630
+ )
631
+ )
632
+
633
+ return cls(markers=tuple(markers), marker_values=visitor.marker_values())
634
+
635
+ @classmethod
636
+ def from_dict(cls, data):
637
+ # type: (Dict[str, Any]) -> ExtraMarkers
638
+ return cls(
639
+ markers=tuple(Marker(marker) for marker in data.pop("markers", ())),
640
+ marker_values=tuple(
641
+ Values.from_dict(marker_values) for marker_values in data.pop("marker_values", ())
642
+ ),
643
+ )
644
+
645
+ markers = attr.ib(default=()) # type: Tuple[Marker, ...]
646
+ marker_values = attr.ib(default=()) # type: Tuple[Values, ...]
647
+
648
+ def to_dict(self):
649
+ # type: () -> Dict[str, Any]
650
+ return {
651
+ "markers": [str(marker) for marker in self.markers],
652
+ "marker_values": [values.to_dict() for values in self.marker_values],
653
+ }
654
+
655
+ def get_values(self, marker_name):
656
+ # type: (str) -> Values
657
+ for values in self.marker_values:
658
+ if marker_name == values.marker_name:
659
+ return values
660
+ return Values(marker_name)
661
+
662
+ def __iter__(self):
663
+ # type: () -> Iterator[Marker]
664
+ return iter(self.markers)
665
+
666
+
667
+ @attr.s(frozen=True)
668
+ class MarkerEnv(object):
669
+ @classmethod
670
+ def from_dict(cls, data):
671
+ # type: (Dict[str, Any]) -> MarkerEnv
672
+ return cls(**data)
673
+
674
+ @classmethod
675
+ def create(
676
+ cls,
677
+ extras, # type: Iterable[str]
678
+ universal_target=None, # type: Optional[UniversalTarget]
679
+ ):
680
+ # type: (...) -> MarkerEnv
681
+
682
+ implementations = [] # type: List[InterpreterImplementation.Value]
683
+ if universal_target and universal_target.implementation:
684
+ implementations.append(universal_target.implementation)
685
+ else:
686
+ implementations.extend(InterpreterImplementation.values())
687
+
688
+ python_full_versions = (
689
+ list(iter_compatible_versions(universal_target.requires_python))
690
+ if universal_target
691
+ else []
692
+ )
693
+ python_versions = OrderedSet(
694
+ python_full_version[:2] for python_full_version in python_full_versions
695
+ )
696
+
697
+ os_names = []
698
+ platform_systems = []
699
+ sys_platforms = []
700
+ target_systems = universal_target.systems if universal_target else ()
701
+ for target_system in target_systems:
702
+ if target_system is TargetSystem.LINUX:
703
+ os_names.append("posix")
704
+ platform_systems.append("Linux")
705
+ sys_platforms.append("linux")
706
+ sys_platforms.append("linux2")
707
+ elif target_system is TargetSystem.MAC:
708
+ os_names.append("posix")
709
+ platform_systems.append("Darwin")
710
+ sys_platforms.append("darwin")
711
+ elif target_system is TargetSystem.WINDOWS:
712
+ os_names.append("nt")
713
+ platform_systems.append("Windows")
714
+ sys_platforms.append("win32")
715
+
716
+ return cls(
717
+ extras=SortedTuple(ProjectName(extra).normalized for extra in extras),
718
+ os_names=SortedTuple(os_names),
719
+ platform_systems=SortedTuple(platform_systems),
720
+ sys_platforms=SortedTuple(sys_platforms),
721
+ platform_python_implementations=SortedTuple(map(str, implementations)),
722
+ python_versions=SortedTuple(
723
+ ".".join(map(str, python_version)) for python_version in python_versions
724
+ ),
725
+ python_full_versions=SortedTuple(
726
+ ".".join(map(str, python_full_version))
727
+ for python_full_version in python_full_versions
728
+ ),
729
+ extra_markers=universal_target.extra_markers if universal_target else ExtraMarkers(),
730
+ )
731
+
732
+ extras = attr.ib() # type: SortedTuple[str]
733
+ os_names = attr.ib() # type: SortedTuple[str]
734
+ platform_systems = attr.ib() # type: SortedTuple[str]
735
+ sys_platforms = attr.ib() # type: SortedTuple[str]
736
+ platform_python_implementations = attr.ib() # type: SortedTuple[str]
737
+ python_versions = attr.ib() # type: SortedTuple[str]
738
+ python_full_versions = attr.ib() # type: SortedTuple[str]
739
+ extra_markers = attr.ib() # type: ExtraMarkers
740
+
741
+ def as_dict(self):
742
+ # type: () -> Dict[str, Any]
743
+ return attr.asdict(self)
744
+
745
+ def evaluate(self, marker):
746
+ # type: (Marker) -> bool
747
+ eval_marker = _parse_marker(marker)
748
+ return eval_marker(self)
749
+
750
+
751
+ def _as_platform_system_marker(system):
752
+ # type: (TargetSystem.Value) -> str
753
+
754
+ if system is TargetSystem.LINUX:
755
+ platform_system = "Linux"
756
+ elif system is TargetSystem.MAC:
757
+ platform_system = "Darwin"
758
+ else:
759
+ platform_system = "Windows"
760
+
761
+ return "platform_system == '{platform_system}'".format(platform_system=platform_system)
762
+
763
+
764
+ def _as_python_version_marker(specifier):
765
+ # type: (SpecifierSet) -> Optional[str]
766
+
767
+ clauses = [
768
+ "python_full_version {operator} '{version}'".format(
769
+ operator=spec.operator, version=spec.version
770
+ )
771
+ for spec in specifier
772
+ ]
773
+ if not clauses:
774
+ return None
775
+
776
+ if len(clauses) == 1:
777
+ return clauses[0]
778
+
779
+ return "({clauses})".format(clauses=" and ".join(clauses))
780
+
781
+
782
+ @attr.s(frozen=True)
783
+ class UniversalTarget(object):
784
+ @classmethod
785
+ def from_dict(cls, data):
786
+ # type: (Dict[str, Any]) -> UniversalTarget
787
+
788
+ raw_implementation = data.pop("implementation", None)
789
+ implementation = None # type: Optional[InterpreterImplementation.Value]
790
+ if raw_implementation:
791
+ if not isinstance(raw_implementation, string):
792
+ raise AssertionError(
793
+ reportable_unexpected_error_msg(
794
+ "Expected UniversalTarget `implementation` value to be a str, found "
795
+ "{value} of type {type}.",
796
+ value=raw_implementation,
797
+ type=type(raw_implementation),
798
+ )
799
+ )
800
+ implementation = InterpreterImplementation.for_value(raw_implementation)
801
+
802
+ return cls(
803
+ implementation=implementation,
804
+ requires_python=tuple(
805
+ SpecifierSet(specifier) for specifier in data.pop("requires_python", ())
806
+ ),
807
+ systems=tuple(TargetSystem.for_value(system) for system in data.pop("systems", ())),
808
+ extra_markers=ExtraMarkers.from_dict(data.pop("extra_markers", {})),
809
+ )
810
+
811
+ implementation = attr.ib(default=None) # type: Optional[InterpreterImplementation.Value]
812
+ requires_python = attr.ib(default=()) # type: Tuple[SpecifierSet, ...]
813
+ systems = attr.ib(default=()) # type: Tuple[TargetSystem.Value, ...]
814
+ extra_markers = attr.ib(default=ExtraMarkers()) # type: ExtraMarkers
815
+
816
+ def to_dict(self):
817
+ # type: () -> Dict[str, Any]
818
+ return {
819
+ "implementation": str(self.implementation) if self.implementation else None,
820
+ "requires_python": [str(specifier) for specifier in self.requires_python],
821
+ "systems": [str(system) for system in self.systems],
822
+ "extra_markers": self.extra_markers.to_dict(),
823
+ }
824
+
825
+ def iter_interpreter_constraints(self):
826
+ # type: () -> Iterator[InterpreterConstraint]
827
+ for specifier in self.requires_python:
828
+ yield InterpreterConstraint(specifier=specifier, implementation=self.implementation)
829
+
830
+ def are_exhaustive(self, markers):
831
+ # type: (Sequence[Marker]) -> bool
832
+
833
+ if len(markers) == 0:
834
+ return True
835
+
836
+ use_python_full_version = any(
837
+ has_marker(marker, "python_full_version") for marker in markers
838
+ )
839
+ python_full_versions = tuple(iter_compatible_versions(self.requires_python))
840
+ versions = (
841
+ python_full_versions
842
+ if use_python_full_version
843
+ else tuple(
844
+ OrderedSet(python_full_version[:2] for python_full_version in python_full_versions)
845
+ )
846
+ )
847
+ target_systems = self.systems or TargetSystem.values()
848
+ marker_envs = OrderedSet(
849
+ MarkerEnv.create(
850
+ extras=(),
851
+ universal_target=attr.evolve(
852
+ self,
853
+ requires_python=tuple(
854
+ [SpecifierSet("=={version}".format(version=".".join(map(str, version))))]
855
+ ),
856
+ systems=tuple([target_system]),
857
+ ),
858
+ )
859
+ for version in versions
860
+ for target_system in target_systems
861
+ )
862
+
863
+ for marker in markers:
864
+ eval_marker = _parse_marker(marker)
865
+ for marker_env in tuple(marker_envs):
866
+ if eval_marker(marker_env):
867
+ marker_envs.remove(marker_env)
868
+ return not marker_envs
869
+
870
+ def marker_env(self, *extras):
871
+ # type: (*str) -> MarkerEnv
872
+ return MarkerEnv.create(extras=extras, universal_target=self)
873
+
874
+ def marker(self):
875
+ # type: () -> Optional[Marker]
876
+ clauses = [] # type: List[str]
877
+ if self.systems and frozenset(self.systems) != frozenset(TargetSystem.values()):
878
+ if len(self.systems) == 1:
879
+ clauses.append(_as_platform_system_marker(self.systems[0]))
880
+ else:
881
+ clauses.append(
882
+ "({clauses})".format(
883
+ clauses=" or ".join(
884
+ _as_platform_system_marker(system) for system in self.systems
885
+ )
886
+ )
887
+ )
888
+ if self.implementation:
889
+ clauses.append(
890
+ "platform_python_implementation == '{implementation}'".format(
891
+ implementation=self.implementation
892
+ )
893
+ )
894
+ if len(self.requires_python) == 1:
895
+ python_version_marker = _as_python_version_marker(self.requires_python[0])
896
+ if python_version_marker:
897
+ clauses.append(python_version_marker)
898
+ elif self.requires_python:
899
+ python_version_markers = [] # type: List[str]
900
+ for requires_python in self.requires_python:
901
+ python_version_marker = _as_python_version_marker(requires_python)
902
+ if python_version_marker:
903
+ python_version_markers.append(python_version_marker)
904
+ clauses.append(
905
+ "({requires_pythons})".format(requires_pythons=" or ".join(python_version_markers))
906
+ )
907
+ clauses.extend("({marker})".format(marker=marker) for marker in self.extra_markers)
908
+ return Marker(" and ".join(clauses)) if clauses else None