pip 25.1.1__py3-none-any.whl → 25.3__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.
Files changed (236) hide show
  1. pip/__init__.py +3 -3
  2. pip/_internal/__init__.py +2 -2
  3. pip/_internal/build_env.py +186 -94
  4. pip/_internal/cache.py +17 -15
  5. pip/_internal/cli/autocompletion.py +13 -4
  6. pip/_internal/cli/base_command.py +18 -7
  7. pip/_internal/cli/cmdoptions.py +57 -80
  8. pip/_internal/cli/command_context.py +4 -3
  9. pip/_internal/cli/index_command.py +11 -9
  10. pip/_internal/cli/main.py +3 -2
  11. pip/_internal/cli/main_parser.py +4 -3
  12. pip/_internal/cli/parser.py +24 -20
  13. pip/_internal/cli/progress_bars.py +19 -12
  14. pip/_internal/cli/req_command.py +57 -33
  15. pip/_internal/cli/spinners.py +81 -5
  16. pip/_internal/commands/__init__.py +5 -3
  17. pip/_internal/commands/cache.py +18 -15
  18. pip/_internal/commands/check.py +1 -2
  19. pip/_internal/commands/completion.py +1 -2
  20. pip/_internal/commands/configuration.py +26 -18
  21. pip/_internal/commands/debug.py +8 -6
  22. pip/_internal/commands/download.py +6 -10
  23. pip/_internal/commands/freeze.py +2 -3
  24. pip/_internal/commands/hash.py +1 -2
  25. pip/_internal/commands/help.py +1 -2
  26. pip/_internal/commands/index.py +15 -9
  27. pip/_internal/commands/inspect.py +4 -4
  28. pip/_internal/commands/install.py +63 -53
  29. pip/_internal/commands/list.py +35 -26
  30. pip/_internal/commands/lock.py +4 -8
  31. pip/_internal/commands/search.py +14 -12
  32. pip/_internal/commands/show.py +14 -11
  33. pip/_internal/commands/uninstall.py +1 -2
  34. pip/_internal/commands/wheel.py +7 -13
  35. pip/_internal/configuration.py +40 -27
  36. pip/_internal/distributions/base.py +6 -4
  37. pip/_internal/distributions/installed.py +8 -4
  38. pip/_internal/distributions/sdist.py +33 -27
  39. pip/_internal/distributions/wheel.py +6 -4
  40. pip/_internal/exceptions.py +78 -42
  41. pip/_internal/index/collector.py +24 -29
  42. pip/_internal/index/package_finder.py +73 -64
  43. pip/_internal/index/sources.py +17 -14
  44. pip/_internal/locations/__init__.py +18 -16
  45. pip/_internal/locations/_distutils.py +12 -11
  46. pip/_internal/locations/_sysconfig.py +5 -4
  47. pip/_internal/locations/base.py +4 -3
  48. pip/_internal/main.py +2 -2
  49. pip/_internal/metadata/__init__.py +14 -7
  50. pip/_internal/metadata/_json.py +5 -4
  51. pip/_internal/metadata/base.py +22 -27
  52. pip/_internal/metadata/importlib/_compat.py +6 -4
  53. pip/_internal/metadata/importlib/_dists.py +20 -19
  54. pip/_internal/metadata/importlib/_envs.py +9 -6
  55. pip/_internal/metadata/pkg_resources.py +11 -14
  56. pip/_internal/models/direct_url.py +24 -21
  57. pip/_internal/models/format_control.py +5 -5
  58. pip/_internal/models/installation_report.py +4 -3
  59. pip/_internal/models/link.py +39 -34
  60. pip/_internal/models/pylock.py +27 -22
  61. pip/_internal/models/search_scope.py +6 -7
  62. pip/_internal/models/selection_prefs.py +3 -3
  63. pip/_internal/models/target_python.py +10 -9
  64. pip/_internal/models/wheel.py +12 -71
  65. pip/_internal/network/auth.py +20 -22
  66. pip/_internal/network/cache.py +28 -17
  67. pip/_internal/network/download.py +169 -141
  68. pip/_internal/network/lazy_wheel.py +15 -10
  69. pip/_internal/network/session.py +32 -27
  70. pip/_internal/network/utils.py +2 -2
  71. pip/_internal/network/xmlrpc.py +2 -2
  72. pip/_internal/operations/build/build_tracker.py +10 -8
  73. pip/_internal/operations/build/wheel.py +7 -6
  74. pip/_internal/operations/build/wheel_editable.py +7 -6
  75. pip/_internal/operations/check.py +21 -26
  76. pip/_internal/operations/freeze.py +12 -9
  77. pip/_internal/operations/install/wheel.py +49 -41
  78. pip/_internal/operations/prepare.py +42 -31
  79. pip/_internal/pyproject.py +7 -69
  80. pip/_internal/req/__init__.py +12 -12
  81. pip/_internal/req/constructors.py +68 -62
  82. pip/_internal/req/req_dependency_group.py +7 -11
  83. pip/_internal/req/req_file.py +32 -36
  84. pip/_internal/req/req_install.py +64 -170
  85. pip/_internal/req/req_set.py +4 -5
  86. pip/_internal/req/req_uninstall.py +20 -17
  87. pip/_internal/resolution/base.py +3 -3
  88. pip/_internal/resolution/legacy/resolver.py +21 -20
  89. pip/_internal/resolution/resolvelib/base.py +16 -13
  90. pip/_internal/resolution/resolvelib/candidates.py +49 -37
  91. pip/_internal/resolution/resolvelib/factory.py +72 -50
  92. pip/_internal/resolution/resolvelib/found_candidates.py +11 -9
  93. pip/_internal/resolution/resolvelib/provider.py +24 -20
  94. pip/_internal/resolution/resolvelib/reporter.py +26 -11
  95. pip/_internal/resolution/resolvelib/requirements.py +8 -6
  96. pip/_internal/resolution/resolvelib/resolver.py +41 -29
  97. pip/_internal/self_outdated_check.py +19 -9
  98. pip/_internal/utils/appdirs.py +1 -2
  99. pip/_internal/utils/compat.py +7 -1
  100. pip/_internal/utils/compatibility_tags.py +17 -16
  101. pip/_internal/utils/deprecation.py +11 -9
  102. pip/_internal/utils/direct_url_helpers.py +2 -2
  103. pip/_internal/utils/egg_link.py +6 -5
  104. pip/_internal/utils/entrypoints.py +3 -2
  105. pip/_internal/utils/filesystem.py +20 -5
  106. pip/_internal/utils/filetypes.py +4 -6
  107. pip/_internal/utils/glibc.py +6 -5
  108. pip/_internal/utils/hashes.py +9 -6
  109. pip/_internal/utils/logging.py +8 -5
  110. pip/_internal/utils/misc.py +37 -45
  111. pip/_internal/utils/packaging.py +3 -2
  112. pip/_internal/utils/retry.py +7 -4
  113. pip/_internal/utils/subprocess.py +20 -17
  114. pip/_internal/utils/temp_dir.py +10 -12
  115. pip/_internal/utils/unpacking.py +31 -4
  116. pip/_internal/utils/urls.py +1 -1
  117. pip/_internal/utils/virtualenv.py +3 -2
  118. pip/_internal/utils/wheel.py +3 -4
  119. pip/_internal/vcs/bazaar.py +26 -8
  120. pip/_internal/vcs/git.py +59 -24
  121. pip/_internal/vcs/mercurial.py +34 -11
  122. pip/_internal/vcs/subversion.py +27 -16
  123. pip/_internal/vcs/versioncontrol.py +56 -51
  124. pip/_internal/wheel_builder.py +30 -101
  125. pip/_vendor/README.rst +180 -0
  126. pip/_vendor/cachecontrol/LICENSE.txt +13 -0
  127. pip/_vendor/cachecontrol/__init__.py +1 -1
  128. pip/_vendor/certifi/LICENSE +20 -0
  129. pip/_vendor/certifi/__init__.py +1 -1
  130. pip/_vendor/certifi/cacert.pem +164 -261
  131. pip/_vendor/certifi/core.py +1 -32
  132. pip/_vendor/dependency_groups/LICENSE.txt +9 -0
  133. pip/_vendor/distlib/LICENSE.txt +284 -0
  134. pip/_vendor/distlib/__init__.py +2 -2
  135. pip/_vendor/distlib/scripts.py +1 -1
  136. pip/_vendor/distro/LICENSE +202 -0
  137. pip/_vendor/idna/LICENSE.md +31 -0
  138. pip/_vendor/msgpack/COPYING +14 -0
  139. pip/_vendor/msgpack/__init__.py +2 -2
  140. pip/_vendor/packaging/LICENSE +3 -0
  141. pip/_vendor/packaging/LICENSE.APACHE +177 -0
  142. pip/_vendor/packaging/LICENSE.BSD +23 -0
  143. pip/_vendor/pkg_resources/LICENSE +17 -0
  144. pip/_vendor/pkg_resources/__init__.py +1 -1
  145. pip/_vendor/platformdirs/LICENSE +21 -0
  146. pip/_vendor/platformdirs/api.py +1 -1
  147. pip/_vendor/platformdirs/macos.py +10 -8
  148. pip/_vendor/platformdirs/version.py +16 -3
  149. pip/_vendor/pygments/LICENSE +25 -0
  150. pip/_vendor/pygments/__init__.py +1 -1
  151. pip/_vendor/pyproject_hooks/LICENSE +21 -0
  152. pip/_vendor/requests/LICENSE +175 -0
  153. pip/_vendor/requests/__version__.py +2 -2
  154. pip/_vendor/requests/adapters.py +17 -40
  155. pip/_vendor/requests/compat.py +12 -0
  156. pip/_vendor/requests/models.py +3 -1
  157. pip/_vendor/requests/sessions.py +1 -1
  158. pip/_vendor/requests/utils.py +6 -16
  159. pip/_vendor/resolvelib/LICENSE +13 -0
  160. pip/_vendor/resolvelib/__init__.py +3 -3
  161. pip/_vendor/resolvelib/reporters.py +1 -1
  162. pip/_vendor/resolvelib/resolvers/__init__.py +4 -4
  163. pip/_vendor/resolvelib/resolvers/abstract.py +3 -3
  164. pip/_vendor/resolvelib/resolvers/resolution.py +96 -10
  165. pip/_vendor/rich/LICENSE +19 -0
  166. pip/_vendor/rich/__main__.py +12 -40
  167. pip/_vendor/rich/_inspect.py +1 -1
  168. pip/_vendor/rich/_ratio.py +1 -7
  169. pip/_vendor/rich/align.py +1 -7
  170. pip/_vendor/rich/box.py +1 -7
  171. pip/_vendor/rich/console.py +25 -20
  172. pip/_vendor/rich/control.py +1 -7
  173. pip/_vendor/rich/diagnose.py +1 -0
  174. pip/_vendor/rich/emoji.py +1 -6
  175. pip/_vendor/rich/live.py +32 -7
  176. pip/_vendor/rich/live_render.py +1 -7
  177. pip/_vendor/rich/logging.py +1 -1
  178. pip/_vendor/rich/panel.py +3 -4
  179. pip/_vendor/rich/progress.py +15 -15
  180. pip/_vendor/rich/spinner.py +7 -13
  181. pip/_vendor/rich/style.py +7 -11
  182. pip/_vendor/rich/syntax.py +24 -5
  183. pip/_vendor/rich/traceback.py +32 -17
  184. pip/_vendor/tomli/LICENSE +21 -0
  185. pip/_vendor/tomli/__init__.py +1 -1
  186. pip/_vendor/tomli/_parser.py +28 -21
  187. pip/_vendor/tomli/_re.py +8 -5
  188. pip/_vendor/tomli_w/LICENSE +21 -0
  189. pip/_vendor/truststore/LICENSE +21 -0
  190. pip/_vendor/truststore/__init__.py +1 -1
  191. pip/_vendor/truststore/_api.py +15 -7
  192. pip/_vendor/truststore/_openssl.py +3 -1
  193. pip/_vendor/urllib3/LICENSE.txt +21 -0
  194. pip/_vendor/vendor.txt +11 -12
  195. {pip-25.1.1.dist-info → pip-25.3.dist-info}/METADATA +32 -11
  196. {pip-25.1.1.dist-info → pip-25.3.dist-info}/RECORD +221 -192
  197. {pip-25.1.1.dist-info → pip-25.3.dist-info}/WHEEL +1 -2
  198. pip-25.3.dist-info/entry_points.txt +4 -0
  199. {pip-25.1.1.dist-info → pip-25.3.dist-info}/licenses/AUTHORS.txt +21 -0
  200. pip-25.3.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +13 -0
  201. pip-25.3.dist-info/licenses/src/pip/_vendor/certifi/LICENSE +20 -0
  202. pip-25.3.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +9 -0
  203. pip-25.3.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt +284 -0
  204. pip-25.3.dist-info/licenses/src/pip/_vendor/distro/LICENSE +202 -0
  205. pip-25.3.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md +31 -0
  206. pip-25.3.dist-info/licenses/src/pip/_vendor/msgpack/COPYING +14 -0
  207. pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE +3 -0
  208. pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +177 -0
  209. pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD +23 -0
  210. pip-25.3.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE +17 -0
  211. pip-25.3.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE +21 -0
  212. pip-25.3.dist-info/licenses/src/pip/_vendor/pygments/LICENSE +25 -0
  213. pip-25.3.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +21 -0
  214. pip-25.3.dist-info/licenses/src/pip/_vendor/requests/LICENSE +175 -0
  215. pip-25.3.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE +13 -0
  216. pip-25.3.dist-info/licenses/src/pip/_vendor/rich/LICENSE +19 -0
  217. pip-25.3.dist-info/licenses/src/pip/_vendor/tomli/LICENSE +21 -0
  218. pip-25.3.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE +21 -0
  219. pip-25.3.dist-info/licenses/src/pip/_vendor/truststore/LICENSE +21 -0
  220. pip-25.3.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt +21 -0
  221. pip/_internal/operations/build/metadata_legacy.py +0 -73
  222. pip/_internal/operations/build/wheel_legacy.py +0 -118
  223. pip/_internal/operations/install/editable_legacy.py +0 -46
  224. pip/_internal/utils/setuptools_build.py +0 -147
  225. pip/_vendor/distlib/database.py +0 -1329
  226. pip/_vendor/distlib/index.py +0 -508
  227. pip/_vendor/distlib/locators.py +0 -1295
  228. pip/_vendor/distlib/manifest.py +0 -384
  229. pip/_vendor/distlib/markers.py +0 -162
  230. pip/_vendor/distlib/metadata.py +0 -1031
  231. pip/_vendor/distlib/version.py +0 -750
  232. pip/_vendor/distlib/wheel.py +0 -1100
  233. pip/_vendor/typing_extensions.py +0 -4584
  234. pip-25.1.1.dist-info/entry_points.txt +0 -3
  235. pip-25.1.1.dist-info/top_level.txt +0 -1
  236. {pip-25.1.1.dist-info → pip-25.3.dist-info}/licenses/LICENSE.txt +0 -0
