pip 25.1.1__py3-none-any.whl → 25.2__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 (202) hide show
  1. pip/__init__.py +3 -3
  2. pip/_internal/__init__.py +2 -2
  3. pip/_internal/build_env.py +118 -94
  4. pip/_internal/cache.py +16 -14
  5. pip/_internal/cli/autocompletion.py +13 -4
  6. pip/_internal/cli/base_command.py +18 -7
  7. pip/_internal/cli/cmdoptions.py +14 -9
  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 +26 -22
  13. pip/_internal/cli/progress_bars.py +19 -12
  14. pip/_internal/cli/req_command.py +16 -12
  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 +2 -3
  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 +44 -39
  29. pip/_internal/commands/list.py +35 -26
  30. pip/_internal/commands/lock.py +1 -2
  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 +2 -3
  35. pip/_internal/configuration.py +39 -25
  36. pip/_internal/distributions/base.py +6 -4
  37. pip/_internal/distributions/installed.py +8 -4
  38. pip/_internal/distributions/sdist.py +20 -13
  39. pip/_internal/distributions/wheel.py +6 -4
  40. pip/_internal/exceptions.py +58 -39
  41. pip/_internal/index/collector.py +24 -29
  42. pip/_internal/index/package_finder.py +70 -61
  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 +8 -6
  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 +12 -17
  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 +7 -5
  65. pip/_internal/network/auth.py +20 -22
  66. pip/_internal/network/cache.py +22 -6
  67. pip/_internal/network/download.py +169 -141
  68. pip/_internal/network/lazy_wheel.py +10 -7
  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 +3 -2
  74. pip/_internal/operations/build/wheel_editable.py +3 -2
  75. pip/_internal/operations/build/wheel_legacy.py +9 -8
  76. pip/_internal/operations/check.py +21 -26
  77. pip/_internal/operations/freeze.py +12 -9
  78. pip/_internal/operations/install/editable_legacy.py +5 -3
  79. pip/_internal/operations/install/wheel.py +49 -41
  80. pip/_internal/operations/prepare.py +35 -30
  81. pip/_internal/pyproject.py +7 -10
  82. pip/_internal/req/__init__.py +12 -10
  83. pip/_internal/req/constructors.py +33 -31
  84. pip/_internal/req/req_dependency_group.py +7 -11
  85. pip/_internal/req/req_file.py +32 -35
  86. pip/_internal/req/req_install.py +37 -34
  87. pip/_internal/req/req_set.py +4 -5
  88. pip/_internal/req/req_uninstall.py +20 -17
  89. pip/_internal/resolution/base.py +3 -3
  90. pip/_internal/resolution/legacy/resolver.py +21 -20
  91. pip/_internal/resolution/resolvelib/base.py +16 -13
  92. pip/_internal/resolution/resolvelib/candidates.py +29 -26
  93. pip/_internal/resolution/resolvelib/factory.py +41 -50
  94. pip/_internal/resolution/resolvelib/found_candidates.py +11 -9
  95. pip/_internal/resolution/resolvelib/provider.py +15 -20
  96. pip/_internal/resolution/resolvelib/reporter.py +5 -3
  97. pip/_internal/resolution/resolvelib/requirements.py +8 -6
  98. pip/_internal/resolution/resolvelib/resolver.py +39 -23
  99. pip/_internal/self_outdated_check.py +8 -6
  100. pip/_internal/utils/appdirs.py +1 -2
  101. pip/_internal/utils/compat.py +7 -1
  102. pip/_internal/utils/compatibility_tags.py +17 -16
  103. pip/_internal/utils/deprecation.py +11 -9
  104. pip/_internal/utils/direct_url_helpers.py +2 -2
  105. pip/_internal/utils/egg_link.py +6 -5
  106. pip/_internal/utils/entrypoints.py +3 -2
  107. pip/_internal/utils/filesystem.py +8 -5
  108. pip/_internal/utils/filetypes.py +4 -6
  109. pip/_internal/utils/glibc.py +6 -5
  110. pip/_internal/utils/hashes.py +9 -6
  111. pip/_internal/utils/logging.py +8 -5
  112. pip/_internal/utils/misc.py +37 -45
  113. pip/_internal/utils/packaging.py +3 -2
  114. pip/_internal/utils/retry.py +7 -4
  115. pip/_internal/utils/setuptools_build.py +12 -10
  116. pip/_internal/utils/subprocess.py +20 -17
  117. pip/_internal/utils/temp_dir.py +10 -12
  118. pip/_internal/utils/unpacking.py +6 -4
  119. pip/_internal/utils/urls.py +1 -1
  120. pip/_internal/utils/virtualenv.py +3 -2
  121. pip/_internal/utils/wheel.py +3 -4
  122. pip/_internal/vcs/bazaar.py +26 -8
  123. pip/_internal/vcs/git.py +59 -24
  124. pip/_internal/vcs/mercurial.py +34 -11
  125. pip/_internal/vcs/subversion.py +27 -16
  126. pip/_internal/vcs/versioncontrol.py +56 -51
  127. pip/_internal/wheel_builder.py +14 -12
  128. pip/_vendor/cachecontrol/__init__.py +1 -1
  129. pip/_vendor/certifi/__init__.py +1 -1
  130. pip/_vendor/certifi/cacert.pem +102 -221
  131. pip/_vendor/certifi/core.py +1 -32
  132. pip/_vendor/distlib/__init__.py +2 -2
  133. pip/_vendor/distlib/scripts.py +1 -1
  134. pip/_vendor/msgpack/__init__.py +2 -2
  135. pip/_vendor/pkg_resources/__init__.py +1 -1
  136. pip/_vendor/platformdirs/version.py +2 -2
  137. pip/_vendor/pygments/__init__.py +1 -1
  138. pip/_vendor/requests/__version__.py +2 -2
  139. pip/_vendor/requests/compat.py +12 -0
  140. pip/_vendor/requests/models.py +3 -1
  141. pip/_vendor/requests/utils.py +6 -16
  142. pip/_vendor/resolvelib/__init__.py +3 -3
  143. pip/_vendor/resolvelib/reporters.py +1 -1
  144. pip/_vendor/resolvelib/resolvers/__init__.py +4 -4
  145. pip/_vendor/resolvelib/resolvers/resolution.py +91 -10
  146. pip/_vendor/rich/__main__.py +12 -40
  147. pip/_vendor/rich/_inspect.py +1 -1
  148. pip/_vendor/rich/_ratio.py +1 -7
  149. pip/_vendor/rich/align.py +1 -7
  150. pip/_vendor/rich/box.py +1 -7
  151. pip/_vendor/rich/console.py +25 -20
  152. pip/_vendor/rich/control.py +1 -7
  153. pip/_vendor/rich/diagnose.py +1 -0
  154. pip/_vendor/rich/emoji.py +1 -6
  155. pip/_vendor/rich/live.py +32 -7
  156. pip/_vendor/rich/live_render.py +1 -7
  157. pip/_vendor/rich/logging.py +1 -1
  158. pip/_vendor/rich/panel.py +3 -4
  159. pip/_vendor/rich/progress.py +15 -15
  160. pip/_vendor/rich/spinner.py +7 -13
  161. pip/_vendor/rich/syntax.py +24 -5
  162. pip/_vendor/rich/traceback.py +32 -17
  163. pip/_vendor/truststore/_api.py +1 -1
  164. pip/_vendor/vendor.txt +9 -10
  165. {pip-25.1.1.dist-info → pip-25.2.dist-info}/METADATA +26 -4
  166. {pip-25.1.1.dist-info → pip-25.2.dist-info}/RECORD +193 -180
  167. {pip-25.1.1.dist-info → pip-25.2.dist-info}/WHEEL +1 -1
  168. {pip-25.1.1.dist-info → pip-25.2.dist-info}/licenses/AUTHORS.txt +12 -0
  169. pip-25.2.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +13 -0
  170. pip-25.2.dist-info/licenses/src/pip/_vendor/certifi/LICENSE +20 -0
  171. pip-25.2.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +9 -0
  172. pip-25.2.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt +284 -0
  173. pip-25.2.dist-info/licenses/src/pip/_vendor/distro/LICENSE +202 -0
  174. pip-25.2.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md +31 -0
  175. pip-25.2.dist-info/licenses/src/pip/_vendor/msgpack/COPYING +14 -0
  176. pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE +3 -0
  177. pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +177 -0
  178. pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD +23 -0
  179. pip-25.2.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE +17 -0
  180. pip-25.2.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE +21 -0
  181. pip-25.2.dist-info/licenses/src/pip/_vendor/pygments/LICENSE +25 -0
  182. pip-25.2.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +21 -0
  183. pip-25.2.dist-info/licenses/src/pip/_vendor/requests/LICENSE +175 -0
  184. pip-25.2.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE +13 -0
  185. pip-25.2.dist-info/licenses/src/pip/_vendor/rich/LICENSE +19 -0
  186. pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE +21 -0
  187. pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE-HEADER +3 -0
  188. pip-25.2.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE +21 -0
  189. pip-25.2.dist-info/licenses/src/pip/_vendor/truststore/LICENSE +21 -0
  190. pip-25.2.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt +21 -0
  191. pip/_vendor/distlib/database.py +0 -1329
  192. pip/_vendor/distlib/index.py +0 -508
  193. pip/_vendor/distlib/locators.py +0 -1295
  194. pip/_vendor/distlib/manifest.py +0 -384
  195. pip/_vendor/distlib/markers.py +0 -162
  196. pip/_vendor/distlib/metadata.py +0 -1031
  197. pip/_vendor/distlib/version.py +0 -750
  198. pip/_vendor/distlib/wheel.py +0 -1100
  199. pip/_vendor/typing_extensions.py +0 -4584
  200. {pip-25.1.1.dist-info → pip-25.2.dist-info}/entry_points.txt +0 -0
  201. {pip-25.1.1.dist-info → pip-25.2.dist-info}/licenses/LICENSE.txt +0 -0
  202. {pip-25.1.1.dist-info → pip-25.2.dist-info}/top_level.txt +0 -0
