pex 2.58.0__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.

Files changed (57) hide show
  1. pex/bin/pex.py +1 -0
  2. pex/build_system/pep_517.py +4 -1
  3. pex/cli/commands/venv.py +5 -1
  4. pex/dependency_configuration.py +6 -2
  5. pex/dist_metadata.py +30 -8
  6. pex/docs/html/_pagefind/fragment/en_23dc437.pf_fragment +0 -0
  7. pex/docs/html/_pagefind/fragment/{en_a1e44ad.pf_fragment → en_3b86fa5.pf_fragment} +0 -0
  8. pex/docs/html/_pagefind/fragment/en_65c3a5b.pf_fragment +0 -0
  9. pex/docs/html/_pagefind/fragment/en_6c2e6ff.pf_fragment +0 -0
  10. pex/docs/html/_pagefind/fragment/{en_cb46368.pf_fragment → en_7320871.pf_fragment} +0 -0
  11. pex/docs/html/_pagefind/fragment/en_7cfdecb.pf_fragment +0 -0
  12. pex/docs/html/_pagefind/fragment/en_89e2d7c.pf_fragment +0 -0
  13. pex/docs/html/_pagefind/fragment/{en_1bcc8ee.pf_fragment → en_d4675e3.pf_fragment} +0 -0
  14. pex/docs/html/_pagefind/index/en_34a4497.pf_index +0 -0
  15. pex/docs/html/_pagefind/pagefind-entry.json +1 -1
  16. pex/docs/html/_pagefind/pagefind.en_4ade7b3598.pf_meta +0 -0
  17. pex/docs/html/_static/documentation_options.js +1 -1
  18. pex/docs/html/api/vars.html +5 -5
  19. pex/docs/html/buildingpex.html +5 -5
  20. pex/docs/html/genindex.html +5 -5
  21. pex/docs/html/index.html +5 -5
  22. pex/docs/html/recipes.html +5 -5
  23. pex/docs/html/scie.html +5 -5
  24. pex/docs/html/search.html +5 -5
  25. pex/docs/html/whatispex.html +5 -5
  26. pex/environment.py +17 -3
  27. pex/pep_425.py +11 -2
  28. pex/pep_427.py +218 -73
  29. pex/pip/installation.py +1 -1
  30. pex/pip/tool.py +1 -1
  31. pex/resolve/configured_resolve.py +20 -0
  32. pex/resolve/configured_resolver.py +7 -3
  33. pex/resolve/pex_repository_resolver.py +1 -1
  34. pex/resolve/resolver_configuration.py +17 -0
  35. pex/resolve/resolver_options.py +88 -16
  36. pex/resolve/resolvers.py +3 -0
  37. pex/resolve/target_options.py +18 -2
  38. pex/resolve/venv_resolver.py +446 -0
  39. pex/resolver.py +2 -4
  40. pex/targets.py +9 -4
  41. pex/vendor/__main__.py +1 -1
  42. pex/version.py +1 -1
  43. pex/wheel.py +89 -27
  44. {pex-2.58.0.dist-info → pex-2.59.0.dist-info}/METADATA +4 -4
  45. {pex-2.58.0.dist-info → pex-2.59.0.dist-info}/RECORD +50 -49
  46. pex/docs/html/_pagefind/fragment/en_17c092c.pf_fragment +0 -0
  47. pex/docs/html/_pagefind/fragment/en_4f6b776.pf_fragment +0 -0
  48. pex/docs/html/_pagefind/fragment/en_7be5753.pf_fragment +0 -0
  49. pex/docs/html/_pagefind/fragment/en_dbfc5c3.pf_fragment +0 -0
  50. pex/docs/html/_pagefind/fragment/en_ea2f1cd.pf_fragment +0 -0
  51. pex/docs/html/_pagefind/index/en_a415613.pf_index +0 -0
  52. pex/docs/html/_pagefind/pagefind.en_3d1b9e5425.pf_meta +0 -0
  53. {pex-2.58.0.dist-info → pex-2.59.0.dist-info}/WHEEL +0 -0
  54. {pex-2.58.0.dist-info → pex-2.59.0.dist-info}/entry_points.txt +0 -0
  55. {pex-2.58.0.dist-info → pex-2.59.0.dist-info}/licenses/LICENSE +0 -0
  56. {pex-2.58.0.dist-info → pex-2.59.0.dist-info}/pylock/pylock.toml +0 -0
  57. {pex-2.58.0.dist-info → pex-2.59.0.dist-info}/top_level.txt +0 -0
