pex 2.54.2__py2.py3-none-any.whl → 2.69.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pex might be problematic. Click here for more details.

Files changed (180) hide show
  1. pex/auth.py +1 -1
  2. pex/bin/pex.py +15 -2
  3. pex/build_backend/configuration.py +5 -5
  4. pex/build_backend/wrap.py +27 -23
  5. pex/build_system/pep_517.py +4 -1
  6. pex/cache/dirs.py +17 -12
  7. pex/cli/commands/lock.py +302 -165
  8. pex/cli/commands/pip/core.py +4 -12
  9. pex/cli/commands/pip/wheel.py +1 -1
  10. pex/cli/commands/run.py +13 -20
  11. pex/cli/commands/venv.py +85 -16
  12. pex/cli/pex.py +11 -4
  13. pex/common.py +57 -7
  14. pex/compatibility.py +1 -1
  15. pex/dependency_configuration.py +87 -15
  16. pex/dist_metadata.py +143 -25
  17. pex/docs/html/_pagefind/fragment/en_4250138.pf_fragment +0 -0
  18. pex/docs/html/_pagefind/fragment/en_7125dad.pf_fragment +0 -0
  19. pex/docs/html/_pagefind/fragment/en_785d562.pf_fragment +0 -0
  20. pex/docs/html/_pagefind/fragment/en_8e94bb8.pf_fragment +0 -0
  21. pex/docs/html/_pagefind/fragment/en_a0396bb.pf_fragment +0 -0
  22. pex/docs/html/_pagefind/fragment/en_a8a3588.pf_fragment +0 -0
  23. pex/docs/html/_pagefind/fragment/en_c07d988.pf_fragment +0 -0
  24. pex/docs/html/_pagefind/fragment/en_d718411.pf_fragment +0 -0
  25. pex/docs/html/_pagefind/index/en_a2e3c5e.pf_index +0 -0
  26. pex/docs/html/_pagefind/pagefind-entry.json +1 -1
  27. pex/docs/html/_pagefind/pagefind.en_4ce1afa9e3.pf_meta +0 -0
  28. pex/docs/html/_static/documentation_options.js +1 -1
  29. pex/docs/html/_static/pygments.css +164 -146
  30. pex/docs/html/_static/styles/furo.css +1 -1
  31. pex/docs/html/_static/styles/furo.css.map +1 -1
  32. pex/docs/html/api/vars.html +25 -34
  33. pex/docs/html/buildingpex.html +25 -34
  34. pex/docs/html/genindex.html +24 -33
  35. pex/docs/html/index.html +25 -34
  36. pex/docs/html/recipes.html +25 -34
  37. pex/docs/html/scie.html +25 -34
  38. pex/docs/html/search.html +24 -33
  39. pex/docs/html/whatispex.html +25 -34
  40. pex/entry_points_txt.py +98 -0
  41. pex/environment.py +54 -33
  42. pex/finders.py +1 -1
  43. pex/hashing.py +71 -9
  44. pex/installed_wheel.py +141 -0
  45. pex/interpreter.py +41 -38
  46. pex/interpreter_constraints.py +25 -25
  47. pex/interpreter_implementation.py +40 -0
  48. pex/jobs.py +13 -6
  49. pex/pep_376.py +68 -384
  50. pex/pep_425.py +11 -2
  51. pex/pep_427.py +937 -205
  52. pex/pep_508.py +4 -5
  53. pex/pex_builder.py +5 -8
  54. pex/pex_info.py +14 -9
  55. pex/pip/dependencies/__init__.py +85 -13
  56. pex/pip/dependencies/requires.py +38 -3
  57. pex/pip/foreign_platform/__init__.py +4 -3
  58. pex/pip/installation.py +2 -2
  59. pex/pip/local_project.py +6 -14
  60. pex/pip/package_repositories/__init__.py +78 -0
  61. pex/pip/package_repositories/link_collector.py +96 -0
  62. pex/pip/tool.py +139 -33
  63. pex/pip/vcs.py +109 -43
  64. pex/pip/version.py +8 -1
  65. pex/requirements.py +121 -16
  66. pex/resolve/config.py +5 -1
  67. pex/resolve/configured_resolve.py +32 -10
  68. pex/resolve/configured_resolver.py +10 -39
  69. pex/resolve/downloads.py +4 -3
  70. pex/resolve/lock_downloader.py +16 -23
  71. pex/resolve/lock_resolver.py +41 -51
  72. pex/resolve/locked_resolve.py +89 -32
  73. pex/resolve/locker.py +145 -101
  74. pex/resolve/locker_patches.py +123 -197
  75. pex/resolve/lockfile/create.py +232 -87
  76. pex/resolve/lockfile/download_manager.py +5 -1
  77. pex/resolve/lockfile/json_codec.py +103 -28
  78. pex/resolve/lockfile/model.py +13 -35
  79. pex/resolve/lockfile/pep_751.py +117 -98
  80. pex/resolve/lockfile/requires_dist.py +17 -262
  81. pex/resolve/lockfile/subset.py +11 -0
  82. pex/resolve/lockfile/targets.py +445 -0
  83. pex/resolve/lockfile/updater.py +22 -10
  84. pex/resolve/package_repository.py +406 -0
  85. pex/resolve/pex_repository_resolver.py +1 -1
  86. pex/resolve/pre_resolved_resolver.py +19 -16
  87. pex/resolve/project.py +233 -47
  88. pex/resolve/requirement_configuration.py +28 -10
  89. pex/resolve/resolver_configuration.py +18 -32
  90. pex/resolve/resolver_options.py +234 -28
  91. pex/resolve/resolvers.py +3 -12
  92. pex/resolve/target_options.py +18 -2
  93. pex/resolve/target_system.py +908 -0
  94. pex/resolve/venv_resolver.py +670 -0
  95. pex/resolver.py +673 -209
  96. pex/scie/__init__.py +40 -1
  97. pex/scie/model.py +2 -0
  98. pex/scie/science.py +25 -3
  99. pex/sdist.py +219 -0
  100. pex/sh_boot.py +24 -21
  101. pex/sysconfig.py +5 -3
  102. pex/targets.py +31 -10
  103. pex/third_party/__init__.py +1 -1
  104. pex/tools/commands/repository.py +48 -25
  105. pex/vendor/__init__.py +4 -9
  106. pex/vendor/__main__.py +65 -41
  107. pex/vendor/_vendored/ansicolors/.layout.json +1 -1
  108. pex/vendor/_vendored/ansicolors/ansicolors-1.1.8.dist-info/RECORD +11 -0
  109. pex/vendor/_vendored/ansicolors/ansicolors-1.1.8.pex-info/original-whl-info.json +1 -0
  110. pex/vendor/_vendored/appdirs/.layout.json +1 -1
  111. pex/vendor/_vendored/appdirs/appdirs-1.4.4.dist-info/RECORD +7 -0
  112. pex/vendor/_vendored/appdirs/appdirs-1.4.4.pex-info/original-whl-info.json +1 -0
  113. pex/vendor/_vendored/attrs/.layout.json +1 -1
  114. pex/vendor/_vendored/attrs/attrs-21.5.0.dev0.dist-info/RECORD +37 -0
  115. pex/vendor/_vendored/attrs/attrs-21.5.0.dev0.pex-info/original-whl-info.json +1 -0
  116. pex/vendor/_vendored/packaging_20_9/.layout.json +1 -1
  117. pex/vendor/_vendored/packaging_20_9/packaging-20.9.dist-info/RECORD +20 -0
  118. pex/vendor/_vendored/packaging_20_9/packaging-20.9.pex-info/original-whl-info.json +1 -0
  119. pex/vendor/_vendored/packaging_20_9/pyparsing-2.4.7.dist-info/RECORD +7 -0
  120. pex/vendor/_vendored/packaging_20_9/pyparsing-2.4.7.pex-info/original-whl-info.json +1 -0
  121. pex/vendor/_vendored/packaging_21_3/.layout.json +1 -1
  122. pex/vendor/_vendored/packaging_21_3/packaging-21.3.dist-info/RECORD +20 -0
  123. pex/vendor/_vendored/packaging_21_3/packaging-21.3.pex-info/original-whl-info.json +1 -0
  124. pex/vendor/_vendored/packaging_21_3/pyparsing-3.0.7.dist-info/RECORD +18 -0
  125. pex/vendor/_vendored/packaging_21_3/pyparsing-3.0.7.pex-info/original-whl-info.json +1 -0
  126. pex/vendor/_vendored/packaging_24_0/.layout.json +1 -1
  127. pex/vendor/_vendored/packaging_24_0/packaging-24.0.dist-info/RECORD +22 -0
  128. pex/vendor/_vendored/packaging_24_0/packaging-24.0.pex-info/original-whl-info.json +1 -0
  129. pex/vendor/_vendored/packaging_25_0/.layout.json +1 -1
  130. pex/vendor/_vendored/packaging_25_0/packaging-25.0.dist-info/RECORD +24 -0
  131. pex/vendor/_vendored/packaging_25_0/packaging-25.0.pex-info/original-whl-info.json +1 -0
  132. pex/vendor/_vendored/pip/.layout.json +1 -1
  133. pex/vendor/_vendored/pip/pip/_vendor/certifi/cacert.pem +63 -1
  134. pex/vendor/_vendored/pip/pip-20.3.4.dist-info/RECORD +388 -0
  135. pex/vendor/_vendored/pip/pip-20.3.4.pex-info/original-whl-info.json +1 -0
  136. pex/vendor/_vendored/setuptools/.layout.json +1 -1
  137. pex/vendor/_vendored/setuptools/setuptools-44.0.0+3acb925dd708430aeaf197ea53ac8a752f7c1863.dist-info/RECORD +107 -0
  138. pex/vendor/_vendored/setuptools/setuptools-44.0.0+3acb925dd708430aeaf197ea53ac8a752f7c1863.pex-info/original-whl-info.json +1 -0
  139. pex/vendor/_vendored/toml/.layout.json +1 -1
  140. pex/vendor/_vendored/toml/toml-0.10.2.dist-info/RECORD +11 -0
  141. pex/vendor/_vendored/toml/toml-0.10.2.pex-info/original-whl-info.json +1 -0
  142. pex/vendor/_vendored/tomli/.layout.json +1 -1
  143. pex/vendor/_vendored/tomli/tomli-2.0.1.dist-info/RECORD +10 -0
  144. pex/vendor/_vendored/tomli/tomli-2.0.1.pex-info/original-whl-info.json +1 -0
  145. pex/venv/installer.py +46 -19
  146. pex/venv/venv_pex.py +6 -3
  147. pex/version.py +1 -1
  148. pex/wheel.py +188 -40
  149. pex/whl.py +67 -0
  150. pex/windows/__init__.py +14 -11
  151. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/METADATA +6 -5
  152. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/RECORD +157 -133
  153. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/entry_points.txt +1 -0
  154. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/pylock/pylock.toml +1 -1
  155. pex/docs/html/_pagefind/fragment/en_42c9d8c.pf_fragment +0 -0
  156. pex/docs/html/_pagefind/fragment/en_45dd5a2.pf_fragment +0 -0
  157. pex/docs/html/_pagefind/fragment/en_4ca74d2.pf_fragment +0 -0
  158. pex/docs/html/_pagefind/fragment/en_77273d5.pf_fragment +0 -0
  159. pex/docs/html/_pagefind/fragment/en_87a59c5.pf_fragment +0 -0
  160. pex/docs/html/_pagefind/fragment/en_8dc89b5.pf_fragment +0 -0
  161. pex/docs/html/_pagefind/fragment/en_9d1319b.pf_fragment +0 -0
  162. pex/docs/html/_pagefind/fragment/en_e55df9d.pf_fragment +0 -0
  163. pex/docs/html/_pagefind/index/en_1e98c6f.pf_index +0 -0
  164. pex/docs/html/_pagefind/pagefind.en_d1c488ecae.pf_meta +0 -0
  165. pex/vendor/_vendored/ansicolors/ansicolors-1.1.8.dist-info/INSTALLER +0 -1
  166. pex/vendor/_vendored/appdirs/appdirs-1.4.4.dist-info/INSTALLER +0 -1
  167. pex/vendor/_vendored/attrs/attrs-21.5.0.dev0.dist-info/INSTALLER +0 -1
  168. pex/vendor/_vendored/packaging_20_9/packaging-20.9.dist-info/INSTALLER +0 -1
  169. pex/vendor/_vendored/packaging_20_9/pyparsing-2.4.7.dist-info/INSTALLER +0 -1
  170. pex/vendor/_vendored/packaging_21_3/packaging-21.3.dist-info/INSTALLER +0 -1
  171. pex/vendor/_vendored/packaging_21_3/pyparsing-3.0.7.dist-info/INSTALLER +0 -1
  172. pex/vendor/_vendored/packaging_24_0/packaging-24.0.dist-info/INSTALLER +0 -1
  173. pex/vendor/_vendored/packaging_25_0/packaging-25.0.dist-info/INSTALLER +0 -1
  174. pex/vendor/_vendored/pip/pip-20.3.4.dist-info/INSTALLER +0 -1
  175. pex/vendor/_vendored/setuptools/setuptools-44.0.0+3acb925dd708430aeaf197ea53ac8a752f7c1863.dist-info/INSTALLER +0 -1
  176. pex/vendor/_vendored/toml/toml-0.10.2.dist-info/INSTALLER +0 -1
  177. pex/vendor/_vendored/tomli/tomli-2.0.1.dist-info/INSTALLER +0 -1
  178. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/WHEEL +0 -0
  179. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/licenses/LICENSE +0 -0
  180. {pex-2.54.2.dist-info → pex-2.69.0.dist-info}/top_level.txt +0 -0