@@ -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
  }
@@ -394,9 +435,32 @@ class Resolution(Generic[RT, CT, KT]):
394
435
  # pinning the virtual "root" package in the graph.
395
436
  self._push_new_state()
396
437
 
438
+ # Variables for optimistic backjumping
439
+ optimistic_rounds_cutoff: int | None = None
440
+ optimistic_backjumping_start_round: int | None = None
441
+
397
442
  for round_index in range(max_rounds):
398
443
  self._r.starting_round(index=round_index)
399
444
 
445
+ # Handle if optimistic backjumping has been running for too long
446
+ if self._optimistic_backjumping_ratio and self._save_states is not None:
447
+ if optimistic_backjumping_start_round is None:
448
+ optimistic_backjumping_start_round = round_index
449
+ optimistic_rounds_cutoff = int(
450
+ (max_rounds - round_index) * self._optimistic_backjumping_ratio
451
+ )
452
+
453
+ if optimistic_rounds_cutoff <= 0:
454
+ self._rollback_states()
455
+ continue
456
+ elif optimistic_rounds_cutoff is not None:
457
+ if (
458
+ round_index - optimistic_backjumping_start_round
459
+ >= optimistic_rounds_cutoff
460
+ ):
461
+ self._rollback_states()
462
+ continue
463
+
400
464
  unsatisfied_names = [
401
465
  key
402
466
  for key, criterion in self.state.criteria.items()
@@ -448,12 +512,29 @@ class Resolution(Generic[RT, CT, KT]):
448
512
  # Backjump if pinning fails. The backjump process puts us in
449
513
  # an unpinned state, so we can work on it in the next round.
450
514
  self._r.resolving_conflicts(causes=causes)
451
- success = self._backjump(causes)
452
- self.state.backtrack_causes[:] = causes
453
515
 
454
- # Dead ends everywhere. Give up.
455
- if not success:
456
- raise ResolutionImpossible(self.state.backtrack_causes)
516
+ try:
517
+ success = self._backjump(causes)
518
+ except ResolutionImpossible:
519
+ if self._optimistic_backjumping_ratio and self._save_states:
520
+ failed_optimistic_backjumping = True
521
+ else:
522
+ raise
523
+ else:
524
+ failed_optimistic_backjumping = bool(
525
+ not success
526
+ and self._optimistic_backjumping_ratio
527
+ and self._save_states
528
+ )
529
+
530
+ if failed_optimistic_backjumping and self._save_states:
531
+ self._rollback_states()
532
+ else:
533
+ self.state.backtrack_causes[:] = causes
534
+
535
+ # Dead ends everywhere. Give up.
536
+ if not success:
537
+ raise ResolutionImpossible(self.state.backtrack_causes)
457
538
  else:
458
539
  # discard as information sources any invalidated names
459
540
  # (unsatisfied names that were previously satisfied)
@@ -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
@@ -22,27 +22,21 @@ from typing import (
22
22
  Dict,
23
23
  Iterable,
24
24
  List,
25
+ Literal,
25
26
  Mapping,
26
27
  NamedTuple,
27
28
  Optional,
29
+ Protocol,
28
30
  TextIO,
29
31
  Tuple,
30
32
  Type,
31
33
  Union,
32
34
  cast,
35
+ runtime_checkable,
33
36
  )
34
37
 
35
38
  from pip._vendor.rich._null_file import NULL_FILE
36
39
 
37
- if sys.version_info >= (3, 8):
38
- from typing import Literal, Protocol, runtime_checkable
39
- else:
40
- from pip._vendor.typing_extensions import (
41
- Literal,
42
- Protocol,
43
- runtime_checkable,
44
- ) # pragma: no cover
45
-
46
40
  from . import errors, themes
47
41
  from ._emoji_replace import _emoji_replace
48
42
  from ._export_format import CONSOLE_HTML_FORMAT, CONSOLE_SVG_FORMAT
@@ -739,6 +733,14 @@ class Console:
739
733
  if no_color is not None
740
734
  else self._environ.get("NO_COLOR", "") != ""
741
735
  )