@@ -27,7 +27,6 @@ from pip._vendor.urllib3.poolmanager import PoolManager, proxy_from_url
27
27
  from pip._vendor.urllib3.util import Timeout as TimeoutSauce
28
28
  from pip._vendor.urllib3.util import parse_url
29
29
  from pip._vendor.urllib3.util.retry import Retry
30
- from pip._vendor.urllib3.util.ssl_ import create_urllib3_context
31
30
 
32
31
  from .auth import _basic_auth_str
33
32
  from .compat import basestring, urlparse
@@ -74,19 +73,6 @@ DEFAULT_RETRIES = 0
74
73
  DEFAULT_POOL_TIMEOUT = None
75
74
 
76
75
 
77
- try:
78
- import ssl # noqa: F401
79
-
80
- _preloaded_ssl_context = create_urllib3_context()
81
- _preloaded_ssl_context.load_verify_locations(
82
- extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)
83
- )
84
- except ImportError:
85
- # Bypass default SSLContext creation when Python
86
- # interpreter isn't built with the ssl module.
87
- _preloaded_ssl_context = None
88
-
89
-
90
76
  def _urllib3_request_context(
91
77
  request: "PreparedRequest",
92
78
  verify: "bool | str | None",
@@ -99,19 +85,9 @@ def _urllib3_request_context(
99
85
  scheme = parsed_request_url.scheme.lower()
100
86
  port = parsed_request_url.port
101
87
 
102
- # Determine if we have and should use our default SSLContext
103
- # to optimize performance on standard requests.
104
- poolmanager_kwargs = getattr(poolmanager, "connection_pool_kw", {})
105
- has_poolmanager_ssl_context = poolmanager_kwargs.get("ssl_context")
106
- should_use_default_ssl_context = (
107
- _preloaded_ssl_context is not None and not has_poolmanager_ssl_context
108
- )
109
-
110
88
  cert_reqs = "CERT_REQUIRED"
111
89
  if verify is False:
112
90
  cert_reqs = "CERT_NONE"
113
- elif verify is True and should_use_default_ssl_context:
114
- pool_kwargs["ssl_context"] = _preloaded_ssl_context
115
91
  elif isinstance(verify, str):
116
92
  if not os.path.isdir(verify):
117
93
  pool_kwargs["ca_certs"] = verify
@@ -314,26 +290,27 @@ class HTTPAdapter(BaseAdapter):
314
290
  :param cert: The SSL certificate to verify.
315
291
  """
316
292
  if url.lower().startswith("https") and verify:
317
- conn.cert_reqs = "CERT_REQUIRED"
293
+ cert_loc = None
318
294
 
319
- # Only load the CA certificates if 'verify' is a string indicating the CA bundle to use.
320
- # Otherwise, if verify is a boolean, we don't load anything since
321
- # the connection will be using a context with the default certificates already loaded,
322
- # and this avoids a call to the slow load_verify_locations()
295
+ # Allow self-specified cert location.
323
296
  if verify is not True:
324
- # `verify` must be a str with a path then
325
297
  cert_loc = verify
326
298
 
327
- if not os.path.exists(cert_loc):
328
- raise OSError(
329
- f"Could not find a suitable TLS CA certificate bundle, "
330
- f"invalid path: {cert_loc}"
331
- )
299
+ if not cert_loc:
300
+ cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)
332
301
 
333
- if not os.path.isdir(cert_loc):
334
- conn.ca_certs = cert_loc
335
- else:
336
- conn.ca_cert_dir = cert_loc
302
+ if not cert_loc or not os.path.exists(cert_loc):
303
+ raise OSError(
304
+ f"Could not find a suitable TLS CA certificate bundle, "
305
+ f"invalid path: {cert_loc}"
306
+ )
307
+
308
+ conn.cert_reqs = "CERT_REQUIRED"
309
+
310
+ if not os.path.isdir(cert_loc):
311
+ conn.ca_certs = cert_loc
312
+ else:
313
+ conn.ca_cert_dir = cert_loc
337
314
  else:
338
315
  conn.cert_reqs = "CERT_NONE"
339
316
  conn.ca_certs = None
@@ -410,7 +387,7 @@ class HTTPAdapter(BaseAdapter):
410
387
  ``"cert_reqs"`` will be set
411
388
  * If ``verify`` is a string, (i.e., it is a user-specified trust bundle)
412
389
  ``"ca_certs"`` will be set if the string is not a directory recognized
413
- by :py:func:`os.path.isdir`, otherwise ``"ca_certs_dir"`` will be
390
+ by :py:func:`os.path.isdir`, otherwise ``"ca_cert_dir"`` will be
414
391
  set.
415
392
  * If ``"cert"`` is specified, ``"cert_file"`` will always be set. If
416
393
  ``"cert"`` is a tuple with a second item, ``"key_file"`` will also
@@ -9,6 +9,18 @@ compatibility until the next major version.
9
9
 
10
10
  import sys
11
11
 
12
+ # -------
13
+ # urllib3
14
+ # -------
15
+ from pip._vendor.urllib3 import __version__ as urllib3_version
16
+
17
+ # Detect which major version of urllib3 is being used.
18
+ try:
19
+ is_urllib3_1 = int(urllib3_version.split(".")[0]) == 1
20
+ except (TypeError, AttributeError):
21
+ # If we can't discern a version, prefer old functionality.
22
+ is_urllib3_1 = True
23
+
12
24
  # -------------------
13
25
  # Character Detection
14
26
  # -------------------
@@ -945,7 +945,9 @@ class Response:
945
945
  return content
946
946
 
947
947
  def json(self, **kwargs):
948
- r"""Returns the json-encoded content of a response, if any.
948
+ r"""Decodes the JSON response body (if any) as a Python object.
949
+
950
+ This may return a dictionary, list, etc. depending on what is in the response.
949
951
 
950
952
  :param \*\*kwargs: Optional arguments that ``json.loads`` takes.
951
953
  :raises requests.exceptions.JSONDecodeError: If the response body does not
@@ -535,7 +535,7 @@ class Session(SessionRedirectMixin):
535
535
  for multipart encoding upload.
536
536
  :param auth: (optional) Auth tuple or callable to enable
537
537
  Basic/Digest/Custom HTTP Auth.
538
- :param timeout: (optional) How long to wait for the server to send
538
+ :param timeout: (optional) How many seconds to wait for the server to send
539
539
  data before giving up, as a float, or a :ref:`(connect timeout,
540
540
  read timeout) <timeouts>` tuple.
541
541
  :type timeout: float or tuple
@@ -38,6 +38,7 @@ from .compat import (
38
38
  getproxies,
39
39
  getproxies_environment,
40
40
  integer_types,
41
+ is_urllib3_1,
41
42
  )
42
43
  from .compat import parse_http_list as _parse_list_header
43
44
  from .compat import (
@@ -136,7 +137,9 @@ def super_len(o):
136
137
  total_length = None
137
138
  current_position = 0
138
139
 
139
- if isinstance(o, str):
140
+ if not is_urllib3_1 and isinstance(o, str):
141
+ # urllib3 2.x+ treats all strings as utf-8 instead
142
+ # of latin-1 (iso-8859-1) like http.client.
140
143
  o = o.encode("utf-8")
141
144
 
142
145
  if hasattr(o, "__len__"):
@@ -216,14 +219,7 @@ def get_netrc_auth(url, raise_errors=False):
216
219
  netrc_path = None
217
220
 
218
221
  for f in netrc_locations:
219
- try:
220
- loc = os.path.expanduser(f)
221
- except KeyError:
222
- # os.path.expanduser can fail when $HOME is undefined and
223
- # getpwuid fails. See https://bugs.python.org/issue20164 &
224
- # https://github.com/psf/requests/issues/1846
225
- return
226
-
222
+ loc = os.path.expanduser(f)
227
223
  if os.path.exists(loc):
228
224
  netrc_path = loc
229
225
  break
@@ -233,13 +229,7 @@ def get_netrc_auth(url, raise_errors=False):
233
229
  return
234
230
 
235
231
  ri = urlparse(url)
236
-
237
- # Strip port numbers from netloc. This weird `if...encode`` dance is
238
- # used for Python 3.2, which doesn't support unicode literals.
239
- splitstr = b":"
240
- if isinstance(url, str):
241
- splitstr = splitstr.decode("ascii")
242
- host = ri.netloc.split(splitstr)[0]
232
+ host = ri.hostname
243
233
 
244
234
  try:
245
235
  _netrc = netrc(netrc_path).authenticators(host)
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2018, Tzu-ping Chung <uranusjr@gmail.com>
2
+
3
+ Permission to use, copy, modify, and distribute this software for any
4
+ purpose with or without fee is hereby granted, provided that the above
5
+ copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -1,17 +1,17 @@
1
1
  __all__ = [
2
- "__version__",
3
2
  "AbstractProvider",
4
3
  "AbstractResolver",
5
4
  "BaseReporter",
6
5
  "InconsistentCandidate",
7
- "Resolver",
8
6
  "RequirementsConflicted",
9
7
  "ResolutionError",
10
8
  "ResolutionImpossible",
11
9
  "ResolutionTooDeep",
10
+ "Resolver",
11
+ "__version__",
12
12
  ]
13
13
 
14
- __version__ = "1.1.0"
14
+ __version__ = "1.2.1"
15
15
 
16
16
 
17
17
  from .providers import AbstractProvider
@@ -9,7 +9,7 @@ if TYPE_CHECKING:
9
9
 
10
10
 
11
11
  class BaseReporter(Generic[RT, CT, KT]):
12
- """Delegate class to provider progress reporting for the resolver."""
12
+ """Delegate class to provide progress reporting for the resolver."""
13
13
 
14
14
  def starting(self) -> None:
15
15
  """Called before the resolution actually starts."""
@@ -13,15 +13,15 @@ from .resolution import Resolution, Resolver
13
13
 
14
14
  __all__ = [
15
15
  "AbstractResolver",
16
+ "Criterion",
16
17
  "InconsistentCandidate",
17
- "Resolver",
18
- "Resolution",
18
+ "RequirementInformation",
19
19
  "RequirementsConflicted",
20
+ "Resolution",
20
21
  "ResolutionError",
21
22
  "ResolutionImpossible",
22
23
  "ResolutionTooDeep",
23
- "RequirementInformation",
24
+ "Resolver",
24
25
  "ResolverException",
25
26
  "Result",
26
- "Criterion",
27
27
  ]
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import collections
4
- from typing import TYPE_CHECKING, Any, Generic, Iterable, Mapping, NamedTuple
4
+ from typing import TYPE_CHECKING, Any, Generic, Iterable, NamedTuple
5
5
 
6
6
  from ..structs import CT, KT, RT, DirectedGraph
7
7
 
@@ -11,9 +11,9 @@ if TYPE_CHECKING:
11
11
  from .criterion import Criterion
12
12
 
13
13
  class Result(NamedTuple, Generic[RT, CT, KT]):
14
- mapping: Mapping[KT, CT]
14
+ mapping: dict[KT, CT]
15
15
  graph: DirectedGraph[KT | None]
16
- criteria: Mapping[KT, Criterion[RT, CT]]
16
+ criteria: dict[KT, Criterion[RT, CT]]
17
17
 
18
18
  else:
19
19
  Result = collections.namedtuple("Result", ["mapping", "graph", "criteria"])
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import collections
4
4
  import itertools
5
5
  import operator
6
- from typing import TYPE_CHECKING, Collection, Generic, Iterable, Mapping
6
+ from typing import TYPE_CHECKING, Generic
7
7
 
8
8
  from ..structs import (
9
9
  CT,
@@ -27,9 +27,13 @@ from .exceptions import (
27
27
  )
28
28
 
29
29
  if TYPE_CHECKING:
30
+ from collections.abc import Collection, Iterable, Mapping
31
+
30
32
  from ..providers import AbstractProvider, Preference
31
33
  from ..reporters import BaseReporter
32
34
 
35
+ _OPTIMISTIC_BACKJUMPING_RATIO: float = 0.1
36
+
33
37
 
34
38
  def _build_result(state: State[RT, CT, KT]) -> Result[RT, CT, KT]:
35
39
  mapping = state.mapping
@@ -77,6 +81,11 @@ class Resolution(Generic[RT, CT, KT]):
77
81
  self._r = reporter
78
82
  self._states: list[State[RT, CT, KT]] = []
79
83
 
84
+ # Optimistic backjumping variables
85
+ self._optimistic_backjumping_ratio = _OPTIMISTIC_BACKJUMPING_RATIO
86
+ self._save_states: list[State[RT, CT, KT]] | None = None
87
+ self._optimistic_start_round: int | None = None
88
+
80
89
  @property
81
90
  def state(self) -> State[RT, CT, KT]:
82
91
  try:
@@ -274,6 +283,25 @@ class Resolution(Generic[RT, CT, KT]):
274
283
  )
275
284
  return True
276
285
 
286
+ def _save_state(self) -> None:
287
+ """Save states for potential rollback if optimistic backjumping fails."""
288
+ if self._save_states is None:
289
+ self._save_states = [
290
+ State(
291
+ mapping=s.mapping.copy(),
292
+ criteria=s.criteria.copy(),
293
+ backtrack_causes=s.backtrack_causes[:],
294
+ )
295
+ for s in self._states
296
+ ]
297
+
298
+ def _rollback_states(self) -> None:
299
+ """Rollback states and disable optimistic backjumping."""
300
+ self._optimistic_backjumping_ratio = 0.0
301
+ if self._save_states:
302
+ self._states = self._save_states
303
+ self._save_states = None
304
+
277
305
  def _backjump(self, causes: list[RequirementInformation[RT, CT]]) -> bool:
278
306
  """Perform backjumping.
279
307
 
@@ -324,13 +352,26 @@ class Resolution(Generic[RT, CT, KT]):
324
352
  except (IndexError, KeyError):
325
353
  raise ResolutionImpossible(causes) from None
326
354
 
327
- # Only backjump if the current broken state is
328
- # an incompatible dependency
329
- if name not in incompatible_deps:
355
+ if (
356
+ not self._optimistic_backjumping_ratio
357
+ and name not in incompatible_deps
358
+ ):
359
+ # For safe backjumping only backjump if the current dependency
360
+ # is not the same as the incompatible dependency
330
361
  break
331
362
 
363
+ # On the first time a non-safe backjump is done the state
364
+ # is saved so we can restore it later if the resolution fails
365
+ if (
366
+ self._optimistic_backjumping_ratio
367
+ and self._save_states is None
368
+ and name not in incompatible_deps
369
+ ):
370
+ self._save_state()
371
+
332
372
  # If the current dependencies and the incompatible dependencies
333
- # are overlapping then we have found a cause of the incompatibility
373
+ # are overlapping then we have likely found a cause of the
374
+ # incompatibility
334
375
  current_dependencies = {
335
376
  self._p.identify(d) for d in self._p.get_dependencies(candidate)
336
377
  }
@@ -343,6 +384,11 @@ class Resolution(Generic[RT, CT, KT]):
343
384
  if not broken_state.mapping:
344
385
  break
345
386
 
387
+ # Guard: We need at least two state to remain to both
388
+ # backtrack and push a new state
389
+ if len(self._states) <= 1:
390
+ raise ResolutionImpossible(causes)
391
+
346
392
  incompatibilities_from_broken = [
347
393
  (k, list(v.incompatibilities)) for k, v in broken_state.criteria.items()
348
394
  ]
@@ -394,9 +440,32 @@ class Resolution(Generic[RT, CT, KT]):
394
440
  # pinning the virtual "root" package in the graph.
395
441
  self._push_new_state()
396
442
 
443
+ # Variables for optimistic backjumping
444
+ optimistic_rounds_cutoff: int | None = None
445
+ optimistic_backjumping_start_round: int | None = None
446
+
397
447
  for round_index in range(max_rounds):
398
448
  self._r.starting_round(index=round_index)
399
449
 
450
+ # Handle if optimistic backjumping has been running for too long
451
+ if self._optimistic_backjumping_ratio and self._save_states is not None:
452
+ if optimistic_backjumping_start_round is None:
453
+ optimistic_backjumping_start_round = round_index
454
+ optimistic_rounds_cutoff = int(
455
+ (max_rounds - round_index) * self._optimistic_backjumping_ratio
456
+ )
457
+
458
+ if optimistic_rounds_cutoff <= 0:
459
+ self._rollback_states()
460
+ continue
461
+ elif optimistic_rounds_cutoff is not None:
462
+ if (
463
+ round_index - optimistic_backjumping_start_round
464
+ >= optimistic_rounds_cutoff
465
+ ):
466
+ self._rollback_states()
467
+ continue
468
+
400
469
  unsatisfied_names = [
401
470
  key
402
471
  for key, criterion in self.state.criteria.items()
@@ -448,12 +517,29 @@ class Resolution(Generic[RT, CT, KT]):
448
517
  # Backjump if pinning fails. The backjump process puts us in
449
518
  # an unpinned state, so we can work on it in the next round.
450
519
  self._r.resolving_conflicts(causes=causes)
451
- success = self._backjump(causes)
452
- self.state.backtrack_causes[:] = causes
453
520
 
454
- # Dead ends everywhere. Give up.
455
- if not success:
456
- raise ResolutionImpossible(self.state.backtrack_causes)
521
+ try:
522
+ success = self._backjump(causes)
523
+ except ResolutionImpossible:
524
+ if self._optimistic_backjumping_ratio and self._save_states:
525
+ failed_optimistic_backjumping = True
526
+ else:
527
+ raise
528
+ else:
529
+ failed_optimistic_backjumping = bool(
530
+ not success
531
+ and self._optimistic_backjumping_ratio
532
+ and self._save_states
533
+ )
534
+
535
+ if failed_optimistic_backjumping and self._save_states:
536
+ self._rollback_states()
537
+ else:
538
+ self.state.backtrack_causes[:] = causes
539
+
540
+ # Dead ends everywhere. Give up.
541
+ if not success:
542
+ raise ResolutionImpossible(self.state.backtrack_causes)
457
543
  else:
458
544
  # discard as information sources any invalidated names
459
545
  # (unsatisfied names that were previously satisfied)
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2020 Will McGugan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -207,6 +207,8 @@ Supports much of the *markdown* __syntax__!
207
207
 
208
208
 
209
209
  if __name__ == "__main__": # pragma: no cover
210
+ from pip._vendor.rich.panel import Panel
211
+
210
212
  console = Console(
211
213
  file=io.StringIO(),
212
214
  force_terminal=True,
@@ -227,47 +229,17 @@ if __name__ == "__main__": # pragma: no cover
227
229
  c = Console(record=True)
228
230
  c.print(test_card)
229
231
 
230
- print(f"rendered in {pre_cache_taken}ms (cold cache)")
231
- print(f"rendered in {taken}ms (warm cache)")
232
-
233
- from pip._vendor.rich.panel import Panel
234
-
235
232
  console = Console()
236
-
237
- sponsor_message = Table.grid(padding=1)
238
- sponsor_message.add_column(style="green", justify="right")
239
- sponsor_message.add_column(no_wrap=True)
240
-
241
- sponsor_message.add_row(
242
- "Textualize",
243
- "[u blue link=https://github.com/textualize]https://github.com/textualize",
244
- )
245
- sponsor_message.add_row(
246
- "Twitter",
247
- "[u blue link=https://twitter.com/willmcgugan]https://twitter.com/willmcgugan",
248
- )
249
-
250
- intro_message = Text.from_markup(
251
- """\
252
- We hope you enjoy using Rich!
253
-
254
- Rich is maintained with [red]:heart:[/] by [link=https://www.textualize.io]Textualize.io[/]
255
-
256
- - Will McGugan"""
257
- )
258
-
259
- message = Table.grid(padding=2)
260
- message.add_column()
261
- message.add_column(no_wrap=True)
262
- message.add_row(intro_message, sponsor_message)
263
-
233
+ console.print(f"[dim]rendered in [not dim]{pre_cache_taken}ms[/] (cold cache)")
234
+ console.print(f"[dim]rendered in [not dim]{taken}ms[/] (warm cache)")
235
+ console.print()
264
236
  console.print(
265
237
  Panel.fit(
266
- message,
267
- box=box.ROUNDED,
268
- padding=(1, 2),
269
- title="[b red]Thanks for trying out Rich!",
270
- border_style="bright_blue",
271
- ),
272
- justify="center",
238
+ "[b magenta]Hope you enjoy using Rich![/]\n\n"
239
+ "Please consider sponsoring me if you get value from my work.\n\n"
240
+ "Even the price of a ☕ can brighten my day!\n\n"
241
+ "https://github.com/sponsors/willmcgugan",
242
+ border_style="red",
243
+ title="Help ensure Rich is maintained",
244
+ )
273
245
  )
@@ -214,7 +214,7 @@ class Inspect(JupyterMixin):
214
214
  def _get_formatted_doc(self, object_: Any) -> Optional[str]:
215
215
  """
216
216
  Extract the docstring of an object, process it and returns it.
217
- The processing consists in cleaning up the doctring's indentation,
217
+ The processing consists in cleaning up the docstring's indentation,
218
218
  taking only its 1st paragraph if `self.help` is not True,
219
219
  and escape its control codes.
220
220
 
@@ -1,12 +1,6 @@
1
- import sys
2
1
  from fractions import Fraction
3
2
  from math import ceil
4
- from typing import cast, List, Optional, Sequence
5
-
6
- if sys.version_info >= (3, 8):
7
- from typing import Protocol
8
- else:
9
- from pip._vendor.typing_extensions import Protocol # pragma: no cover
3
+ from typing import cast, List, Optional, Sequence, Protocol
10
4
 
11
5
 
12
6
  class Edge(Protocol):
pip/_vendor/rich/align.py CHANGED
@@ -1,11 +1,5 @@
1
- import sys
2
1
  from itertools import chain
3
- from typing import TYPE_CHECKING, Iterable, Optional
4
-
5
- if sys.version_info >= (3, 8):
6
- from typing import Literal
7
- else:
8
- from pip._vendor.typing_extensions import Literal # pragma: no cover
2
+ from typing import TYPE_CHECKING, Iterable, Optional, Literal
9
3
 
10
4
  from .constrain import Constrain
11
5
  from .jupyter import JupyterMixin
pip/_vendor/rich/box.py CHANGED
@@ -1,10 +1,4 @@
1
- import sys
2
- from typing import TYPE_CHECKING, Iterable, List
3
-
4
- if sys.version_info >= (3, 8):
5
- from typing import Literal
6
- else:
7
- from pip._vendor.typing_extensions import Literal # pragma: no cover
1
+ from typing import TYPE_CHECKING, Iterable, List, Literal
8
2
 
9
3
 
10
4
  from ._loop import loop_last