@@ -5,22 +5,26 @@ from __future__ import absolute_import
5
5
 
6
6
  import glob
7
7
  import os
8
+ import sys
8
9
  import tempfile
9
10
  from argparse import Action, ArgumentError, ArgumentTypeError, Namespace, _ActionsContainer
11
+ from collections import OrderedDict, defaultdict
10
12
 
11
13
  from pex import pex_warnings
12
14
  from pex.argparse import HandleBoolAction
15
+ from pex.common import pluralize
13
16
  from pex.dist_metadata import Requirement, is_sdist, is_wheel
14
17
  from pex.fetcher import initialize_ssl_context
15
18
  from pex.network_configuration import NetworkConfiguration
16
19
  from pex.orderedset import OrderedSet
20
+ from pex.os import is_exe
17
21
  from pex.pep_503 import ProjectName
18
22
  from pex.pip.version import PipVersion, PipVersionValue
19
23
  from pex.resolve.lockfile import json_codec
20
24
  from pex.resolve.lockfile.model import Lockfile
25
+ from pex.resolve.package_repository import PYPI, Repo, ReposConfiguration, Scope
21
26
  from pex.resolve.path_mappings import PathMapping, PathMappings
22
27
  from pex.resolve.resolver_configuration import (
23
- PYPI,
24
28
  BuildConfiguration,
25
29
  LockRepositoryConfiguration,
26
30
  PexRepositoryConfiguration,
@@ -28,41 +32,81 @@ from pex.resolve.resolver_configuration import (
28
32
  PipLog,
29
33
  PreResolvedConfiguration,
30
34
  PylockRepositoryConfiguration,
31
- ReposConfiguration,
32
35
  ResolverVersion,
36
+ VenvRepositoryConfiguration,
33
37
  )
34
38
  from pex.result import Error
35
39
  from pex.tracer import TRACER
36
40
  from pex.typing import TYPE_CHECKING, cast
41
+ from pex.venv.virtualenv import InvalidVirtualenvError, Virtualenv
37
42
 
38
43
  if TYPE_CHECKING:
39
- from typing import List, Optional, Union
44
+ from typing import DefaultDict, Iterable, List, Mapping, Optional, Tuple, Union
40
45
 
41
46
 
42
- class _ManylinuxAction(Action):
47
+ class _HandleTransitiveAction(Action):
43
48
  def __init__(self, *args, **kwargs):
44
- kwargs["nargs"] = "?"
45
- super(_ManylinuxAction, self).__init__(*args, **kwargs)
49
+ kwargs["nargs"] = 0
50
+ super(_HandleTransitiveAction, self).__init__(*args, **kwargs)
46
51
 
47
52
  def __call__(self, parser, namespace, value, option_str=None):
48
- if option_str.startswith("--no"):
49
- setattr(namespace, self.dest, None)
50
- elif value.startswith("manylinux"):
51
- setattr(namespace, self.dest, value)
52
- else:
53
- raise ArgumentTypeError(
54
- "Please specify a manylinux standard; ie: --manylinux=manylinux1. "
55
- "Given {}".format(value)
56
- )
53
+ setattr(namespace, self.dest, option_str == "--transitive")
57
54
 
58
55
 
59
- class _HandleTransitiveAction(Action):
56
+ class _ResolveVenvAction(Action):
60
57
  def __init__(self, *args, **kwargs):
61
- kwargs["nargs"] = 0
62
- super(_HandleTransitiveAction, self).__init__(*args, **kwargs)
58
+ kwargs["nargs"] = "?"
59
+ super(_ResolveVenvAction, self).__init__(*args, default=[], **kwargs)
63
60
 
64
61
  def __call__(self, parser, namespace, value, option_str=None):
65
- setattr(namespace, self.dest, option_str == "--transitive")
62
+ venvs = getattr(namespace, self.dest)
63
+ if value:
64
+ if not os.path.exists(value):
65
+ raise ArgumentError(
66
+ argument=self,
67
+ message=(
68
+ "The {option} {value} does not point to an existing path.".format(
69
+ option=option_str, value=value
70
+ )
71
+ ),
72
+ )
73
+
74
+ venv = None # type: Optional[Virtualenv]
75
+ if is_exe(value):
76
+ venv = Virtualenv.enclosing(value)
77
+ else:
78
+ try:
79
+ venv = Virtualenv(value)
80
+ except InvalidVirtualenvError:
81
+ pass
82
+ if not venv:
83
+ raise ArgumentError(
84
+ argument=self,
85
+ message=(
86
+ "The {option} {value} is not a valid virtual environment interpreter "
87
+ "path.".format(option=option_str, value=value)
88
+ ),
89
+ )
90
+ venvs.append(venv)
91
+ else:
92
+ current_venv = Virtualenv.enclosing(python=sys.executable)
93
+ if not current_venv:
94
+ try:
95
+ current_venv = Virtualenv(venv_dir=".")
96
+ except InvalidVirtualenvError:
97
+ raise ArgumentError(
98
+ argument=self,
99
+ message=(
100
+ "The {option} option was requested but Pex is not currently running "
101
+ "from a venv to use as the implicit {option} nor is the current "
102
+ "directory a venv.\n"
103
+ "You'll need to specify the path to a virtual environment directory or "
104
+ "virtual environment interpreter as an argument.".format(
105
+ option=option_str
106
+ )
107
+ ),
108
+ )
109
+ venvs.append(current_venv)
66
110
 
67
111
 
68
112
  def register(
@@ -71,6 +115,7 @@ def register(
71
115
  include_pex_lock=False, # type: bool
72
116
  include_pylock=False, # type: bool
73
117
  include_pre_resolved=False, # type: bool
118
+ include_venv_repository=False, # type: bool
74
119
  ):
75
120
  # type: (...) -> None
76
121
  """Register resolver configuration options with the given parser.
@@ -81,6 +126,7 @@ def register(
81
126
  :param include_pylock: Whether to include the `--pylock` / `--pep-751-lock` option.
82
127
  :param include_pre_resolved: Whether to include the `--pre-resolved-dist` and
83
128
  `--pre-resolved-dists` options.
129
+ :param include_venv_repository: Whether to include the `--venv-repository` option.
84
130
  """
85
131
 
86
132
  default_resolver_configuration = PipConfiguration()
@@ -191,6 +237,8 @@ def register(
191
237
  repository_types += 1
192
238
  if include_pre_resolved:
193
239
  repository_types += 1
240
+ if include_venv_repository:
241
+ repository_types += 1
194
242
 
195
243
  repository_choice = parser.add_mutually_exclusive_group() if repository_types > 1 else parser
196
244
  if include_pex_repository:
@@ -270,6 +318,23 @@ def register(
270
318
  "default, Pex will ensure the dependencies added form a closure."
271
319
  ),
272
320
  )
321
+ if include_venv_repository:
322
+ repository_choice.add_argument(
323
+ "--venv-repository",
324
+ dest="venv_repositories",
325
+ action=_ResolveVenvAction,
326
+ help=(
327
+ "Resolve requirements from the given virtual environment instead of from "
328
+ "--index servers, --find-links repos or a --lock file. The virtual environment to "
329
+ "resolve from can be specified as the path to the venv or the path of its"
330
+ "interpreter. If no value is specified, the current active venv is used. Multiple "
331
+ "virtual environments may be specified via multiple --venv-repository options and "
332
+ "the resolve will be the combined results. Each virtual environment will be "
333
+ "resolved from individually and must contain the full transitive closure of "
334
+ "requirements. This allows for creating a multi-platform PEX by specifying "
335
+ "multiple virtual environments; say one for Python 3.12 and one for Python 3.13."
336
+ ),
337
+ )
273
338
 
274
339
  parser.add_argument(
275
340
  "--pre",
@@ -525,21 +590,64 @@ def register_repos_options(parser):
525
590
  "-f",
526
591
  "--find-links",
527
592
  "--repo",
528
- metavar="PATH/URL",
593
+ metavar="(NAME=)PATH/URL",
529
594
  action="append",
530
595
  dest="find_links",
531
596
  type=str,
532
- help="Additional repository path (directory or URL) to look for requirements.",
597
+ default=[],
598
+ help=(
599
+ "Additional repository path (directory or URL) to look for requirements. If the "
600
+ "repository should only be used as a source for a subset of distributions, you can "
601
+ "associate a name to refer to it by in `--source` mappings by using "
602
+ "`<name>=<path or url>` syntax, where the name cannot itself contain `=`. For example; "
603
+ "`local=/mnt/share/py/find-links` or `internal=https://dev.corp.internal/py-repo`."
604
+ ),
533
605
  )
534
606
  parser.add_argument(
535
607
  "-i",
536
608
  "--index",
537
609
  "--index-url",
538
- metavar="URL",
610
+ metavar="(NAME=)URL",
539
611
  action="append",
540
612
  dest="indexes",
541
613
  type=str,
542
- help="Additional cheeseshop indices to use to satisfy requirements.",
614
+ default=[],
615
+ help=(
616
+ "Additional cheeseshop indices to use to satisfy requirements. If the index should "
617
+ "only be used as a source for a subset of distributions, you can associate a name to "
618
+ "refer to it by in `--source` mappings by using `<name>=<url>` syntax, where the name "
619
+ "itself cannot contain `=`. For example; internal=https://dev.corp.internal/py-simple`."
620
+ ),
621
+ )
622
+ parser.add_argument(
623
+ "--source",
624
+ metavar="NAME=SCOPE",
625
+ action="append",
626
+ dest="repo_scopes",
627
+ type=str,
628
+ default=[],
629
+ help=(
630
+ "Defines a limited scope to use a named find links repo or index in. A source takes "
631
+ "the form `<name>=<scope>` where name is as defined for `--find-links` and `--index`; "
632
+ "namely a string not containing `=`. The scope can be a project name; e.g.: "
633
+ "`internal=torch` to resolve the `torch` project from the `internal` repo, a project "
634
+ "name and marker; e.g.: `internal=torch; sys_platform != 'darwin'` to resolve the "
635
+ "`torch` project from the `internal` repo unless targeting macOS, or just a marker; "
636
+ "e.g.: `piwheels=platform_machine == 'armv7l' to resolve from the `piwheels` repo "
637
+ "whenever 32bit arm machines are being targeted. N.B. wherever you use a project name "
638
+ "in a scope, you can use a regex instead."
639
+ ),
640
+ )
641
+ parser.add_argument(
642
+ "--derive-sources-from-requirements-files",
643
+ dest="derive_scopes_from_requirements_files",
644
+ action=HandleBoolAction,
645
+ default=False,
646
+ help=(
647
+ "If any requirements files are specified that contain `-f` / `--find-links`, `-i` / "
648
+ "`--index-url`, or `--extra-index-url` options, automatically map these repos as the "
649
+ "`--source` for the requirements (if any) declared in that same requirements file."
650
+ ),
543
651
  )
544
652
 
545
653
 
@@ -632,6 +740,7 @@ if TYPE_CHECKING:
632
740
  PipConfiguration,
633
741
  PreResolvedConfiguration,
634
742
  PylockRepositoryConfiguration,
743
+ VenvRepositoryConfiguration,
635
744
  ]
636
745
 
637
746
 
@@ -699,6 +808,18 @@ def configure(
699
808
  sdists=tuple(sdists), wheels=tuple(wheels), pip_configuration=pip_configuration
700
809
  )
701
810
 
811
+ venvs = getattr(options, "venv_repositories", None)
812
+ if venvs:
813
+ return VenvRepositoryConfiguration(venvs=tuple(venvs), pip_configuration=pip_configuration)
814
+
815
+ if pylock:
816
+ return PylockRepositoryConfiguration(
817
+ lock_file_path=pylock,
818
+ extras=frozenset(options.pylock_extras),
819
+ dependency_groups=frozenset(options.pylock_dependency_groups),
820
+ pip_configuration=pip_configuration,
821
+ )
822
+
702
823
  return pip_configuration
703
824
 
704
825
 
@@ -766,17 +887,102 @@ def create_pip_configuration(
766
887
  )
767
888
 
768
889
 
890
+ def _parse_repo_scope(repo_scope):
891
+ # type: (str) -> Tuple[str, Scope]
892
+
893
+ def create_invalid_error(footer=None):
894
+ # type: (Optional[str]) -> Exception
895
+ error_msg_lines = ["Invalid --source value: {source}".format(source=repo_scope)]
896
+ if footer:
897
+ error_msg_lines.append(footer)
898
+ return InvalidConfigurationError(os.linesep.join(error_msg_lines))
899
+
900
+ name, _, scope = repo_scope.partition("=")
901
+ if not scope:
902
+ raise create_invalid_error("A source must be of the form `<name>=<scope>`.")
903
+ try:
904
+ return name, Scope.parse(scope)
905
+ except ValueError as e:
906
+ raise create_invalid_error(str(e))
907
+
908
+
909
+ def _parse_package_repositories(
910
+ package_repository_type, # type: str
911
+ scopes_by_name, # type: Mapping[str, Iterable[Scope]]
912
+ repositories, # type: Iterable[str]
913
+ ):
914
+ # type: (...) -> OrderedDict[str, Repo]
915
+
916
+ parsed = OrderedDict() # type: OrderedDict[str, List[str]]
917
+ for repository in repositories:
918
+ name, _, location = repository.partition("=")
919
+ if name in scopes_by_name:
920
+ parsed.setdefault(name, []).append(location)
921
+ else:
922
+ parsed.setdefault("", []).append(repository)
923
+
924
+ duplicate_names = [name for name, repos in parsed.items() if len(repos) > 1]
925
+ if duplicate_names:
926
+ raise InvalidConfigurationError(
927
+ "The following names are being re-used across multiple {package_repositories}: "
928
+ "{duplicate_names}\n"
929
+ "{package_repository} names must be unique.".format(
930
+ package_repositories=pluralize(2, package_repository_type),
931
+ duplicate_names=" ".join(sorted(duplicate_names)),
932
+ package_repository=package_repository_type.capitalize(),
933
+ )
934
+ )
935
+
936
+ package_repositories = OrderedDict() # type: OrderedDict[str, Repo]
937
+ for name, locations in parsed.items():
938
+ if name:
939
+ package_repositories[name] = Repo(locations[0], scopes=tuple(scopes_by_name[name]))
940
+ else:
941
+ package_repositories[""] = Repo(locations[0])
942
+ return package_repositories
943
+
944
+
769
945
  def create_repos_configuration(options):
770
946
  # type: (Namespace) -> ReposConfiguration
771
947
  """Creates a repos configuration from options registered by `register_repos_options`.
772
948
 
773
949
  :param options: The Pip resolver configuration options.
774
950
  """
775
- indexes = OrderedSet(
776
- ([PYPI] if options.pypi else []) + (options.indexes or [])
777
- ) # type: OrderedSet[str]
778
- find_links = OrderedSet(options.find_links or ()) # type: OrderedSet[str]
779
- return ReposConfiguration.create(indexes=tuple(indexes), find_links=tuple(find_links))
951
+ indexes = OrderedSet(([Repo(PYPI)] if options.pypi else [])) # type: OrderedSet[Repo]
952
+ scopes_by_name = defaultdict(OrderedSet) # type: DefaultDict[str, OrderedSet[Scope]]
953
+ for repo_scope in options.repo_scopes:
954
+ name, scope = _parse_repo_scope(repo_scope)
955
+ scopes_by_name[name].add(scope)
956
+
957
+ if not scopes_by_name:
958
+ indexes.update(Repo(index) for index in options.indexes)
959
+ return ReposConfiguration.create(
960
+ indexes=tuple(indexes),
961
+ find_links=tuple(Repo(find_links_repo) for find_links_repo in options.find_links),
962
+ derive_scopes_from_requirements_files=options.derive_scopes_from_requirements_files,
963
+ )
964
+
965
+ parsed_indexes = _parse_package_repositories("index", scopes_by_name, options.indexes)
966
+ parsed_find_links = _parse_package_repositories(
967
+ "find links repository", scopes_by_name, options.find_links
968
+ )
969
+
970
+ duplicate_names = set(parsed_indexes).intersection(parsed_find_links)
971
+ if duplicate_names:
972
+ raise InvalidConfigurationError(
973
+ "The following names are being re-used across indexes and find links repos: "
974
+ "{duplicate_names}\n"
975
+ "Package repository names must be unique.".format(
976
+ duplicate_names=" ".join(sorted(duplicate_names))
977
+ )
978
+ )
979
+
980
+ indexes.update(parsed_indexes.values())
981
+ return ReposConfiguration.create(
982
+ indexes=tuple(indexes),
983
+ find_links=tuple(parsed_find_links.values()),
984
+ derive_scopes_from_requirements_files=options.derive_scopes_from_requirements_files,
985
+ )
780
986
 
781
987
 
782
988
  def create_network_configuration(options):
pex/resolve/resolvers.py CHANGED
@@ -16,7 +16,6 @@ from pex.fingerprinted_distribution import FingerprintedDistribution
16
16
  from pex.pep_427 import InstallableType
17
17
  from pex.pep_503 import ProjectName
18
18
  from pex.pip.version import PipVersionValue
19
- from pex.resolve.lockfile.model import Lockfile
20
19
  from pex.sorted_tuple import SortedTuple
21
20
  from pex.targets import AbbreviatedPlatform, Target, Targets
22
21
  from pex.typing import TYPE_CHECKING
@@ -247,17 +246,6 @@ class Resolver(object):
247
246
  # type: () -> bool
248
247
  raise NotImplementedError()
249
248
 
250
- @abstractmethod
251
- def resolve_lock(
252
- self,
253
- lock, # type: Lockfile
254
- targets=Targets(), # type: Targets
255
- pip_version=None, # type: Optional[PipVersionValue]
256
- result_type=InstallableType.INSTALLED_WHEEL_CHROOT, # type: InstallableType.Value
257
- ):
258
- # type: (...) -> ResolveResult
259
- raise NotImplementedError()
260
-
261
249
  @abstractmethod
262
250
  def resolve_requirements(
263
251
  self,
@@ -267,6 +255,9 @@ class Resolver(object):
267
255
  transitive=None, # type: Optional[bool]
268
256
  extra_resolver_requirements=None, # type: Optional[Tuple[Requirement, ...]]
269
257
  result_type=InstallableType.INSTALLED_WHEEL_CHROOT, # type: InstallableType.Value
258
+ constraint_files=None, # type: Optional[Iterable[str]]
259
+ compile=False, # type: bool
260
+ ignore_errors=False, # type: bool
270
261
  ):
271
262
  # type: (...) -> ResolveResult
272
263
  raise NotImplementedError()
@@ -6,7 +6,7 @@ from __future__ import absolute_import
6
6
  import json
7
7
  import os.path
8
8
  import sys
9
- from argparse import ArgumentTypeError, Namespace, _ActionsContainer
9
+ from argparse import Action, ArgumentTypeError, Namespace, _ActionsContainer
10
10
 
11
11
  from pex.argparse import HandleBoolAction
12
12
  from pex.interpreter_constraints import InterpreterConstraints
@@ -16,7 +16,6 @@ from pex.pep_508 import MarkerEnvironment
16
16
  from pex.platforms import Platform, PlatformSpec
17
17
  from pex.resolve import abbreviated_platforms
18
18
  from pex.resolve.resolver_configuration import PipConfiguration
19
- from pex.resolve.resolver_options import _ManylinuxAction
20
19
  from pex.resolve.target_configuration import InterpreterConfiguration, TargetConfiguration
21
20
  from pex.targets import CompletePlatform
22
21
  from pex.typing import TYPE_CHECKING
@@ -25,6 +24,23 @@ if TYPE_CHECKING:
25
24
  from typing import Optional
26
25
 
27
26
 
27
+ class _ManylinuxAction(Action):
28
+ def __init__(self, *args, **kwargs):
29
+ kwargs["nargs"] = "?"
30
+ super(_ManylinuxAction, self).__init__(*args, **kwargs)
31
+
32
+ def __call__(self, parser, namespace, value, option_str=None):
33
+ if option_str.startswith("--no"):
34
+ setattr(namespace, self.dest, None)
35
+ elif value.startswith("manylinux"):
36
+ setattr(namespace, self.dest, value)
37
+ else:
38
+ raise ArgumentTypeError(
39
+ "Please specify a manylinux standard; ie: --manylinux=manylinux1. "
40
+ "Given {}".format(value)
41
+ )
42
+
43
+
28
44
  def register(
29
45
  parser, # type: _ActionsContainer
30
46
  include_platforms=True, # type: bool