736
+ if force_interactive is None:
737
+ tty_interactive = self._environ.get("TTY_INTERACTIVE", None)
738
+ if tty_interactive is not None:
739
+ if tty_interactive == "0":
740
+ force_interactive = False
741
+ elif tty_interactive == "1":
742
+ force_interactive = True
743
+
742
744
  self.is_interactive = (
743
745
  (self.is_terminal and not self.is_dumb_terminal)
744
746
  if force_interactive is None
@@ -751,7 +753,7 @@ class Console:
751
753
  )
752
754
  self._record_buffer: List[Segment] = []
753
755
  self._render_hooks: List[RenderHook] = []
754
- self._live: Optional["Live"] = None
756
+ self._live_stack: List[Live] = []
755
757
  self._is_alt_screen = False
756
758
 
757
759
  def __repr__(self) -> str:
@@ -823,24 +825,26 @@ class Console:
823
825
  self._buffer_index -= 1
824
826
  self._check_buffer()
825
827
 
826
- def set_live(self, live: "Live") -> None:
827
- """Set Live instance. Used by Live context manager.
828
+ def set_live(self, live: "Live") -> bool:
829
+ """Set Live instance. Used by Live context manager (no need to call directly).
828
830
 
829
831
  Args:
830
832
  live (Live): Live instance using this Console.
