pex 2.58.1__py2.py3-none-any.whl → 2.59.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/bin/pex.py +1 -0
- pex/cli/commands/venv.py +5 -1
- pex/dependency_configuration.py +6 -2
- pex/dist_metadata.py +30 -8
- pex/docs/html/_pagefind/fragment/en_23dc437.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_3b86fa5.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_65c3a5b.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_6c2e6ff.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_7320871.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_7cfdecb.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_89e2d7c.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_d4675e3.pf_fragment +0 -0
- pex/docs/html/_pagefind/index/en_34a4497.pf_index +0 -0
- pex/docs/html/_pagefind/pagefind-entry.json +1 -1
- pex/docs/html/_pagefind/pagefind.en_4ade7b3598.pf_meta +0 -0
- pex/docs/html/_static/documentation_options.js +1 -1
- pex/docs/html/api/vars.html +5 -5
- pex/docs/html/buildingpex.html +5 -5
- pex/docs/html/genindex.html +5 -5
- pex/docs/html/index.html +5 -5
- pex/docs/html/recipes.html +5 -5
- pex/docs/html/scie.html +5 -5
- pex/docs/html/search.html +5 -5
- pex/docs/html/whatispex.html +5 -5
- pex/environment.py +17 -3
- pex/pep_425.py +11 -2
- pex/pep_427.py +218 -73
- pex/pip/installation.py +1 -1
- pex/pip/tool.py +1 -1
- pex/resolve/configured_resolve.py +20 -0
- pex/resolve/configured_resolver.py +7 -3
- pex/resolve/pex_repository_resolver.py +1 -1
- pex/resolve/resolver_configuration.py +17 -0
- pex/resolve/resolver_options.py +88 -16
- pex/resolve/resolvers.py +3 -0
- pex/resolve/target_options.py +18 -2
- pex/resolve/venv_resolver.py +446 -0
- pex/resolver.py +2 -4
- pex/targets.py +9 -4
- pex/vendor/__main__.py +1 -1
- pex/version.py +1 -1
- pex/wheel.py +89 -27
- {pex-2.58.1.dist-info → pex-2.59.0.dist-info}/METADATA +4 -4
- {pex-2.58.1.dist-info → pex-2.59.0.dist-info}/RECORD +49 -48
- pex/docs/html/_pagefind/fragment/en_1fd6283.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_3981384.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_5b3a816.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_668cbbe.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_69b66dc.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_93e5ea3.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_9ec1cdb.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_edf644d.pf_fragment +0 -0
- pex/docs/html/_pagefind/index/en_b7cb935.pf_index +0 -0
- pex/docs/html/_pagefind/pagefind.en_bc4997bc6d.pf_meta +0 -0
- {pex-2.58.1.dist-info → pex-2.59.0.dist-info}/WHEEL +0 -0
- {pex-2.58.1.dist-info → pex-2.59.0.dist-info}/entry_points.txt +0 -0
- {pex-2.58.1.dist-info → pex-2.59.0.dist-info}/licenses/LICENSE +0 -0
- {pex-2.58.1.dist-info → pex-2.59.0.dist-info}/pylock/pylock.toml +0 -0
- {pex-2.58.1.dist-info → pex-2.59.0.dist-info}/top_level.txt +0 -0
pex/pep_427.py
CHANGED
|
@@ -9,20 +9,23 @@ import re
|
|
|
9
9
|
import shutil
|
|
10
10
|
import subprocess
|
|
11
11
|
import sys
|
|
12
|
+
import zipfile
|
|
12
13
|
from contextlib import closing
|
|
13
14
|
from fileinput import FileInput
|
|
14
15
|
from textwrap import dedent
|
|
15
16
|
|
|
16
17
|
from pex import pex_warnings, windows
|
|
17
|
-
from pex.common import is_pyc_file, iter_copytree, open_zip, safe_open, touch
|
|
18
|
+
from pex.common import is_pyc_file, iter_copytree, open_zip, safe_mkdir, safe_open, touch
|
|
18
19
|
from pex.compatibility import commonpath, get_stdout_bytes_buffer, safe_commonpath
|
|
19
20
|
from pex.dist_metadata import (
|
|
20
21
|
CallableEntryPoint,
|
|
22
|
+
DistMetadata,
|
|
21
23
|
Distribution,
|
|
24
|
+
MetadataFiles,
|
|
22
25
|
NamedEntryPoint,
|
|
23
|
-
ProjectNameAndVersion,
|
|
24
26
|
)
|
|
25
27
|
from pex.enum import Enum
|
|
28
|
+
from pex.exceptions import reportable_unexpected_error_msg
|
|
26
29
|
from pex.executables import chmod_plus_x
|
|
27
30
|
from pex.interpreter import PythonInterpreter
|
|
28
31
|
from pex.os import WINDOWS
|
|
@@ -43,6 +46,7 @@ if TYPE_CHECKING:
|
|
|
43
46
|
Optional,
|
|
44
47
|
Text,
|
|
45
48
|
Tuple,
|
|
49
|
+
Union,
|
|
46
50
|
)
|
|
47
51
|
|
|
48
52
|
import attr # vendor:skip
|
|
@@ -69,6 +73,7 @@ InstallableType.seal()
|
|
|
69
73
|
class InstallPaths(object):
|
|
70
74
|
|
|
71
75
|
CHROOT_STASH = ".prefix"
|
|
76
|
+
PATHS = "purelib", "platlib", "headers", "scripts", "data"
|
|
72
77
|
|
|
73
78
|
@classmethod
|
|
74
79
|
def chroot(
|
|
@@ -118,30 +123,121 @@ class InstallPaths(object):
|
|
|
118
123
|
return self.data
|
|
119
124
|
raise KeyError("Not a known install path: {item}".format(item=item))
|
|
120
125
|
|
|
126
|
+
def __str__(self):
|
|
127
|
+
# type: () -> str
|
|
128
|
+
return "\n".join(
|
|
129
|
+
"{path}={value}".format(path=item, value=self[item]) for item in InstallPaths.PATHS
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@attr.s(frozen=True)
|
|
134
|
+
class InstallableWheel(object):
|
|
135
|
+
@classmethod
|
|
136
|
+
def from_installation(
|
|
137
|
+
cls,
|
|
138
|
+
interpreter, # type: PythonInterpreter
|
|
139
|
+
distribution, # type: Distribution
|
|
140
|
+
):
|
|
141
|
+
# type: (...) -> InstallableWheel
|
|
142
|
+
return cls(
|
|
143
|
+
wheel=Wheel.from_distribution(distribution),
|
|
144
|
+
install_paths=InstallPaths.interpreter(interpreter),
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
@classmethod
|
|
148
|
+
def from_whl(cls, whl):
|
|
149
|
+
# type: (str) -> InstallableWheel
|
|
150
|
+
return cls(wheel=Wheel.load(whl))
|
|
151
|
+
|
|
152
|
+
wheel = attr.ib() # type: Wheel
|
|
153
|
+
is_whl = attr.ib(init=False) # type: bool
|
|
154
|
+
install_paths = attr.ib(default=None) # type: Optional[InstallPaths]
|
|
155
|
+
|
|
156
|
+
def __attrs_post_init__(self):
|
|
157
|
+
is_whl = zipfile.is_zipfile(self.wheel.location)
|
|
158
|
+
|
|
159
|
+
if is_whl and self.install_paths:
|
|
160
|
+
raise ValueError(
|
|
161
|
+
"A wheel file should have no installed paths but given the following paths for "
|
|
162
|
+
"{wheel}:\n"
|
|
163
|
+
"{install_paths}".format(
|
|
164
|
+
wheel=self.wheel.location, install_paths=self.install_paths
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
if not is_whl and not self.install_paths:
|
|
169
|
+
raise ValueError(
|
|
170
|
+
"The wheel for {source} is installed but not given its install paths".format(
|
|
171
|
+
source=self.source
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
object.__setattr__(self, "is_whl", is_whl)
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def location(self):
|
|
179
|
+
# type: () -> str
|
|
180
|
+
return self.wheel.location
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def source(self):
|
|
184
|
+
# type: () -> str
|
|
185
|
+
return self.wheel.source
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def metadata_files(self):
|
|
189
|
+
# type: () -> MetadataFiles
|
|
190
|
+
return self.wheel.metadata_files
|
|
191
|
+
|
|
192
|
+
@property
|
|
193
|
+
def root_is_purelib(self):
|
|
194
|
+
# type: () -> bool
|
|
195
|
+
return self.wheel.root_is_purelib
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def data_dir(self):
|
|
199
|
+
# type: () -> str
|
|
200
|
+
return self.wheel.data_dir
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def wheel_file_name(self):
|
|
204
|
+
# type: () -> str
|
|
205
|
+
return self.wheel.wheel_file_name
|
|
206
|
+
|
|
207
|
+
def dist_metadata(self):
|
|
208
|
+
# type: () -> DistMetadata
|
|
209
|
+
return self.wheel.dist_metadata()
|
|
210
|
+
|
|
211
|
+
def metadata_path(self, *components):
|
|
212
|
+
# type: (*str) -> str
|
|
213
|
+
return self.wheel.metadata_path(*components)
|
|
214
|
+
|
|
121
215
|
|
|
122
216
|
class WheelInstallError(WheelError):
|
|
123
217
|
"""Indicates an error installing a `.whl` file."""
|
|
124
218
|
|
|
125
219
|
|
|
126
220
|
def install_wheel_chroot(
|
|
127
|
-
|
|
221
|
+
wheel, # type: Union[str, InstallableWheel]
|
|
128
222
|
destination, # type: str
|
|
129
223
|
compile=False, # type: bool
|
|
130
224
|
requested=True, # type: bool
|
|
131
225
|
):
|
|
132
226
|
# type: (...) -> InstalledWheel
|
|
133
227
|
|
|
134
|
-
|
|
135
|
-
|
|
228
|
+
wheel_to_install = (
|
|
229
|
+
wheel if isinstance(wheel, InstallableWheel) else InstallableWheel.from_whl(wheel)
|
|
230
|
+
)
|
|
231
|
+
install_wheel(
|
|
232
|
+
wheel_to_install,
|
|
136
233
|
InstallPaths.chroot(
|
|
137
|
-
destination,
|
|
138
|
-
project_name=ProjectNameAndVersion.from_filename(wheel_path).canonicalized_project_name,
|
|
234
|
+
destination, project_name=wheel_to_install.metadata_files.metadata.project_name
|
|
139
235
|
),
|
|
140
236
|
compile=compile,
|
|
141
237
|
requested=requested,
|
|
142
238
|
)
|
|
143
239
|
|
|
144
|
-
record_relpath =
|
|
240
|
+
record_relpath = wheel_to_install.metadata_files.metadata_file_rel_path("RECORD")
|
|
145
241
|
assert (
|
|
146
242
|
record_relpath is not None
|
|
147
243
|
), "The {module}.install_wheel function should always create a RECORD.".format(module=__name__)
|
|
@@ -149,20 +245,23 @@ def install_wheel_chroot(
|
|
|
149
245
|
prefix_dir=destination,
|
|
150
246
|
stash_dir=InstallPaths.CHROOT_STASH,
|
|
151
247
|
record_relpath=record_relpath,
|
|
152
|
-
root_is_purelib=
|
|
248
|
+
root_is_purelib=wheel_to_install.root_is_purelib,
|
|
153
249
|
)
|
|
154
250
|
|
|
155
251
|
|
|
156
252
|
def install_wheel_interpreter(
|
|
157
|
-
|
|
253
|
+
wheel, # type: Union[str, InstallableWheel]
|
|
158
254
|
interpreter, # type: PythonInterpreter
|
|
159
255
|
compile=True, # type: bool
|
|
160
256
|
requested=True, # type: bool
|
|
161
257
|
):
|
|
162
|
-
# type: (...) ->
|
|
258
|
+
# type: (...) -> None
|
|
163
259
|
|
|
164
|
-
|
|
165
|
-
|
|
260
|
+
wheel_to_install = (
|
|
261
|
+
wheel if isinstance(wheel, InstallableWheel) else InstallableWheel.from_whl(wheel)
|
|
262
|
+
)
|
|
263
|
+
install_wheel(
|
|
264
|
+
wheel_to_install,
|
|
166
265
|
InstallPaths.interpreter(interpreter),
|
|
167
266
|
interpreter=interpreter,
|
|
168
267
|
compile=compile,
|
|
@@ -171,16 +270,15 @@ def install_wheel_interpreter(
|
|
|
171
270
|
|
|
172
271
|
|
|
173
272
|
def install_wheel(
|
|
174
|
-
|
|
273
|
+
wheel, # type: InstallableWheel
|
|
175
274
|
install_paths, # type: InstallPaths
|
|
176
275
|
interpreter=None, # type: Optional[PythonInterpreter]
|
|
177
276
|
compile=False, # type: bool
|
|
178
277
|
requested=True, # type: bool
|
|
179
278
|
):
|
|
180
|
-
# type: (...) ->
|
|
279
|
+
# type: (...) -> None
|
|
181
280
|
|
|
182
281
|
# See: https://packaging.python.org/en/latest/specifications/binary-distribution-format/#installing-a-wheel-distribution-1-0-py32-none-any-whl
|
|
183
|
-
wheel = Wheel.load(wheel_path)
|
|
184
282
|
dest = install_paths.purelib if wheel.root_is_purelib else install_paths.platlib
|
|
185
283
|
|
|
186
284
|
record_relpath = wheel.metadata_path("RECORD")
|
|
@@ -203,68 +301,117 @@ def install_wheel(
|
|
|
203
301
|
continue
|
|
204
302
|
file_abspath = os.path.join(root_dir, name)
|
|
205
303
|
if record_relpath == name:
|
|
206
|
-
# We'll generate a new RECORD below.
|
|
304
|
+
# We'll generate a new RECORD below as needed.
|
|
207
305
|
os.unlink(file_abspath)
|
|
208
306
|
continue
|
|
209
307
|
installed_files.append(
|
|
210
308
|
InstalledWheel.create_installed_file(path=file_abspath, dest_dir=dest)
|
|
211
309
|
)
|
|
212
310
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
311
|
+
if wheel.is_whl:
|
|
312
|
+
with open_zip(wheel.location) as zf:
|
|
313
|
+
# 1. Unpack
|
|
314
|
+
zf.extractall(dest)
|
|
315
|
+
# TODO(John Sirois): Consider verifying signatures.
|
|
316
|
+
# N.B.: Pip does not and its also not clear what good this does. A zip can be easily
|
|
317
|
+
# poked on a per-entry basis allowing forging a RECORD entry and its associated file.
|
|
318
|
+
# Only an outer fingerprint of the whole wheel really solves this sort of tampering.
|
|
319
|
+
record_files(
|
|
320
|
+
root_dir=dest,
|
|
321
|
+
names=[
|
|
322
|
+
name
|
|
323
|
+
for name in zf.namelist()
|
|
324
|
+
if not name.endswith("/")
|
|
325
|
+
and data_rel_path != safe_commonpath((data_rel_path, name))
|
|
326
|
+
],
|
|
327
|
+
)
|
|
328
|
+
if os.path.isdir(data_path):
|
|
329
|
+
# 2. Spread
|
|
330
|
+
for entry in sorted(os.listdir(data_path)):
|
|
331
|
+
try:
|
|
332
|
+
dest_dir = install_paths[entry]
|
|
333
|
+
except KeyError as e:
|
|
334
|
+
raise WheelInstallError(
|
|
335
|
+
"The wheel at {wheel_path} is invalid and cannot be installed: "
|
|
336
|
+
"{err}".format(wheel_path=wheel.source, err=e)
|
|
337
|
+
)
|
|
338
|
+
entry_path = os.path.join(data_path, entry)
|
|
339
|
+
copied = [dst for _, dst in iter_copytree(entry_path, dest_dir)]
|
|
340
|
+
if copied and "scripts" == entry:
|
|
341
|
+
for script in copied:
|
|
342
|
+
chmod_plus_x(script)
|
|
343
|
+
if interpreter:
|
|
344
|
+
with closing(
|
|
345
|
+
FileInput(files=copied, inplace=True, mode="rb")
|
|
346
|
+
) as script_fi:
|
|
347
|
+
for line in cast("Iterator[bytes]", script_fi):
|
|
348
|
+
buffer = get_stdout_bytes_buffer()
|
|
349
|
+
if script_fi.isfirstline() and re.match(br"^#!pythonw?", line):
|
|
350
|
+
_, _, shebang_args = line.partition(b" ")
|
|
351
|
+
buffer.write(
|
|
352
|
+
"{shebang}\n".format(
|
|
353
|
+
shebang=interpreter.shebang(
|
|
354
|
+
args=shebang_args.decode("utf-8")
|
|
355
|
+
)
|
|
356
|
+
).encode("utf-8")
|
|
357
|
+
)
|
|
358
|
+
else:
|
|
359
|
+
# N.B.: These lines include the newline already.
|
|
360
|
+
buffer.write(cast(bytes, line))
|
|
361
|
+
|
|
362
|
+
record_files(
|
|
363
|
+
root_dir=dest_dir,
|
|
364
|
+
names=[
|
|
365
|
+
os.path.relpath(os.path.join(root, f), entry_path)
|
|
366
|
+
for root, _, files in os.walk(entry_path)
|
|
367
|
+
for f in files
|
|
368
|
+
],
|
|
369
|
+
)
|
|
370
|
+
shutil.rmtree(data_path)
|
|
371
|
+
elif wheel.install_paths:
|
|
372
|
+
record_data = wheel.metadata_files.read("RECORD")
|
|
373
|
+
if not record_data:
|
|
374
|
+
raise WheelInstallError(
|
|
375
|
+
"Cannot re-install installed wheel for {source} because it has no installation "
|
|
376
|
+
"RECORD metadata.".format(source=wheel.source)
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
for installed_file in Record.read(iter(record_data.decode("utf-8").splitlines())):
|
|
380
|
+
if installed_file.path == record_relpath:
|
|
381
|
+
# We'll generate a new RECORD below as needed.
|
|
382
|
+
continue
|
|
383
|
+
src_file = os.path.realpath(os.path.join(wheel.location, installed_file.path))
|
|
384
|
+
dst_file = os.path.realpath(os.path.join(dest, installed_file.path))
|
|
385
|
+
if dest == commonpath((dest, dst_file)):
|
|
386
|
+
safe_mkdir(os.path.dirname(dst_file))
|
|
387
|
+
shutil.copy(src_file, dst_file)
|
|
388
|
+
installed_files.append(installed_file)
|
|
389
|
+
continue
|
|
390
|
+
|
|
391
|
+
for path in InstallPaths.PATHS:
|
|
392
|
+
installed_path = wheel.install_paths[path]
|
|
393
|
+
if installed_path == commonpath((installed_path, src_file)):
|
|
394
|
+
dst_file = os.path.join(
|
|
395
|
+
install_paths[path], os.path.relpath(src_file, installed_path)
|
|
236
396
|
)
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if interpreter:
|
|
243
|
-
with closing(FileInput(files=copied, inplace=True, mode="rb")) as script_fi:
|
|
244
|
-
for line in cast("Iterator[bytes]", script_fi):
|
|
245
|
-
buffer = get_stdout_bytes_buffer()
|
|
246
|
-
if script_fi.isfirstline() and re.match(br"^#!pythonw?", line):
|
|
247
|
-
_, _, shebang_args = line.partition(b" ")
|
|
248
|
-
buffer.write(
|
|
249
|
-
"{shebang}\n".format(
|
|
250
|
-
shebang=interpreter.shebang(
|
|
251
|
-
args=shebang_args.decode("utf-8")
|
|
252
|
-
)
|
|
253
|
-
).encode("utf-8")
|
|
254
|
-
)
|
|
255
|
-
else:
|
|
256
|
-
# N.B.: These lines include the newline already.
|
|
257
|
-
buffer.write(cast(bytes, line))
|
|
258
|
-
|
|
259
|
-
record_files(
|
|
260
|
-
root_dir=dest_dir,
|
|
261
|
-
names=[
|
|
262
|
-
os.path.relpath(os.path.join(root, f), entry_path)
|
|
263
|
-
for root, _, files in os.walk(entry_path)
|
|
264
|
-
for f in files
|
|
265
|
-
],
|
|
397
|
+
break
|
|
398
|
+
else:
|
|
399
|
+
raise WheelInstallError(
|
|
400
|
+
"Encountered a file from {source} with no identifiable target install path: "
|
|
401
|
+
"{file}".format(source=wheel.source, file=installed_file.path)
|
|
266
402
|
)
|
|
267
|
-
|
|
403
|
+
|
|
404
|
+
safe_mkdir(os.path.dirname(dst_file))
|
|
405
|
+
shutil.copy(src_file, dst_file)
|
|
406
|
+
installed_files.append(
|
|
407
|
+
InstalledFile(
|
|
408
|
+
path=os.path.relpath(dst_file, dest),
|
|
409
|
+
hash=installed_file.hash,
|
|
410
|
+
size=installed_file.size,
|
|
411
|
+
)
|
|
412
|
+
)
|
|
413
|
+
else:
|
|
414
|
+
raise AssertionError(reportable_unexpected_error_msg())
|
|
268
415
|
|
|
269
416
|
if compile:
|
|
270
417
|
args = [
|
|
@@ -285,7 +432,7 @@ def install_wheel(
|
|
|
285
432
|
if process.returncode != 0:
|
|
286
433
|
pex_warnings.warn(
|
|
287
434
|
"Failed to compile some .py files for install of {wheel} to {dest}:\n"
|
|
288
|
-
"{stderr}".format(wheel=
|
|
435
|
+
"{stderr}".format(wheel=wheel.source, dest=dest, stderr=stderr.decode("utf-8"))
|
|
289
436
|
)
|
|
290
437
|
for root, _, files in os.walk(commonpath(py_files)):
|
|
291
438
|
for f in files:
|
|
@@ -316,8 +463,6 @@ def install_wheel(
|
|
|
316
463
|
installed_files.append(InstalledFile(path=record_relpath, hash=None, size=None))
|
|
317
464
|
Record.write(dst=record_abspath, installed_files=installed_files)
|
|
318
465
|
|
|
319
|
-
return wheel
|
|
320
|
-
|
|
321
466
|
|
|
322
467
|
def install_scripts(
|
|
323
468
|
install_paths, # type: InstallPaths
|
pex/pip/installation.py
CHANGED
|
@@ -221,7 +221,7 @@ def _install_wheel(wheel_path):
|
|
|
221
221
|
with atomic_directory(installed_wheel_dir) as atomic_dir:
|
|
222
222
|
if not atomic_dir.is_finalized():
|
|
223
223
|
installed_wheel = pep_427.install_wheel_chroot(
|
|
224
|
-
|
|
224
|
+
wheel=wheel_path, destination=atomic_dir.work_dir
|
|
225
225
|
)
|
|
226
226
|
runtime_key_dir = InstalledWheelDir.create(
|
|
227
227
|
wheel_name=wheel_name,
|
pex/pip/tool.py
CHANGED
|
@@ -750,7 +750,7 @@ class Pip(object):
|
|
|
750
750
|
build_configuration=BuildConfiguration.create(allow_builds=False),
|
|
751
751
|
).wait()
|
|
752
752
|
for wheel in glob.glob(os.path.join(atomic_dir.work_dir, "*.whl")):
|
|
753
|
-
install_wheel_interpreter(
|
|
753
|
+
install_wheel_interpreter(wheel=wheel, interpreter=pip_interpreter)
|
|
754
754
|
|
|
755
755
|
def spawn_build_wheels(
|
|
756
756
|
self,
|
|
@@ -17,8 +17,10 @@ from pex.resolve.resolver_configuration import (
|
|
|
17
17
|
PexRepositoryConfiguration,
|
|
18
18
|
PreResolvedConfiguration,
|
|
19
19
|
PylockRepositoryConfiguration,
|
|
20
|
+
VenvRepositoryConfiguration,
|
|
20
21
|
)
|
|
21
22
|
from pex.resolve.resolvers import ResolveResult
|
|
23
|
+
from pex.resolve.venv_resolver import resolve_from_venv
|
|
22
24
|
from pex.resolver import resolve as resolve_via_pip
|
|
23
25
|
from pex.result import try_
|
|
24
26
|
from pex.targets import Targets
|
|
@@ -139,6 +141,24 @@ def resolve(
|
|
|
139
141
|
result_type=result_type,
|
|
140
142
|
dependency_configuration=dependency_configuration,
|
|
141
143
|
)
|
|
144
|
+
elif isinstance(resolver_configuration, VenvRepositoryConfiguration):
|
|
145
|
+
with TRACER.timed(
|
|
146
|
+
"Resolving requirements from venv at {venv}.".format(
|
|
147
|
+
venv=resolver_configuration.venv.venv_dir
|
|
148
|
+
)
|
|
149
|
+
):
|
|
150
|
+
return try_(
|
|
151
|
+
resolve_from_venv(
|
|
152
|
+
targets=targets,
|
|
153
|
+
venv=resolver_configuration.venv,
|
|
154
|
+
requirement_configuration=requirement_configuration,
|
|
155
|
+
pip_configuration=resolver_configuration.pip_configuration,
|
|
156
|
+
compile=compile_pyc,
|
|
157
|
+
ignore_errors=ignore_errors,
|
|
158
|
+
result_type=result_type,
|
|
159
|
+
dependency_configuration=dependency_configuration,
|
|
160
|
+
)
|
|
161
|
+
)
|
|
142
162
|
else:
|
|
143
163
|
with TRACER.timed("Resolving requirements."):
|
|
144
164
|
return resolve_via_pip(
|
|
@@ -58,20 +58,24 @@ class ConfiguredResolver(Resolver):
|
|
|
58
58
|
transitive=None, # type: Optional[bool]
|
|
59
59
|
extra_resolver_requirements=None, # type: Optional[Tuple[Requirement, ...]]
|
|
60
60
|
result_type=InstallableType.INSTALLED_WHEEL_CHROOT, # type: InstallableType.Value
|
|
61
|
+
constraint_files=None, # type: Optional[Iterable[str]]
|
|
62
|
+
compile=False, # type: bool
|
|
63
|
+
ignore_errors=False, # type: bool
|
|
61
64
|
):
|
|
62
65
|
# type: (...) -> ResolveResult
|
|
63
66
|
return resolver.resolve(
|
|
64
67
|
targets=targets,
|
|
65
68
|
requirements=requirements,
|
|
66
|
-
|
|
69
|
+
constraint_files=constraint_files,
|
|
70
|
+
allow_prereleases=self.pip_configuration.allow_prereleases,
|
|
67
71
|
transitive=transitive if transitive is not None else self.pip_configuration.transitive,
|
|
68
72
|
repos_configuration=self.pip_configuration.repos_configuration,
|
|
69
73
|
resolver_version=self.pip_configuration.resolver_version,
|
|
70
74
|
network_configuration=self.pip_configuration.network_configuration,
|
|
71
75
|
build_configuration=self.pip_configuration.build_configuration,
|
|
72
|
-
compile=
|
|
76
|
+
compile=compile,
|
|
73
77
|
max_parallel_jobs=self.pip_configuration.max_jobs,
|
|
74
|
-
ignore_errors=
|
|
78
|
+
ignore_errors=ignore_errors,
|
|
75
79
|
verify_wheels=True,
|
|
76
80
|
pip_version=pip_version or self.pip_configuration.version,
|
|
77
81
|
resolver=self,
|
|
@@ -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)
|
|
@@ -12,6 +12,7 @@ from pex.pep_503 import ProjectName
|
|
|
12
12
|
from pex.pip.version import PipVersion, PipVersionValue
|
|
13
13
|
from pex.resolve.package_repository import ReposConfiguration
|
|
14
14
|
from pex.typing import TYPE_CHECKING
|
|
15
|
+
from pex.venv.virtualenv import Virtualenv
|
|
15
16
|
|
|
16
17
|
if TYPE_CHECKING:
|
|
17
18
|
from typing import Callable, FrozenSet, Iterable, Optional, Tuple, Union
|
|
@@ -246,3 +247,19 @@ class PreResolvedConfiguration(object):
|
|
|
246
247
|
def network_configuration(self):
|
|
247
248
|
# type: () -> NetworkConfiguration
|
|
248
249
|
return self.pip_configuration.network_configuration
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@attr.s(frozen=True)
|
|
253
|
+
class VenvRepositoryConfiguration(object):
|
|
254
|
+
venv = attr.ib() # type: Virtualenv
|
|
255
|
+
pip_configuration = attr.ib() # type: PipConfiguration
|
|
256
|
+
|
|
257
|
+
@property
|
|
258
|
+
def repos_configuration(self):
|
|
259
|
+
# type: () -> ReposConfiguration
|
|
260
|
+
return self.pip_configuration.repos_configuration
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def network_configuration(self):
|
|
264
|
+
# type: () -> NetworkConfiguration
|
|
265
|
+
return self.pip_configuration.network_configuration
|