From d381be8e0bfbf1405b21f4d1a54fdbfc2436d72b Mon Sep 17 00:00:00 2001 From: Polina Date: Thu, 2 Jul 2026 19:34:05 +0000 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=B2=20=C2=AB?= =?UTF-8?q?venv/Lib/site-packages/pip/=5Fvendor/rich=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../site-packages/pip/_vendor/rich/measure.py | 151 +++++++++ .../site-packages/pip/_vendor/rich/padding.py | 141 ++++++++ .../site-packages/pip/_vendor/rich/pager.py | 34 ++ .../site-packages/pip/_vendor/rich/palette.py | 100 ++++++ .../site-packages/pip/_vendor/rich/panel.py | 317 ++++++++++++++++++ 5 files changed, 743 insertions(+) create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/measure.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/padding.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/pager.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/palette.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/panel.py diff --git a/venv/Lib/site-packages/pip/_vendor/rich/measure.py b/venv/Lib/site-packages/pip/_vendor/rich/measure.py new file mode 100644 index 0000000..a508ffa --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/rich/measure.py @@ -0,0 +1,151 @@ +from operator import itemgetter +from typing import TYPE_CHECKING, Callable, NamedTuple, Optional, Sequence + +from . import errors +from .protocol import is_renderable, rich_cast + +if TYPE_CHECKING: + from .console import Console, ConsoleOptions, RenderableType + + +class Measurement(NamedTuple): + """Stores the minimum and maximum widths (in characters) required to render an object.""" + + minimum: int + """Minimum number of cells required to render.""" + maximum: int + """Maximum number of cells required to render.""" + + @property + def span(self) -> int: + """Get difference between maximum and minimum.""" + return self.maximum - self.minimum + + def normalize(self) -> "Measurement": + """Get measurement that ensures that minimum <= maximum and minimum >= 0 + + Returns: + Measurement: A normalized measurement. + """ + minimum, maximum = self + minimum = min(max(0, minimum), maximum) + return Measurement(max(0, minimum), max(0, max(minimum, maximum))) + + def with_maximum(self, width: int) -> "Measurement": + """Get a RenderableWith where the widths are <= width. + + Args: + width (int): Maximum desired width. + + Returns: + Measurement: New Measurement object. + """ + minimum, maximum = self + return Measurement(min(minimum, width), min(maximum, width)) + + def with_minimum(self, width: int) -> "Measurement": + """Get a RenderableWith where the widths are >= width. + + Args: + width (int): Minimum desired width. + + Returns: + Measurement: New Measurement object. + """ + minimum, maximum = self + width = max(0, width) + return Measurement(max(minimum, width), max(maximum, width)) + + def clamp( + self, min_width: Optional[int] = None, max_width: Optional[int] = None + ) -> "Measurement": + """Clamp a measurement within the specified range. + + Args: + min_width (int): Minimum desired width, or ``None`` for no minimum. Defaults to None. + max_width (int): Maximum desired width, or ``None`` for no maximum. Defaults to None. + + Returns: + Measurement: New Measurement object. + """ + measurement = self + if min_width is not None: + measurement = measurement.with_minimum(min_width) + if max_width is not None: + measurement = measurement.with_maximum(max_width) + return measurement + + @classmethod + def get( + cls, console: "Console", options: "ConsoleOptions", renderable: "RenderableType" + ) -> "Measurement": + """Get a measurement for a renderable. + + Args: + console (~rich.console.Console): Console instance. + options (~rich.console.ConsoleOptions): Console options. + renderable (RenderableType): An object that may be rendered with Rich. + + Raises: + errors.NotRenderableError: If the object is not renderable. + + Returns: + Measurement: Measurement object containing range of character widths required to render the object. + """ + _max_width = options.max_width + if _max_width < 1: + return Measurement(0, 0) + if isinstance(renderable, str): + renderable = console.render_str( + renderable, markup=options.markup, highlight=False + ) + renderable = rich_cast(renderable) + if is_renderable(renderable): + get_console_width: Optional[ + Callable[["Console", "ConsoleOptions"], "Measurement"] + ] = getattr(renderable, "__rich_measure__", None) + if get_console_width is not None: + render_width = ( + get_console_width(console, options) + .normalize() + .with_maximum(_max_width) + ) + if render_width.maximum < 1: + return Measurement(0, 0) + return render_width.normalize() + else: + return Measurement(0, _max_width) + else: + raise errors.NotRenderableError( + f"Unable to get render width for {renderable!r}; " + "a str, Segment, or object with __rich_console__ method is required" + ) + + +def measure_renderables( + console: "Console", + options: "ConsoleOptions", + renderables: Sequence["RenderableType"], +) -> "Measurement": + """Get a measurement that would fit a number of renderables. + + Args: + console (~rich.console.Console): Console instance. + options (~rich.console.ConsoleOptions): Console options. + renderables (Iterable[RenderableType]): One or more renderable objects. + + Returns: + Measurement: Measurement object containing range of character widths required to + contain all given renderables. + """ + if not renderables: + return Measurement(0, 0) + get_measurement = Measurement.get + measurements = [ + get_measurement(console, options, renderable) for renderable in renderables + ] + measured_width = Measurement( + max(measurements, key=itemgetter(0)).minimum, + max(measurements, key=itemgetter(1)).maximum, + ) + return measured_width diff --git a/venv/Lib/site-packages/pip/_vendor/rich/padding.py b/venv/Lib/site-packages/pip/_vendor/rich/padding.py new file mode 100644 index 0000000..0161cd1 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/rich/padding.py @@ -0,0 +1,141 @@ +from typing import TYPE_CHECKING, List, Optional, Tuple, Union + +if TYPE_CHECKING: + from .console import ( + Console, + ConsoleOptions, + RenderableType, + RenderResult, + ) + +from .jupyter import JupyterMixin +from .measure import Measurement +from .segment import Segment +from .style import Style + +PaddingDimensions = Union[int, Tuple[int], Tuple[int, int], Tuple[int, int, int, int]] + + +class Padding(JupyterMixin): + """Draw space around content. + + Example: + >>> print(Padding("Hello", (2, 4), style="on blue")) + + Args: + renderable (RenderableType): String or other renderable. + pad (Union[int, Tuple[int]]): Padding for top, right, bottom, and left borders. + May be specified with 1, 2, or 4 integers (CSS style). + style (Union[str, Style], optional): Style for padding characters. Defaults to "none". + expand (bool, optional): Expand padding to fit available width. Defaults to True. + """ + + def __init__( + self, + renderable: "RenderableType", + pad: "PaddingDimensions" = (0, 0, 0, 0), + *, + style: Union[str, Style] = "none", + expand: bool = True, + ): + self.renderable = renderable + self.top, self.right, self.bottom, self.left = self.unpack(pad) + self.style = style + self.expand = expand + + @classmethod + def indent(cls, renderable: "RenderableType", level: int) -> "Padding": + """Make padding instance to render an indent. + + Args: + renderable (RenderableType): String or other renderable. + level (int): Number of characters to indent. + + Returns: + Padding: A Padding instance. + """ + + return Padding(renderable, pad=(0, 0, 0, level), expand=False) + + @staticmethod + def unpack(pad: "PaddingDimensions") -> Tuple[int, int, int, int]: + """Unpack padding specified in CSS style.""" + if isinstance(pad, int): + return (pad, pad, pad, pad) + if len(pad) == 1: + _pad = pad[0] + return (_pad, _pad, _pad, _pad) + if len(pad) == 2: + pad_top, pad_right = pad + return (pad_top, pad_right, pad_top, pad_right) + if len(pad) == 4: + top, right, bottom, left = pad + return (top, right, bottom, left) + raise ValueError(f"1, 2 or 4 integers required for padding; {len(pad)} given") + + def __repr__(self) -> str: + return f"Padding({self.renderable!r}, ({self.top},{self.right},{self.bottom},{self.left}))" + + def __rich_console__( + self, console: "Console", options: "ConsoleOptions" + ) -> "RenderResult": + style = console.get_style(self.style) + if self.expand: + width = options.max_width + else: + width = min( + Measurement.get(console, options, self.renderable).maximum + + self.left + + self.right, + options.max_width, + ) + render_options = options.update_width(width - self.left - self.right) + if render_options.height is not None: + render_options = render_options.update_height( + height=render_options.height - self.top - self.bottom + ) + lines = console.render_lines( + self.renderable, render_options, style=style, pad=True + ) + _Segment = Segment + + left = _Segment(" " * self.left, style) if self.left else None + right = ( + [_Segment(f'{" " * self.right}', style), _Segment.line()] + if self.right + else [_Segment.line()] + ) + blank_line: Optional[List[Segment]] = None + if self.top: + blank_line = [_Segment(f'{" " * width}\n', style)] + yield from blank_line * self.top + if left: + for line in lines: + yield left + yield from line + yield from right + else: + for line in lines: + yield from line + yield from right + if self.bottom: + blank_line = blank_line or [_Segment(f'{" " * width}\n', style)] + yield from blank_line * self.bottom + + def __rich_measure__( + self, console: "Console", options: "ConsoleOptions" + ) -> "Measurement": + max_width = options.max_width + extra_width = self.left + self.right + if max_width - extra_width < 1: + return Measurement(max_width, max_width) + measure_min, measure_max = Measurement.get(console, options, self.renderable) + measurement = Measurement(measure_min + extra_width, measure_max + extra_width) + measurement = measurement.with_maximum(max_width) + return measurement + + +if __name__ == "__main__": # pragma: no cover + from pip._vendor.rich import print + + print(Padding("Hello, World", (2, 4), style="on blue")) diff --git a/venv/Lib/site-packages/pip/_vendor/rich/pager.py b/venv/Lib/site-packages/pip/_vendor/rich/pager.py new file mode 100644 index 0000000..a3f7aa6 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/rich/pager.py @@ -0,0 +1,34 @@ +from abc import ABC, abstractmethod +from typing import Any + + +class Pager(ABC): + """Base class for a pager.""" + + @abstractmethod + def show(self, content: str) -> None: + """Show content in pager. + + Args: + content (str): Content to be displayed. + """ + + +class SystemPager(Pager): + """Uses the pager installed on the system.""" + + def _pager(self, content: str) -> Any: #  pragma: no cover + return __import__("pydoc").pager(content) + + def show(self, content: str) -> None: + """Use the same pager used by pydoc.""" + self._pager(content) + + +if __name__ == "__main__": # pragma: no cover + from .__main__ import make_test_card + from .console import Console + + console = Console() + with console.pager(styles=True): + console.print(make_test_card()) diff --git a/venv/Lib/site-packages/pip/_vendor/rich/palette.py b/venv/Lib/site-packages/pip/_vendor/rich/palette.py new file mode 100644 index 0000000..fa0c4dd --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/rich/palette.py @@ -0,0 +1,100 @@ +from math import sqrt +from functools import lru_cache +from typing import Sequence, Tuple, TYPE_CHECKING + +from .color_triplet import ColorTriplet + +if TYPE_CHECKING: + from pip._vendor.rich.table import Table + + +class Palette: + """A palette of available colors.""" + + def __init__(self, colors: Sequence[Tuple[int, int, int]]): + self._colors = colors + + def __getitem__(self, number: int) -> ColorTriplet: + return ColorTriplet(*self._colors[number]) + + def __rich__(self) -> "Table": + from pip._vendor.rich.color import Color + from pip._vendor.rich.style import Style + from pip._vendor.rich.text import Text + from pip._vendor.rich.table import Table + + table = Table( + "index", + "RGB", + "Color", + title="Palette", + caption=f"{len(self._colors)} colors", + highlight=True, + caption_justify="right", + ) + for index, color in enumerate(self._colors): + table.add_row( + str(index), + repr(color), + Text(" " * 16, style=Style(bgcolor=Color.from_rgb(*color))), + ) + return table + + # This is somewhat inefficient and needs caching + @lru_cache(maxsize=1024) + def match(self, color: Tuple[int, int, int]) -> int: + """Find a color from a palette that most closely matches a given color. + + Args: + color (Tuple[int, int, int]): RGB components in range 0 > 255. + + Returns: + int: Index of closes matching color. + """ + red1, green1, blue1 = color + _sqrt = sqrt + get_color = self._colors.__getitem__ + + def get_color_distance(index: int) -> float: + """Get the distance to a color.""" + red2, green2, blue2 = get_color(index) + red_mean = (red1 + red2) // 2 + red = red1 - red2 + green = green1 - green2 + blue = blue1 - blue2 + return _sqrt( + (((512 + red_mean) * red * red) >> 8) + + 4 * green * green + + (((767 - red_mean) * blue * blue) >> 8) + ) + + min_index = min(range(len(self._colors)), key=get_color_distance) + return min_index + + +if __name__ == "__main__": # pragma: no cover + import colorsys + from typing import Iterable + from pip._vendor.rich.color import Color + from pip._vendor.rich.console import Console, ConsoleOptions + from pip._vendor.rich.segment import Segment + from pip._vendor.rich.style import Style + + class ColorBox: + def __rich_console__( + self, console: Console, options: ConsoleOptions + ) -> Iterable[Segment]: + height = console.size.height - 3 + for y in range(0, height): + for x in range(options.max_width): + h = x / options.max_width + l = y / (height + 1) + r1, g1, b1 = colorsys.hls_to_rgb(h, l, 1.0) + r2, g2, b2 = colorsys.hls_to_rgb(h, l + (1 / height / 2), 1.0) + bgcolor = Color.from_rgb(r1 * 255, g1 * 255, b1 * 255) + color = Color.from_rgb(r2 * 255, g2 * 255, b2 * 255) + yield Segment("▄", Style(color=color, bgcolor=bgcolor)) + yield Segment.line() + + console = Console() + console.print(ColorBox()) diff --git a/venv/Lib/site-packages/pip/_vendor/rich/panel.py b/venv/Lib/site-packages/pip/_vendor/rich/panel.py new file mode 100644 index 0000000..07587e3 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/rich/panel.py @@ -0,0 +1,317 @@ +from typing import TYPE_CHECKING, Optional + +from .align import AlignMethod +from .box import ROUNDED, Box +from .cells import cell_len +from .jupyter import JupyterMixin +from .measure import Measurement, measure_renderables +from .padding import Padding, PaddingDimensions +from .segment import Segment +from .style import Style, StyleType +from .text import Text, TextType + +if TYPE_CHECKING: + from .console import Console, ConsoleOptions, RenderableType, RenderResult + + +class Panel(JupyterMixin): + """A console renderable that draws a border around its contents. + + Example: + >>> console.print(Panel("Hello, World!")) + + Args: + renderable (RenderableType): A console renderable object. + box (Box): A Box instance that defines the look of the border (see :ref:`appendix_box`. Defaults to box.ROUNDED. + title (Optional[TextType], optional): Optional title displayed in panel header. Defaults to None. + title_align (AlignMethod, optional): Alignment of title. Defaults to "center". + subtitle (Optional[TextType], optional): Optional subtitle displayed in panel footer. Defaults to None. + subtitle_align (AlignMethod, optional): Alignment of subtitle. Defaults to "center". + safe_box (bool, optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True. + expand (bool, optional): If True the panel will stretch to fill the console width, otherwise it will be sized to fit the contents. Defaults to True. + style (str, optional): The style of the panel (border and contents). Defaults to "none". + border_style (str, optional): The style of the border. Defaults to "none". + width (Optional[int], optional): Optional width of panel. Defaults to None to auto-detect. + height (Optional[int], optional): Optional height of panel. Defaults to None to auto-detect. + padding (Optional[PaddingDimensions]): Optional padding around renderable. Defaults to 0. + highlight (bool, optional): Enable automatic highlighting of panel title (if str). Defaults to False. + """ + + def __init__( + self, + renderable: "RenderableType", + box: Box = ROUNDED, + *, + title: Optional[TextType] = None, + title_align: AlignMethod = "center", + subtitle: Optional[TextType] = None, + subtitle_align: AlignMethod = "center", + safe_box: Optional[bool] = None, + expand: bool = True, + style: StyleType = "none", + border_style: StyleType = "none", + width: Optional[int] = None, + height: Optional[int] = None, + padding: PaddingDimensions = (0, 1), + highlight: bool = False, + ) -> None: + self.renderable = renderable + self.box = box + self.title = title + self.title_align: AlignMethod = title_align + self.subtitle = subtitle + self.subtitle_align = subtitle_align + self.safe_box = safe_box + self.expand = expand + self.style = style + self.border_style = border_style + self.width = width + self.height = height + self.padding = padding + self.highlight = highlight + + @classmethod + def fit( + cls, + renderable: "RenderableType", + box: Box = ROUNDED, + *, + title: Optional[TextType] = None, + title_align: AlignMethod = "center", + subtitle: Optional[TextType] = None, + subtitle_align: AlignMethod = "center", + safe_box: Optional[bool] = None, + style: StyleType = "none", + border_style: StyleType = "none", + width: Optional[int] = None, + height: Optional[int] = None, + padding: PaddingDimensions = (0, 1), + highlight: bool = False, + ) -> "Panel": + """An alternative constructor that sets expand=False.""" + return cls( + renderable, + box, + title=title, + title_align=title_align, + subtitle=subtitle, + subtitle_align=subtitle_align, + safe_box=safe_box, + style=style, + border_style=border_style, + width=width, + height=height, + padding=padding, + highlight=highlight, + expand=False, + ) + + @property + def _title(self) -> Optional[Text]: + if self.title: + title_text = ( + Text.from_markup(self.title) + if isinstance(self.title, str) + else self.title.copy() + ) + title_text.end = "" + title_text.plain = title_text.plain.replace("\n", " ") + title_text.no_wrap = True + title_text.expand_tabs() + title_text.pad(1) + return title_text + return None + + @property + def _subtitle(self) -> Optional[Text]: + if self.subtitle: + subtitle_text = ( + Text.from_markup(self.subtitle) + if isinstance(self.subtitle, str) + else self.subtitle.copy() + ) + subtitle_text.end = "" + subtitle_text.plain = subtitle_text.plain.replace("\n", " ") + subtitle_text.no_wrap = True + subtitle_text.expand_tabs() + subtitle_text.pad(1) + return subtitle_text + return None + + def __rich_console__( + self, console: "Console", options: "ConsoleOptions" + ) -> "RenderResult": + _padding = Padding.unpack(self.padding) + renderable = ( + Padding(self.renderable, _padding) if any(_padding) else self.renderable + ) + style = console.get_style(self.style) + border_style = style + console.get_style(self.border_style) + width = ( + options.max_width + if self.width is None + else min(options.max_width, self.width) + ) + + safe_box: bool = console.safe_box if self.safe_box is None else self.safe_box + box = self.box.substitute(options, safe=safe_box) + + def align_text( + text: Text, width: int, align: str, character: str, style: Style + ) -> Text: + """Gets new aligned text. + + Args: + text (Text): Title or subtitle text. + width (int): Desired width. + align (str): Alignment. + character (str): Character for alignment. + style (Style): Border style + + Returns: + Text: New text instance + """ + text = text.copy() + text.truncate(width) + excess_space = width - cell_len(text.plain) + if text.style: + text.stylize(console.get_style(text.style)) + + if excess_space: + if align == "left": + return Text.assemble( + text, + (character * excess_space, style), + no_wrap=True, + end="", + ) + elif align == "center": + left = excess_space // 2 + return Text.assemble( + (character * left, style), + text, + (character * (excess_space - left), style), + no_wrap=True, + end="", + ) + else: + return Text.assemble( + (character * excess_space, style), + text, + no_wrap=True, + end="", + ) + return text + + title_text = self._title + if title_text is not None: + title_text.stylize_before(border_style) + + child_width = ( + width - 2 + if self.expand + else console.measure( + renderable, options=options.update_width(width - 2) + ).maximum + ) + child_height = self.height or options.height or None + if child_height: + child_height -= 2 + if title_text is not None: + child_width = min( + options.max_width - 2, max(child_width, title_text.cell_len + 2) + ) + + width = child_width + 2 + child_options = options.update( + width=child_width, height=child_height, highlight=self.highlight + ) + lines = console.render_lines(renderable, child_options, style=style) + + line_start = Segment(box.mid_left, border_style) + line_end = Segment(f"{box.mid_right}", border_style) + new_line = Segment.line() + if title_text is None or width <= 4: + yield Segment(box.get_top([width - 2]), border_style) + else: + title_text = align_text( + title_text, + width - 4, + self.title_align, + box.top, + border_style, + ) + yield Segment(box.top_left + box.top, border_style) + yield from console.render(title_text, child_options.update_width(width - 4)) + yield Segment(box.top + box.top_right, border_style) + + yield new_line + for line in lines: + yield line_start + yield from line + yield line_end + yield new_line + + subtitle_text = self._subtitle + if subtitle_text is not None: + subtitle_text.stylize_before(border_style) + + if subtitle_text is None or width <= 4: + yield Segment(box.get_bottom([width - 2]), border_style) + else: + subtitle_text = align_text( + subtitle_text, + width - 4, + self.subtitle_align, + box.bottom, + border_style, + ) + yield Segment(box.bottom_left + box.bottom, border_style) + yield from console.render( + subtitle_text, child_options.update_width(width - 4) + ) + yield Segment(box.bottom + box.bottom_right, border_style) + + yield new_line + + def __rich_measure__( + self, console: "Console", options: "ConsoleOptions" + ) -> "Measurement": + _title = self._title + _, right, _, left = Padding.unpack(self.padding) + padding = left + right + renderables = [self.renderable, _title] if _title else [self.renderable] + + if self.width is None: + width = ( + measure_renderables( + console, + options.update_width(options.max_width - padding - 2), + renderables, + ).maximum + + padding + + 2 + ) + else: + width = self.width + return Measurement(width, width) + + +if __name__ == "__main__": # pragma: no cover + from .console import Console + + c = Console() + + from .box import DOUBLE, ROUNDED + from .padding import Padding + + p = Panel( + "Hello, World!", + title="rich.Panel", + style="white on blue", + box=DOUBLE, + padding=1, + ) + + c.print() + c.print(p)