831
833
 
834
+ Returns:
835
+ Boolean that indicates if the live is the topmost of the stack.
836
+
832
837
  Raises:
833
838
  errors.LiveError: If this Console has a Live context currently active.
834
839
  """
835
840
  with self._lock:
836
- if self._live is not None:
837
- raise errors.LiveError("Only one live display may be active at once")
838
- self._live = live
841
+ self._live_stack.append(live)
842
+ return len(self._live_stack) == 1
839
843
 
840
844
  def clear_live(self) -> None:
841
- """Clear the Live instance."""
845
+ """Clear the Live instance. Used by the Live context manager (no need to call directly)."""
842
846
  with self._lock:
843
- self._live = None
847
+ self._live_stack.pop()
844
848
 
845
849
  def push_render_hook(self, hook: RenderHook) -> None:
846
850
  """Add a new render hook to the stack.
@@ -992,12 +996,13 @@ class Console:
992
996
  @property
993
997
  def options(self) -> ConsoleOptions:
994
998
  """Get default console options."""
999
+ size = self.size
995
1000
  return ConsoleOptions(
996
- max_height=self.size.height,
997
- size=self.size,
1001
+ max_height=size.height,
1002
+ size=size,
998
1003
  legacy_windows=self.legacy_windows,
999
1004
  min_width=1,
1000
- max_width=self.width,
1005
+ max_width=size.width,
1001
1006
  encoding=self.encoding,
1002
1007
  is_terminal=self.is_terminal,
1003
1008
  )
