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
pex/resolver.py
CHANGED
|
@@ -8,15 +8,14 @@ import functools
|
|
|
8
8
|
import glob
|
|
9
9
|
import hashlib
|
|
10
10
|
import itertools
|
|
11
|
+
import json
|
|
11
12
|
import os
|
|
12
|
-
import tarfile
|
|
13
13
|
import zipfile
|
|
14
14
|
from abc import abstractmethod
|
|
15
15
|
from collections import OrderedDict, defaultdict
|
|
16
16
|
|
|
17
|
-
from pex import targets
|
|
17
|
+
from pex import sdist, targets
|
|
18
18
|
from pex.atomic_directory import AtomicDirectory, atomic_directory
|
|
19
|
-
from pex.auth import PasswordEntry
|
|
20
19
|
from pex.cache.dirs import BuiltWheelDir, CacheDir
|
|
21
20
|
from pex.common import (
|
|
22
21
|
open_zip,
|
|
@@ -31,24 +30,29 @@ from pex.dependency_configuration import DependencyConfiguration
|
|
|
31
30
|
from pex.dist_metadata import (
|
|
32
31
|
DistMetadata,
|
|
33
32
|
Distribution,
|
|
33
|
+
ProjectMetadata,
|
|
34
34
|
Requirement,
|
|
35
35
|
is_tar_sdist,
|
|
36
36
|
is_wheel,
|
|
37
37
|
is_zip_sdist,
|
|
38
38
|
)
|
|
39
|
+
from pex.exceptions import production_assert
|
|
39
40
|
from pex.fingerprinted_distribution import FingerprintedDistribution
|
|
41
|
+
from pex.installed_wheel import InstalledWheel
|
|
40
42
|
from pex.jobs import Raise, SpawnedJob, execute_parallel, iter_map_parallel
|
|
41
43
|
from pex.network_configuration import NetworkConfiguration
|
|
42
44
|
from pex.orderedset import OrderedSet
|
|
43
|
-
from pex.pep_376 import InstalledWheel
|
|
44
45
|
from pex.pep_425 import CompatibilityTags
|
|
45
46
|
from pex.pep_427 import InstallableType, WheelError, install_wheel_chroot
|
|
47
|
+
from pex.pep_440 import Version
|
|
46
48
|
from pex.pep_503 import ProjectName
|
|
47
49
|
from pex.pip.download_observer import DownloadObserver
|
|
48
50
|
from pex.pip.installation import get_pip
|
|
51
|
+
from pex.pip.local_project import digest_local_project
|
|
49
52
|
from pex.pip.tool import PackageIndexConfiguration
|
|
50
53
|
from pex.pip.version import PipVersionValue
|
|
51
54
|
from pex.requirements import LocalProjectRequirement, URLRequirement
|
|
55
|
+
from pex.resolve.package_repository import ReposConfiguration
|
|
52
56
|
from pex.resolve.requirement_configuration import RequirementConfiguration
|
|
53
57
|
from pex.resolve.resolver_configuration import BuildConfiguration, PipLog, ResolverVersion
|
|
54
58
|
from pex.resolve.resolvers import (
|
|
@@ -59,7 +63,10 @@ from pex.resolve.resolvers import (
|
|
|
59
63
|
Untranslatable,
|
|
60
64
|
check_resolve,
|
|
61
65
|
)
|
|
66
|
+
from pex.resolve.target_system import TargetSystem, UniversalTarget
|
|
67
|
+
from pex.result import Error
|
|
62
68
|
from pex.targets import AbbreviatedPlatform, CompletePlatform, LocalInterpreter, Target, Targets
|
|
69
|
+
from pex.third_party.packaging.specifiers import SpecifierSet
|
|
63
70
|
from pex.third_party.packaging.tags import Tag
|
|
64
71
|
from pex.tracer import TRACER
|
|
65
72
|
from pex.typing import TYPE_CHECKING
|
|
@@ -79,6 +86,7 @@ if TYPE_CHECKING:
|
|
|
79
86
|
Sequence,
|
|
80
87
|
Set,
|
|
81
88
|
Tuple,
|
|
89
|
+
Union,
|
|
82
90
|
)
|
|
83
91
|
|
|
84
92
|
import attr # vendor:skip
|
|
@@ -88,59 +96,135 @@ else:
|
|
|
88
96
|
from pex.third_party import attr
|
|
89
97
|
|
|
90
98
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
@attr.s(frozen=True)
|
|
100
|
+
class DownloadTarget(object):
|
|
101
|
+
@classmethod
|
|
102
|
+
def current(cls):
|
|
103
|
+
# type: () -> DownloadTarget
|
|
104
|
+
return cls(target=targets.current())
|
|
105
|
+
|
|
106
|
+
target = attr.ib() # type: Target
|
|
107
|
+
universal_target = attr.ib(default=None) # type: Optional[UniversalTarget]
|
|
108
|
+
|
|
109
|
+
def render_description(self):
|
|
110
|
+
# type: () -> str
|
|
111
|
+
target_description = self.target.render_description()
|
|
112
|
+
if self.universal_target:
|
|
113
|
+
description_components = ["universal resolve"]
|
|
114
|
+
if self.universal_target.systems and frozenset(
|
|
115
|
+
self.universal_target.systems
|
|
116
|
+
) != frozenset(TargetSystem.values()):
|
|
117
|
+
description_components.append(
|
|
118
|
+
"targeting {systems}".format(
|
|
119
|
+
systems=" and ".join(map(str, self.universal_target.systems))
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
if self.universal_target.implementation:
|
|
123
|
+
description_components.append(
|
|
124
|
+
"for {impl}".format(impl=self.universal_target.implementation)
|
|
125
|
+
)
|
|
126
|
+
description_components.append("using {target}".format(target=target_description))
|
|
127
|
+
return " ".join(description_components)
|
|
128
|
+
return target_description
|
|
129
|
+
|
|
130
|
+
def id(self, complete=False):
|
|
131
|
+
# type: (bool) -> str
|
|
132
|
+
|
|
133
|
+
if self.universal_target:
|
|
134
|
+
id_components = ["universal"]
|
|
135
|
+
if self.universal_target.implementation:
|
|
136
|
+
id_components.append(str(self.universal_target.implementation))
|
|
137
|
+
id_components.append(
|
|
138
|
+
"-and-".join(map(str, self.universal_target.systems or TargetSystem.values()))
|
|
139
|
+
)
|
|
140
|
+
if complete:
|
|
141
|
+
id_components.append(
|
|
142
|
+
hashlib.sha1(str(self.universal_target.marker()).encode("utf-8")).hexdigest()
|
|
143
|
+
)
|
|
144
|
+
return "-".join(id_components)
|
|
145
|
+
|
|
146
|
+
if isinstance(self.target, LocalInterpreter):
|
|
147
|
+
# e.g.: CPython 2.7.18
|
|
148
|
+
return self.target.interpreter.version_string
|
|
149
|
+
if isinstance(self.target, AbbreviatedPlatform):
|
|
150
|
+
return str(self.target.platform)
|
|
151
|
+
if isinstance(self.target, CompletePlatform):
|
|
152
|
+
return str(self.target.platform.tag)
|
|
153
|
+
|
|
154
|
+
return self.target.id
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _uniqued_download_requests(requests=None):
|
|
158
|
+
# type: (Optional[Iterable[DownloadRequest]]) -> Tuple[DownloadRequest, ...]
|
|
159
|
+
return tuple(OrderedSet(requests)) if requests is not None else ()
|
|
94
160
|
|
|
95
161
|
|
|
96
162
|
@attr.s(frozen=True)
|
|
97
163
|
class PipLogManager(object):
|
|
164
|
+
@staticmethod
|
|
165
|
+
def _target_id(download_target):
|
|
166
|
+
# type: (DownloadTarget) -> str
|
|
167
|
+
|
|
168
|
+
universal_target = download_target.universal_target
|
|
169
|
+
if universal_target:
|
|
170
|
+
id_components = ["universal"]
|
|
171
|
+
if universal_target.implementation:
|
|
172
|
+
id_components.append(str(universal_target.implementation))
|
|
173
|
+
id_components.extend(map(str, universal_target.systems or TargetSystem.values()))
|
|
174
|
+
return "-and-".join(id_components)
|
|
175
|
+
|
|
176
|
+
target = download_target.target
|
|
177
|
+
if isinstance(target, LocalInterpreter):
|
|
178
|
+
# e.g.: CPython 2.7.18
|
|
179
|
+
return target.interpreter.version_string
|
|
180
|
+
if isinstance(target, AbbreviatedPlatform):
|
|
181
|
+
return str(target.platform)
|
|
182
|
+
if isinstance(target, CompletePlatform):
|
|
183
|
+
return str(target.platform.tag)
|
|
184
|
+
return target.id
|
|
185
|
+
|
|
98
186
|
@classmethod
|
|
99
187
|
def create(
|
|
100
188
|
cls,
|
|
101
189
|
log, # type: Optional[PipLog]
|
|
102
|
-
|
|
190
|
+
download_targets, # type: Sequence[DownloadTarget]
|
|
103
191
|
):
|
|
104
192
|
# type: (...) -> PipLogManager
|
|
105
|
-
|
|
106
|
-
if log and len(
|
|
107
|
-
|
|
193
|
+
log_by_download_target = {} # type: Dict[DownloadTarget, str]
|
|
194
|
+
if log and len(download_targets) == 1:
|
|
195
|
+
log_by_download_target[download_targets[0]] = log.path
|
|
108
196
|
elif log:
|
|
109
197
|
log_dir = safe_mkdtemp(prefix="pex-pip-log.")
|
|
110
|
-
|
|
111
|
-
(
|
|
112
|
-
|
|
198
|
+
log_by_download_target.update(
|
|
199
|
+
(
|
|
200
|
+
download_target,
|
|
201
|
+
os.path.join(
|
|
202
|
+
log_dir,
|
|
203
|
+
"pip.{target}.log".format(target=download_target.id(complete=True)),
|
|
204
|
+
),
|
|
205
|
+
)
|
|
206
|
+
for download_target in download_targets
|
|
113
207
|
)
|
|
114
|
-
return cls(log=log,
|
|
208
|
+
return cls(log=log, log_by_download_target=log_by_download_target)
|
|
115
209
|
|
|
116
210
|
log = attr.ib() # type: Optional[PipLog]
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
@staticmethod
|
|
120
|
-
def _target_id(target):
|
|
121
|
-
# type: (Target) -> str
|
|
122
|
-
if isinstance(target, LocalInterpreter):
|
|
123
|
-
# e.g.: CPython 2.7.18
|
|
124
|
-
return target.interpreter.version_string
|
|
125
|
-
if isinstance(target, AbbreviatedPlatform):
|
|
126
|
-
return str(target.platform)
|
|
127
|
-
if isinstance(target, CompletePlatform):
|
|
128
|
-
return str(target.platform.tag)
|
|
129
|
-
return target.id
|
|
211
|
+
_log_by_download_target = attr.ib() # type: Mapping[DownloadTarget, str]
|
|
130
212
|
|
|
131
213
|
def finalize_log(self):
|
|
132
214
|
# type: () -> None
|
|
133
215
|
if not self.log:
|
|
134
216
|
return
|
|
135
217
|
|
|
136
|
-
target_count = len(self.
|
|
218
|
+
target_count = len(self._log_by_download_target)
|
|
137
219
|
if target_count <= 1:
|
|
138
220
|
return
|
|
139
221
|
|
|
140
222
|
with safe_open(self.log.path, "a") as out_fp:
|
|
141
|
-
for index, (
|
|
223
|
+
for index, (download_target, log) in enumerate(
|
|
224
|
+
self._log_by_download_target.items(), start=1
|
|
225
|
+
):
|
|
142
226
|
prefix = "{index}/{count}]{target}".format(
|
|
143
|
-
index=index, count=target_count, target=
|
|
227
|
+
index=index, count=target_count, target=download_target.id()
|
|
144
228
|
)
|
|
145
229
|
if not os.path.exists(log):
|
|
146
230
|
print(
|
|
@@ -153,18 +237,60 @@ class PipLogManager(object):
|
|
|
153
237
|
for line in in_fp:
|
|
154
238
|
out_fp.write("{prefix}: {line}".format(prefix=prefix, line=line))
|
|
155
239
|
|
|
156
|
-
def get_log(self,
|
|
157
|
-
# type: (
|
|
158
|
-
return self.
|
|
240
|
+
def get_log(self, download_target):
|
|
241
|
+
# type: (DownloadTarget) -> Optional[str]
|
|
242
|
+
return self._log_by_download_target.get(download_target)
|
|
159
243
|
|
|
160
244
|
|
|
161
245
|
@attr.s(frozen=True)
|
|
162
|
-
class
|
|
163
|
-
|
|
246
|
+
class Report(object):
|
|
247
|
+
@classmethod
|
|
248
|
+
def parse(
|
|
249
|
+
cls,
|
|
250
|
+
download_target, # type: DownloadTarget
|
|
251
|
+
report, # type: str
|
|
252
|
+
):
|
|
253
|
+
# type: (...) -> Report
|
|
254
|
+
|
|
255
|
+
with open(report) as fp:
|
|
256
|
+
data = json.load(fp)
|
|
257
|
+
|
|
258
|
+
project_metadata = [] # type: List[ProjectMetadata]
|
|
259
|
+
for distribution in data["install"]:
|
|
260
|
+
metadata = distribution["metadata"]
|
|
261
|
+
project_metadata.append(
|
|
262
|
+
ProjectMetadata(
|
|
263
|
+
project_name=ProjectName(metadata["name"]),
|
|
264
|
+
version=Version(metadata["version"]),
|
|
265
|
+
requires_python=SpecifierSet(metadata.get("requires_python", "")),
|
|
266
|
+
requires_dists=tuple(
|
|
267
|
+
Requirement.parse(requirement)
|
|
268
|
+
for requirement in metadata.get("requires_dist", ())
|
|
269
|
+
),
|
|
270
|
+
)
|
|
271
|
+
)
|
|
272
|
+
return cls(
|
|
273
|
+
download_target=download_target,
|
|
274
|
+
metadata={metadata.project_name: metadata for metadata in project_metadata},
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
download_target = attr.ib() # type: DownloadTarget
|
|
278
|
+
metadata = attr.ib() # type: Mapping[ProjectName, ProjectMetadata]
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@attr.s(frozen=True)
|
|
282
|
+
class Reports(object):
|
|
283
|
+
reports = attr.ib(default=()) # type: Tuple[Report, ...]
|
|
284
|
+
|
|
285
|
+
def __iter__(self):
|
|
286
|
+
# type: () -> Iterator[Report]
|
|
287
|
+
return iter(self.reports)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@attr.s(frozen=True)
|
|
291
|
+
class _PipSession(object):
|
|
292
|
+
requests = attr.ib(converter=_uniqued_download_requests) # type: Tuple[DownloadRequest, ...]
|
|
164
293
|
direct_requirements = attr.ib() # type: Iterable[ParsedRequirement]
|
|
165
|
-
requirements = attr.ib(default=None) # type: Optional[Iterable[str]]
|
|
166
|
-
requirement_files = attr.ib(default=None) # type: Optional[Iterable[str]]
|
|
167
|
-
constraint_files = attr.ib(default=None) # type: Optional[Iterable[str]]
|
|
168
294
|
allow_prereleases = attr.ib(default=False) # type: bool
|
|
169
295
|
transitive = attr.ib(default=True) # type: bool
|
|
170
296
|
package_index_configuration = attr.ib(default=None) # type: Optional[PackageIndexConfiguration]
|
|
@@ -181,12 +307,114 @@ class DownloadRequest(object):
|
|
|
181
307
|
# type: () -> Iterator[BuildRequest]
|
|
182
308
|
for requirement in self.direct_requirements:
|
|
183
309
|
if isinstance(requirement, LocalProjectRequirement):
|
|
184
|
-
for
|
|
185
|
-
yield BuildRequest.
|
|
310
|
+
for request in self.requests:
|
|
311
|
+
yield BuildRequest.for_directory(
|
|
312
|
+
target=request.target, source_path=requirement.path
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
def generate_reports(self, max_parallel_jobs=None):
|
|
316
|
+
# type: (Optional[int]) -> Reports
|
|
317
|
+
|
|
318
|
+
if not self.requests or not any(request.has_requirements for request in self.requests):
|
|
319
|
+
# Nothing to report.
|
|
320
|
+
return Reports()
|
|
321
|
+
|
|
322
|
+
dest = safe_mkdtemp(
|
|
323
|
+
prefix="resolver_report.", dir=safe_mkdir(CacheDir.DOWNLOADS.path(".tmp"))
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
log_manager = PipLogManager.create(
|
|
327
|
+
self.pip_log,
|
|
328
|
+
download_targets=tuple(request.download_target for request in self.requests),
|
|
329
|
+
)
|
|
330
|
+
if self.pip_log and not self.pip_log.user_specified:
|
|
331
|
+
TRACER.log(
|
|
332
|
+
"Preserving `pip install --dry-run` log at {log_path}".format(
|
|
333
|
+
log_path=self.pip_log.path
|
|
334
|
+
),
|
|
335
|
+
V=ENV.PEX_VERBOSE,
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
spawn_report = functools.partial(
|
|
339
|
+
self._spawn_report, resolved_target_dir=dest, log_manager=log_manager
|
|
340
|
+
)
|
|
341
|
+
with TRACER.timed(
|
|
342
|
+
"Resolving for:\n {}".format(
|
|
343
|
+
"\n ".join(request.render_description() for request in self.requests)
|
|
344
|
+
)
|
|
345
|
+
):
|
|
346
|
+
try:
|
|
347
|
+
return Reports(
|
|
348
|
+
reports=tuple(
|
|
349
|
+
Report.parse(download_request.download_target, report)
|
|
350
|
+
for download_request, report in zip(
|
|
351
|
+
self.requests,
|
|
352
|
+
execute_parallel(
|
|
353
|
+
inputs=self.requests,
|
|
354
|
+
spawn_func=spawn_report,
|
|
355
|
+
error_handler=Raise[DownloadRequest, str](Unsatisfiable),
|
|
356
|
+
max_jobs=max_parallel_jobs,
|
|
357
|
+
),
|
|
358
|
+
)
|
|
359
|
+
)
|
|
360
|
+
)
|
|
361
|
+
finally:
|
|
362
|
+
log_manager.finalize_log()
|
|
363
|
+
|
|
364
|
+
def _spawn_report(
|
|
365
|
+
self,
|
|
366
|
+
request, # type: DownloadRequest
|
|
367
|
+
resolved_target_dir, # type: str
|
|
368
|
+
log_manager, # type: PipLogManager
|
|
369
|
+
):
|
|
370
|
+
# type: (...) -> SpawnedJob[str]
|
|
371
|
+
|
|
372
|
+
report_dir = safe_mkdir(
|
|
373
|
+
os.path.join(resolved_target_dir, request.download_target.id(complete=True))
|
|
374
|
+
)
|
|
375
|
+
report = os.path.join(report_dir, "pip-report.json")
|
|
376
|
+
download_target = request.download_target
|
|
377
|
+
observer = (
|
|
378
|
+
self.observer.observe_download(
|
|
379
|
+
download_target=download_target, download_dir=resolved_target_dir
|
|
380
|
+
)
|
|
381
|
+
if self.observer
|
|
382
|
+
else None
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
target = download_target.target
|
|
386
|
+
|
|
387
|
+
download_job = get_pip(
|
|
388
|
+
interpreter=target.get_interpreter(),
|
|
389
|
+
version=self.pip_version,
|
|
390
|
+
resolver=self.resolver,
|
|
391
|
+
extra_requirements=(
|
|
392
|
+
self.package_index_configuration.extra_pip_requirements
|
|
393
|
+
if self.package_index_configuration
|
|
394
|
+
else ()
|
|
395
|
+
),
|
|
396
|
+
).spawn_report(
|
|
397
|
+
report_path=report,
|
|
398
|
+
requirements=request.requirements,
|
|
399
|
+
requirement_files=request.requirement_files,
|
|
400
|
+
constraint_files=request.constraint_files,
|
|
401
|
+
allow_prereleases=self.allow_prereleases,
|
|
402
|
+
transitive=self.transitive,
|
|
403
|
+
target=target,
|
|
404
|
+
package_index_configuration=self.package_index_configuration,
|
|
405
|
+
build_configuration=self.build_configuration,
|
|
406
|
+
observer=observer,
|
|
407
|
+
dependency_configuration=self.dependency_configuration,
|
|
408
|
+
universal_target=download_target.universal_target,
|
|
409
|
+
log=log_manager.get_log(download_target),
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
return SpawnedJob.wait(job=download_job, result=report)
|
|
186
413
|
|
|
187
414
|
def download_distributions(self, dest=None, max_parallel_jobs=None):
|
|
188
415
|
# type: (...) -> List[DownloadResult]
|
|
189
|
-
|
|
416
|
+
|
|
417
|
+
if not self.requests or not any(request.has_requirements for request in self.requests):
|
|
190
418
|
# Nothing to resolve.
|
|
191
419
|
return []
|
|
192
420
|
|
|
@@ -194,48 +422,32 @@ class DownloadRequest(object):
|
|
|
194
422
|
prefix="resolver_download.", dir=safe_mkdir(CacheDir.DOWNLOADS.path(".tmp"))
|
|
195
423
|
)
|
|
196
424
|
|
|
197
|
-
log_manager = PipLogManager.create(
|
|
425
|
+
log_manager = PipLogManager.create(
|
|
426
|
+
self.pip_log,
|
|
427
|
+
download_targets=tuple(request.download_target for request in self.requests),
|
|
428
|
+
)
|
|
198
429
|
if self.pip_log and not self.pip_log.user_specified:
|
|
199
430
|
TRACER.log(
|
|
200
431
|
"Preserving `pip download` log at {log_path}".format(log_path=self.pip_log.path),
|
|
201
432
|
V=ENV.PEX_VERBOSE,
|
|
202
433
|
)
|
|
203
434
|
|
|
204
|
-
requirement_config = RequirementConfiguration(
|
|
205
|
-
requirements=self.requirements,
|
|
206
|
-
requirement_files=self.requirement_files,
|
|
207
|
-
constraint_files=self.constraint_files,
|
|
208
|
-
)
|
|
209
|
-
network_configuration = (
|
|
210
|
-
self.package_index_configuration.network_configuration
|
|
211
|
-
if self.package_index_configuration
|
|
212
|
-
else None
|
|
213
|
-
)
|
|
214
|
-
subdirectory_by_filename = {} # type: Dict[str, str]
|
|
215
|
-
for parsed_requirement in requirement_config.parse_requirements(network_configuration):
|
|
216
|
-
if not isinstance(parsed_requirement, URLRequirement):
|
|
217
|
-
continue
|
|
218
|
-
subdirectory = parsed_requirement.subdirectory
|
|
219
|
-
if subdirectory:
|
|
220
|
-
subdirectory_by_filename[parsed_requirement.filename] = subdirectory
|
|
221
|
-
|
|
222
435
|
spawn_download = functools.partial(
|
|
223
436
|
self._spawn_download,
|
|
224
437
|
resolved_dists_dir=dest,
|
|
225
438
|
log_manager=log_manager,
|
|
226
|
-
subdirectory_by_filename=subdirectory_by_filename,
|
|
227
439
|
)
|
|
228
440
|
with TRACER.timed(
|
|
229
441
|
"Resolving for:\n {}".format(
|
|
230
|
-
"\n ".join(
|
|
442
|
+
"\n ".join(request.render_description() for request in self.requests)
|
|
231
443
|
)
|
|
232
444
|
):
|
|
233
445
|
try:
|
|
234
446
|
return list(
|
|
235
447
|
execute_parallel(
|
|
236
|
-
inputs=self.
|
|
448
|
+
inputs=self.requests,
|
|
237
449
|
spawn_func=spawn_download,
|
|
238
|
-
error_handler=Raise[
|
|
450
|
+
error_handler=Raise[DownloadRequest, DownloadResult](Unsatisfiable),
|
|
239
451
|
max_jobs=max_parallel_jobs,
|
|
240
452
|
)
|
|
241
453
|
)
|
|
@@ -244,20 +456,42 @@ class DownloadRequest(object):
|
|
|
244
456
|
|
|
245
457
|
def _spawn_download(
|
|
246
458
|
self,
|
|
247
|
-
|
|
459
|
+
request, # type: DownloadRequest
|
|
248
460
|
resolved_dists_dir, # type: str
|
|
249
461
|
log_manager, # type: PipLogManager
|
|
250
|
-
subdirectory_by_filename, # type: Mapping[str, str]
|
|
251
462
|
):
|
|
252
463
|
# type: (...) -> SpawnedJob[DownloadResult]
|
|
253
|
-
|
|
464
|
+
|
|
465
|
+
requirement_config = RequirementConfiguration(
|
|
466
|
+
requirements=request.requirements,
|
|
467
|
+
requirement_files=request.requirement_files,
|
|
468
|
+
constraint_files=request.constraint_files,
|
|
469
|
+
)
|
|
470
|
+
network_configuration = (
|
|
471
|
+
self.package_index_configuration.network_configuration
|
|
472
|
+
if self.package_index_configuration
|
|
473
|
+
else None
|
|
474
|
+
)
|
|
475
|
+
subdirectory_by_filename = {} # type: Dict[str, str]
|
|
476
|
+
for parsed_requirement in requirement_config.parse_requirements(network_configuration):
|
|
477
|
+
if not isinstance(parsed_requirement, URLRequirement):
|
|
478
|
+
continue
|
|
479
|
+
subdirectory = parsed_requirement.subdirectory
|
|
480
|
+
if subdirectory:
|
|
481
|
+
subdirectory_by_filename[parsed_requirement.filename] = subdirectory
|
|
482
|
+
|
|
483
|
+
download_target = request.download_target
|
|
484
|
+
download_dir = os.path.join(resolved_dists_dir, download_target.id(complete=True))
|
|
254
485
|
observer = (
|
|
255
|
-
self.observer.observe_download(
|
|
486
|
+
self.observer.observe_download(
|
|
487
|
+
download_target=download_target, download_dir=download_dir
|
|
488
|
+
)
|
|
256
489
|
if self.observer
|
|
257
490
|
else None
|
|
258
491
|
)
|
|
259
492
|
|
|
260
|
-
download_result = DownloadResult(
|
|
493
|
+
download_result = DownloadResult(download_target, download_dir, subdirectory_by_filename)
|
|
494
|
+
target = download_target.target
|
|
261
495
|
download_job = get_pip(
|
|
262
496
|
interpreter=target.get_interpreter(),
|
|
263
497
|
version=self.pip_version,
|
|
@@ -269,9 +503,9 @@ class DownloadRequest(object):
|
|
|
269
503
|
),
|
|
270
504
|
).spawn_download_distributions(
|
|
271
505
|
download_dir=download_dir,
|
|
272
|
-
requirements=
|
|
273
|
-
requirement_files=
|
|
274
|
-
constraint_files=
|
|
506
|
+
requirements=request.requirements,
|
|
507
|
+
requirement_files=request.requirement_files,
|
|
508
|
+
constraint_files=request.constraint_files,
|
|
275
509
|
allow_prereleases=self.allow_prereleases,
|
|
276
510
|
transitive=self.transitive,
|
|
277
511
|
target=target,
|
|
@@ -279,7 +513,8 @@ class DownloadRequest(object):
|
|
|
279
513
|
build_configuration=self.build_configuration,
|
|
280
514
|
observer=observer,
|
|
281
515
|
dependency_configuration=self.dependency_configuration,
|
|
282
|
-
|
|
516
|
+
universal_target=download_target.universal_target,
|
|
517
|
+
log=log_manager.get_log(download_target),
|
|
283
518
|
)
|
|
284
519
|
|
|
285
520
|
return SpawnedJob.wait(job=download_job, result=download_result)
|
|
@@ -292,7 +527,7 @@ class DownloadResult(object):
|
|
|
292
527
|
# type: (str) -> bool
|
|
293
528
|
return is_wheel(path) and zipfile.is_zipfile(path)
|
|
294
529
|
|
|
295
|
-
|
|
530
|
+
download_target = attr.ib() # type: DownloadTarget
|
|
296
531
|
download_dir = attr.ib() # type: str
|
|
297
532
|
subdirectory_by_filename = attr.ib() # type: Mapping[str, str]
|
|
298
533
|
|
|
@@ -310,70 +545,135 @@ class DownloadResult(object):
|
|
|
310
545
|
subdirectory = self.subdirectory_by_filename.get(
|
|
311
546
|
os.path.basename(distribution_path)
|
|
312
547
|
)
|
|
313
|
-
|
|
314
|
-
|
|
548
|
+
production_assert(
|
|
549
|
+
os.path.isfile(distribution_path),
|
|
550
|
+
(
|
|
551
|
+
"Pip download results should always be files and never local project "
|
|
552
|
+
"directories."
|
|
553
|
+
),
|
|
554
|
+
)
|
|
555
|
+
yield BuildRequest.for_file(
|
|
556
|
+
target=self.download_target,
|
|
557
|
+
source_path=distribution_path,
|
|
558
|
+
subdirectory=subdirectory,
|
|
315
559
|
)
|
|
316
560
|
|
|
317
561
|
def install_requests(self):
|
|
318
562
|
# type: () -> Iterator[InstallRequest]
|
|
319
563
|
for distribution_path in self._iter_distribution_paths():
|
|
320
564
|
if self._is_wheel(distribution_path):
|
|
321
|
-
yield InstallRequest.create(
|
|
565
|
+
yield InstallRequest.create(
|
|
566
|
+
target=self.download_target, wheel_path=distribution_path
|
|
567
|
+
)
|
|
322
568
|
|
|
323
569
|
|
|
324
570
|
class IntegrityError(Exception):
|
|
325
571
|
pass
|
|
326
572
|
|
|
327
573
|
|
|
328
|
-
|
|
574
|
+
if TYPE_CHECKING:
|
|
575
|
+
from pex.hashing import Hasher
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def _hasher():
|
|
579
|
+
# type: () -> Hasher
|
|
580
|
+
|
|
581
|
+
return hashlib.sha256()
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
def _fingerprint_file(path):
|
|
329
585
|
# type: (str) -> str
|
|
586
|
+
return CacheHelper.hash(path, digest=_hasher())
|
|
330
587
|
|
|
331
|
-
# We switched from sha1 to sha256 at the transition from using `pip install --target` to
|
|
332
|
-
# `pip install --prefix` to serve two purposes:
|
|
333
|
-
# 1. Insulate the new installation scheme from the old.
|
|
334
|
-
# 2. Move past sha1 which was shown to have practical collision attacks in 2019.
|
|
335
|
-
#
|
|
336
|
-
# The installation scheme switch was the primary purpose and switching hashes proved a pragmatic
|
|
337
|
-
# insulation. If the `pip install --prefix` re-arrangement scheme evolves, then some other
|
|
338
|
-
# option than switching hashing algorithms will be needed, like post-fixing a running version
|
|
339
|
-
# integer or just mixing one into the hashed content.
|
|
340
|
-
#
|
|
341
|
-
# See: https://github.com/pex-tool/pex/issues/1655 for a general overview of these cache
|
|
342
|
-
# structure concerns.
|
|
343
|
-
hasher = hashlib.sha256
|
|
344
588
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
return CacheHelper.
|
|
589
|
+
def _fingerprint_directory(path):
|
|
590
|
+
# type: (str) -> str
|
|
591
|
+
return CacheHelper.dir_hash(path, digest=_hasher())
|
|
348
592
|
|
|
349
593
|
|
|
350
594
|
class BuildError(Exception):
|
|
351
595
|
pass
|
|
352
596
|
|
|
353
597
|
|
|
598
|
+
def _fingerprint_local_project(
|
|
599
|
+
path, # type: str
|
|
600
|
+
target, # type: Target
|
|
601
|
+
resolver=None, # type: Optional[Resolver]
|
|
602
|
+
pip_version=None, # type: Optional[PipVersionValue]
|
|
603
|
+
):
|
|
604
|
+
# type: (...) -> str
|
|
605
|
+
if resolver:
|
|
606
|
+
build_system_resolver = resolver
|
|
607
|
+
else:
|
|
608
|
+
from pex.resolve.configured_resolver import ConfiguredResolver
|
|
609
|
+
|
|
610
|
+
build_system_resolver = ConfiguredResolver.default()
|
|
611
|
+
|
|
612
|
+
hasher = _hasher()
|
|
613
|
+
result = digest_local_project(
|
|
614
|
+
directory=path,
|
|
615
|
+
digest=hasher,
|
|
616
|
+
target=target,
|
|
617
|
+
resolver=build_system_resolver,
|
|
618
|
+
pip_version=pip_version,
|
|
619
|
+
)
|
|
620
|
+
if isinstance(result, Error):
|
|
621
|
+
raise BuildError(
|
|
622
|
+
"Failed to create an sdist for hashing from the local project at {path}: "
|
|
623
|
+
"{err}".format(path=path, err=result)
|
|
624
|
+
)
|
|
625
|
+
return hasher.hexdigest()
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def _as_download_target(target):
|
|
629
|
+
# type: (Union[DownloadTarget, Target]) -> DownloadTarget
|
|
630
|
+
return target if isinstance(target, DownloadTarget) else DownloadTarget(target)
|
|
631
|
+
|
|
632
|
+
|
|
354
633
|
@attr.s(frozen=True)
|
|
355
634
|
class BuildRequest(object):
|
|
356
635
|
@classmethod
|
|
357
|
-
def
|
|
636
|
+
def for_file(
|
|
358
637
|
cls,
|
|
359
|
-
target, # type: Target
|
|
638
|
+
target, # type: Union[DownloadTarget, Target]
|
|
360
639
|
source_path, # type: str
|
|
361
640
|
subdirectory=None, # type: Optional[str]
|
|
362
641
|
):
|
|
363
642
|
# type: (...) -> BuildRequest
|
|
364
|
-
fingerprint =
|
|
643
|
+
fingerprint = _fingerprint_file(source_path)
|
|
365
644
|
return cls(
|
|
366
|
-
|
|
645
|
+
download_target=_as_download_target(target),
|
|
367
646
|
source_path=source_path,
|
|
368
647
|
fingerprint=fingerprint,
|
|
369
648
|
subdirectory=subdirectory,
|
|
370
649
|
)
|
|
371
650
|
|
|
372
|
-
|
|
651
|
+
@classmethod
|
|
652
|
+
def for_directory(
|
|
653
|
+
cls,
|
|
654
|
+
target, # type: Union[DownloadTarget, Target]
|
|
655
|
+
source_path, # type: str
|
|
656
|
+
subdirectory=None, # type: Optional[str]
|
|
657
|
+
):
|
|
658
|
+
# type: (...) -> BuildRequest
|
|
659
|
+
download_target = _as_download_target(target)
|
|
660
|
+
return cls(
|
|
661
|
+
download_target=download_target,
|
|
662
|
+
source_path=source_path,
|
|
663
|
+
fingerprint=None,
|
|
664
|
+
subdirectory=subdirectory,
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
download_target = attr.ib(converter=_as_download_target) # type: DownloadTarget
|
|
373
668
|
source_path = attr.ib() # type: str
|
|
374
|
-
fingerprint = attr.ib() # type: str
|
|
669
|
+
fingerprint = attr.ib() # type: Optional[str]
|
|
375
670
|
subdirectory = attr.ib() # type: Optional[str]
|
|
376
671
|
|
|
672
|
+
@property
|
|
673
|
+
def target(self):
|
|
674
|
+
# type: () -> Target
|
|
675
|
+
return self.download_target.target
|
|
676
|
+
|
|
377
677
|
def prepare(self):
|
|
378
678
|
# type: () -> str
|
|
379
679
|
|
|
@@ -386,26 +686,26 @@ class BuildRequest(object):
|
|
|
386
686
|
if is_zip_sdist(self.source_path):
|
|
387
687
|
with open_zip(self.source_path) as zf:
|
|
388
688
|
zf.extractall(extract_dir)
|
|
389
|
-
elif is_tar_sdist(self.source_path):
|
|
390
|
-
with tarfile.open(self.source_path) as tf:
|
|
391
|
-
tf.extractall(extract_dir)
|
|
392
|
-
else:
|
|
393
|
-
raise BuildError(
|
|
394
|
-
"Unexpected archive type for sdist {project}".format(project=self.source_path)
|
|
395
|
-
)
|
|
396
689
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
690
|
+
listing = os.listdir(extract_dir)
|
|
691
|
+
if len(listing) != 1:
|
|
692
|
+
raise BuildError(
|
|
693
|
+
"Expected one top-level project directory to be extracted from {project}, "
|
|
694
|
+
"found {count}: {listing}".format(
|
|
695
|
+
project=self.source_path, count=len(listing), listing=", ".join(listing)
|
|
696
|
+
)
|
|
403
697
|
)
|
|
404
|
-
)
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
698
|
+
project_directory = os.path.join(extract_dir, listing[0])
|
|
699
|
+
if self.subdirectory:
|
|
700
|
+
project_directory = os.path.join(project_directory, self.subdirectory)
|
|
701
|
+
return project_directory
|
|
702
|
+
|
|
703
|
+
if is_tar_sdist(self.source_path):
|
|
704
|
+
return sdist.extract_tarball(self.source_path, dest_dir=extract_dir)
|
|
705
|
+
|
|
706
|
+
raise BuildError(
|
|
707
|
+
"Unexpected archive type for sdist {project}".format(project=self.source_path)
|
|
708
|
+
)
|
|
409
709
|
|
|
410
710
|
def result(self, source_path=None):
|
|
411
711
|
# type: (Optional[str]) -> BuildResult
|
|
@@ -421,12 +721,16 @@ class BuildResult(object):
|
|
|
421
721
|
source_path=None, # type: Optional[str]
|
|
422
722
|
):
|
|
423
723
|
# type: (...) -> BuildResult
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
724
|
+
if build_request.fingerprint:
|
|
725
|
+
built_wheel = BuiltWheelDir.create(
|
|
726
|
+
sdist=source_path or build_request.source_path,
|
|
727
|
+
fingerprint=build_request.fingerprint,
|
|
728
|
+
target=build_request.target,
|
|
729
|
+
)
|
|
730
|
+
target_dir = built_wheel.dist_dir
|
|
731
|
+
else:
|
|
732
|
+
target_dir = os.path.join(safe_mkdtemp(), "build")
|
|
733
|
+
return cls(request=build_request, atomic_dir=AtomicDirectory(target_dir))
|
|
430
734
|
|
|
431
735
|
request = attr.ib() # type: BuildRequest
|
|
432
736
|
_atomic_dir = attr.ib() # type: AtomicDirectory
|
|
@@ -483,7 +787,7 @@ class BuildResult(object):
|
|
|
483
787
|
return frozenset(platforms), is_linux
|
|
484
788
|
|
|
485
789
|
wheel_platform_tags, is_linux_wheel = collect_platforms(
|
|
486
|
-
CompatibilityTags.from_wheel(wheel
|
|
790
|
+
CompatibilityTags.from_wheel(wheel)
|
|
487
791
|
)
|
|
488
792
|
abbreviated_target_platform_tags, is_linux_abbreviated_target = collect_platforms(
|
|
489
793
|
self.request.target.supported_tags
|
|
@@ -522,7 +826,7 @@ class BuildResult(object):
|
|
|
522
826
|
target=self.request.target.render_description(),
|
|
523
827
|
)
|
|
524
828
|
)
|
|
525
|
-
return InstallRequest.create(self.request.target, wheel_path)
|
|
829
|
+
return InstallRequest.create(self.request.target, wheel_path, was_built_locally=True)
|
|
526
830
|
|
|
527
831
|
|
|
528
832
|
@attr.s(frozen=True)
|
|
@@ -530,16 +834,28 @@ class InstallRequest(object):
|
|
|
530
834
|
@classmethod
|
|
531
835
|
def create(
|
|
532
836
|
cls,
|
|
533
|
-
target, # type: Target
|
|
837
|
+
target, # type: Union[DownloadTarget, Target]
|
|
534
838
|
wheel_path, # type: str
|
|
839
|
+
was_built_locally=False, # type: bool
|
|
535
840
|
):
|
|
536
841
|
# type: (...) -> InstallRequest
|
|
537
|
-
fingerprint =
|
|
538
|
-
return cls(
|
|
842
|
+
fingerprint = _fingerprint_file(wheel_path)
|
|
843
|
+
return cls(
|
|
844
|
+
download_target=_as_download_target(target),
|
|
845
|
+
wheel_path=wheel_path,
|
|
846
|
+
fingerprint=fingerprint,
|
|
847
|
+
was_built_locally=was_built_locally,
|
|
848
|
+
)
|
|
539
849
|
|
|
540
|
-
|
|
850
|
+
download_target = attr.ib(converter=_as_download_target) # type: DownloadTarget
|
|
541
851
|
wheel_path = attr.ib() # type: str
|
|
542
852
|
fingerprint = attr.ib() # type: str
|
|
853
|
+
was_built_locally = attr.ib(default=False) # type: bool
|
|
854
|
+
|
|
855
|
+
@property
|
|
856
|
+
def target(self):
|
|
857
|
+
# type: () -> Target
|
|
858
|
+
return self.download_target.target
|
|
543
859
|
|
|
544
860
|
@property
|
|
545
861
|
def wheel_file(self):
|
|
@@ -656,7 +972,7 @@ class InstallResult(object):
|
|
|
656
972
|
else:
|
|
657
973
|
cached_fingerprint = installed_wheel.fingerprint
|
|
658
974
|
|
|
659
|
-
wheel_dir_hash = cached_fingerprint or
|
|
975
|
+
wheel_dir_hash = cached_fingerprint or _fingerprint_directory(self.install_chroot)
|
|
660
976
|
runtime_key_dir = os.path.join(self._installation_root, wheel_dir_hash)
|
|
661
977
|
with atomic_directory(runtime_key_dir) as atomic_dir:
|
|
662
978
|
if not atomic_dir.is_finalized():
|
|
@@ -879,7 +1195,9 @@ def _perform_install(
|
|
|
879
1195
|
# type: (...) -> InstallResult
|
|
880
1196
|
install_result = install_request.result(installed_wheels_dir)
|
|
881
1197
|
install_wheel_chroot(
|
|
882
|
-
|
|
1198
|
+
wheel=install_request.wheel_path,
|
|
1199
|
+
destination=install_result.build_chroot,
|
|
1200
|
+
normalize_file_stat=install_request.was_built_locally,
|
|
883
1201
|
)
|
|
884
1202
|
return install_result
|
|
885
1203
|
|
|
@@ -972,8 +1290,10 @@ class BuildAndInstallRequest(object):
|
|
|
972
1290
|
)
|
|
973
1291
|
if is_wheel(dist_path):
|
|
974
1292
|
to_install.add(InstallRequest.create(install_request.target, dist_path))
|
|
1293
|
+
elif os.path.isdir(dist_path):
|
|
1294
|
+
to_build.add(BuildRequest.for_directory(install_request.target, dist_path))
|
|
975
1295
|
else:
|
|
976
|
-
to_build.add(BuildRequest.
|
|
1296
|
+
to_build.add(BuildRequest.for_file(install_request.target, dist_path))
|
|
977
1297
|
already_analyzed.add(metadata.project_name)
|
|
978
1298
|
|
|
979
1299
|
all_install_requests = OrderedSet(install_requests)
|
|
@@ -1137,7 +1457,7 @@ def _parse_reqs(
|
|
|
1137
1457
|
requirement_files=None, # type: Optional[Iterable[str]]
|
|
1138
1458
|
network_configuration=None, # type: Optional[NetworkConfiguration]
|
|
1139
1459
|
):
|
|
1140
|
-
# type: (...) ->
|
|
1460
|
+
# type: (...) -> Tuple[ParsedRequirement, ...]
|
|
1141
1461
|
requirement_configuration = RequirementConfiguration(
|
|
1142
1462
|
requirements=requirements, requirement_files=requirement_files
|
|
1143
1463
|
)
|
|
@@ -1151,11 +1471,9 @@ def resolve(
|
|
|
1151
1471
|
constraint_files=None, # type: Optional[Iterable[str]]
|
|
1152
1472
|
allow_prereleases=False, # type: bool
|
|
1153
1473
|
transitive=True, # type: bool
|
|
1154
|
-
|
|
1155
|
-
find_links=None, # type: Optional[Sequence[str]]
|
|
1474
|
+
repos_configuration=ReposConfiguration(), # type: ReposConfiguration
|
|
1156
1475
|
resolver_version=None, # type: Optional[ResolverVersion.Value]
|
|
1157
1476
|
network_configuration=None, # type: Optional[NetworkConfiguration]
|
|
1158
|
-
password_entries=(), # type: Iterable[PasswordEntry]
|
|
1159
1477
|
build_configuration=BuildConfiguration(), # type: BuildConfiguration
|
|
1160
1478
|
compile=False, # type: bool
|
|
1161
1479
|
max_parallel_jobs=None, # type: Optional[int]
|
|
@@ -1182,19 +1500,13 @@ def resolve(
|
|
|
1182
1500
|
:keyword constraint_files: A sequence of constraint file paths.
|
|
1183
1501
|
:keyword allow_prereleases: Whether to include pre-release and development versions when
|
|
1184
1502
|
resolving requirements. Defaults to ``False``, but any requirements that explicitly request
|
|
1185
|
-
|
|
1503
|
+
pre-release or development versions will override this setting.
|
|
1186
1504
|
:keyword transitive: Whether to resolve transitive dependencies of requirements.
|
|
1187
1505
|
Defaults to ``True``.
|
|
1188
|
-
:keyword
|
|
1189
|
-
distributions. Defaults to ``None`` which indicates to use the default pypi index. To turn off
|
|
1190
|
-
use of all indexes, pass an empty list.
|
|
1191
|
-
:keyword find_links: A list or URLs, paths to local html files or directory paths. If URLs or
|
|
1192
|
-
local html file paths, these are parsed for links to distributions. If a local directory path,
|
|
1193
|
-
its listing is used to discover distributions.
|
|
1506
|
+
:keyword repos_configuration: Configuration for package repositories to resolve packages from.
|
|
1194
1507
|
:keyword resolver_version: The resolver version to use.
|
|
1195
1508
|
:keyword network_configuration: Configuration for network requests made downloading and building
|
|
1196
1509
|
distributions.
|
|
1197
|
-
:keyword password_entries: Any known authentication information needed for resolving.
|
|
1198
1510
|
:keyword build_configuration: The configuration for building resolved projects.
|
|
1199
1511
|
:keyword compile: Whether to pre-compile resolved distribution python sources.
|
|
1200
1512
|
Defaults to ``False``.
|
|
@@ -1212,6 +1524,23 @@ def resolve(
|
|
|
1212
1524
|
:raises ValueError: If `build=False` and `use_wheel=False`.
|
|
1213
1525
|
"""
|
|
1214
1526
|
|
|
1527
|
+
if not build_configuration.allow_wheels:
|
|
1528
|
+
foreign_targets = [
|
|
1529
|
+
target
|
|
1530
|
+
for target in targets.unique_targets()
|
|
1531
|
+
if not isinstance(target, LocalInterpreter)
|
|
1532
|
+
]
|
|
1533
|
+
if foreign_targets:
|
|
1534
|
+
raise ValueError(
|
|
1535
|
+
"Cannot ignore wheels (use_wheel=False) when resolving for foreign {platforms}: "
|
|
1536
|
+
"{foreign_platforms}".format(
|
|
1537
|
+
platforms=pluralize(foreign_targets, "platform"),
|
|
1538
|
+
foreign_platforms=", ".join(
|
|
1539
|
+
target.render_description() for target in foreign_targets
|
|
1540
|
+
),
|
|
1541
|
+
)
|
|
1542
|
+
)
|
|
1543
|
+
|
|
1215
1544
|
# A resolve happens in four stages broken into two phases:
|
|
1216
1545
|
# 1. Download phase: resolves sdists and wheels in a single operation per distribution target.
|
|
1217
1546
|
# 2. Install phase:
|
|
@@ -1246,38 +1575,26 @@ def resolve(
|
|
|
1246
1575
|
package_index_configuration = PackageIndexConfiguration.create(
|
|
1247
1576
|
pip_version=pip_version,
|
|
1248
1577
|
resolver_version=resolver_version,
|
|
1249
|
-
|
|
1250
|
-
find_links=find_links,
|
|
1578
|
+
repos_configuration=repos_configuration,
|
|
1251
1579
|
network_configuration=network_configuration,
|
|
1252
|
-
password_entries=password_entries,
|
|
1253
1580
|
use_pip_config=use_pip_config,
|
|
1254
1581
|
extra_pip_requirements=extra_pip_requirements,
|
|
1255
1582
|
keyring_provider=keyring_provider,
|
|
1256
1583
|
)
|
|
1257
1584
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
target
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
"{foreign_platforms}".format(
|
|
1268
|
-
platforms=pluralize(foreign_targets, "platform"),
|
|
1269
|
-
foreign_platforms=", ".join(
|
|
1270
|
-
target.render_description() for target in foreign_targets
|
|
1271
|
-
),
|
|
1272
|
-
)
|
|
1273
|
-
)
|
|
1585
|
+
requests = tuple(
|
|
1586
|
+
DownloadRequest(
|
|
1587
|
+
download_target=DownloadTarget(target=target),
|
|
1588
|
+
requirements=requirements,
|
|
1589
|
+
requirement_files=requirement_files,
|
|
1590
|
+
constraint_files=constraint_files,
|
|
1591
|
+
)
|
|
1592
|
+
for target in targets.unique_targets()
|
|
1593
|
+
)
|
|
1274
1594
|
|
|
1275
1595
|
build_requests, download_results = _download_internal(
|
|
1276
|
-
|
|
1596
|
+
requests=requests,
|
|
1277
1597
|
direct_requirements=direct_requirements,
|
|
1278
|
-
requirements=requirements,
|
|
1279
|
-
requirement_files=requirement_files,
|
|
1280
|
-
constraint_files=constraint_files,
|
|
1281
1598
|
allow_prereleases=allow_prereleases,
|
|
1282
1599
|
transitive=transitive,
|
|
1283
1600
|
package_index_configuration=package_index_configuration,
|
|
@@ -1325,11 +1642,8 @@ def resolve(
|
|
|
1325
1642
|
|
|
1326
1643
|
|
|
1327
1644
|
def _download_internal(
|
|
1328
|
-
|
|
1645
|
+
requests, # type: Tuple[DownloadRequest, ...]
|
|
1329
1646
|
direct_requirements, # type: Iterable[ParsedRequirement]
|
|
1330
|
-
requirements=None, # type: Optional[Iterable[str]]
|
|
1331
|
-
requirement_files=None, # type: Optional[Iterable[str]]
|
|
1332
|
-
constraint_files=None, # type: Optional[Iterable[str]]
|
|
1333
1647
|
allow_prereleases=False, # type: bool
|
|
1334
1648
|
transitive=True, # type: bool
|
|
1335
1649
|
package_index_configuration=None, # type: Optional[PackageIndexConfiguration]
|
|
@@ -1344,13 +1658,9 @@ def _download_internal(
|
|
|
1344
1658
|
):
|
|
1345
1659
|
# type: (...) -> Tuple[List[BuildRequest], List[DownloadResult]]
|
|
1346
1660
|
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
targets=unique_targets,
|
|
1661
|
+
pip_session = _PipSession(
|
|
1662
|
+
requests=requests,
|
|
1350
1663
|
direct_requirements=direct_requirements,
|
|
1351
|
-
requirements=requirements,
|
|
1352
|
-
requirement_files=requirement_files,
|
|
1353
|
-
constraint_files=constraint_files,
|
|
1354
1664
|
allow_prereleases=allow_prereleases,
|
|
1355
1665
|
transitive=transitive,
|
|
1356
1666
|
package_index_configuration=package_index_configuration,
|
|
@@ -1362,8 +1672,8 @@ def _download_internal(
|
|
|
1362
1672
|
dependency_configuration=dependency_configuration,
|
|
1363
1673
|
)
|
|
1364
1674
|
|
|
1365
|
-
local_projects = list(
|
|
1366
|
-
download_results =
|
|
1675
|
+
local_projects = list(pip_session.iter_local_projects())
|
|
1676
|
+
download_results = pip_session.download_distributions(
|
|
1367
1677
|
dest=dest, max_parallel_jobs=max_parallel_jobs
|
|
1368
1678
|
)
|
|
1369
1679
|
return local_projects, download_results
|
|
@@ -1373,12 +1683,13 @@ def _download_internal(
|
|
|
1373
1683
|
class LocalDistribution(object):
|
|
1374
1684
|
path = attr.ib() # type: str
|
|
1375
1685
|
fingerprint = attr.ib() # type: str
|
|
1376
|
-
|
|
1686
|
+
download_target = attr.ib(factory=DownloadTarget.current) # type: DownloadTarget
|
|
1377
1687
|
subdirectory = attr.ib(default=None) # type: Optional[str]
|
|
1378
1688
|
|
|
1379
|
-
@
|
|
1380
|
-
def
|
|
1381
|
-
|
|
1689
|
+
@property
|
|
1690
|
+
def target(self):
|
|
1691
|
+
# type: () -> Target
|
|
1692
|
+
return self.download_target.target
|
|
1382
1693
|
|
|
1383
1694
|
@property
|
|
1384
1695
|
def is_wheel(self):
|
|
@@ -1394,8 +1705,8 @@ class ResolveObserver(object):
|
|
|
1394
1705
|
@abstractmethod
|
|
1395
1706
|
def observe_download(
|
|
1396
1707
|
self,
|
|
1397
|
-
|
|
1398
|
-
download_dir,
|
|
1708
|
+
download_target, # type: DownloadTarget
|
|
1709
|
+
download_dir, # type: str
|
|
1399
1710
|
):
|
|
1400
1711
|
# type: (...) -> DownloadObserver
|
|
1401
1712
|
raise NotImplementedError()
|
|
@@ -1408,11 +1719,9 @@ def download(
|
|
|
1408
1719
|
constraint_files=None, # type: Optional[Iterable[str]]
|
|
1409
1720
|
allow_prereleases=False, # type: bool
|
|
1410
1721
|
transitive=True, # type: bool
|
|
1411
|
-
|
|
1412
|
-
find_links=None, # type: Optional[Sequence[str]]
|
|
1722
|
+
repos_configuration=ReposConfiguration(), # type: ReposConfiguration
|
|
1413
1723
|
resolver_version=None, # type: Optional[ResolverVersion.Value]
|
|
1414
1724
|
network_configuration=None, # type: Optional[NetworkConfiguration]
|
|
1415
|
-
password_entries=(), # type: Iterable[PasswordEntry]
|
|
1416
1725
|
build_configuration=BuildConfiguration(), # type: BuildConfiguration
|
|
1417
1726
|
dest=None, # type: Optional[str]
|
|
1418
1727
|
max_parallel_jobs=None, # type: Optional[int]
|
|
@@ -1434,19 +1743,13 @@ def download(
|
|
|
1434
1743
|
:keyword constraint_files: A sequence of constraint file paths.
|
|
1435
1744
|
:keyword allow_prereleases: Whether to include pre-release and development versions when
|
|
1436
1745
|
resolving requirements. Defaults to ``False``, but any requirements that explicitly request
|
|
1437
|
-
|
|
1746
|
+
pre-release or development versions will override this setting.
|
|
1438
1747
|
:keyword transitive: Whether to resolve transitive dependencies of requirements.
|
|
1439
1748
|
Defaults to ``True``.
|
|
1440
|
-
:keyword
|
|
1441
|
-
for distributions. Defaults to ``None`` which indicates to use the default pypi index. To turn
|
|
1442
|
-
off use of all indexes, pass an empty list.
|
|
1443
|
-
:keyword find_links: A list of URLs, paths to local html files or directory paths. If URLs or
|
|
1444
|
-
local html file paths, these are parsed for links to distributions. If a local directory path,
|
|
1445
|
-
its listing is used to discover distributions.
|
|
1749
|
+
:keyword repos_configuration: Configuration for package repositories to resolve packages from.
|
|
1446
1750
|
:keyword resolver_version: The resolver version to use.
|
|
1447
1751
|
:keyword network_configuration: Configuration for network requests made downloading and building
|
|
1448
1752
|
distributions.
|
|
1449
|
-
:keyword password_entries: Any known authentication information needed for downloading.
|
|
1450
1753
|
:keyword build_configuration: The configuration for building resolved projects.
|
|
1451
1754
|
:keyword dest: A directory path to download distributions to.
|
|
1452
1755
|
:keyword max_parallel_jobs: The maximum number of parallel jobs to use when resolving,
|
|
@@ -1459,24 +1762,127 @@ def download(
|
|
|
1459
1762
|
:raises ValueError: If a foreign platform was provided in `platforms`, and `use_wheel=False`.
|
|
1460
1763
|
:raises ValueError: If `build=False` and `use_wheel=False`.
|
|
1461
1764
|
"""
|
|
1462
|
-
|
|
1765
|
+
return download_requests(
|
|
1766
|
+
requests=tuple(
|
|
1767
|
+
DownloadRequest(
|
|
1768
|
+
download_target=DownloadTarget(target),
|
|
1769
|
+
requirements=requirements,
|
|
1770
|
+
requirement_files=requirement_files,
|
|
1771
|
+
constraint_files=constraint_files,
|
|
1772
|
+
)
|
|
1773
|
+
for target in targets.unique_targets()
|
|
1774
|
+
),
|
|
1775
|
+
direct_requirements=_parse_reqs(requirements, requirement_files, network_configuration),
|
|
1776
|
+
allow_prereleases=allow_prereleases,
|
|
1777
|
+
transitive=transitive,
|
|
1778
|
+
repos_configuration=repos_configuration,
|
|
1779
|
+
resolver_version=resolver_version,
|
|
1780
|
+
network_configuration=network_configuration,
|
|
1781
|
+
build_configuration=build_configuration,
|
|
1782
|
+
dest=dest,
|
|
1783
|
+
max_parallel_jobs=max_parallel_jobs,
|
|
1784
|
+
observer=observer,
|
|
1785
|
+
pip_log=pip_log,
|
|
1786
|
+
pip_version=pip_version,
|
|
1787
|
+
resolver=resolver,
|
|
1788
|
+
use_pip_config=use_pip_config,
|
|
1789
|
+
extra_pip_requirements=extra_pip_requirements,
|
|
1790
|
+
keyring_provider=keyring_provider,
|
|
1791
|
+
dependency_configuration=dependency_configuration,
|
|
1792
|
+
)
|
|
1793
|
+
|
|
1794
|
+
|
|
1795
|
+
def _as_str_tuple(items):
|
|
1796
|
+
# type: (Optional[Iterable[str]]) -> Tuple[str, ...]
|
|
1797
|
+
if not items:
|
|
1798
|
+
return ()
|
|
1799
|
+
return items if isinstance(items, tuple) else tuple(items)
|
|
1800
|
+
|
|
1801
|
+
|
|
1802
|
+
@attr.s(frozen=True)
|
|
1803
|
+
class DownloadRequest(object):
|
|
1804
|
+
@classmethod
|
|
1805
|
+
def create(
|
|
1806
|
+
cls,
|
|
1807
|
+
target, # type: Target
|
|
1808
|
+
universal_target=None, # type: Optional[UniversalTarget]
|
|
1809
|
+
requirement_configuration=RequirementConfiguration(), # type: RequirementConfiguration
|
|
1810
|
+
provenance=None, # type: Optional[str]
|
|
1811
|
+
):
|
|
1812
|
+
# type: (...) -> DownloadRequest
|
|
1813
|
+
return cls(
|
|
1814
|
+
download_target=DownloadTarget(target=target, universal_target=universal_target),
|
|
1815
|
+
requirements=requirement_configuration.requirements,
|
|
1816
|
+
requirement_files=requirement_configuration.requirement_files,
|
|
1817
|
+
constraint_files=requirement_configuration.constraint_files,
|
|
1818
|
+
provenance=provenance,
|
|
1819
|
+
)
|
|
1820
|
+
|
|
1821
|
+
download_target = attr.ib() # type: DownloadTarget
|
|
1822
|
+
requirements = attr.ib(default=(), converter=_as_str_tuple) # type: Tuple[str, ...]
|
|
1823
|
+
requirement_files = attr.ib(default=(), converter=_as_str_tuple) # type: Tuple[str, ...]
|
|
1824
|
+
constraint_files = attr.ib(default=(), converter=_as_str_tuple) # type: Tuple[str, ...]
|
|
1825
|
+
provenance = attr.ib(default=None) # type: Optional[str]
|
|
1826
|
+
|
|
1827
|
+
def render_description(self):
|
|
1828
|
+
# type: () -> str
|
|
1829
|
+
description = self.download_target.render_description()
|
|
1830
|
+
if not self.provenance:
|
|
1831
|
+
return description
|
|
1832
|
+
return "{description} from {provenance}".format(
|
|
1833
|
+
description=description, provenance=self.provenance
|
|
1834
|
+
)
|
|
1835
|
+
|
|
1836
|
+
@property
|
|
1837
|
+
def target(self):
|
|
1838
|
+
# type: () -> Target
|
|
1839
|
+
return self.download_target.target
|
|
1840
|
+
|
|
1841
|
+
@property
|
|
1842
|
+
def universal_target(self):
|
|
1843
|
+
# type: () -> Optional[UniversalTarget]
|
|
1844
|
+
return self.download_target.universal_target
|
|
1845
|
+
|
|
1846
|
+
@property
|
|
1847
|
+
def has_requirements(self):
|
|
1848
|
+
return bool(self.requirements) or bool(self.requirement_files)
|
|
1849
|
+
|
|
1850
|
+
|
|
1851
|
+
def download_requests(
|
|
1852
|
+
requests, # type: Tuple[DownloadRequest, ...]
|
|
1853
|
+
direct_requirements, # type: Tuple[ParsedRequirement, ...]
|
|
1854
|
+
allow_prereleases=False, # type: bool
|
|
1855
|
+
transitive=True, # type: bool
|
|
1856
|
+
repos_configuration=ReposConfiguration(), # type: ReposConfiguration
|
|
1857
|
+
resolver_version=None, # type: Optional[ResolverVersion.Value]
|
|
1858
|
+
network_configuration=None, # type: Optional[NetworkConfiguration]
|
|
1859
|
+
build_configuration=BuildConfiguration(), # type: BuildConfiguration
|
|
1860
|
+
dest=None, # type: Optional[str]
|
|
1861
|
+
max_parallel_jobs=None, # type: Optional[int]
|
|
1862
|
+
observer=None, # type: Optional[ResolveObserver]
|
|
1863
|
+
pip_log=None, # type: Optional[PipLog]
|
|
1864
|
+
pip_version=None, # type: Optional[PipVersionValue]
|
|
1865
|
+
resolver=None, # type: Optional[Resolver]
|
|
1866
|
+
use_pip_config=False, # type: bool
|
|
1867
|
+
extra_pip_requirements=(), # type: Tuple[Requirement, ...]
|
|
1868
|
+
keyring_provider=None, # type: Optional[str]
|
|
1869
|
+
dependency_configuration=DependencyConfiguration(), # type: DependencyConfiguration
|
|
1870
|
+
):
|
|
1871
|
+
# type: (...) -> Downloaded
|
|
1872
|
+
|
|
1463
1873
|
package_index_configuration = PackageIndexConfiguration.create(
|
|
1464
1874
|
pip_version=pip_version,
|
|
1465
1875
|
resolver_version=resolver_version,
|
|
1466
|
-
|
|
1467
|
-
find_links=find_links,
|
|
1876
|
+
repos_configuration=repos_configuration,
|
|
1468
1877
|
network_configuration=network_configuration,
|
|
1469
|
-
password_entries=password_entries,
|
|
1470
1878
|
use_pip_config=use_pip_config,
|
|
1471
1879
|
extra_pip_requirements=extra_pip_requirements,
|
|
1472
1880
|
keyring_provider=keyring_provider,
|
|
1473
1881
|
)
|
|
1882
|
+
|
|
1474
1883
|
build_requests, download_results = _download_internal(
|
|
1475
|
-
|
|
1884
|
+
requests=requests,
|
|
1476
1885
|
direct_requirements=direct_requirements,
|
|
1477
|
-
requirements=requirements,
|
|
1478
|
-
requirement_files=requirement_files,
|
|
1479
|
-
constraint_files=constraint_files,
|
|
1480
1886
|
allow_prereleases=allow_prereleases,
|
|
1481
1887
|
transitive=transitive,
|
|
1482
1888
|
package_index_configuration=package_index_configuration,
|
|
@@ -1495,11 +1901,21 @@ def download(
|
|
|
1495
1901
|
def add_build_requests(requests):
|
|
1496
1902
|
# type: (Iterable[BuildRequest]) -> None
|
|
1497
1903
|
for request in requests:
|
|
1904
|
+
if request.fingerprint:
|
|
1905
|
+
fingerprint = request.fingerprint
|
|
1906
|
+
else:
|
|
1907
|
+
production_assert(os.path.isdir(request.source_path))
|
|
1908
|
+
fingerprint = _fingerprint_local_project(
|
|
1909
|
+
path=request.source_path,
|
|
1910
|
+
target=request.target,
|
|
1911
|
+
resolver=resolver,
|
|
1912
|
+
pip_version=pip_version,
|
|
1913
|
+
)
|
|
1498
1914
|
local_distributions.append(
|
|
1499
1915
|
LocalDistribution(
|
|
1500
|
-
|
|
1916
|
+
download_target=request.download_target,
|
|
1501
1917
|
path=request.source_path,
|
|
1502
|
-
fingerprint=
|
|
1918
|
+
fingerprint=fingerprint,
|
|
1503
1919
|
subdirectory=request.subdirectory,
|
|
1504
1920
|
)
|
|
1505
1921
|
)
|
|
@@ -1510,10 +1926,58 @@ def download(
|
|
|
1510
1926
|
for install_request in download_result.install_requests():
|
|
1511
1927
|
local_distributions.append(
|
|
1512
1928
|
LocalDistribution(
|
|
1513
|
-
|
|
1929
|
+
download_target=install_request.download_target,
|
|
1514
1930
|
path=install_request.wheel_path,
|
|
1515
1931
|
fingerprint=install_request.fingerprint,
|
|
1516
1932
|
)
|
|
1517
1933
|
)
|
|
1518
1934
|
|
|
1519
1935
|
return Downloaded(local_distributions=tuple(local_distributions))
|
|
1936
|
+
|
|
1937
|
+
|
|
1938
|
+
def reports(
|
|
1939
|
+
requests, # type: Tuple[DownloadRequest, ...]
|
|
1940
|
+
direct_requirements, # type: Tuple[ParsedRequirement, ...]
|
|
1941
|
+
allow_prereleases=False, # type: bool
|
|
1942
|
+
transitive=True, # type: bool
|
|
1943
|
+
repos_configuration=ReposConfiguration(), # type: ReposConfiguration
|
|
1944
|
+
resolver_version=None, # type: Optional[ResolverVersion.Value]
|
|
1945
|
+
network_configuration=None, # type: Optional[NetworkConfiguration]
|
|
1946
|
+
build_configuration=BuildConfiguration(), # type: BuildConfiguration
|
|
1947
|
+
max_parallel_jobs=None, # type: Optional[int]
|
|
1948
|
+
observer=None, # type: Optional[ResolveObserver]
|
|
1949
|
+
pip_log=None, # type: Optional[PipLog]
|
|
1950
|
+
pip_version=None, # type: Optional[PipVersionValue]
|
|
1951
|
+
resolver=None, # type: Optional[Resolver]
|
|
1952
|
+
use_pip_config=False, # type: bool
|
|
1953
|
+
extra_pip_requirements=(), # type: Tuple[Requirement, ...]
|
|
1954
|
+
keyring_provider=None, # type: Optional[str]
|
|
1955
|
+
dependency_configuration=DependencyConfiguration(), # type: DependencyConfiguration
|
|
1956
|
+
):
|
|
1957
|
+
# type: (...) -> Reports
|
|
1958
|
+
|
|
1959
|
+
package_index_configuration = PackageIndexConfiguration.create(
|
|
1960
|
+
pip_version=pip_version,
|
|
1961
|
+
resolver_version=resolver_version,
|
|
1962
|
+
repos_configuration=repos_configuration,
|
|
1963
|
+
network_configuration=network_configuration,
|
|
1964
|
+
use_pip_config=use_pip_config,
|
|
1965
|
+
extra_pip_requirements=extra_pip_requirements,
|
|
1966
|
+
keyring_provider=keyring_provider,
|
|
1967
|
+
)
|
|
1968
|
+
|
|
1969
|
+
pip_session = _PipSession(
|
|
1970
|
+
requests=requests,
|
|
1971
|
+
direct_requirements=direct_requirements,
|
|
1972
|
+
allow_prereleases=allow_prereleases,
|
|
1973
|
+
transitive=transitive,
|
|
1974
|
+
package_index_configuration=package_index_configuration,
|
|
1975
|
+
build_configuration=build_configuration,
|
|
1976
|
+
observer=observer,
|
|
1977
|
+
pip_log=pip_log,
|
|
1978
|
+
pip_version=pip_version,
|
|
1979
|
+
resolver=resolver,
|
|
1980
|
+
dependency_configuration=dependency_configuration,
|
|
1981
|
+
)
|
|
1982
|
+
|
|
1983
|
+
return pip_session.generate_reports(max_parallel_jobs=max_parallel_jobs)
|