summaryrefslogtreecommitdiff
path: root/src/pip/_vendor/rich
diff options
context:
space:
mode:
Diffstat (limited to 'src/pip/_vendor/rich')
-rw-r--r--src/pip/_vendor/rich/_export_format.py4
-rw-r--r--src/pip/_vendor/rich/_fileno.py24
-rw-r--r--src/pip/_vendor/rich/_null_file.py14
-rw-r--r--src/pip/_vendor/rich/align.py2
-rw-r--r--src/pip/_vendor/rich/ansi.py3
-rw-r--r--src/pip/_vendor/rich/cells.py2
-rw-r--r--src/pip/_vendor/rich/color.py16
-rw-r--r--src/pip/_vendor/rich/console.py59
-rw-r--r--src/pip/_vendor/rich/default_styles.py10
-rw-r--r--src/pip/_vendor/rich/file_proxy.py5
-rw-r--r--src/pip/_vendor/rich/highlighter.py2
-rw-r--r--src/pip/_vendor/rich/json.py4
-rw-r--r--src/pip/_vendor/rich/live.py2
-rw-r--r--src/pip/_vendor/rich/pretty.py103
-rw-r--r--src/pip/_vendor/rich/progress.py21
-rw-r--r--src/pip/_vendor/rich/repr.py2
-rw-r--r--src/pip/_vendor/rich/rule.py6
-rw-r--r--src/pip/_vendor/rich/segment.py8
-rw-r--r--src/pip/_vendor/rich/spinner.py23
-rw-r--r--src/pip/_vendor/rich/style.py23
-rw-r--r--src/pip/_vendor/rich/syntax.py11
-rw-r--r--src/pip/_vendor/rich/text.py8
-rw-r--r--src/pip/_vendor/rich/theme.py7
-rw-r--r--src/pip/_vendor/rich/traceback.py139
24 files changed, 302 insertions, 196 deletions
diff --git a/src/pip/_vendor/rich/_export_format.py b/src/pip/_vendor/rich/_export_format.py
index b79c13069..094d2dc22 100644
--- a/src/pip/_vendor/rich/_export_format.py
+++ b/src/pip/_vendor/rich/_export_format.py
@@ -12,9 +12,7 @@ body {{
</head>
<html>
<body>
- <code>
- <pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">{code}</pre>
- </code>
+ <pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><code>{code}</code></pre>
</body>
</html>
"""
diff --git a/src/pip/_vendor/rich/_fileno.py b/src/pip/_vendor/rich/_fileno.py
new file mode 100644
index 000000000..b17ee6511
--- /dev/null
+++ b/src/pip/_vendor/rich/_fileno.py
@@ -0,0 +1,24 @@
+from __future__ import annotations
+
+from typing import IO, Callable
+
+
+def get_fileno(file_like: IO[str]) -> int | None:
+ """Get fileno() from a file, accounting for poorly implemented file-like objects.
+
+ Args:
+ file_like (IO): A file-like object.
+
+ Returns:
+ int | None: The result of fileno if available, or None if operation failed.
+ """
+ fileno: Callable[[], int] | None = getattr(file_like, "fileno", None)
+ if fileno is not None:
+ try:
+ return fileno()
+ except Exception:
+ # `fileno` is documented as potentially raising a OSError
+ # Alas, from the issues, there are so many poorly implemented file-like objects,
+ # that `fileno()` can raise just about anything.
+ return None
+ return None
diff --git a/src/pip/_vendor/rich/_null_file.py b/src/pip/_vendor/rich/_null_file.py
index 49038bfcb..b659673ef 100644
--- a/src/pip/_vendor/rich/_null_file.py
+++ b/src/pip/_vendor/rich/_null_file.py
@@ -3,20 +3,6 @@ from typing import IO, Iterable, Iterator, List, Optional, Type
class NullFile(IO[str]):
-
- # TODO: "mode", "name" and "closed" are only required for Python 3.6.
-
- @property
- def mode(self) -> str:
- return ""
-
- @property
- def name(self) -> str:
- return "NullFile"
-
- def closed(self) -> bool:
- return False
-
def close(self) -> None:
pass
diff --git a/src/pip/_vendor/rich/align.py b/src/pip/_vendor/rich/align.py
index d5abb5947..c310b66e7 100644
--- a/src/pip/_vendor/rich/align.py
+++ b/src/pip/_vendor/rich/align.py
@@ -303,7 +303,7 @@ if __name__ == "__main__": # pragma: no cover
),
width=60,
style="on dark_blue",
- title="Algin",
+ title="Align",
)
console.print(
diff --git a/src/pip/_vendor/rich/ansi.py b/src/pip/_vendor/rich/ansi.py
index 92ef51941..66365e653 100644
--- a/src/pip/_vendor/rich/ansi.py
+++ b/src/pip/_vendor/rich/ansi.py
@@ -43,6 +43,9 @@ def _ansi_tokenize(ansi_text: str) -> Iterable[_AnsiToken]:
if start > position:
yield _AnsiToken(ansi_text[position:start])
if sgr:
+ if sgr == "(":
+ position = end + 1
+ continue
if sgr.endswith("m"):
yield _AnsiToken("", sgr[1:-1], osc)
else:
diff --git a/src/pip/_vendor/rich/cells.py b/src/pip/_vendor/rich/cells.py
index 139b949f7..9354f9e31 100644
--- a/src/pip/_vendor/rich/cells.py
+++ b/src/pip/_vendor/rich/cells.py
@@ -60,7 +60,7 @@ def _get_codepoint_cell_size(codepoint: int) -> int:
"""Get the cell size of a character.
Args:
- character (str): A single character.
+ codepoint (int): Codepoint of a character.
Returns:
int: Number of cells (0, 1 or 2) occupied by that character.
diff --git a/src/pip/_vendor/rich/color.py b/src/pip/_vendor/rich/color.py
index ef2e895d7..dfe455937 100644
--- a/src/pip/_vendor/rich/color.py
+++ b/src/pip/_vendor/rich/color.py
@@ -513,15 +513,14 @@ class Color(NamedTuple):
def downgrade(self, system: ColorSystem) -> "Color":
"""Downgrade a color system to a system with fewer colors."""
- if self.type in [ColorType.DEFAULT, system]:
+ if self.type in (ColorType.DEFAULT, system):
return self
# Convert to 8-bit color from truecolor color
if system == ColorSystem.EIGHT_BIT and self.system == ColorSystem.TRUECOLOR:
assert self.triplet is not None
- red, green, blue = self.triplet.normalized
- _h, l, s = rgb_to_hls(red, green, blue)
- # If saturation is under 10% assume it is grayscale
- if s < 0.1:
+ _h, l, s = rgb_to_hls(*self.triplet.normalized)
+ # If saturation is under 15% assume it is grayscale
+ if s < 0.15:
gray = round(l * 25.0)
if gray == 0:
color_number = 16
@@ -531,8 +530,13 @@ class Color(NamedTuple):
color_number = 231 + gray
return Color(self.name, ColorType.EIGHT_BIT, number=color_number)
+ red, green, blue = self.triplet
+ six_red = red / 95 if red < 95 else 1 + (red - 95) / 40
+ six_green = green / 95 if green < 95 else 1 + (green - 95) / 40
+ six_blue = blue / 95 if blue < 95 else 1 + (blue - 95) / 40
+
color_number = (
- 16 + 36 * round(red * 5.0) + 6 * round(green * 5.0) + round(blue * 5.0)
+ 16 + 36 * round(six_red) + 6 * round(six_green) + round(six_blue)
)
return Color(self.name, ColorType.EIGHT_BIT, number=color_number)
diff --git a/src/pip/_vendor/rich/console.py b/src/pip/_vendor/rich/console.py
index f805f2dea..7c363dfdc 100644
--- a/src/pip/_vendor/rich/console.py
+++ b/src/pip/_vendor/rich/console.py
@@ -1,5 +1,4 @@
import inspect
-import io
import os
import platform
import sys
@@ -48,6 +47,7 @@ else:
from . import errors, themes
from ._emoji_replace import _emoji_replace
from ._export_format import CONSOLE_HTML_FORMAT, CONSOLE_SVG_FORMAT
+from ._fileno import get_fileno
from ._log_render import FormatTimeCallable, LogRender
from .align import Align, AlignMethod
from .color import ColorSystem, blend_rgb
@@ -711,11 +711,6 @@ class Console:
self._force_terminal = None
if force_terminal is not None:
self._force_terminal = force_terminal
- else:
- # If FORCE_COLOR env var has any value at all, we force terminal.
- force_color = self._environ.get("FORCE_COLOR")
- if force_color is not None:
- self._force_terminal = True
self._file = file
self.quiet = quiet
@@ -758,7 +753,7 @@ class Console:
self._is_alt_screen = False
def __repr__(self) -> str:
- return f"<console width={self.width} {str(self._color_system)}>"
+ return f"<console width={self.width} {self._color_system!s}>"
@property
def file(self) -> IO[str]:
@@ -949,6 +944,15 @@ class Console:
# Return False for Idle which claims to be a tty but can't handle ansi codes
return False
+ if self.is_jupyter:
+ # return False for Jupyter, which may have FORCE_COLOR set
+ return False
+
+ # If FORCE_COLOR env var has any value at all, we assume a terminal.
+ force_color = self._environ.get("FORCE_COLOR")
+ if force_color is not None:
+ self._force_terminal = True
+
isatty: Optional[Callable[[], bool]] = getattr(self.file, "isatty", None)
try:
return False if isatty is None else isatty()
@@ -1146,7 +1150,7 @@ class Console:
status: RenderableType,
*,
spinner: str = "dots",
- spinner_style: str = "status.spinner",
+ spinner_style: StyleType = "status.spinner",
speed: float = 1.0,
refresh_per_second: float = 12.5,
) -> "Status":
@@ -1523,7 +1527,7 @@ class Console:
if text:
sep_text = Text(sep, justify=justify, end=end)
append(sep_text.join(text))
- del text[:]
+ text.clear()
for renderable in objects:
renderable = rich_cast(renderable)
@@ -2006,12 +2010,11 @@ class Console:
if WINDOWS:
use_legacy_windows_render = False
if self.legacy_windows:
- try:
+ fileno = get_fileno(self.file)
+ if fileno is not None:
use_legacy_windows_render = (
- self.file.fileno() in _STD_STREAMS_OUTPUT
+ fileno in _STD_STREAMS_OUTPUT
)
- except (ValueError, io.UnsupportedOperation):
- pass
if use_legacy_windows_render:
from pip._vendor.rich._win32_console import LegacyWindowsTerm
@@ -2026,13 +2029,31 @@ class Console:
# Either a non-std stream on legacy Windows, or modern Windows.
text = self._render_buffer(self._buffer[:])
# https://bugs.python.org/issue37871
+ # https://github.com/python/cpython/issues/82052
+ # We need to avoid writing more than 32Kb in a single write, due to the above bug
write = self.file.write
- for line in text.splitlines(True):
- try:
- write(line)
- except UnicodeEncodeError as error:
- error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***"
- raise
+ # Worse case scenario, every character is 4 bytes of utf-8
+ MAX_WRITE = 32 * 1024 // 4
+ try:
+ if len(text) <= MAX_WRITE:
+ write(text)
+ else:
+ batch: List[str] = []
+ batch_append = batch.append
+ size = 0
+ for line in text.splitlines(True):
+ if size + len(line) > MAX_WRITE and batch:
+ write("".join(batch))
+ batch.clear()
+ size = 0
+ batch_append(line)
+ size += len(line)
+ if batch:
+ write("".join(batch))
+ batch.clear()
+ except UnicodeEncodeError as error:
+ error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***"
+ raise
else:
text = self._render_buffer(self._buffer[:])
try:
diff --git a/src/pip/_vendor/rich/default_styles.py b/src/pip/_vendor/rich/default_styles.py
index 46e9ea52c..dca37193a 100644
--- a/src/pip/_vendor/rich/default_styles.py
+++ b/src/pip/_vendor/rich/default_styles.py
@@ -138,10 +138,11 @@ DEFAULT_STYLES: Dict[str, Style] = {
"tree.line": Style(),
"markdown.paragraph": Style(),
"markdown.text": Style(),
- "markdown.emph": Style(italic=True),
+ "markdown.em": Style(italic=True),
+ "markdown.emph": Style(italic=True), # For commonmark backwards compatibility
"markdown.strong": Style(bold=True),
- "markdown.code": Style(bgcolor="black", color="bright_white"),
- "markdown.code_block": Style(dim=True, color="cyan", bgcolor="black"),
+ "markdown.code": Style(bold=True, color="cyan", bgcolor="black"),
+ "markdown.code_block": Style(color="cyan", bgcolor="black"),
"markdown.block_quote": Style(color="magenta"),
"markdown.list": Style(color="cyan"),
"markdown.item": Style(),
@@ -157,7 +158,8 @@ DEFAULT_STYLES: Dict[str, Style] = {
"markdown.h6": Style(italic=True),
"markdown.h7": Style(italic=True, dim=True),
"markdown.link": Style(color="bright_blue"),
- "markdown.link_url": Style(color="blue"),
+ "markdown.link_url": Style(color="blue", underline=True),
+ "markdown.s": Style(strike=True),
"iso8601.date": Style(color="blue"),
"iso8601.time": Style(color="magenta"),
"iso8601.timezone": Style(color="yellow"),
diff --git a/src/pip/_vendor/rich/file_proxy.py b/src/pip/_vendor/rich/file_proxy.py
index cc69f22f3..4b0b0da6c 100644
--- a/src/pip/_vendor/rich/file_proxy.py
+++ b/src/pip/_vendor/rich/file_proxy.py
@@ -34,7 +34,7 @@ class FileProxy(io.TextIOBase):
line, new_line, text = text.partition("\n")
if new_line:
lines.append("".join(buffer) + line)
- del buffer[:]
+ buffer.clear()
else:
buffer.append(line)
break
@@ -52,3 +52,6 @@ class FileProxy(io.TextIOBase):
if output:
self.__console.print(output)
del self.__buffer[:]
+
+ def fileno(self) -> int:
+ return self.__file.fileno()
diff --git a/src/pip/_vendor/rich/highlighter.py b/src/pip/_vendor/rich/highlighter.py
index 82293dffc..c2646794a 100644
--- a/src/pip/_vendor/rich/highlighter.py
+++ b/src/pip/_vendor/rich/highlighter.py
@@ -82,7 +82,7 @@ class ReprHighlighter(RegexHighlighter):
base_style = "repr."
highlights = [
- r"(?P<tag_start><)(?P<tag_name>[-\w.:|]*)(?P<tag_contents>[\w\W]*?)(?P<tag_end>>)",
+ r"(?P<tag_start><)(?P<tag_name>[-\w.:|]*)(?P<tag_contents>[\w\W]*)(?P<tag_end>>)",
r'(?P<attrib_name>[\w_]{1,50})=(?P<attrib_value>"?[\w_]+"?)?',
r"(?P<brace>[][{}()])",
_combine_regex(
diff --git a/src/pip/_vendor/rich/json.py b/src/pip/_vendor/rich/json.py
index 21b642ab8..ea94493f2 100644
--- a/src/pip/_vendor/rich/json.py
+++ b/src/pip/_vendor/rich/json.py
@@ -1,3 +1,4 @@
+from pathlib import Path
from json import loads, dumps
from typing import Any, Callable, Optional, Union
@@ -131,8 +132,7 @@ if __name__ == "__main__":
if args.path == "-":
json_data = sys.stdin.read()
else:
- with open(args.path, "rt") as json_file:
- json_data = json_file.read()
+ json_data = Path(args.path).read_text()
except Exception as error:
error_console.print(f"Unable to read {args.path!r}; {error}")
sys.exit(-1)
diff --git a/src/pip/_vendor/rich/live.py b/src/pip/_vendor/rich/live.py
index e635fe5c9..3ebbbc4cc 100644
--- a/src/pip/_vendor/rich/live.py
+++ b/src/pip/_vendor/rich/live.py
@@ -210,6 +210,8 @@ class Live(JupyterMixin, RenderHook):
renderable (RenderableType): New renderable to use.
refresh (bool, optional): Refresh the display. Defaults to False.
"""
+ if isinstance(renderable, str):
+ renderable = self.console.render_str(renderable)
with self._lock:
self._renderable = renderable
if refresh:
diff --git a/src/pip/_vendor/rich/pretty.py b/src/pip/_vendor/rich/pretty.py
index 847b558c9..2bd9eb007 100644
--- a/src/pip/_vendor/rich/pretty.py
+++ b/src/pip/_vendor/rich/pretty.py
@@ -30,7 +30,7 @@ from pip._vendor.rich.repr import RichReprResult
try:
import attr as _attr_module
- _has_attrs = True
+ _has_attrs = hasattr(_attr_module, "ib")
except ImportError: # pragma: no cover
_has_attrs = False
@@ -55,13 +55,6 @@ if TYPE_CHECKING:
)
-JUPYTER_CLASSES_TO_NOT_RENDER = {
- # Matplotlib "Artists" manage their own rendering in a Jupyter notebook, and we should not try to render them too.
- # "Typically, all [Matplotlib] visible elements in a figure are subclasses of Artist."
- "matplotlib.artist.Artist",
-}
-
-
def _is_attr_object(obj: Any) -> bool:
"""Check if an object was created with attrs module."""
return _has_attrs and _attr_module.has(type(obj))
@@ -122,69 +115,40 @@ def _ipy_display_hook(
max_string: Optional[int] = None,
max_depth: Optional[int] = None,
expand_all: bool = False,
-) -> None:
+) -> Union[str, None]:
# needed here to prevent circular import:
- from ._inspect import is_object_one_of_types
from .console import ConsoleRenderable
# always skip rich generated jupyter renderables or None values
if _safe_isinstance(value, JupyterRenderable) or value is None:
- return
+ return None
console = console or get_console()
- if console.is_jupyter:
- # Delegate rendering to IPython if the object (and IPython) supports it
- # https://ipython.readthedocs.io/en/stable/config/integrating.html#rich-display
- ipython_repr_methods = [
- "_repr_html_",
- "_repr_markdown_",
- "_repr_json_",
- "_repr_latex_",
- "_repr_jpeg_",
- "_repr_png_",
- "_repr_svg_",
- "_repr_mimebundle_",
- ]
- for repr_method in ipython_repr_methods:
- method = getattr(value, repr_method, None)
- if inspect.ismethod(method):
- # Calling the method ourselves isn't ideal. The interface for the `_repr_*_` methods
- # specifies that if they return None, then they should not be rendered
- # by the notebook.
- try:
- repr_result = method()
- except Exception:
- continue # If the method raises, treat it as if it doesn't exist, try any others
- if repr_result is not None:
- return # Delegate rendering to IPython
-
- # When in a Jupyter notebook let's avoid the display of some specific classes,
- # as they result in the rendering of useless and noisy lines such as `<Figure size 432x288 with 1 Axes>`.
- # What does this do?
- # --> if the class has "matplotlib.artist.Artist" in its hierarchy for example, we don't render it.
- if is_object_one_of_types(value, JUPYTER_CLASSES_TO_NOT_RENDER):
- return
-
- # certain renderables should start on a new line
- if _safe_isinstance(value, ConsoleRenderable):
- console.line()
-
- console.print(
- value
- if _safe_isinstance(value, RichRenderable)
- else Pretty(
- value,
- overflow=overflow,
- indent_guides=indent_guides,
- max_length=max_length,
- max_string=max_string,
- max_depth=max_depth,
- expand_all=expand_all,
- margin=12,
- ),
- crop=crop,
- new_line_start=True,
- )
+
+ with console.capture() as capture:
+ # certain renderables should start on a new line
+ if _safe_isinstance(value, ConsoleRenderable):
+ console.line()
+ console.print(
+ value
+ if _safe_isinstance(value, RichRenderable)
+ else Pretty(
+ value,
+ overflow=overflow,
+ indent_guides=indent_guides,
+ max_length=max_length,
+ max_string=max_string,
+ max_depth=max_depth,
+ expand_all=expand_all,
+ margin=12,
+ ),
+ crop=crop,
+ new_line_start=True,
+ end="",
+ )
+ # strip trailing newline, not usually part of a text repr
+ # I'm not sure if this should be prevented at a lower level
+ return capture.get().rstrip("\n")
def _safe_isinstance(
@@ -247,7 +211,7 @@ def install(
)
builtins._ = value # type: ignore[attr-defined]
- try: # pragma: no cover
+ if "get_ipython" in globals():
ip = get_ipython() # type: ignore[name-defined]
from IPython.core.formatters import BaseFormatter
@@ -272,7 +236,7 @@ def install(
# replace plain text formatter with rich formatter
rich_formatter = RichFormatter()
ip.display_formatter.formatters["text/plain"] = rich_formatter
- except Exception:
+ else:
sys.displayhook = display_hook
@@ -371,6 +335,7 @@ class Pretty(JupyterMixin):
indent_size=self.indent_size,
max_length=self.max_length,
max_string=self.max_string,
+ max_depth=self.max_depth,
expand_all=self.expand_all,
)
text_width = (
@@ -433,7 +398,7 @@ class Node:
is_tuple: bool = False
is_namedtuple: bool = False
children: Optional[List["Node"]] = None
- key_separator = ": "
+ key_separator: str = ": "
separator: str = ", "
def iter_tokens(self) -> Iterable[str]:
@@ -642,7 +607,6 @@ def traverse(
return Node(value_repr="...")
obj_type = type(obj)
- py_version = (sys.version_info.major, sys.version_info.minor)
children: List[Node]
reached_max_depth = max_depth is not None and depth >= max_depth
@@ -780,7 +744,7 @@ def traverse(
is_dataclass(obj)
and not _safe_isinstance(obj, type)
and not fake_attributes
- and (_is_dataclass_repr(obj) or py_version == (3, 6))
+ and _is_dataclass_repr(obj)
):
push_visited(obj_id)
children = []
@@ -793,6 +757,7 @@ def traverse(
close_brace=")",
children=children,
last=root,
+ empty=f"{obj.__class__.__name__}()",
)
for last, field in loop_last(
diff --git a/src/pip/_vendor/rich/progress.py b/src/pip/_vendor/rich/progress.py
index e7d163c13..8b0a315f3 100644
--- a/src/pip/_vendor/rich/progress.py
+++ b/src/pip/_vendor/rich/progress.py
@@ -4,12 +4,12 @@ import typing
import warnings
from abc import ABC, abstractmethod
from collections import deque
-from collections.abc import Sized
from dataclasses import dataclass, field
from datetime import timedelta
from io import RawIOBase, UnsupportedOperation
from math import ceil
from mmap import mmap
+from operator import length_hint
from os import PathLike, stat
from threading import Event, RLock, Thread
from types import TracebackType
@@ -151,7 +151,7 @@ def track(
pulse_style=pulse_style,
),
TaskProgressColumn(show_speed=show_speed),
- TimeRemainingColumn(),
+ TimeRemainingColumn(elapsed_when_finished=True),
)
)
progress = Progress(
@@ -677,7 +677,7 @@ class TimeElapsedColumn(ProgressColumn):
"""Renders time elapsed."""
def render(self, task: "Task") -> Text:
- """Show time remaining."""
+ """Show time elapsed."""
elapsed = task.finished_time if task.finished else task.elapsed
if elapsed is None:
return Text("-:--:--", style="progress.elapsed")
@@ -1197,18 +1197,13 @@ class Progress(JupyterMixin):
Returns:
Iterable[ProgressType]: An iterable of values taken from the provided sequence.
"""
-
- task_total: Optional[float] = None
if total is None:
- if isinstance(sequence, Sized):
- task_total = float(len(sequence))
- else:
- task_total = total
+ total = float(length_hint(sequence)) or None
if task_id is None:
- task_id = self.add_task(description, total=task_total)
+ task_id = self.add_task(description, total=total)
else:
- self.update(task_id, total=task_total)
+ self.update(task_id, total=total)
if self.live.auto_refresh:
with _TrackThread(self, task_id, update_period) as track_thread:
@@ -1342,7 +1337,7 @@ class Progress(JupyterMixin):
RuntimeWarning,
)
buffering = -1
- elif _mode == "rt" or _mode == "r":
+ elif _mode in ("rt", "r"):
if buffering == 0:
raise ValueError("can't have unbuffered text I/O")
elif buffering == 1:
@@ -1363,7 +1358,7 @@ class Progress(JupyterMixin):
reader = _Reader(handle, self, task_id, close_handle=True)
# wrap the reader in a `TextIOWrapper` if text mode
- if mode == "r" or mode == "rt":
+ if mode in ("r", "rt"):
return io.TextIOWrapper(
reader,
encoding=encoding,
diff --git a/src/pip/_vendor/rich/repr.py b/src/pip/_vendor/rich/repr.py
index 72d1a7e30..f284bcafa 100644
--- a/src/pip/_vendor/rich/repr.py
+++ b/src/pip/_vendor/rich/repr.py
@@ -55,7 +55,7 @@ def auto(
if key is None:
append(repr(value))
else:
- if len(default) and default[0] == value:
+ if default and default[0] == value:
continue
append(f"{key}={value!r}")
else:
diff --git a/src/pip/_vendor/rich/rule.py b/src/pip/_vendor/rich/rule.py
index 0b78f7a4e..fd00ce6e4 100644
--- a/src/pip/_vendor/rich/rule.py
+++ b/src/pip/_vendor/rich/rule.py
@@ -51,13 +51,9 @@ class Rule(JupyterMixin):
) -> RenderResult:
width = options.max_width
- # Python3.6 doesn't have an isascii method on str
- isascii = getattr(str, "isascii", None) or (
- lambda s: all(ord(c) < 128 for c in s)
- )
characters = (
"-"
- if (options.ascii_only and not isascii(self.characters))
+ if (options.ascii_only and not self.characters.isascii())
else self.characters
)
diff --git a/src/pip/_vendor/rich/segment.py b/src/pip/_vendor/rich/segment.py
index 1ea5435ad..e12579846 100644
--- a/src/pip/_vendor/rich/segment.py
+++ b/src/pip/_vendor/rich/segment.py
@@ -119,7 +119,7 @@ class Segment(NamedTuple):
cell_size = get_character_cell_size
- pos = int((cut / cell_length) * len(text))
+ pos = int((cut / cell_length) * (len(text) - 1))
before = text[:pos]
cell_pos = cell_len(before)
@@ -303,7 +303,7 @@ class Segment(NamedTuple):
if include_new_lines:
cropped_line.append(new_line_segment)
yield cropped_line
- del line[:]
+ line.clear()
else:
append(segment)
if line:
@@ -365,7 +365,7 @@ class Segment(NamedTuple):
int: The length of the line.
"""
_cell_len = cell_len
- return sum(_cell_len(segment.text) for segment in line)
+ return sum(_cell_len(text) for text, style, control in line if not control)
@classmethod
def get_shape(cls, lines: List[List["Segment"]]) -> Tuple[int, int]:
@@ -727,7 +727,7 @@ console.print(text)"""
console.print(Syntax(code, "python", line_numbers=True))
console.print()
console.print(
- "When you call [b]print()[/b], Rich [i]renders[/i] the object in to the the following:\n"
+ "When you call [b]print()[/b], Rich [i]renders[/i] the object in to the following:\n"
)
fragments = list(console.render(text))
console.print(fragments)
diff --git a/src/pip/_vendor/rich/spinner.py b/src/pip/_vendor/rich/spinner.py
index 0879088e1..91ea630e1 100644
--- a/src/pip/_vendor/rich/spinner.py
+++ b/src/pip/_vendor/rich/spinner.py
@@ -11,6 +11,18 @@ if TYPE_CHECKING:
class Spinner:
+ """A spinner animation.
+
+ Args:
+ name (str): Name of spinner (run python -m rich.spinner).
+ text (RenderableType, optional): A renderable to display at the right of the spinner (str or Text typically). Defaults to "".
+ style (StyleType, optional): Style for spinner animation. Defaults to None.
+ speed (float, optional): Speed factor for animation. Defaults to 1.0.
+
+ Raises:
+ KeyError: If name isn't one of the supported spinner animations.
+ """
+
def __init__(
self,
name: str,
@@ -19,17 +31,6 @@ class Spinner:
style: Optional["StyleType"] = None,
speed: float = 1.0,
) -> None:
- """A spinner animation.
-
- Args:
- name (str): Name of spinner (run python -m rich.spinner).
- text (RenderableType, optional): A renderable to display at the right of the spinner (str or Text typically). Defaults to "".
- style (StyleType, optional): Style for spinner animation. Defaults to None.
- speed (float, optional): Speed factor for animation. Defaults to 1.0.
-
- Raises:
- KeyError: If name isn't one of the supported spinner animations.
- """
try:
spinner = SPINNERS[name]
except KeyError:
diff --git a/src/pip/_vendor/rich/style.py b/src/pip/_vendor/rich/style.py
index ad388aadb..313c88949 100644
--- a/src/pip/_vendor/rich/style.py
+++ b/src/pip/_vendor/rich/style.py
@@ -645,6 +645,29 @@ class Style:
style._meta = self._meta
return style
+ @lru_cache(maxsize=128)
+ def clear_meta_and_links(self) -> "Style":
+ """Get a copy of this style with link and meta information removed.
+
+ Returns:
+ Style: New style object.
+ """
+ if self._null:
+ return NULL_STYLE
+ style: Style = self.__new__(Style)
+ style._ansi = self._ansi
+ style._style_definition = self._style_definition
+ style._color = self._color
+ style._bgcolor = self._bgcolor
+ style._attributes = self._attributes
+ style._set_attributes = self._set_attributes
+ style._link = None
+ style._link_id = ""
+ style._hash = self._hash
+ style._null = False
+ style._meta = None
+ return style
+
def update_link(self, link: Optional[str] = None) -> "Style":
"""Get a copy with a different value for link.
diff --git a/src/pip/_vendor/rich/syntax.py b/src/pip/_vendor/rich/syntax.py
index 01bdd0439..25b226a3a 100644
--- a/src/pip/_vendor/rich/syntax.py
+++ b/src/pip/_vendor/rich/syntax.py
@@ -4,6 +4,7 @@ import re
import sys
import textwrap
from abc import ABC, abstractmethod
+from pathlib import Path
from typing import (
Any,
Dict,
@@ -338,8 +339,7 @@ class Syntax(JupyterMixin):
Returns:
[Syntax]: A Syntax object that may be printed to the console
"""
- with open(path, "rt", encoding=encoding) as code_file:
- code = code_file.read()
+ code = Path(path).read_text(encoding=encoding)
if not lexer:
lexer = cls.guess_lexer(path, code=code)
@@ -494,7 +494,10 @@ class Syntax(JupyterMixin):
# Skip over tokens until line start
while line_no < _line_start:
- _token_type, token = next(tokens)
+ try:
+ _token_type, token = next(tokens)
+ except StopIteration:
+ break
yield (token, None)
if token.endswith("\n"):
line_no += 1
@@ -671,6 +674,8 @@ class Syntax(JupyterMixin):
line_offset = max(0, start_line - 1)
lines: Union[List[Text], Lines] = text.split("\n", allow_blank=ends_on_nl)
if self.line_range:
+ if line_offset > len(lines):
+ return
lines = lines[line_offset:end_line]
if self.indent_guides and not options.ascii_only:
diff --git a/src/pip/_vendor/rich/text.py b/src/pip/_vendor/rich/text.py
index b14055aa7..998cb87da 100644
--- a/src/pip/_vendor/rich/text.py
+++ b/src/pip/_vendor/rich/text.py
@@ -53,11 +53,7 @@ class Span(NamedTuple):
"""Style associated with the span."""
def __repr__(self) -> str:
- return (
- f"Span({self.start}, {self.end}, {self.style!r})"
- if (isinstance(self.style, Style) and self.style._meta)
- else f"Span({self.start}, {self.end}, {repr(self.style)})"
- )
+ return f"Span({self.start}, {self.end}, {self.style!r})"
def __bool__(self) -> bool:
return self.end > self.start
@@ -1204,7 +1200,7 @@ class Text(JupyterMixin):
width (int): Maximum characters in a line.
Returns:
- Lines: List of lines.
+ Lines: Lines container.
"""
lines: Lines = Lines()
append = lines.append
diff --git a/src/pip/_vendor/rich/theme.py b/src/pip/_vendor/rich/theme.py
index bfb3c7f82..471dfb2f9 100644
--- a/src/pip/_vendor/rich/theme.py
+++ b/src/pip/_vendor/rich/theme.py
@@ -56,17 +56,20 @@ class Theme:
return theme
@classmethod
- def read(cls, path: str, inherit: bool = True) -> "Theme":
+ def read(
+ cls, path: str, inherit: bool = True, encoding: Optional[str] = None
+ ) -> "Theme":
"""Read a theme from a path.
Args:
path (str): Path to a config file readable by Python configparser module.
inherit (bool, optional): Inherit default styles. Defaults to True.
+ encoding (str, optional): Encoding of the config file. Defaults to None.
Returns:
Theme: A new theme instance.
"""
- with open(path, "rt") as config_file:
+ with open(path, "rt", encoding=encoding) as config_file:
return cls.from_file(config_file, source=path, inherit=inherit)
diff --git a/src/pip/_vendor/rich/traceback.py b/src/pip/_vendor/rich/traceback.py
index 1f481298f..c4ffe1f99 100644
--- a/src/pip/_vendor/rich/traceback.py
+++ b/src/pip/_vendor/rich/traceback.py
@@ -1,12 +1,24 @@
from __future__ import absolute_import
+import linecache
import os
import platform
import sys
from dataclasses import dataclass, field
from traceback import walk_tb
from types import ModuleType, TracebackType
-from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Type, Union
+from typing import (
+ Any,
+ Callable,
+ Dict,
+ Iterable,
+ List,
+ Optional,
+ Sequence,
+ Tuple,
+ Type,
+ Union,
+)
from pip._vendor.pygments.lexers import guess_lexer_for_filename
from pip._vendor.pygments.token import Comment, Keyword, Name, Number, Operator, String
@@ -41,6 +53,10 @@ def install(
theme: Optional[str] = None,
word_wrap: bool = False,
show_locals: bool = False,
+ locals_max_length: int = LOCALS_MAX_LENGTH,
+ locals_max_string: int = LOCALS_MAX_STRING,
+ locals_hide_dunder: bool = True,
+ locals_hide_sunder: Optional[bool] = None,
indent_guides: bool = True,
suppress: Iterable[Union[str, ModuleType]] = (),
max_frames: int = 100,
@@ -58,6 +74,11 @@ def install(
a theme appropriate for the platform.
word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.
show_locals (bool, optional): Enable display of local variables. Defaults to False.
+ locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+ Defaults to 10.
+ locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
+ locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
+ locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
@@ -65,7 +86,13 @@ def install(
Callable: The previous exception handler that was replaced.
"""
- traceback_console = Console(file=sys.stderr) if console is None else console
+ traceback_console = Console(stderr=True) if console is None else console
+
+ locals_hide_sunder = (
+ True
+ if (traceback_console.is_jupyter and locals_hide_sunder is None)
+ else locals_hide_sunder
+ )
def excepthook(
type_: Type[BaseException],
@@ -82,6 +109,10 @@ def install(
theme=theme,
word_wrap=word_wrap,
show_locals=show_locals,
+ locals_max_length=locals_max_length,
+ locals_max_string=locals_max_string,
+ locals_hide_dunder=locals_hide_dunder,
+ locals_hide_sunder=bool(locals_hide_sunder),
indent_guides=indent_guides,
suppress=suppress,
max_frames=max_frames,
@@ -192,6 +223,8 @@ class Traceback:
locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
Defaults to 10.
locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
+ locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
+ locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
@@ -208,14 +241,17 @@ class Traceback:
def __init__(
self,
trace: Optional[Trace] = None,
+ *,
width: Optional[int] = 100,
extra_lines: int = 3,
theme: Optional[str] = None,
word_wrap: bool = False,
show_locals: bool = False,
- indent_guides: bool = True,
locals_max_length: int = LOCALS_MAX_LENGTH,
locals_max_string: int = LOCALS_MAX_STRING,
+ locals_hide_dunder: bool = True,
+ locals_hide_sunder: bool = False,
+ indent_guides: bool = True,
suppress: Iterable[Union[str, ModuleType]] = (),
max_frames: int = 100,
):
@@ -237,6 +273,8 @@ class Traceback:
self.indent_guides = indent_guides
self.locals_max_length = locals_max_length
self.locals_max_string = locals_max_string
+ self.locals_hide_dunder = locals_hide_dunder
+ self.locals_hide_sunder = locals_hide_sunder
self.suppress: Sequence[str] = []
for suppress_entity in suppress:
@@ -257,14 +295,17 @@ class Traceback:
exc_type: Type[Any],
exc_value: BaseException,
traceback: Optional[TracebackType],
+ *,
width: Optional[int] = 100,
extra_lines: int = 3,
theme: Optional[str] = None,
word_wrap: bool = False,
show_locals: bool = False,
- indent_guides: bool = True,
locals_max_length: int = LOCALS_MAX_LENGTH,
locals_max_string: int = LOCALS_MAX_STRING,
+ locals_hide_dunder: bool = True,
+ locals_hide_sunder: bool = False,
+ indent_guides: bool = True,
suppress: Iterable[Union[str, ModuleType]] = (),
max_frames: int = 100,
) -> "Traceback":
@@ -283,6 +324,8 @@ class Traceback:
locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
Defaults to 10.
locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
+ locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
+ locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
suppress (Iterable[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
@@ -290,8 +333,16 @@ class Traceback:
Traceback: A Traceback instance that may be printed.
"""
rich_traceback = cls.extract(
- exc_type, exc_value, traceback, show_locals=show_locals
+ exc_type,
+ exc_value,
+ traceback,
+ show_locals=show_locals,
+ locals_max_length=locals_max_length,
+ locals_max_string=locals_max_string,
+ locals_hide_dunder=locals_hide_dunder,
+ locals_hide_sunder=locals_hide_sunder,
)
+
return cls(
rich_traceback,
width=width,
@@ -302,6 +353,8 @@ class Traceback:
indent_guides=indent_guides,
locals_max_length=locals_max_length,
locals_max_string=locals_max_string,
+ locals_hide_dunder=locals_hide_dunder,
+ locals_hide_sunder=locals_hide_sunder,
suppress=suppress,
max_frames=max_frames,
)
@@ -312,9 +365,12 @@ class Traceback:
exc_type: Type[BaseException],
exc_value: BaseException,
traceback: Optional[TracebackType],
+ *,
show_locals: bool = False,
locals_max_length: int = LOCALS_MAX_LENGTH,
locals_max_string: int = LOCALS_MAX_STRING,
+ locals_hide_dunder: bool = True,
+ locals_hide_sunder: bool = False,
) -> Trace:
"""Extract traceback information.
@@ -326,6 +382,8 @@ class Traceback:
locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
Defaults to 10.
locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
+ locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
+ locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
Returns:
Trace: A Trace instance which you can use to construct a `Traceback`.
@@ -362,6 +420,20 @@ class Traceback:
stacks.append(stack)
append = stack.frames.append
+ def get_locals(
+ iter_locals: Iterable[Tuple[str, object]]
+ ) -> Iterable[Tuple[str, object]]:
+ """Extract locals from an iterator of key pairs."""
+ if not (locals_hide_dunder or locals_hide_sunder):
+ yield from iter_locals
+ return
+ for key, value in iter_locals:
+ if locals_hide_dunder and key.startswith("__"):
+ continue
+ if locals_hide_sunder and key.startswith("_"):
+ continue
+ yield key, value
+
for frame_summary, line_no in walk_tb(traceback):
filename = frame_summary.f_code.co_filename
if filename and not filename.startswith("<"):
@@ -369,6 +441,7 @@ class Traceback:
filename = os.path.join(_IMPORT_CWD, filename)
if frame_summary.f_locals.get("_rich_traceback_omit", False):
continue
+
frame = Frame(
filename=filename or "?",
lineno=line_no,
@@ -379,7 +452,7 @@ class Traceback:
max_length=locals_max_length,
max_string=locals_max_string,
)
- for key, value in frame_summary.f_locals.items()
+ for key, value in get_locals(frame_summary.f_locals.items())
}
if show_locals
else None,
@@ -494,13 +567,14 @@ class Traceback:
highlighter = ReprHighlighter()
path_highlighter = PathHighlighter()
if syntax_error.filename != "<stdin>":
- text = Text.assemble(
- (f" {syntax_error.filename}", "pygments.string"),
- (":", "pygments.text"),
- (str(syntax_error.lineno), "pygments.number"),
- style="pygments.text",
- )
- yield path_highlighter(text)
+ if os.path.exists(syntax_error.filename):
+ text = Text.assemble(
+ (f" {syntax_error.filename}", "pygments.string"),
+ (":", "pygments.text"),
+ (str(syntax_error.lineno), "pygments.number"),
+ style="pygments.text",
+ )
+ yield path_highlighter(text)
syntax_error_text = highlighter(syntax_error.line.rstrip())
syntax_error_text.no_wrap = True
offset = min(syntax_error.offset - 1, len(syntax_error_text))
@@ -531,7 +605,6 @@ class Traceback:
def _render_stack(self, stack: Stack) -> RenderResult:
path_highlighter = PathHighlighter()
theme = self.theme
- code_cache: Dict[str, str] = {}
def read_code(filename: str) -> str:
"""Read files, and cache results on filename.
@@ -542,14 +615,7 @@ class Traceback:
Returns:
str: Contents of file
"""
- code = code_cache.get(filename)
- if code is None:
- with open(
- filename, "rt", encoding="utf-8", errors="replace"
- ) as code_file:
- code = code_file.read()
- code_cache[filename] = code
- return code
+ return "".join(linecache.getlines(filename))
def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]:
if frame.locals:
@@ -588,14 +654,23 @@ class Traceback:
frame_filename = frame.filename
suppressed = any(frame_filename.startswith(path) for path in self.suppress)
- text = Text.assemble(
- path_highlighter(Text(frame.filename, style="pygments.string")),
- (":", "pygments.text"),
- (str(frame.lineno), "pygments.number"),
- " in ",
- (frame.name, "pygments.function"),
- style="pygments.text",
- )
+ if os.path.exists(frame.filename):
+ text = Text.assemble(
+ path_highlighter(Text(frame.filename, style="pygments.string")),
+ (":", "pygments.text"),
+ (str(frame.lineno), "pygments.number"),
+ " in ",
+ (frame.name, "pygments.function"),
+ style="pygments.text",
+ )
+ else:
+ text = Text.assemble(
+ "in ",
+ (frame.name, "pygments.function"),
+ (":", "pygments.text"),
+ (str(frame.lineno), "pygments.number"),
+ style="pygments.text",
+ )
if not frame.filename.startswith("<") and not first:
yield ""
yield text
@@ -605,6 +680,10 @@ class Traceback:
if not suppressed:
try:
code = read_code(frame.filename)
+ if not code:
+ # code may be an empty string if the file doesn't exist, OR
+ # if the traceback filename is generated dynamically
+ continue
lexer_name = self._guess_lexer(frame.filename, code)
syntax = Syntax(
code,