@@ -1,11 +1,5 @@
1
- import sys
2
1
  import time
3
- from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Union
4
-
5
- if sys.version_info >= (3, 8):
6
- from typing import Final
7
- else:
8
- from pip._vendor.typing_extensions import Final # pragma: no cover
2
+ from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Union, Final
9
3
 
10
4
  from .segment import ControlCode, ControlType, Segment
11
5
 
@@ -26,6 +26,7 @@ def report() -> None: # pragma: no cover
26
26
  "TERM_PROGRAM",
27
27
  "TERM",
28
28
  "TTY_COMPATIBLE",
29
+ "TTY_INTERACTIVE",
29
30
  "VSCODE_VERBOSE_LOGGING",
30
31
  )
31
32
  env = {name: os.getenv(name) for name in env_names}
pip/_vendor/rich/emoji.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import sys
2
- from typing import TYPE_CHECKING, Optional, Union
2
+ from typing import TYPE_CHECKING, Optional, Union, Literal
3
3
 
4
4
  from .jupyter import JupyterMixin
5
5
  from .segment import Segment
@@ -7,11 +7,6 @@ from .style import Style
7
7
  from ._emoji_codes import EMOJI
8
8
  from ._emoji_replace import _emoji_replace
9
9
 
10
- if sys.version_info >= (3, 8):
11
- from typing import Literal
12
- else:
13
- from pip._vendor.typing_extensions import Literal # pragma: no cover
14
-
15
10
 
16
11
  if TYPE_CHECKING:
17
12
  from .console import Console, ConsoleOptions, RenderResult
pip/_vendor/rich/live.py CHANGED
@@ -1,10 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import sys
2
4
  from threading import Event, RLock, Thread
3
5
  from types import TracebackType
4
- from typing import IO, Any, Callable, List, Optional, TextIO, Type, cast
6
+ from typing import IO, TYPE_CHECKING, Any, Callable, List, Optional, TextIO, Type, cast
5
7
 
6
8
  from . import get_console
7
- from .console import Console, ConsoleRenderable, RenderableType, RenderHook
9
+ from .console import Console, ConsoleRenderable, Group, RenderableType, RenderHook
8
10
  from .control import Control
9
11
  from .file_proxy import FileProxy
10
12
  from .jupyter import JupyterMixin
@@ -12,6 +14,10 @@ from .live_render import LiveRender, VerticalOverflowMethod
12
14
  from .screen import Screen
13
15
  from .text import Text
14
16
 
17
+ if TYPE_CHECKING:
18
+ # Can be replaced with `from typing import Self` in Python 3.11+
19
+ from typing_extensions import Self # pragma: no cover
20
+
15
21
 
16
22
  class _RefreshThread(Thread):
17
23
  """A thread that calls refresh() at regular intervals."""
@@ -87,6 +93,7 @@ class Live(JupyterMixin, RenderHook):
87
93
  self._live_render = LiveRender(
88
94
  self.get_renderable(), vertical_overflow=vertical_overflow
89
95
  )
96
+ self._nested = False
90
97
 
91
98
  @property
92
99
  def is_started(self) -> bool:
@@ -110,8 +117,12 @@ class Live(JupyterMixin, RenderHook):
110
117
  with self._lock:
111
118
  if self._started:
112
119
  return
113
- self.console.set_live(self)
114
120
  self._started = True
121
+
122
+ if not self.console.set_live(self):
123
+ self._nested = True
124
+ return
125
+
115
126
  if self._screen:
116
127
  self._alt_screen = self.console.set_alt_screen(True)
117
128
  self.console.show_cursor(False)
