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.
- pex/auth.py +1 -1
- pex/bin/pex.py +15 -2
- pex/build_backend/configuration.py +5 -5
- pex/build_backend/wrap.py +27 -23
- pex/build_system/pep_517.py +4 -1
- pex/cache/dirs.py +17 -12
- pex/cli/commands/lock.py +302 -165
- pex/cli/commands/pip/core.py +4 -12
- pex/cli/commands/pip/wheel.py +1 -1
- pex/cli/commands/run.py +13 -20
- pex/cli/commands/venv.py +85 -16
- pex/cli/pex.py +11 -4
- pex/common.py +57 -7
- pex/compatibility.py +1 -1
- pex/dependency_configuration.py +87 -15
- pex/dist_metadata.py +143 -25
- pex/docs/html/_pagefind/fragment/en_4250138.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_7125dad.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_785d562.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_8e94bb8.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_a0396bb.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_a8a3588.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_c07d988.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_d718411.pf_fragment +0 -0
- pex/docs/html/_pagefind/index/en_a2e3c5e.pf_index +0 -0
- pex/docs/html/_pagefind/pagefind-entry.json +1 -1
- pex/docs/html/_pagefind/pagefind.en_4ce1afa9e3.pf_meta +0 -0
- pex/docs/html/_static/documentation_options.js +1 -1
- pex/docs/html/_static/pygments.css +164 -146
- pex/docs/html/_static/styles/furo.css +1 -1
- pex/docs/html/_static/styles/furo.css.map +1 -1
- pex/docs/html/api/vars.html +25 -34
- pex/docs/html/buildingpex.html +25 -34
- pex/docs/html/genindex.html +24 -33
- pex/docs/html/index.html +25 -34
- pex/docs/html/recipes.html +25 -34
- pex/docs/html/scie.html +25 -34
- pex/docs/html/search.html +24 -33
- pex/docs/html/whatispex.html +25 -34
- pex/entry_points_txt.py +98 -0
- pex/environment.py +54 -33
- pex/finders.py +1 -1
- pex/hashing.py +71 -9
- pex/installed_wheel.py +141 -0
- pex/interpreter.py +41 -38
- pex/interpreter_constraints.py +25 -25
- pex/interpreter_implementation.py +40 -0
- pex/jobs.py +13 -6
- pex/pep_376.py +68 -384
- pex/pep_425.py +11 -2
- pex/pep_427.py +937 -205
- pex/pep_508.py +4 -5
- pex/pex_builder.py +5 -8
- pex/pex_info.py +14 -9
- pex/pip/dependencies/__init__.py +85 -13
- pex/pip/dependencies/requires.py +38 -3
- pex/pip/foreign_platform/__init__.py +4 -3
- pex/pip/installation.py +2 -2
- pex/pip/local_project.py +6 -14
- pex/pip/package_repositories/__init__.py +78 -0
- pex/pip/package_repositories/link_collector.py +96 -0
- pex/pip/tool.py +139 -33
- pex/pip/vcs.py +109 -43
- pex/pip/version.py +8 -1
- pex/requirements.py +121 -16
- pex/resolve/config.py +5 -1
- pex/resolve/configured_resolve.py +32 -10
- pex/resolve/configured_resolver.py +10 -39
- pex/resolve/downloads.py +4 -3
- pex/resolve/lock_downloader.py +16 -23
- pex/resolve/lock_resolver.py +41 -51
- pex/resolve/locked_resolve.py +89 -32
- pex/resolve/locker.py +145 -101
- pex/resolve/locker_patches.py +123 -197
- pex/resolve/lockfile/create.py +232 -87
- pex/resolve/lockfile/download_manager.py +5 -1
- pex/resolve/lockfile/json_codec.py +103 -28
- pex/resolve/lockfile/model.py +13 -35
- pex/resolve/lockfile/pep_751.py +117 -98
- pex/resolve/lockfile/requires_dist.py +17 -262
- pex/resolve/lockfile/subset.py +11 -0
- pex/resolve/lockfile/targets.py +445 -0
- pex/resolve/lockfile/updater.py +22 -10
- pex/resolve/package_repository.py +406 -0
- pex/resolve/pex_repository_resolver.py +1 -1
- pex/resolve/pre_resolved_resolver.py +19 -16
- pex/resolve/project.py +233 -47
- pex/resolve/requirement_configuration.py +28 -10
- pex/resolve/resolver_configuration.py +18 -32
- pex/resolve/resolver_options.py +234 -28
- pex/resolve/resolvers.py +3 -12
- pex/resolve/target_options.py +18 -2
- pex/resolve/target_system.py +908 -0
- pex/resolve/venv_resolver.py +670 -0
- pex/resolver.py +673 -209
- pex/scie/__init__.py +40 -1
- pex/scie/model.py +2 -0
- pex/scie/science.py +25 -3
- pex/sdist.py +219 -0
- pex/sh_boot.py +24 -21
- pex/sysconfig.py +5 -3
- pex/targets.py +31 -10
- pex/third_party/__init__.py +1 -1
- pex/tools/commands/repository.py +48 -25
- pex/vendor/__init__.py +4 -9
- pex/vendor/__main__.py +65 -41
- pex/vendor/_vendored/ansicolors/.layout.json +1 -1
- pex/vendor/_vendored/ansicolors/ansicolors-1.1.8.dist-info/RECORD +11 -0
- pex/vendor/_vendored/ansicolors/ansicolors-1.1.8.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/appdirs/.layout.json +1 -1
- pex/vendor/_vendored/appdirs/appdirs-1.4.4.dist-info/RECORD +7 -0
- pex/vendor/_vendored/appdirs/appdirs-1.4.4.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/attrs/.layout.json +1 -1
- pex/vendor/_vendored/attrs/attrs-21.5.0.dev0.dist-info/RECORD +37 -0
- pex/vendor/_vendored/attrs/attrs-21.5.0.dev0.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/packaging_20_9/.layout.json +1 -1
- pex/vendor/_vendored/packaging_20_9/packaging-20.9.dist-info/RECORD +20 -0
- pex/vendor/_vendored/packaging_20_9/packaging-20.9.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/packaging_20_9/pyparsing-2.4.7.dist-info/RECORD +7 -0
- pex/vendor/_vendored/packaging_20_9/pyparsing-2.4.7.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/packaging_21_3/.layout.json +1 -1
- pex/vendor/_vendored/packaging_21_3/packaging-21.3.dist-info/RECORD +20 -0
- pex/vendor/_vendored/packaging_21_3/packaging-21.3.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/packaging_21_3/pyparsing-3.0.7.dist-info/RECORD +18 -0
- pex/vendor/_vendored/packaging_21_3/pyparsing-3.0.7.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/packaging_24_0/.layout.json +1 -1
- pex/vendor/_vendored/packaging_24_0/packaging-24.0.dist-info/RECORD +22 -0
- pex/vendor/_vendored/packaging_24_0/packaging-24.0.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/packaging_25_0/.layout.json +1 -1
- pex/vendor/_vendored/packaging_25_0/packaging-25.0.dist-info/RECORD +24 -0
- pex/vendor/_vendored/packaging_25_0/packaging-25.0.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/pip/.layout.json +1 -1
- pex/vendor/_vendored/pip/pip/_vendor/certifi/cacert.pem +63 -1
- pex/vendor/_vendored/pip/pip-20.3.4.dist-info/RECORD +388 -0
- pex/vendor/_vendored/pip/pip-20.3.4.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/setuptools/.layout.json +1 -1
- pex/vendor/_vendored/setuptools/setuptools-44.0.0+3acb925dd708430aeaf197ea53ac8a752f7c1863.dist-info/RECORD +107 -0
- pex/vendor/_vendored/setuptools/setuptools-44.0.0+3acb925dd708430aeaf197ea53ac8a752f7c1863.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/toml/.layout.json +1 -1
- pex/vendor/_vendored/toml/toml-0.10.2.dist-info/RECORD +11 -0
- pex/vendor/_vendored/toml/toml-0.10.2.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/tomli/.layout.json +1 -1
- pex/vendor/_vendored/tomli/tomli-2.0.1.dist-info/RECORD +10 -0
- pex/vendor/_vendored/tomli/tomli-2.0.1.pex-info/original-whl-info.json +1 -0
- pex/venv/installer.py +46 -19
- pex/venv/venv_pex.py +6 -3
- pex/version.py +1 -1
- pex/wheel.py +188 -40
- pex/whl.py +67 -0
- pex/windows/__init__.py +14 -11
- {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/METADATA +6 -5
- {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/RECORD +157 -133
- {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/entry_points.txt +1 -0
- {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/pylock/pylock.toml +1 -1
- pex/docs/html/_pagefind/fragment/en_42c9d8c.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_45dd5a2.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_4ca74d2.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_77273d5.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_87a59c5.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_8dc89b5.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_9d1319b.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_e55df9d.pf_fragment +0 -0
- pex/docs/html/_pagefind/index/en_1e98c6f.pf_index +0 -0
- pex/docs/html/_pagefind/pagefind.en_d1c488ecae.pf_meta +0 -0
- pex/vendor/_vendored/ansicolors/ansicolors-1.1.8.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/appdirs/appdirs-1.4.4.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/attrs/attrs-21.5.0.dev0.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/packaging_20_9/packaging-20.9.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/packaging_20_9/pyparsing-2.4.7.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/packaging_21_3/packaging-21.3.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/packaging_21_3/pyparsing-3.0.7.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/packaging_24_0/packaging-24.0.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/packaging_25_0/packaging-25.0.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/pip/pip-20.3.4.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/setuptools/setuptools-44.0.0+3acb925dd708430aeaf197ea53ac8a752f7c1863.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/toml/toml-0.10.2.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/tomli/tomli-2.0.1.dist-info/INSTALLER +0 -1
- {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/WHEEL +0 -0
- {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
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,
|
|
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:
|
|
53
|
-
wheels, # type:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|