pex/pep_425.py CHANGED
@@ -6,11 +6,12 @@ from __future__ import absolute_import
6
6
  import itertools
7
7
  import os.path
8
8
 
9
- from pex.dist_metadata import is_wheel
9
+ from pex.dist_metadata import Distribution, is_wheel
10
10
  from pex.orderedset import OrderedSet
11
11
  from pex.rank import Rank
12
12
  from pex.third_party.packaging.tags import Tag, parse_tag
13
13
  from pex.typing import TYPE_CHECKING, cast, overload
14
+ from pex.wheel import WHEEL
14
15
 
15
16
  if TYPE_CHECKING:
16
17
  from typing import (
@@ -66,7 +67,13 @@ class CompatibilityTags(object):
66
67
 
67
68
  @classmethod
68
69
  def from_wheel(cls, wheel):
69
- # type: (Text) -> CompatibilityTags
70
+ # type: (Union[Text, Distribution]) -> CompatibilityTags
71
+
72
+ if isinstance(wheel, Distribution):
73
+ if not is_wheel(wheel.location):
74
+ return cls(tags=WHEEL.from_distribution(wheel).tags)
75
+ wheel = wheel.location
76
+
70
77
  if not is_wheel(wheel):
71
78
  raise ValueError(
72
79
  "Can only calculate wheel tags from a filename that ends in .whl per "
@@ -74,6 +81,7 @@ class CompatibilityTags(object):
74
81
  wheel=wheel
75
82
  )
76
83
  )
84
+
77
85
  wheel_stem, _ = os.path.splitext(os.path.basename(wheel))
78
86
  # Wheel filename format: https://peps.python.org/pep-0427/#file-name-convention
79
87
  # `{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl`
@@ -86,6 +94,7 @@ class CompatibilityTags(object):
86
94
  pattern=pattern, wheel=wheel
87
95
  )
88
96
  )
97
+
89
98
  return cls(tags=tuple(parse_tag("-".join(wheel_components[-3:]))))
90
99
 
91
100
  @classmethod
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
- wheel_path, # type: str
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
- wheel = install_wheel(
135
- wheel_path,
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 = wheel.metadata_files.metadata_file_rel_path("RECORD")
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=wheel.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
- wheel_path, # type: str
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: (...) -> Wheel
258
+ # type: (...) -> None
163
259
 
164
- return install_wheel(
165
- wheel_path,
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
- wheel_path, # type: str
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: (...) -> Wheel
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
- with open_zip(wheel_path) as zf:
214
- zf.extractall(dest)
215
- # TODO(John Sirois): Consider verifying signatures.
216
- # N.B.: Pip does not and its also not clear what good this does. A zip can be easily poked
217
- # on a per-entry basis allowing forging a RECORD entry and its associated file. Only an
218
- # outer fingerprint of the whole wheel really solves this sort of tampering.
219
- record_files(
220
- root_dir=dest,
221
- names=[
222
- name
223
- for name in zf.namelist()
224
- if not name.endswith("/")
225
- and data_rel_path != safe_commonpath((data_rel_path, name))
226
- ],
227
- )
228
- if os.path.isdir(data_path):
229
- for entry in sorted(os.listdir(data_path)):
230
- try:
231
- dest_dir = install_paths[entry]
232
- except KeyError as e:
233
- raise WheelInstallError(
234
- "The wheel at {wheel_path} is invalid and cannot be installed: "
235
- "{err}".format(wheel_path=wheel_path, err=e)
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
- entry_path = os.path.join(data_path, entry)
238
- copied = [dst for _, dst in iter_copytree(entry_path, dest_dir)]
239
- if copied and "scripts" == entry:
240
- for script in copied:
241
- chmod_plus_x(script)
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
- shutil.rmtree(data_path)
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=wheel_path, dest=dest, stderr=stderr.decode("utf-8"))
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
- wheel_path=wheel_path, destination=atomic_dir.work_dir
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(wheel_path=wheel, interpreter=pip_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
- allow_prereleases=False,
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=False,
76
+ compile=compile,
73
77
  max_parallel_jobs=self.pip_configuration.max_jobs,
74
- ignore_errors=False,
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 whell files
83
+ # currently critical to _not_ PEXEnvironment.mount(...) if you want to resolve wheel files
84
84
  # instead of installed wheel chroots.
85
85
  pex_env = (
86
86
  PEXEnvironment(pex, target=target)
@@ -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