@@ -136,8 +147,12 @@ class Live(JupyterMixin, RenderHook):
136
147
  with self._lock:
137
148
  if not self._started:
138
149
  return
139
- self.console.clear_live()
140
150
  self._started = False
151
+ self.console.clear_live()
152
+ if self._nested:
153
+ if not self.transient:
154
+ self.console.print(self.renderable)
155
+ return
141
156
 
142
157
  if self.auto_refresh and self._refresh_thread is not None:
143
158
  self._refresh_thread.stop()
@@ -156,13 +171,12 @@ class Live(JupyterMixin, RenderHook):
156
171
  self.console.show_cursor(True)
157
172
  if self._alt_screen:
158
173
  self.console.set_alt_screen(False)
159
-
160
174
  if self.transient and not self._alt_screen:
161
175
  self.console.control(self._live_render.restore_cursor())
162
176
  if self.ipy_widget is not None and self.transient:
163
177
  self.ipy_widget.close() # pragma: no cover
164
178
 
165
- def __enter__(self) -> "Live":
179
+ def __enter__(self) -> Self:
166
180
  self.start(refresh=self._renderable is not None)
167
181
  return self
168
182
 
@@ -200,7 +214,13 @@ class Live(JupyterMixin, RenderHook):
200
214
  Returns:
201
215
  RenderableType: Displayed renderable.
202
216
  """
203
- renderable = self.get_renderable()
217
+ live_stack = self.console._live_stack
218
+ renderable: RenderableType
219
+ if live_stack and self is live_stack[0]:
220
+ # The first Live instance will render everything in the Live stack
221
+ renderable = Group(*[live.get_renderable() for live in live_stack])
222
+ else:
223
+ renderable = self.get_renderable()
204
224
  return Screen(renderable) if self._alt_screen else renderable
205
225
 
206
226
  def update(self, renderable: RenderableType, *, refresh: bool = False) -> None:
@@ -221,6 +241,11 @@ class Live(JupyterMixin, RenderHook):
221
241
  """Update the display of the Live Render."""
222
242
  with self._lock:
223
243
  self._live_render.set_renderable(self.renderable)
244
+ if self._nested:
245
+ if self.console._live_stack:
246
+ self.console._live_stack[0].refresh()
247
+ return
248
+
224
249
  if self.console.is_jupyter: # pragma: no cover
225
250
  try:
226
251
  from IPython.display import display
@@ -1,10 +1,4 @@
1
- import sys
2
- from typing import Optional, Tuple
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 Optional, Tuple, Literal
8
2
 
9
3
 
10
4
  from ._loop import loop_last
@@ -76,7 +76,7 @@ class RichHandler(Handler):
76
76
  markup: bool = False,
77
77
  rich_tracebacks: bool = False,
78
78
  tracebacks_width: Optional[int] = None,
79
- tracebacks_code_width: int = 88,
79
+ tracebacks_code_width: Optional[int] = 88,
80
80
  tracebacks_extra_lines: int = 3,
81
81
  tracebacks_theme: Optional[str] = None,
82
82
  tracebacks_word_wrap: bool = True,
pip/_vendor/rich/panel.py CHANGED
@@ -146,8 +146,7 @@ class Panel(JupyterMixin):
146
146
  Padding(self.renderable, _padding) if any(_padding) else self.renderable
147
147
  )
148
148
  style = console.get_style(self.style)
149
- partial_border_style = console.get_style(self.border_style)
150
- border_style = style + partial_border_style
149
+ border_style = style + console.get_style(self.border_style)
151
150
  width = (
152
151
  options.max_width
153
152
  if self.width is None
@@ -206,7 +205,7 @@ class Panel(JupyterMixin):
206
205
 
207
206
  title_text = self._title
208
207
  if title_text is not None:
209
- title_text.stylize_before(partial_border_style)
208
+ title_text.stylize_before(border_style)
210
209
 
211
210
  child_width = (
212
211
  width - 2
@@ -255,7 +254,7 @@ class Panel(JupyterMixin):
255
254
 
256
255
  subtitle_text = self._subtitle
257
256
  if subtitle_text is not None:
258
- subtitle_text.stylize_before(partial_border_style)
257
+ subtitle_text.stylize_before(border_style)
259
258
 
260
259
  if subtitle_text is None or width <= 4:
261
260
  yield Segment(box.get_bottom([width - 2]), border_style)
@@ -1,5 +1,6 @@
1
+ from __future__ import annotations
2
+
1
3
  import io
2
- import sys
3
4
  import typing
4
5
  import warnings
5
6
  from abc import ABC, abstractmethod
@@ -14,6 +15,7 @@ from os import PathLike, stat
14
15
  from threading import Event, RLock, Thread
15
16
  from types import TracebackType
16
17
  from typing import (
18
+ TYPE_CHECKING,
17
19
  Any,
18
20
  BinaryIO,
19
21
  Callable,
@@ -23,10 +25,10 @@ from typing import (
23
25
  Generic,
24
26
  Iterable,
25
27
  List,
28
+ Literal,
26
29
  NamedTuple,
27
30
  NewType,
28
31
  Optional,
29
- Sequence,
30
32
  TextIO,
31
33
  Tuple,
32
34
  Type,
@@ -34,15 +36,9 @@ from typing import (
34
36
  Union,
35
37
  )
36
38
 
37
- if sys.version_info >= (3, 8):
38
- from typing import Literal
39
- else:
40
- from pip._vendor.typing_extensions import Literal # pragma: no cover
41
-
42
- if sys.version_info >= (3, 11):
43
- from typing import Self
44
- else:
45
- from pip._vendor.typing_extensions import Self # pragma: no cover
39
+ if TYPE_CHECKING:
40
+ # Can be replaced with `from typing import Self` in Python 3.11+
41
+ from typing_extensions import Self # pragma: no cover
46
42
 
47
43
  from . import filesize, get_console
48
44
  from .console import Console, Group, JustifyMethod, RenderableType
@@ -106,7 +102,7 @@ class _TrackThread(Thread):
106
102
 
107
103
 
108
104
  def track(
109
- sequence: Union[Sequence[ProgressType], Iterable[ProgressType]],
105
+ sequence: Iterable[ProgressType],
110
106
  description: str = "Working...",
111
107
  total: Optional[float] = None,
112
108
  completed: int = 0,
@@ -125,8 +121,10 @@ def track(
125
121
  ) -> Iterable[ProgressType]:
126
122
  """Track progress by iterating over a sequence.
127
123
 
124
+ You can also track progress of an iterable, which might require that you additionally specify ``total``.
125
+
128
126
  Args:
129
- sequence (Iterable[ProgressType]): A sequence (must support "len") you wish to iterate over.
127
+ sequence (Iterable[ProgressType]): Values you wish to iterate over and track progress.
130
128
  description (str, optional): Description of task show next to progress bar. Defaults to "Working".
131
129
  total: (float, optional): Total number of steps. Default is len(sequence).
132
130
  completed (int, optional): Number of steps completed so far. Defaults to 0.
@@ -1192,7 +1190,7 @@ class Progress(JupyterMixin):
1192
1190
 
1193
1191
  def track(
1194
1192
  self,
1195
- sequence: Union[Iterable[ProgressType], Sequence[ProgressType]],
1193
+ sequence: Iterable[ProgressType],
1196
1194
  total: Optional[float] = None,
1197
1195
  completed: int = 0,
1198
1196
  task_id: Optional[TaskID] = None,
@@ -1201,8 +1199,10 @@ class Progress(JupyterMixin):
1201
1199
  ) -> Iterable[ProgressType]:
1202
1200
  """Track progress by iterating over a sequence.
1203
1201
 
1202
+ You can also track progress of an iterable, which might require that you additionally specify ``total``.
1203
+
1204
1204
  Args:
1205
- sequence (Sequence[ProgressType]): A sequence of values you want to iterate over and track progress.
1205
+ sequence (Iterable[ProgressType]): Values you want to iterate over and track progress.
1206
1206
  total: (float, optional): Total number of steps. Default is len(sequence).
1207
1207
  completed (int, optional): Number of steps completed so far. Defaults to 0.
1208
1208
  task_id: (TaskID): Task to track. Default is new task.