summaryrefslogtreecommitdiff
path: root/src/pip/_vendor/rich
diff options
context:
space:
mode:
authorPradyun Gedam <pradyunsg@users.noreply.github.com>2021-11-05 12:44:54 +0000
committerPradyun Gedam <pradyunsg@users.noreply.github.com>2021-11-26 11:50:21 +0000
commit7510fb04b6d12487a7de2d49cad8cb29366a6ae2 (patch)
treeba62f995a40f45a06c0b288715ad29d678a436be /src/pip/_vendor/rich
parentc6037c7bbda548caae24b3df698ee6cbc5b0d67a (diff)
downloadpip-7510fb04b6d12487a7de2d49cad8cb29366a6ae2.tar.gz
Vendor rich and required dependencies (pygments, typing_extensions)
This will enable building upon these libraries, to improve the presentation style and output.
Diffstat (limited to 'src/pip/_vendor/rich')
-rw-r--r--src/pip/_vendor/rich/LICENSE19
-rw-r--r--src/pip/_vendor/rich/__init__.py172
-rw-r--r--src/pip/_vendor/rich/__main__.py285
-rw-r--r--src/pip/_vendor/rich/_cell_widths.py451
-rw-r--r--src/pip/_vendor/rich/_emoji_codes.py3610
-rw-r--r--src/pip/_vendor/rich/_emoji_replace.py32
-rw-r--r--src/pip/_vendor/rich/_extension.py10
-rw-r--r--src/pip/_vendor/rich/_inspect.py210
-rw-r--r--src/pip/_vendor/rich/_log_render.py94
-rw-r--r--src/pip/_vendor/rich/_loop.py43
-rw-r--r--src/pip/_vendor/rich/_lru_cache.py34
-rw-r--r--src/pip/_vendor/rich/_palettes.py309
-rw-r--r--src/pip/_vendor/rich/_pick.py17
-rw-r--r--src/pip/_vendor/rich/_ratio.py160
-rw-r--r--src/pip/_vendor/rich/_spinners.py848
-rw-r--r--src/pip/_vendor/rich/_stack.py16
-rw-r--r--src/pip/_vendor/rich/_timer.py19
-rw-r--r--src/pip/_vendor/rich/_windows.py75
-rw-r--r--src/pip/_vendor/rich/_wrap.py55
-rw-r--r--src/pip/_vendor/rich/abc.py33
-rw-r--r--src/pip/_vendor/rich/align.py312
-rw-r--r--src/pip/_vendor/rich/ansi.py228
-rw-r--r--src/pip/_vendor/rich/bar.py94
-rw-r--r--src/pip/_vendor/rich/box.py483
-rw-r--r--src/pip/_vendor/rich/cells.py133
-rw-r--r--src/pip/_vendor/rich/color.py581
-rw-r--r--src/pip/_vendor/rich/color_triplet.py38
-rw-r--r--src/pip/_vendor/rich/columns.py187
-rw-r--r--src/pip/_vendor/rich/console.py2191
-rw-r--r--src/pip/_vendor/rich/constrain.py37
-rw-r--r--src/pip/_vendor/rich/containers.py167
-rw-r--r--src/pip/_vendor/rich/control.py175
-rw-r--r--src/pip/_vendor/rich/default_styles.py159
-rw-r--r--src/pip/_vendor/rich/diagnose.py6
-rw-r--r--src/pip/_vendor/rich/emoji.py96
-rw-r--r--src/pip/_vendor/rich/errors.py34
-rw-r--r--src/pip/_vendor/rich/file_proxy.py54
-rw-r--r--src/pip/_vendor/rich/filesize.py89
-rw-r--r--src/pip/_vendor/rich/highlighter.py147
-rw-r--r--src/pip/_vendor/rich/json.py140
-rw-r--r--src/pip/_vendor/rich/jupyter.py88
-rw-r--r--src/pip/_vendor/rich/layout.py444
-rw-r--r--src/pip/_vendor/rich/live.py377
-rw-r--r--src/pip/_vendor/rich/live_render.py113
-rw-r--r--src/pip/_vendor/rich/logging.py267
-rw-r--r--src/pip/_vendor/rich/markup.py244
-rw-r--r--src/pip/_vendor/rich/measure.py150
-rw-r--r--src/pip/_vendor/rich/padding.py139
-rw-r--r--src/pip/_vendor/rich/pager.py34
-rw-r--r--src/pip/_vendor/rich/palette.py100
-rw-r--r--src/pip/_vendor/rich/panel.py250
-rw-r--r--src/pip/_vendor/rich/pretty.py834
-rw-r--r--src/pip/_vendor/rich/progress.py1034
-rw-r--r--src/pip/_vendor/rich/progress_bar.py216
-rw-r--r--src/pip/_vendor/rich/prompt.py376
-rw-r--r--src/pip/_vendor/rich/protocol.py10
-rw-r--r--src/pip/_vendor/rich/py.typed0
-rw-r--r--src/pip/_vendor/rich/region.py10
-rw-r--r--src/pip/_vendor/rich/repr.py151
-rw-r--r--src/pip/_vendor/rich/rule.py115
-rw-r--r--src/pip/_vendor/rich/scope.py86
-rw-r--r--src/pip/_vendor/rich/screen.py54
-rw-r--r--src/pip/_vendor/rich/segment.py623
-rw-r--r--src/pip/_vendor/rich/spinner.py134
-rw-r--r--src/pip/_vendor/rich/status.py132
-rw-r--r--src/pip/_vendor/rich/style.py785
-rw-r--r--src/pip/_vendor/rich/styled.py42
-rw-r--r--src/pip/_vendor/rich/syntax.py717
-rw-r--r--src/pip/_vendor/rich/table.py919
-rw-r--r--src/pip/_vendor/rich/tabulate.py51
-rw-r--r--src/pip/_vendor/rich/terminal_theme.py55
-rw-r--r--src/pip/_vendor/rich/text.py1196
-rw-r--r--src/pip/_vendor/rich/theme.py112
-rw-r--r--src/pip/_vendor/rich/themes.py5
-rw-r--r--src/pip/_vendor/rich/traceback.py680
-rw-r--r--src/pip/_vendor/rich/tree.py249
76 files changed, 22635 insertions, 0 deletions
diff --git a/src/pip/_vendor/rich/LICENSE b/src/pip/_vendor/rich/LICENSE
new file mode 100644
index 000000000..441550556
--- /dev/null
+++ b/src/pip/_vendor/rich/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2020 Will McGugan
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/pip/_vendor/rich/__init__.py b/src/pip/_vendor/rich/__init__.py
new file mode 100644
index 000000000..50f381576
--- /dev/null
+++ b/src/pip/_vendor/rich/__init__.py
@@ -0,0 +1,172 @@
+"""Rich text and beautiful formatting in the terminal."""
+
+import os
+from typing import Callable, IO, TYPE_CHECKING, Any, Optional
+
+from ._extension import load_ipython_extension
+
+__all__ = ["get_console", "reconfigure", "print", "inspect"]
+
+if TYPE_CHECKING:
+ from .console import Console
+
+# Global console used by alternative print
+_console: Optional["Console"] = None
+
+_IMPORT_CWD = os.path.abspath(os.getcwd())
+
+
+def get_console() -> "Console":
+ """Get a global :class:`~rich.console.Console` instance. This function is used when Rich requires a Console,
+ and hasn't been explicitly given one.
+
+ Returns:
+ Console: A console instance.
+ """
+ global _console
+ if _console is None:
+ from .console import Console
+
+ _console = Console()
+
+ return _console
+
+
+def reconfigure(*args: Any, **kwargs: Any) -> None:
+ """Reconfigures the global console by replacing it with another.
+
+ Args:
+ console (Console): Replacement console instance.
+ """
+ from pip._vendor.rich.console import Console
+
+ new_console = Console(*args, **kwargs)
+ _console = get_console()
+ _console.__dict__ = new_console.__dict__
+
+
+def print(
+ *objects: Any,
+ sep: str = " ",
+ end: str = "\n",
+ file: Optional[IO[str]] = None,
+ flush: bool = False,
+) -> None:
+ r"""Print object(s) supplied via positional arguments.
+ This function has an identical signature to the built-in print.
+ For more advanced features, see the :class:`~rich.console.Console` class.
+
+ Args:
+ sep (str, optional): Separator between printed objects. Defaults to " ".
+ end (str, optional): Character to write at end of output. Defaults to "\\n".
+ file (IO[str], optional): File to write to, or None for stdout. Defaults to None.
+ flush (bool, optional): Has no effect as Rich always flushes output. Defaults to False.
+
+ """
+ from .console import Console
+
+ write_console = get_console() if file is None else Console(file=file)
+ return write_console.print(*objects, sep=sep, end=end)
+
+
+def print_json(
+ json: Optional[str] = None,
+ *,
+ data: Any = None,
+ indent: int = 2,
+ highlight: bool = True,
+ skip_keys: bool = False,
+ ensure_ascii: bool = True,
+ check_circular: bool = True,
+ allow_nan: bool = True,
+ default: Optional[Callable[[Any], Any]] = None,
+ sort_keys: bool = False,
+) -> None:
+ """Pretty prints JSON. Output will be valid JSON.
+
+ Args:
+ json (str): A string containing JSON.
+ data (Any): If json is not supplied, then encode this data.
+ indent (int, optional): Number of spaces to indent. Defaults to 2.
+ highlight (bool, optional): Enable highlighting of output: Defaults to True.
+ skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False.
+ ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False.
+ check_circular (bool, optional): Check for circular references. Defaults to True.
+ allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True.
+ default (Callable, optional): A callable that converts values that can not be encoded
+ in to something that can be JSON encoded. Defaults to None.
+ sort_keys (bool, optional): Sort dictionary keys. Defaults to False.
+ """
+
+ get_console().print_json(
+ json,
+ data=data,
+ indent=indent,
+ highlight=highlight,
+ skip_keys=skip_keys,
+ ensure_ascii=ensure_ascii,
+ check_circular=check_circular,
+ allow_nan=allow_nan,
+ default=default,
+ sort_keys=sort_keys,
+ )
+
+
+def inspect(
+ obj: Any,
+ *,
+ console: Optional["Console"] = None,
+ title: Optional[str] = None,
+ help: bool = False,
+ methods: bool = False,
+ docs: bool = True,
+ private: bool = False,
+ dunder: bool = False,
+ sort: bool = True,
+ all: bool = False,
+ value: bool = True,
+) -> None:
+ """Inspect any Python object.
+
+ * inspect(<OBJECT>) to see summarized info.
+ * inspect(<OBJECT>, methods=True) to see methods.
+ * inspect(<OBJECT>, help=True) to see full (non-abbreviated) help.
+ * inspect(<OBJECT>, private=True) to see private attributes (single underscore).
+ * inspect(<OBJECT>, dunder=True) to see attributes beginning with double underscore.
+ * inspect(<OBJECT>, all=True) to see all attributes.
+
+ Args:
+ obj (Any): An object to inspect.
+ title (str, optional): Title to display over inspect result, or None use type. Defaults to None.
+ help (bool, optional): Show full help text rather than just first paragraph. Defaults to False.
+ methods (bool, optional): Enable inspection of callables. Defaults to False.
+ docs (bool, optional): Also render doc strings. Defaults to True.
+ private (bool, optional): Show private attributes (beginning with underscore). Defaults to False.
+ dunder (bool, optional): Show attributes starting with double underscore. Defaults to False.
+ sort (bool, optional): Sort attributes alphabetically. Defaults to True.
+ all (bool, optional): Show all attributes. Defaults to False.
+ value (bool, optional): Pretty print value. Defaults to True.
+ """
+ _console = console or get_console()
+ from pip._vendor.rich._inspect import Inspect
+
+ # Special case for inspect(inspect)
+ is_inspect = obj is inspect
+
+ _inspect = Inspect(
+ obj,
+ title=title,
+ help=is_inspect or help,
+ methods=is_inspect or methods,
+ docs=is_inspect or docs,
+ private=private,
+ dunder=dunder,
+ sort=sort,
+ all=all,
+ value=value,
+ )
+ _console.print(_inspect)
+
+
+if __name__ == "__main__": # pragma: no cover
+ print("Hello, **World**")
diff --git a/src/pip/_vendor/rich/__main__.py b/src/pip/_vendor/rich/__main__.py
new file mode 100644
index 000000000..60d03efba
--- /dev/null
+++ b/src/pip/_vendor/rich/__main__.py
@@ -0,0 +1,285 @@
+import colorsys
+import io
+from time import process_time
+
+from pip._vendor.rich import box
+from pip._vendor.rich.color import Color
+from pip._vendor.rich.console import (
+ Console,
+ ConsoleOptions,
+ Group,
+ RenderResult,
+ RenderableType,
+)
+from pip._vendor.rich.markdown import Markdown
+from pip._vendor.rich.measure import Measurement
+from pip._vendor.rich.pretty import Pretty
+from pip._vendor.rich.segment import Segment
+from pip._vendor.rich.style import Style
+from pip._vendor.rich.syntax import Syntax
+from pip._vendor.rich.table import Table
+from pip._vendor.rich.text import Text
+
+
+class ColorBox:
+ def __rich_console__(
+ self, console: Console, options: ConsoleOptions
+ ) -> RenderResult:
+ for y in range(0, 5):
+ for x in range(options.max_width):
+ h = x / options.max_width
+ l = 0.1 + ((y / 5) * 0.7)
+ r1, g1, b1 = colorsys.hls_to_rgb(h, l, 1.0)
+ r2, g2, b2 = colorsys.hls_to_rgb(h, l + 0.7 / 10, 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()
+
+ def __rich_measure__(
+ self, console: "Console", options: ConsoleOptions
+ ) -> Measurement:
+ return Measurement(1, options.max_width)
+
+
+def make_test_card() -> Table:
+ """Get a renderable that demonstrates a number of features."""
+ table = Table.grid(padding=1, pad_edge=True)
+ table.title = "Rich features"
+ table.add_column("Feature", no_wrap=True, justify="center", style="bold red")
+ table.add_column("Demonstration")
+
+ color_table = Table(
+ box=None,
+ expand=False,
+ show_header=False,
+ show_edge=False,
+ pad_edge=False,
+ )
+ color_table.add_row(
+ # "[bold yellow]256[/] colors or [bold green]16.7 million[/] colors [blue](if supported by your terminal)[/].",
+ (
+ "✓ [bold green]4-bit color[/]\n"
+ "✓ [bold blue]8-bit color[/]\n"
+ "✓ [bold magenta]Truecolor (16.7 million)[/]\n"
+ "✓ [bold yellow]Dumb terminals[/]\n"
+ "✓ [bold cyan]Automatic color conversion"
+ ),
+ ColorBox(),
+ )
+
+ table.add_row("Colors", color_table)
+
+ table.add_row(
+ "Styles",
+ "All ansi styles: [bold]bold[/], [dim]dim[/], [italic]italic[/italic], [underline]underline[/], [strike]strikethrough[/], [reverse]reverse[/], and even [blink]blink[/].",
+ )
+
+ lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque in metus sed sapien ultricies pretium a at justo. Maecenas luctus velit et auctor maximus."
+ lorem_table = Table.grid(padding=1, collapse_padding=True)
+ lorem_table.pad_edge = False
+ lorem_table.add_row(
+ Text(lorem, justify="left", style="green"),
+ Text(lorem, justify="center", style="yellow"),
+ Text(lorem, justify="right", style="blue"),
+ Text(lorem, justify="full", style="red"),
+ )
+ table.add_row(
+ "Text",
+ Group(
+ Text.from_markup(
+ """Word wrap text. Justify [green]left[/], [yellow]center[/], [blue]right[/] or [red]full[/].\n"""
+ ),
+ lorem_table,
+ ),
+ )
+
+ def comparison(renderable1: RenderableType, renderable2: RenderableType) -> Table:
+ table = Table(show_header=False, pad_edge=False, box=None, expand=True)
+ table.add_column("1", ratio=1)
+ table.add_column("2", ratio=1)
+ table.add_row(renderable1, renderable2)
+ return table
+
+ table.add_row(
+ "Asian\nlanguage\nsupport",
+ ":flag_for_china: 该库支持中文,日文和韩文文本!\n:flag_for_japan: ライブラリは中国語、日本語、韓国語のテキストをサポートしています\n:flag_for_south_korea: 이 라이브러리는 중국어, 일본어 및 한국어 텍스트를 지원합니다",
+ )
+
+ markup_example = (
+ "[bold magenta]Rich[/] supports a simple [i]bbcode[/i]-like [b]markup[/b] for [yellow]color[/], [underline]style[/], and emoji! "
+ ":+1: :apple: :ant: :bear: :baguette_bread: :bus: "
+ )
+ table.add_row("Markup", markup_example)
+
+ example_table = Table(
+ show_edge=False,
+ show_header=True,
+ expand=False,
+ row_styles=["none", "dim"],
+ box=box.SIMPLE,
+ )
+ example_table.add_column("[green]Date", style="green", no_wrap=True)
+ example_table.add_column("[blue]Title", style="blue")
+ example_table.add_column(
+ "[cyan]Production Budget",
+ style="cyan",
+ justify="right",
+ no_wrap=True,
+ )
+ example_table.add_column(
+ "[magenta]Box Office",
+ style="magenta",
+ justify="right",
+ no_wrap=True,
+ )
+ example_table.add_row(
+ "Dec 20, 2019",
+ "Star Wars: The Rise of Skywalker",
+ "$275,000,000",
+ "$375,126,118",
+ )
+ example_table.add_row(
+ "May 25, 2018",
+ "[b]Solo[/]: A Star Wars Story",
+ "$275,000,000",
+ "$393,151,347",
+ )
+ example_table.add_row(
+ "Dec 15, 2017",
+ "Star Wars Ep. VIII: The Last Jedi",
+ "$262,000,000",
+ "[bold]$1,332,539,889[/bold]",
+ )
+ example_table.add_row(
+ "May 19, 1999",
+ "Star Wars Ep. [b]I[/b]: [i]The phantom Menace",
+ "$115,000,000",
+ "$1,027,044,677",
+ )
+
+ table.add_row("Tables", example_table)
+
+ code = '''\
+def iter_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]:
+ """Iterate and generate a tuple with a flag for last value."""
+ iter_values = iter(values)
+ try:
+ previous_value = next(iter_values)
+ except StopIteration:
+ return
+ for value in iter_values:
+ yield False, previous_value
+ previous_value = value
+ yield True, previous_value'''
+
+ pretty_data = {
+ "foo": [
+ 3.1427,
+ (
+ "Paul Atreides",
+ "Vladimir Harkonnen",
+ "Thufir Hawat",
+ ),
+ ],
+ "atomic": (False, True, None),
+ }
+ table.add_row(
+ "Syntax\nhighlighting\n&\npretty\nprinting",
+ comparison(
+ Syntax(code, "python3", line_numbers=True, indent_guides=True),
+ Pretty(pretty_data, indent_guides=True),
+ ),
+ )
+
+ markdown_example = """\
+# Markdown
+
+Supports much of the *markdown* __syntax__!
+
+- Headers
+- Basic formatting: **bold**, *italic*, `code`
+- Block quotes
+- Lists, and more...
+ """
+ table.add_row(
+ "Markdown", comparison("[cyan]" + markdown_example, Markdown(markdown_example))
+ )
+
+ table.add_row(
+ "+more!",
+ """Progress bars, columns, styled logging handler, tracebacks, etc...""",
+ )
+ return table
+
+
+if __name__ == "__main__": # pragma: no cover
+
+ console = Console(
+ file=io.StringIO(),
+ force_terminal=True,
+ )
+ test_card = make_test_card()
+
+ # Print once to warm cache
+ console.print(test_card)
+ console.file = io.StringIO()
+
+ start = process_time()
+ console.print(test_card)
+ taken = round((process_time() - start) * 1000.0, 1)
+
+ text = console.file.getvalue()
+ # https://bugs.python.org/issue37871
+ for line in text.splitlines(True):
+ print(line, end="")
+
+ print(f"rendered in {taken}ms")
+
+ from pip._vendor.rich.panel import Panel
+
+ console = Console()
+
+ sponsor_message = Table.grid(padding=1)
+ sponsor_message.add_column(style="green", justify="right")
+ sponsor_message.add_column(no_wrap=True)
+ sponsor_message.add_row(
+ "Sponsor me",
+ "[u blue link=https://github.com/sponsors/willmcgugan]https://github.com/sponsors/willmcgugan",
+ )
+ sponsor_message.add_row(
+ "Buy me a :coffee:",
+ "[u blue link=https://ko-fi.com/willmcgugan]https://ko-fi.com/willmcgugan",
+ )
+ sponsor_message.add_row(
+ "Twitter",
+ "[u blue link=https://twitter.com/willmcgugan]https://twitter.com/willmcgugan",
+ )
+ sponsor_message.add_row(
+ "Blog", "[u blue link=https://www.willmcgugan.com]https://www.willmcgugan.com"
+ )
+
+ intro_message = Text.from_markup(
+ """\
+It takes a lot of time to develop Rich and to provide support.
+
+Consider supporting my work via Github Sponsors (ask your company / organization), or buy me a coffee to say thanks.
+
+- Will McGugan"""
+ )
+
+ message = Table.grid(padding=2)
+ message.add_column()
+ message.add_column(no_wrap=True)
+ message.add_row(intro_message, sponsor_message)
+
+ console.print(
+ Panel.fit(
+ message,
+ box=box.ROUNDED,
+ padding=(1, 2),
+ title="[b red]Thanks for trying out Rich!",
+ border_style="bright_blue",
+ ),
+ justify="center",
+ )
diff --git a/src/pip/_vendor/rich/_cell_widths.py b/src/pip/_vendor/rich/_cell_widths.py
new file mode 100644
index 000000000..36286df37
--- /dev/null
+++ b/src/pip/_vendor/rich/_cell_widths.py
@@ -0,0 +1,451 @@
+# Auto generated by make_terminal_widths.py
+
+CELL_WIDTHS = [
+ (0, 0, 0),
+ (1, 31, -1),
+ (127, 159, -1),
+ (768, 879, 0),
+ (1155, 1161, 0),
+ (1425, 1469, 0),
+ (1471, 1471, 0),
+ (1473, 1474, 0),
+ (1476, 1477, 0),
+ (1479, 1479, 0),
+ (1552, 1562, 0),
+ (1611, 1631, 0),
+ (1648, 1648, 0),
+ (1750, 1756, 0),
+ (1759, 1764, 0),
+ (1767, 1768, 0),
+ (1770, 1773, 0),
+ (1809, 1809, 0),
+ (1840, 1866, 0),
+ (1958, 1968, 0),
+ (2027, 2035, 0),
+ (2045, 2045, 0),
+ (2070, 2073, 0),
+ (2075, 2083, 0),
+ (2085, 2087, 0),
+ (2089, 2093, 0),
+ (2137, 2139, 0),
+ (2259, 2273, 0),
+ (2275, 2306, 0),
+ (2362, 2362, 0),
+ (2364, 2364, 0),
+ (2369, 2376, 0),
+ (2381, 2381, 0),
+ (2385, 2391, 0),
+ (2402, 2403, 0),
+ (2433, 2433, 0),
+ (2492, 2492, 0),
+ (2497, 2500, 0),
+ (2509, 2509, 0),
+ (2530, 2531, 0),
+ (2558, 2558, 0),
+ (2561, 2562, 0),
+ (2620, 2620, 0),
+ (2625, 2626, 0),
+ (2631, 2632, 0),
+ (2635, 2637, 0),
+ (2641, 2641, 0),
+ (2672, 2673, 0),
+ (2677, 2677, 0),
+ (2689, 2690, 0),
+ (2748, 2748, 0),
+ (2753, 2757, 0),
+ (2759, 2760, 0),
+ (2765, 2765, 0),
+ (2786, 2787, 0),
+ (2810, 2815, 0),
+ (2817, 2817, 0),
+ (2876, 2876, 0),
+ (2879, 2879, 0),
+ (2881, 2884, 0),
+ (2893, 2893, 0),
+ (2901, 2902, 0),
+ (2914, 2915, 0),
+ (2946, 2946, 0),
+ (3008, 3008, 0),
+ (3021, 3021, 0),
+ (3072, 3072, 0),
+ (3076, 3076, 0),
+ (3134, 3136, 0),
+ (3142, 3144, 0),
+ (3146, 3149, 0),
+ (3157, 3158, 0),
+ (3170, 3171, 0),
+ (3201, 3201, 0),
+ (3260, 3260, 0),
+ (3263, 3263, 0),
+ (3270, 3270, 0),
+ (3276, 3277, 0),
+ (3298, 3299, 0),
+ (3328, 3329, 0),
+ (3387, 3388, 0),
+ (3393, 3396, 0),
+ (3405, 3405, 0),
+ (3426, 3427, 0),
+ (3457, 3457, 0),
+ (3530, 3530, 0),
+ (3538, 3540, 0),
+ (3542, 3542, 0),
+ (3633, 3633, 0),
+ (3636, 3642, 0),
+ (3655, 3662, 0),
+ (3761, 3761, 0),
+ (3764, 3772, 0),
+ (3784, 3789, 0),
+ (3864, 3865, 0),
+ (3893, 3893, 0),
+ (3895, 3895, 0),
+ (3897, 3897, 0),
+ (3953, 3966, 0),
+ (3968, 3972, 0),
+ (3974, 3975, 0),
+ (3981, 3991, 0),
+ (3993, 4028, 0),
+ (4038, 4038, 0),
+ (4141, 4144, 0),
+ (4146, 4151, 0),
+ (4153, 4154, 0),
+ (4157, 4158, 0),
+ (4184, 4185, 0),
+ (4190, 4192, 0),
+ (4209, 4212, 0),
+ (4226, 4226, 0),
+ (4229, 4230, 0),
+ (4237, 4237, 0),
+ (4253, 4253, 0),
+ (4352, 4447, 2),
+ (4957, 4959, 0),
+ (5906, 5908, 0),
+ (5938, 5940, 0),
+ (5970, 5971, 0),
+ (6002, 6003, 0),
+ (6068, 6069, 0),
+ (6071, 6077, 0),
+ (6086, 6086, 0),
+ (6089, 6099, 0),
+ (6109, 6109, 0),
+ (6155, 6157, 0),
+ (6277, 6278, 0),
+ (6313, 6313, 0),
+ (6432, 6434, 0),
+ (6439, 6440, 0),
+ (6450, 6450, 0),
+ (6457, 6459, 0),
+ (6679, 6680, 0),
+ (6683, 6683, 0),
+ (6742, 6742, 0),
+ (6744, 6750, 0),
+ (6752, 6752, 0),
+ (6754, 6754, 0),
+ (6757, 6764, 0),
+ (6771, 6780, 0),
+ (6783, 6783, 0),
+ (6832, 6848, 0),
+ (6912, 6915, 0),
+ (6964, 6964, 0),
+ (6966, 6970, 0),
+ (6972, 6972, 0),
+ (6978, 6978, 0),
+ (7019, 7027, 0),
+ (7040, 7041, 0),
+ (7074, 7077, 0),
+ (7080, 7081, 0),
+ (7083, 7085, 0),
+ (7142, 7142, 0),
+ (7144, 7145, 0),
+ (7149, 7149, 0),
+ (7151, 7153, 0),
+ (7212, 7219, 0),
+ (7222, 7223, 0),
+ (7376, 7378, 0),
+ (7380, 7392, 0),
+ (7394, 7400, 0),
+ (7405, 7405, 0),
+ (7412, 7412, 0),
+ (7416, 7417, 0),
+ (7616, 7673, 0),
+ (7675, 7679, 0),
+ (8203, 8207, 0),
+ (8232, 8238, 0),
+ (8288, 8291, 0),
+ (8400, 8432, 0),
+ (8986, 8987, 2),
+ (9001, 9002, 2),
+ (9193, 9196, 2),
+ (9200, 9200, 2),
+ (9203, 9203, 2),
+ (9725, 9726, 2),
+ (9748, 9749, 2),
+ (9800, 9811, 2),
+ (9855, 9855, 2),
+ (9875, 9875, 2),
+ (9889, 9889, 2),
+ (9898, 9899, 2),
+ (9917, 9918, 2),
+ (9924, 9925, 2),
+ (9934, 9934, 2),
+ (9940, 9940, 2),
+ (9962, 9962, 2),
+ (9970, 9971, 2),
+ (9973, 9973, 2),
+ (9978, 9978, 2),
+ (9981, 9981, 2),
+ (9989, 9989, 2),
+ (9994, 9995, 2),
+ (10024, 10024, 2),
+ (10060, 10060, 2),
+ (10062, 10062, 2),
+ (10067, 10069, 2),
+ (10071, 10071, 2),
+ (10133, 10135, 2),
+ (10160, 10160, 2),
+ (10175, 10175, 2),
+ (11035, 11036, 2),
+ (11088, 11088, 2),
+ (11093, 11093, 2),
+ (11503, 11505, 0),
+ (11647, 11647, 0),
+ (11744, 11775, 0),
+ (11904, 11929, 2),
+ (11931, 12019, 2),
+ (12032, 12245, 2),
+ (12272, 12283, 2),
+ (12288, 12329, 2),
+ (12330, 12333, 0),
+ (12334, 12350, 2),
+ (12353, 12438, 2),
+ (12441, 12442, 0),
+ (12443, 12543, 2),
+ (12549, 12591, 2),
+ (12593, 12686, 2),
+ (12688, 12771, 2),
+ (12784, 12830, 2),
+ (12832, 12871, 2),
+ (12880, 19903, 2),
+ (19968, 42124, 2),
+ (42128, 42182, 2),
+ (42607, 42610, 0),
+ (42612, 42621, 0),
+ (42654, 42655, 0),
+ (42736, 42737, 0),
+ (43010, 43010, 0),
+ (43014, 43014, 0),
+ (43019, 43019, 0),
+ (43045, 43046, 0),
+ (43052, 43052, 0),
+ (43204, 43205, 0),
+ (43232, 43249, 0),
+ (43263, 43263, 0),
+ (43302, 43309, 0),
+ (43335, 43345, 0),
+ (43360, 43388, 2),
+ (43392, 43394, 0),
+ (43443, 43443, 0),
+ (43446, 43449, 0),
+ (43452, 43453, 0),
+ (43493, 43493, 0),
+ (43561, 43566, 0),
+ (43569, 43570, 0),
+ (43573, 43574, 0),
+ (43587, 43587, 0),
+ (43596, 43596, 0),
+ (43644, 43644, 0),
+ (43696, 43696, 0),
+ (43698, 43700, 0),
+ (43703, 43704, 0),
+ (43710, 43711, 0),
+ (43713, 43713, 0),
+ (43756, 43757, 0),
+ (43766, 43766, 0),
+ (44005, 44005, 0),
+ (44008, 44008, 0),
+ (44013, 44013, 0),
+ (44032, 55203, 2),
+ (63744, 64255, 2),
+ (64286, 64286, 0),
+ (65024, 65039, 0),
+ (65040, 65049, 2),
+ (65056, 65071, 0),
+ (65072, 65106, 2),
+ (65108, 65126, 2),
+ (65128, 65131, 2),
+ (65281, 65376, 2),
+ (65504, 65510, 2),
+ (66045, 66045, 0),
+ (66272, 66272, 0),
+ (66422, 66426, 0),
+ (68097, 68099, 0),
+ (68101, 68102, 0),
+ (68108, 68111, 0),
+ (68152, 68154, 0),
+ (68159, 68159, 0),
+ (68325, 68326, 0),
+ (68900, 68903, 0),
+ (69291, 69292, 0),
+ (69446, 69456, 0),
+ (69633, 69633, 0),
+ (69688, 69702, 0),
+ (69759, 69761, 0),
+ (69811, 69814, 0),
+ (69817, 69818, 0),
+ (69888, 69890, 0),
+ (69927, 69931, 0),
+ (69933, 69940, 0),
+ (70003, 70003, 0),
+ (70016, 70017, 0),
+ (70070, 70078, 0),
+ (70089, 70092, 0),
+ (70095, 70095, 0),
+ (70191, 70193, 0),
+ (70196, 70196, 0),
+ (70198, 70199, 0),
+ (70206, 70206, 0),
+ (70367, 70367, 0),
+ (70371, 70378, 0),
+ (70400, 70401, 0),
+ (70459, 70460, 0),
+ (70464, 70464, 0),
+ (70502, 70508, 0),
+ (70512, 70516, 0),
+ (70712, 70719, 0),
+ (70722, 70724, 0),
+ (70726, 70726, 0),
+ (70750, 70750, 0),
+ (70835, 70840, 0),
+ (70842, 70842, 0),
+ (70847, 70848, 0),
+ (70850, 70851, 0),
+ (71090, 71093, 0),
+ (71100, 71101, 0),
+ (71103, 71104, 0),
+ (71132, 71133, 0),
+ (71219, 71226, 0),
+ (71229, 71229, 0),
+ (71231, 71232, 0),
+ (71339, 71339, 0),
+ (71341, 71341, 0),
+ (71344, 71349, 0),
+ (71351, 71351, 0),
+ (71453, 71455, 0),
+ (71458, 71461, 0),
+ (71463, 71467, 0),
+ (71727, 71735, 0),
+ (71737, 71738, 0),
+ (71995, 71996, 0),
+ (71998, 71998, 0),
+ (72003, 72003, 0),
+ (72148, 72151, 0),
+ (72154, 72155, 0),
+ (72160, 72160, 0),
+ (72193, 72202, 0),
+ (72243, 72248, 0),
+ (72251, 72254, 0),
+ (72263, 72263, 0),
+ (72273, 72278, 0),
+ (72281, 72283, 0),
+ (72330, 72342, 0),
+ (72344, 72345, 0),
+ (72752, 72758, 0),
+ (72760, 72765, 0),
+ (72767, 72767, 0),
+ (72850, 72871, 0),
+ (72874, 72880, 0),
+ (72882, 72883, 0),
+ (72885, 72886, 0),
+ (73009, 73014, 0),
+ (73018, 73018, 0),
+ (73020, 73021, 0),
+ (73023, 73029, 0),
+ (73031, 73031, 0),
+ (73104, 73105, 0),
+ (73109, 73109, 0),
+ (73111, 73111, 0),
+ (73459, 73460, 0),
+ (92912, 92916, 0),
+ (92976, 92982, 0),
+ (94031, 94031, 0),
+ (94095, 94098, 0),
+ (94176, 94179, 2),
+ (94180, 94180, 0),
+ (94192, 94193, 2),
+ (94208, 100343, 2),
+ (100352, 101589, 2),
+ (101632, 101640, 2),
+ (110592, 110878, 2),
+ (110928, 110930, 2),
+ (110948, 110951, 2),
+ (110960, 111355, 2),
+ (113821, 113822, 0),
+ (119143, 119145, 0),
+ (119163, 119170, 0),
+ (119173, 119179, 0),
+ (119210, 119213, 0),
+ (119362, 119364, 0),
+ (121344, 121398, 0),
+ (121403, 121452, 0),
+ (121461, 121461, 0),
+ (121476, 121476, 0),
+ (121499, 121503, 0),
+ (121505, 121519, 0),
+ (122880, 122886, 0),
+ (122888, 122904, 0),
+ (122907, 122913, 0),
+ (122915, 122916, 0),
+ (122918, 122922, 0),
+ (123184, 123190, 0),
+ (123628, 123631, 0),
+ (125136, 125142, 0),
+ (125252, 125258, 0),
+ (126980, 126980, 2),
+ (127183, 127183, 2),
+ (127374, 127374, 2),
+ (127377, 127386, 2),
+ (127488, 127490, 2),
+ (127504, 127547, 2),
+ (127552, 127560, 2),
+ (127568, 127569, 2),
+ (127584, 127589, 2),
+ (127744, 127776, 2),
+ (127789, 127797, 2),
+ (127799, 127868, 2),
+ (127870, 127891, 2),
+ (127904, 127946, 2),
+ (127951, 127955, 2),
+ (127968, 127984, 2),
+ (127988, 127988, 2),
+ (127992, 128062, 2),
+ (128064, 128064, 2),
+ (128066, 128252, 2),
+ (128255, 128317, 2),
+ (128331, 128334, 2),
+ (128336, 128359, 2),
+ (128378, 128378, 2),
+ (128405, 128406, 2),
+ (128420, 128420, 2),
+ (128507, 128591, 2),
+ (128640, 128709, 2),
+ (128716, 128716, 2),
+ (128720, 128722, 2),
+ (128725, 128727, 2),
+ (128747, 128748, 2),
+ (128756, 128764, 2),
+ (128992, 129003, 2),
+ (129292, 129338, 2),
+ (129340, 129349, 2),
+ (129351, 129400, 2),
+ (129402, 129483, 2),
+ (129485, 129535, 2),
+ (129648, 129652, 2),
+ (129656, 129658, 2),
+ (129664, 129670, 2),
+ (129680, 129704, 2),
+ (129712, 129718, 2),
+ (129728, 129730, 2),
+ (129744, 129750, 2),
+ (131072, 196605, 2),
+ (196608, 262141, 2),
+ (917760, 917999, 0),
+]
diff --git a/src/pip/_vendor/rich/_emoji_codes.py b/src/pip/_vendor/rich/_emoji_codes.py
new file mode 100644
index 000000000..1f2877bb2
--- /dev/null
+++ b/src/pip/_vendor/rich/_emoji_codes.py
@@ -0,0 +1,3610 @@
+EMOJI = {
+ "1st_place_medal": "🥇",
+ "2nd_place_medal": "🥈",
+ "3rd_place_medal": "🥉",
+ "ab_button_(blood_type)": "🆎",
+ "atm_sign": "🏧",
+ "a_button_(blood_type)": "🅰",
+ "afghanistan": "🇦🇫",
+ "albania": "🇦🇱",
+ "algeria": "🇩🇿",
+ "american_samoa": "🇦🇸",
+ "andorra": "🇦🇩",
+ "angola": "🇦🇴",
+ "anguilla": "🇦🇮",
+ "antarctica": "🇦🇶",
+ "antigua_&_barbuda": "🇦🇬",
+ "aquarius": "♒",
+ "argentina": "🇦🇷",
+ "aries": "♈",
+ "armenia": "🇦🇲",
+ "aruba": "🇦🇼",
+ "ascension_island": "🇦🇨",
+ "australia": "🇦🇺",
+ "austria": "🇦🇹",
+ "azerbaijan": "🇦🇿",
+ "back_arrow": "🔙",
+ "b_button_(blood_type)": "🅱",
+ "bahamas": "🇧🇸",
+ "bahrain": "🇧🇭",
+ "bangladesh": "🇧🇩",
+ "barbados": "🇧🇧",
+ "belarus": "🇧🇾",
+ "belgium": "🇧🇪",
+ "belize": "🇧🇿",
+ "benin": "🇧🇯",
+ "bermuda": "🇧🇲",
+ "bhutan": "🇧🇹",
+ "bolivia": "🇧🇴",
+ "bosnia_&_herzegovina": "🇧🇦",
+ "botswana": "🇧🇼",
+ "bouvet_island": "🇧🇻",
+ "brazil": "🇧🇷",
+ "british_indian_ocean_territory": "🇮🇴",
+ "british_virgin_islands": "🇻🇬",
+ "brunei": "🇧🇳",
+ "bulgaria": "🇧🇬",
+ "burkina_faso": "🇧🇫",
+ "burundi": "🇧🇮",
+ "cl_button": "🆑",
+ "cool_button": "🆒",
+ "cambodia": "🇰🇭",
+ "cameroon": "🇨🇲",
+ "canada": "🇨🇦",
+ "canary_islands": "🇮🇨",
+ "cancer": "♋",
+ "cape_verde": "🇨🇻",
+ "capricorn": "♑",
+ "caribbean_netherlands": "🇧🇶",
+ "cayman_islands": "🇰🇾",
+ "central_african_republic": "🇨🇫",
+ "ceuta_&_melilla": "🇪🇦",
+ "chad": "🇹🇩",
+ "chile": "🇨🇱",
+ "china": "🇨🇳",
+ "christmas_island": "🇨🇽",
+ "christmas_tree": "🎄",
+ "clipperton_island": "🇨🇵",
+ "cocos_(keeling)_islands": "🇨🇨",
+ "colombia": "🇨🇴",
+ "comoros": "🇰🇲",
+ "congo_-_brazzaville": "🇨🇬",
+ "congo_-_kinshasa": "🇨🇩",
+ "cook_islands": "🇨🇰",
+ "costa_rica": "🇨🇷",
+ "croatia": "🇭🇷",
+ "cuba": "🇨🇺",
+ "curaçao": "🇨🇼",
+ "cyprus": "🇨🇾",
+ "czechia": "🇨🇿",
+ "côte_d’ivoire": "🇨🇮",
+ "denmark": "🇩🇰",
+ "diego_garcia": "🇩🇬",
+ "djibouti": "🇩🇯",
+ "dominica": "🇩🇲",
+ "dominican_republic": "🇩🇴",
+ "end_arrow": "🔚",
+ "ecuador": "🇪🇨",
+ "egypt": "🇪🇬",
+ "el_salvador": "🇸🇻",
+ "england": "🏴\U000e0067\U000e0062\U000e0065\U000e006e\U000e0067\U000e007f",
+ "equatorial_guinea": "🇬🇶",
+ "eritrea": "🇪🇷",
+ "estonia": "🇪🇪",
+ "ethiopia": "🇪🇹",
+ "european_union": "🇪🇺",
+ "free_button": "🆓",
+ "falkland_islands": "🇫🇰",
+ "faroe_islands": "🇫🇴",
+ "fiji": "🇫🇯",
+ "finland": "🇫🇮",
+ "france": "🇫🇷",
+ "french_guiana": "🇬🇫",
+ "french_polynesia": "🇵🇫",
+ "french_southern_territories": "🇹🇫",
+ "gabon": "🇬🇦",
+ "gambia": "🇬🇲",
+ "gemini": "♊",
+ "georgia": "🇬🇪",
+ "germany": "🇩🇪",
+ "ghana": "🇬🇭",
+ "gibraltar": "🇬🇮",
+ "greece": "🇬🇷",
+ "greenland": "🇬🇱",
+ "grenada": "🇬🇩",
+ "guadeloupe": "🇬🇵",
+ "guam": "🇬🇺",
+ "guatemala": "🇬🇹",
+ "guernsey": "🇬🇬",
+ "guinea": "🇬🇳",
+ "guinea-bissau": "🇬🇼",
+ "guyana": "🇬🇾",
+ "haiti": "🇭🇹",
+ "heard_&_mcdonald_islands": "🇭🇲",
+ "honduras": "🇭🇳",
+ "hong_kong_sar_china": "🇭🇰",
+ "hungary": "🇭🇺",
+ "id_button": "🆔",
+ "iceland": "🇮🇸",
+ "india": "🇮🇳",
+ "indonesia": "🇮🇩",
+ "iran": "🇮🇷",
+ "iraq": "🇮🇶",
+ "ireland": "🇮🇪",
+ "isle_of_man": "🇮🇲",
+ "israel": "🇮🇱",
+ "italy": "🇮🇹",
+ "jamaica": "🇯🇲",
+ "japan": "🗾",
+ "japanese_acceptable_button": "🉑",
+ "japanese_application_button": "🈸",
+ "japanese_bargain_button": "🉐",
+ "japanese_castle": "🏯",
+ "japanese_congratulations_button": "㊗",
+ "japanese_discount_button": "🈹",
+ "japanese_dolls": "🎎",
+ "japanese_free_of_charge_button": "🈚",
+ "japanese_here_button": "🈁",
+ "japanese_monthly_amount_button": "🈷",
+ "japanese_no_vacancy_button": "🈵",
+ "japanese_not_free_of_charge_button": "🈶",
+ "japanese_open_for_business_button": "🈺",
+ "japanese_passing_grade_button": "🈴",
+ "japanese_post_office": "🏣",
+ "japanese_prohibited_button": "🈲",
+ "japanese_reserved_button": "🈯",
+ "japanese_secret_button": "㊙",
+ "japanese_service_charge_button": "🈂",
+ "japanese_symbol_for_beginner": "🔰",
+ "japanese_vacancy_button": "🈳",
+ "jersey": "🇯🇪",
+ "jordan": "🇯🇴",
+ "kazakhstan": "🇰🇿",
+ "kenya": "🇰🇪",
+ "kiribati": "🇰🇮",
+ "kosovo": "🇽🇰",
+ "kuwait": "🇰🇼",
+ "kyrgyzstan": "🇰🇬",
+ "laos": "🇱🇦",
+ "latvia": "🇱🇻",
+ "lebanon": "🇱🇧",
+ "leo": "♌",
+ "lesotho": "🇱🇸",
+ "liberia": "🇱🇷",
+ "libra": "♎",
+ "libya": "🇱🇾",
+ "liechtenstein": "🇱🇮",
+ "lithuania": "🇱🇹",
+ "luxembourg": "🇱🇺",
+ "macau_sar_china": "🇲🇴",
+ "macedonia": "🇲🇰",
+ "madagascar": "🇲🇬",
+ "malawi": "🇲🇼",
+ "malaysia": "🇲🇾",
+ "maldives": "🇲🇻",
+ "mali": "🇲🇱",
+ "malta": "🇲🇹",
+ "marshall_islands": "🇲🇭",
+ "martinique": "🇲🇶",
+ "mauritania": "🇲🇷",
+ "mauritius": "🇲🇺",
+ "mayotte": "🇾🇹",
+ "mexico": "🇲🇽",
+ "micronesia": "🇫🇲",
+ "moldova": "🇲🇩",
+ "monaco": "🇲🇨",
+ "mongolia": "🇲🇳",
+ "montenegro": "🇲🇪",
+ "montserrat": "🇲🇸",
+ "morocco": "🇲🇦",
+ "mozambique": "🇲🇿",
+ "mrs._claus": "🤶",
+ "mrs._claus_dark_skin_tone": "🤶🏿",
+ "mrs._claus_light_skin_tone": "🤶🏻",
+ "mrs._claus_medium-dark_skin_tone": "🤶🏾",
+ "mrs._claus_medium-light_skin_tone": "🤶🏼",
+ "mrs._claus_medium_skin_tone": "🤶🏽",
+ "myanmar_(burma)": "🇲🇲",
+ "new_button": "🆕",
+ "ng_button": "🆖",
+ "namibia": "🇳🇦",
+ "nauru": "🇳🇷",
+ "nepal": "🇳🇵",
+ "netherlands": "🇳🇱",
+ "new_caledonia": "🇳🇨",
+ "new_zealand": "🇳🇿",
+ "nicaragua": "🇳🇮",
+ "niger": "🇳🇪",
+ "nigeria": "🇳🇬",
+ "niue": "🇳🇺",
+ "norfolk_island": "🇳🇫",
+ "north_korea": "🇰🇵",
+ "northern_mariana_islands": "🇲🇵",
+ "norway": "🇳🇴",
+ "ok_button": "🆗",
+ "ok_hand": "👌",
+ "ok_hand_dark_skin_tone": "👌🏿",
+ "ok_hand_light_skin_tone": "👌🏻",
+ "ok_hand_medium-dark_skin_tone": "👌🏾",
+ "ok_hand_medium-light_skin_tone": "👌🏼",
+ "ok_hand_medium_skin_tone": "👌🏽",
+ "on!_arrow": "🔛",
+ "o_button_(blood_type)": "🅾",
+ "oman": "🇴🇲",
+ "ophiuchus": "⛎",
+ "p_button": "🅿",
+ "pakistan": "🇵🇰",
+ "palau": "🇵🇼",
+ "palestinian_territories": "🇵🇸",
+ "panama": "🇵🇦",
+ "papua_new_guinea": "🇵🇬",
+ "paraguay": "🇵🇾",
+ "peru": "🇵🇪",
+ "philippines": "🇵🇭",
+ "pisces": "♓",
+ "pitcairn_islands": "🇵🇳",
+ "poland": "🇵🇱",
+ "portugal": "🇵🇹",
+ "puerto_rico": "🇵🇷",
+ "qatar": "🇶🇦",
+ "romania": "🇷🇴",
+ "russia": "🇷🇺",
+ "rwanda": "🇷🇼",
+ "réunion": "🇷🇪",
+ "soon_arrow": "🔜",
+ "sos_button": "🆘",
+ "sagittarius": "♐",
+ "samoa": "🇼🇸",
+ "san_marino": "🇸🇲",
+ "santa_claus": "🎅",
+ "santa_claus_dark_skin_tone": "🎅🏿",
+ "santa_claus_light_skin_tone": "🎅🏻",
+ "santa_claus_medium-dark_skin_tone": "🎅🏾",
+ "santa_claus_medium-light_skin_tone": "🎅🏼",
+ "santa_claus_medium_skin_tone": "🎅🏽",
+ "saudi_arabia": "🇸🇦",
+ "scorpio": "♏",
+ "scotland": "🏴\U000e0067\U000e0062\U000e0073\U000e0063\U000e0074\U000e007f",
+ "senegal": "🇸🇳",
+ "serbia": "🇷🇸",
+ "seychelles": "🇸🇨",
+ "sierra_leone": "🇸🇱",
+ "singapore": "🇸🇬",
+ "sint_maarten": "🇸🇽",
+ "slovakia": "🇸🇰",
+ "slovenia": "🇸🇮",
+ "solomon_islands": "🇸🇧",
+ "somalia": "🇸🇴",
+ "south_africa": "🇿🇦",
+ "south_georgia_&_south_sandwich_islands": "🇬🇸",
+ "south_korea": "🇰🇷",
+ "south_sudan": "🇸🇸",
+ "spain": "🇪🇸",
+ "sri_lanka": "🇱🇰",
+ "st._barthélemy": "🇧🇱",
+ "st._helena": "🇸🇭",
+ "st._kitts_&_nevis": "🇰🇳",
+ "st._lucia": "🇱🇨",
+ "st._martin": "🇲🇫",
+ "st._pierre_&_miquelon": "🇵🇲",
+ "st._vincent_&_grenadines": "🇻🇨",
+ "statue_of_liberty": "🗽",
+ "sudan": "🇸🇩",
+ "suriname": "🇸🇷",
+ "svalbard_&_jan_mayen": "🇸🇯",
+ "swaziland": "🇸🇿",
+ "sweden": "🇸🇪",
+ "switzerland": "🇨🇭",
+ "syria": "🇸🇾",
+ "são_tomé_&_príncipe": "🇸🇹",
+ "t-rex": "🦖",
+ "top_arrow": "🔝",
+ "taiwan": "🇹🇼",
+ "tajikistan": "🇹🇯",
+ "tanzania": "🇹🇿",
+ "taurus": "♉",
+ "thailand": "🇹🇭",
+ "timor-leste": "🇹🇱",
+ "togo": "🇹🇬",
+ "tokelau": "🇹🇰",
+ "tokyo_tower": "🗼",
+ "tonga": "🇹🇴",
+ "trinidad_&_tobago": "🇹🇹",
+ "tristan_da_cunha": "🇹🇦",
+ "tunisia": "🇹🇳",
+ "turkey": "🦃",
+ "turkmenistan": "🇹🇲",
+ "turks_&_caicos_islands": "🇹🇨",
+ "tuvalu": "🇹🇻",
+ "u.s._outlying_islands": "🇺🇲",
+ "u.s._virgin_islands": "🇻🇮",
+ "up!_button": "🆙",
+ "uganda": "🇺🇬",
+ "ukraine": "🇺🇦",
+ "united_arab_emirates": "🇦🇪",
+ "united_kingdom": "🇬🇧",
+ "united_nations": "🇺🇳",
+ "united_states": "🇺🇸",
+ "uruguay": "🇺🇾",
+ "uzbekistan": "🇺🇿",
+ "vs_button": "🆚",
+ "vanuatu": "🇻🇺",
+ "vatican_city": "🇻🇦",
+ "venezuela": "🇻🇪",
+ "vietnam": "🇻🇳",
+ "virgo": "♍",
+ "wales": "🏴\U000e0067\U000e0062\U000e0077\U000e006c\U000e0073\U000e007f",
+ "wallis_&_futuna": "🇼🇫",
+ "western_sahara": "🇪🇭",
+ "yemen": "🇾🇪",
+ "zambia": "🇿🇲",
+ "zimbabwe": "🇿🇼",
+ "abacus": "🧮",
+ "adhesive_bandage": "🩹",
+ "admission_tickets": "🎟",
+ "adult": "🧑",
+ "adult_dark_skin_tone": "🧑🏿",
+ "adult_light_skin_tone": "🧑🏻",
+ "adult_medium-dark_skin_tone": "🧑🏾",
+ "adult_medium-light_skin_tone": "🧑🏼",
+ "adult_medium_skin_tone": "🧑🏽",
+ "aerial_tramway": "🚡",
+ "airplane": "✈",
+ "airplane_arrival": "🛬",
+ "airplane_departure": "🛫",
+ "alarm_clock": "⏰",
+ "alembic": "⚗",
+ "alien": "👽",
+ "alien_monster": "👾",
+ "ambulance": "🚑",
+ "american_football": "🏈",
+ "amphora": "🏺",
+ "anchor": "⚓",
+ "anger_symbol": "💢",
+ "angry_face": "😠",
+ "angry_face_with_horns": "👿",
+ "anguished_face": "😧",
+ "ant": "🐜",
+ "antenna_bars": "📶",
+ "anxious_face_with_sweat": "😰",
+ "articulated_lorry": "🚛",
+ "artist_palette": "🎨",
+ "astonished_face": "😲",
+ "atom_symbol": "⚛",
+ "auto_rickshaw": "🛺",
+ "automobile": "🚗",
+ "avocado": "🥑",
+ "axe": "🪓",
+ "baby": "👶",
+ "baby_angel": "👼",
+ "baby_angel_dark_skin_tone": "👼🏿",
+ "baby_angel_light_skin_tone": "👼🏻",
+ "baby_angel_medium-dark_skin_tone": "👼🏾",
+ "baby_angel_medium-light_skin_tone": "👼🏼",
+ "baby_angel_medium_skin_tone": "👼🏽",
+ "baby_bottle": "🍼",
+ "baby_chick": "🐤",
+ "baby_dark_skin_tone": "👶🏿",
+ "baby_light_skin_tone": "👶🏻",
+ "baby_medium-dark_skin_tone": "👶🏾",
+ "baby_medium-light_skin_tone": "👶🏼",
+ "baby_medium_skin_tone": "👶🏽",
+ "baby_symbol": "🚼",
+ "backhand_index_pointing_down": "👇",
+ "backhand_index_pointing_down_dark_skin_tone": "👇🏿",
+ "backhand_index_pointing_down_light_skin_tone": "👇🏻",
+ "backhand_index_pointing_down_medium-dark_skin_tone": "👇🏾",
+ "backhand_index_pointing_down_medium-light_skin_tone": "👇🏼",
+ "backhand_index_pointing_down_medium_skin_tone": "👇🏽",
+ "backhand_index_pointing_left": "👈",
+ "backhand_index_pointing_left_dark_skin_tone": "👈🏿",
+ "backhand_index_pointing_left_light_skin_tone": "👈🏻",
+ "backhand_index_pointing_left_medium-dark_skin_tone": "👈🏾",
+ "backhand_index_pointing_left_medium-light_skin_tone": "👈🏼",
+ "backhand_index_pointing_left_medium_skin_tone": "👈🏽",
+ "backhand_index_pointing_right": "👉",
+ "backhand_index_pointing_right_dark_skin_tone": "👉🏿",
+ "backhand_index_pointing_right_light_skin_tone": "👉🏻",
+ "backhand_index_pointing_right_medium-dark_skin_tone": "👉🏾",
+ "backhand_index_pointing_right_medium-light_skin_tone": "👉🏼",
+ "backhand_index_pointing_right_medium_skin_tone": "👉🏽",
+ "backhand_index_pointing_up": "👆",
+ "backhand_index_pointing_up_dark_skin_tone": "👆🏿",
+ "backhand_index_pointing_up_light_skin_tone": "👆🏻",
+ "backhand_index_pointing_up_medium-dark_skin_tone": "👆🏾",
+ "backhand_index_pointing_up_medium-light_skin_tone": "👆🏼",
+ "backhand_index_pointing_up_medium_skin_tone": "👆🏽",
+ "bacon": "🥓",
+ "badger": "🦡",
+ "badminton": "🏸",
+ "bagel": "🥯",
+ "baggage_claim": "🛄",
+ "baguette_bread": "🥖",
+ "balance_scale": "⚖",
+ "bald": "🦲",
+ "bald_man": "👨\u200d🦲",
+ "bald_woman": "👩\u200d🦲",
+ "ballet_shoes": "🩰",
+ "balloon": "🎈",
+ "ballot_box_with_ballot": "🗳",
+ "ballot_box_with_check": "☑",
+ "banana": "🍌",
+ "banjo": "🪕",
+ "bank": "🏦",
+ "bar_chart": "📊",
+ "barber_pole": "💈",
+ "baseball": "⚾",
+ "basket": "🧺",
+ "basketball": "🏀",
+ "bat": "🦇",
+ "bathtub": "🛁",
+ "battery": "🔋",
+ "beach_with_umbrella": "🏖",
+ "beaming_face_with_smiling_eyes": "😁",
+ "bear_face": "🐻",
+ "bearded_person": "🧔",
+ "bearded_person_dark_skin_tone": "🧔🏿",
+ "bearded_person_light_skin_tone": "🧔🏻",
+ "bearded_person_medium-dark_skin_tone": "🧔🏾",
+ "bearded_person_medium-light_skin_tone": "🧔🏼",
+ "bearded_person_medium_skin_tone": "🧔🏽",
+ "beating_heart": "💓",
+ "bed": "🛏",
+ "beer_mug": "🍺",
+ "bell": "🔔",
+ "bell_with_slash": "🔕",
+ "bellhop_bell": "🛎",
+ "bento_box": "🍱",
+ "beverage_box": "🧃",
+ "bicycle": "🚲",
+ "bikini": "👙",
+ "billed_cap": "🧢",
+ "biohazard": "☣",
+ "bird": "🐦",
+ "birthday_cake": "🎂",
+ "black_circle": "⚫",
+ "black_flag": "🏴",
+ "black_heart": "🖤",
+ "black_large_square": "⬛",
+ "black_medium-small_square": "◾",
+ "black_medium_square": "◼",
+ "black_nib": "✒",
+ "black_small_square": "▪",
+ "black_square_button": "🔲",
+ "blond-haired_man": "👱\u200d♂️",
+ "blond-haired_man_dark_skin_tone": "👱🏿\u200d♂️",
+ "blond-haired_man_light_skin_tone": "👱🏻\u200d♂️",
+ "blond-haired_man_medium-dark_skin_tone": "👱🏾\u200d♂️",
+ "blond-haired_man_medium-light_skin_tone": "👱🏼\u200d♂️",
+ "blond-haired_man_medium_skin_tone": "👱🏽\u200d♂️",
+ "blond-haired_person": "👱",
+ "blond-haired_person_dark_skin_tone": "👱🏿",
+ "blond-haired_person_light_skin_tone": "👱🏻",
+ "blond-haired_person_medium-dark_skin_tone": "👱🏾",
+ "blond-haired_person_medium-light_skin_tone": "👱🏼",
+ "blond-haired_person_medium_skin_tone": "👱🏽",
+ "blond-haired_woman": "👱\u200d♀️",
+ "blond-haired_woman_dark_skin_tone": "👱🏿\u200d♀️",
+ "blond-haired_woman_light_skin_tone": "👱🏻\u200d♀️",
+ "blond-haired_woman_medium-dark_skin_tone": "👱🏾\u200d♀️",
+ "blond-haired_woman_medium-light_skin_tone": "👱🏼\u200d♀️",
+ "blond-haired_woman_medium_skin_tone": "👱🏽\u200d♀️",
+ "blossom": "🌼",
+ "blowfish": "🐡",
+ "blue_book": "📘",
+ "blue_circle": "🔵",
+ "blue_heart": "💙",
+ "blue_square": "🟦",
+ "boar": "🐗",
+ "bomb": "💣",
+ "bone": "🦴",
+ "bookmark": "🔖",
+ "bookmark_tabs": "📑",
+ "books": "📚",
+ "bottle_with_popping_cork": "🍾",
+ "bouquet": "💐",
+ "bow_and_arrow": "🏹",
+ "bowl_with_spoon": "🥣",
+ "bowling": "🎳",
+ "boxing_glove": "🥊",
+ "boy": "👦",
+ "boy_dark_skin_tone": "👦🏿",
+ "boy_light_skin_tone": "👦🏻",
+ "boy_medium-dark_skin_tone": "👦🏾",
+ "boy_medium-light_skin_tone": "👦🏼",
+ "boy_medium_skin_tone": "👦🏽",
+ "brain": "🧠",
+ "bread": "🍞",
+ "breast-feeding": "🤱",
+ "breast-feeding_dark_skin_tone": "🤱🏿",
+ "breast-feeding_light_skin_tone": "🤱🏻",
+ "breast-feeding_medium-dark_skin_tone": "🤱🏾",
+ "breast-feeding_medium-light_skin_tone": "🤱🏼",
+ "breast-feeding_medium_skin_tone": "🤱🏽",
+ "brick": "🧱",
+ "bride_with_veil": "👰",
+ "bride_with_veil_dark_skin_tone": "👰🏿",
+ "bride_with_veil_light_skin_tone": "👰🏻",
+ "bride_with_veil_medium-dark_skin_tone": "👰🏾",
+ "bride_with_veil_medium-light_skin_tone": "👰🏼",
+ "bride_with_veil_medium_skin_tone": "👰🏽",
+ "bridge_at_night": "🌉",
+ "briefcase": "💼",
+ "briefs": "🩲",
+ "bright_button": "🔆",
+ "broccoli": "🥦",
+ "broken_heart": "💔",
+ "broom": "🧹",
+ "brown_circle": "🟤",
+ "brown_heart": "🤎",
+ "brown_square": "🟫",
+ "bug": "🐛",
+ "building_construction": "🏗",
+ "bullet_train": "🚅",
+ "burrito": "🌯",
+ "bus": "🚌",
+ "bus_stop": "🚏",
+ "bust_in_silhouette": "👤",
+ "busts_in_silhouette": "👥",
+ "butter": "🧈",
+ "butterfly": "🦋",
+ "cactus": "🌵",
+ "calendar": "📆",
+ "call_me_hand": "🤙",
+ "call_me_hand_dark_skin_tone": "🤙🏿",
+ "call_me_hand_light_skin_tone": "🤙🏻",
+ "call_me_hand_medium-dark_skin_tone": "🤙🏾",
+ "call_me_hand_medium-light_skin_tone": "🤙🏼",
+ "call_me_hand_medium_skin_tone": "🤙🏽",
+ "camel": "🐫",
+ "camera": "📷",
+ "camera_with_flash": "📸",
+ "camping": "🏕",
+ "candle": "🕯",
+ "candy": "🍬",
+ "canned_food": "🥫",
+ "canoe": "🛶",
+ "card_file_box": "🗃",
+ "card_index": "📇",
+ "card_index_dividers": "🗂",
+ "carousel_horse": "🎠",
+ "carp_streamer": "🎏",
+ "carrot": "🥕",
+ "castle": "🏰",
+ "cat": "🐱",
+ "cat_face": "🐱",
+ "cat_face_with_tears_of_joy": "😹",
+ "cat_face_with_wry_smile": "😼",
+ "chains": "⛓",
+ "chair": "🪑",
+ "chart_decreasing": "📉",
+ "chart_increasing": "📈",
+ "chart_increasing_with_yen": "💹",
+ "cheese_wedge": "🧀",
+ "chequered_flag": "🏁",
+ "cherries": "🍒",
+ "cherry_blossom": "🌸",
+ "chess_pawn": "♟",
+ "chestnut": "🌰",
+ "chicken": "🐔",
+ "child": "🧒",
+ "child_dark_skin_tone": "🧒🏿",
+ "child_light_skin_tone": "🧒🏻",
+ "child_medium-dark_skin_tone": "🧒🏾",
+ "child_medium-light_skin_tone": "🧒🏼",
+ "child_medium_skin_tone": "🧒🏽",
+ "children_crossing": "🚸",
+ "chipmunk": "🐿",
+ "chocolate_bar": "🍫",
+ "chopsticks": "🥢",
+ "church": "⛪",
+ "cigarette": "🚬",
+ "cinema": "🎦",
+ "circled_m": "Ⓜ",
+ "circus_tent": "🎪",
+ "cityscape": "🏙",
+ "cityscape_at_dusk": "🌆",
+ "clamp": "🗜",
+ "clapper_board": "🎬",
+ "clapping_hands": "👏",
+ "clapping_hands_dark_skin_tone": "👏🏿",
+ "clapping_hands_light_skin_tone": "👏🏻",
+ "clapping_hands_medium-dark_skin_tone": "👏🏾",
+ "clapping_hands_medium-light_skin_tone": "👏🏼",
+ "clapping_hands_medium_skin_tone": "👏🏽",
+ "classical_building": "🏛",
+ "clinking_beer_mugs": "🍻",
+ "clinking_glasses": "🥂",
+ "clipboard": "📋",
+ "clockwise_vertical_arrows": "🔃",
+ "closed_book": "📕",
+ "closed_mailbox_with_lowered_flag": "📪",
+ "closed_mailbox_with_raised_flag": "📫",
+ "closed_umbrella": "🌂",
+ "cloud": "☁",
+ "cloud_with_lightning": "🌩",
+ "cloud_with_lightning_and_rain": "⛈",
+ "cloud_with_rain": "🌧",
+ "cloud_with_snow": "🌨",
+ "clown_face": "🤡",
+ "club_suit": "♣",
+ "clutch_bag": "👝",
+ "coat": "🧥",
+ "cocktail_glass": "🍸",
+ "coconut": "🥥",
+ "coffin": "⚰",
+ "cold_face": "🥶",
+ "collision": "💥",
+ "comet": "☄",
+ "compass": "🧭",
+ "computer_disk": "💽",
+ "computer_mouse": "🖱",
+ "confetti_ball": "🎊",
+ "confounded_face": "😖",
+ "confused_face": "😕",
+ "construction": "🚧",
+ "construction_worker": "👷",
+ "construction_worker_dark_skin_tone": "👷🏿",
+ "construction_worker_light_skin_tone": "👷🏻",
+ "construction_worker_medium-dark_skin_tone": "👷🏾",
+ "construction_worker_medium-light_skin_tone": "👷🏼",
+ "construction_worker_medium_skin_tone": "👷🏽",
+ "control_knobs": "🎛",
+ "convenience_store": "🏪",
+ "cooked_rice": "🍚",
+ "cookie": "🍪",
+ "cooking": "🍳",
+ "copyright": "©",
+ "couch_and_lamp": "🛋",
+ "counterclockwise_arrows_button": "🔄",
+ "couple_with_heart": "💑",
+ "couple_with_heart_man_man": "👨\u200d❤️\u200d👨",
+ "couple_with_heart_woman_man": "👩\u200d❤️\u200d👨",
+ "couple_with_heart_woman_woman": "👩\u200d❤️\u200d👩",
+ "cow": "🐮",
+ "cow_face": "🐮",
+ "cowboy_hat_face": "🤠",
+ "crab": "🦀",
+ "crayon": "🖍",
+ "credit_card": "💳",
+ "crescent_moon": "🌙",
+ "cricket": "🦗",
+ "cricket_game": "🏏",
+ "crocodile": "🐊",
+ "croissant": "🥐",
+ "cross_mark": "❌",
+ "cross_mark_button": "❎",
+ "crossed_fingers": "🤞",
+ "crossed_fingers_dark_skin_tone": "🤞🏿",
+ "crossed_fingers_light_skin_tone": "🤞🏻",
+ "crossed_fingers_medium-dark_skin_tone": "🤞🏾",
+ "crossed_fingers_medium-light_skin_tone": "🤞🏼",
+ "crossed_fingers_medium_skin_tone": "🤞🏽",
+ "crossed_flags": "🎌",
+ "crossed_swords": "⚔",
+ "crown": "👑",
+ "crying_cat_face": "😿",
+ "crying_face": "😢",
+ "crystal_ball": "🔮",
+ "cucumber": "🥒",
+ "cupcake": "🧁",
+ "cup_with_straw": "🥤",
+ "curling_stone": "🥌",
+ "curly_hair": "🦱",
+ "curly-haired_man": "👨\u200d🦱",
+ "curly-haired_woman": "👩\u200d🦱",
+ "curly_loop": "➰",
+ "currency_exchange": "💱",
+ "curry_rice": "🍛",
+ "custard": "🍮",
+ "customs": "🛃",
+ "cut_of_meat": "🥩",
+ "cyclone": "🌀",
+ "dagger": "🗡",
+ "dango": "🍡",
+ "dashing_away": "💨",
+ "deaf_person": "🧏",
+ "deciduous_tree": "🌳",
+ "deer": "🦌",
+ "delivery_truck": "🚚",
+ "department_store": "🏬",
+ "derelict_house": "🏚",
+ "desert": "🏜",
+ "desert_island": "🏝",
+ "desktop_computer": "🖥",
+ "detective": "🕵",
+ "detective_dark_skin_tone": "🕵🏿",
+ "detective_light_skin_tone": "🕵🏻",
+ "detective_medium-dark_skin_tone": "🕵🏾",
+ "detective_medium-light_skin_tone": "🕵🏼",
+ "detective_medium_skin_tone": "🕵🏽",
+ "diamond_suit": "♦",
+ "diamond_with_a_dot": "💠",
+ "dim_button": "🔅",
+ "direct_hit": "🎯",
+ "disappointed_face": "😞",
+ "diving_mask": "🤿",
+ "diya_lamp": "🪔",
+ "dizzy": "💫",
+ "dizzy_face": "😵",
+ "dna": "🧬",
+ "dog": "🐶",
+ "dog_face": "🐶",
+ "dollar_banknote": "💵",
+ "dolphin": "🐬",
+ "door": "🚪",
+ "dotted_six-pointed_star": "🔯",
+ "double_curly_loop": "➿",
+ "double_exclamation_mark": "‼",
+ "doughnut": "🍩",
+ "dove": "🕊",
+ "down-left_arrow": "↙",
+ "down-right_arrow": "↘",
+ "down_arrow": "⬇",
+ "downcast_face_with_sweat": "😓",
+ "downwards_button": "🔽",
+ "dragon": "🐉",
+ "dragon_face": "🐲",
+ "dress": "👗",
+ "drooling_face": "🤤",
+ "drop_of_blood": "🩸",
+ "droplet": "💧",
+ "drum": "🥁",
+ "duck": "🦆",
+ "dumpling": "🥟",
+ "dvd": "📀",
+ "e-mail": "📧",
+ "eagle": "🦅",
+ "ear": "👂",
+ "ear_dark_skin_tone": "👂🏿",
+ "ear_light_skin_tone": "👂🏻",
+ "ear_medium-dark_skin_tone": "👂🏾",
+ "ear_medium-light_skin_tone": "👂🏼",
+ "ear_medium_skin_tone": "👂🏽",
+ "ear_of_corn": "🌽",
+ "ear_with_hearing_aid": "🦻",
+ "egg": "🍳",
+ "eggplant": "🍆",
+ "eight-pointed_star": "✴",
+ "eight-spoked_asterisk": "✳",
+ "eight-thirty": "🕣",
+ "eight_o’clock": "🕗",
+ "eject_button": "⏏",
+ "electric_plug": "🔌",
+ "elephant": "🐘",
+ "eleven-thirty": "🕦",
+ "eleven_o’clock": "🕚",
+ "elf": "🧝",
+ "elf_dark_skin_tone": "🧝🏿",
+ "elf_light_skin_tone": "🧝🏻",
+ "elf_medium-dark_skin_tone": "🧝🏾",
+ "elf_medium-light_skin_tone": "🧝🏼",
+ "elf_medium_skin_tone": "🧝🏽",
+ "envelope": "✉",
+ "envelope_with_arrow": "📩",
+ "euro_banknote": "💶",
+ "evergreen_tree": "🌲",
+ "ewe": "🐑",
+ "exclamation_mark": "❗",
+ "exclamation_question_mark": "⁉",
+ "exploding_head": "🤯",
+ "expressionless_face": "😑",
+ "eye": "👁",
+ "eye_in_speech_bubble": "👁️\u200d🗨️",
+ "eyes": "👀",
+ "face_blowing_a_kiss": "😘",
+ "face_savoring_food": "😋",
+ "face_screaming_in_fear": "😱",
+ "face_vomiting": "🤮",
+ "face_with_hand_over_mouth": "🤭",
+ "face_with_head-bandage": "🤕",
+ "face_with_medical_mask": "😷",
+ "face_with_monocle": "🧐",
+ "face_with_open_mouth": "😮",
+ "face_with_raised_eyebrow": "🤨",
+ "face_with_rolling_eyes": "🙄",
+ "face_with_steam_from_nose": "😤",
+ "face_with_symbols_on_mouth": "🤬",
+ "face_with_tears_of_joy": "😂",
+ "face_with_thermometer": "🤒",
+ "face_with_tongue": "😛",
+ "face_without_mouth": "😶",
+ "factory": "🏭",
+ "fairy": "🧚",
+ "fairy_dark_skin_tone": "🧚🏿",
+ "fairy_light_skin_tone": "🧚🏻",
+ "fairy_medium-dark_skin_tone": "🧚🏾",
+ "fairy_medium-light_skin_tone": "🧚🏼",
+ "fairy_medium_skin_tone": "🧚🏽",
+ "falafel": "🧆",
+ "fallen_leaf": "🍂",
+ "family": "👪",
+ "family_man_boy": "👨\u200d👦",
+ "family_man_boy_boy": "👨\u200d👦\u200d👦",
+ "family_man_girl": "👨\u200d👧",
+ "family_man_girl_boy": "👨\u200d👧\u200d👦",
+ "family_man_girl_girl": "👨\u200d👧\u200d👧",
+ "family_man_man_boy": "👨\u200d👨\u200d👦",
+ "family_man_man_boy_boy": "👨\u200d👨\u200d👦\u200d👦",
+ "family_man_man_girl": "👨\u200d👨\u200d👧",
+ "family_man_man_girl_boy": "👨\u200d👨\u200d👧\u200d👦",
+ "family_man_man_girl_girl": "👨\u200d👨\u200d👧\u200d👧",
+ "family_man_woman_boy": "👨\u200d👩\u200d👦",
+ "family_man_woman_boy_boy": "👨\u200d👩\u200d👦\u200d👦",
+ "family_man_woman_girl": "👨\u200d👩\u200d👧",
+ "family_man_woman_girl_boy": "👨\u200d👩\u200d👧\u200d👦",
+ "family_man_woman_girl_girl": "👨\u200d👩\u200d👧\u200d👧",
+ "family_woman_boy": "👩\u200d👦",
+ "family_woman_boy_boy": "👩\u200d👦\u200d👦",
+ "family_woman_girl": "👩\u200d👧",
+ "family_woman_girl_boy": "👩\u200d👧\u200d👦",
+ "family_woman_girl_girl": "👩\u200d👧\u200d👧",
+ "family_woman_woman_boy": "👩\u200d👩\u200d👦",
+ "family_woman_woman_boy_boy": "👩\u200d👩\u200d👦\u200d👦",
+ "family_woman_woman_girl": "👩\u200d👩\u200d👧",
+ "family_woman_woman_girl_boy": "👩\u200d👩\u200d👧\u200d👦",
+ "family_woman_woman_girl_girl": "👩\u200d👩\u200d👧\u200d👧",
+ "fast-forward_button": "⏩",
+ "fast_down_button": "⏬",
+ "fast_reverse_button": "⏪",
+ "fast_up_button": "⏫",
+ "fax_machine": "📠",
+ "fearful_face": "😨",
+ "female_sign": "♀",
+ "ferris_wheel": "🎡",
+ "ferry": "⛴",
+ "field_hockey": "🏑",
+ "file_cabinet": "🗄",
+ "file_folder": "📁",
+ "film_frames": "🎞",
+ "film_projector": "📽",
+ "fire": "🔥",
+ "fire_extinguisher": "🧯",
+ "firecracker": "🧨",
+ "fire_engine": "🚒",
+ "fireworks": "🎆",
+ "first_quarter_moon": "🌓",
+ "first_quarter_moon_face": "🌛",
+ "fish": "🐟",
+ "fish_cake_with_swirl": "🍥",
+ "fishing_pole": "🎣",
+ "five-thirty": "🕠",
+ "five_o’clock": "🕔",
+ "flag_in_hole": "⛳",
+ "flamingo": "🦩",
+ "flashlight": "🔦",
+ "flat_shoe": "🥿",
+ "fleur-de-lis": "⚜",
+ "flexed_biceps": "💪",
+ "flexed_biceps_dark_skin_tone": "💪🏿",
+ "flexed_biceps_light_skin_tone": "💪🏻",
+ "flexed_biceps_medium-dark_skin_tone": "💪🏾",
+ "flexed_biceps_medium-light_skin_tone": "💪🏼",
+ "flexed_biceps_medium_skin_tone": "💪🏽",
+ "floppy_disk": "💾",
+ "flower_playing_cards": "🎴",
+ "flushed_face": "😳",
+ "flying_disc": "🥏",
+ "flying_saucer": "🛸",
+ "fog": "🌫",
+ "foggy": "🌁",
+ "folded_hands": "🙏",
+ "folded_hands_dark_skin_tone": "🙏🏿",
+ "folded_hands_light_skin_tone": "🙏🏻",
+ "folded_hands_medium-dark_skin_tone": "🙏🏾",
+ "folded_hands_medium-light_skin_tone": "🙏🏼",
+ "folded_hands_medium_skin_tone": "🙏🏽",
+ "foot": "🦶",
+ "footprints": "👣",
+ "fork_and_knife": "🍴",
+ "fork_and_knife_with_plate": "🍽",
+ "fortune_cookie": "🥠",
+ "fountain": "⛲",
+ "fountain_pen": "🖋",
+ "four-thirty": "🕟",
+ "four_leaf_clover": "🍀",
+ "four_o’clock": "🕓",
+ "fox_face": "🦊",
+ "framed_picture": "🖼",
+ "french_fries": "🍟",
+ "fried_shrimp": "🍤",
+ "frog_face": "🐸",
+ "front-facing_baby_chick": "🐥",
+ "frowning_face": "☹",
+ "frowning_face_with_open_mouth": "😦",
+ "fuel_pump": "⛽",
+ "full_moon": "🌕",
+ "full_moon_face": "🌝",
+ "funeral_urn": "⚱",
+ "game_die": "🎲",
+ "garlic": "🧄",
+ "gear": "⚙",
+ "gem_stone": "💎",
+ "genie": "🧞",
+ "ghost": "👻",
+ "giraffe": "🦒",
+ "girl": "👧",
+ "girl_dark_skin_tone": "👧🏿",
+ "girl_light_skin_tone": "👧🏻",
+ "girl_medium-dark_skin_tone": "👧🏾",
+ "girl_medium-light_skin_tone": "👧🏼",
+ "girl_medium_skin_tone": "👧🏽",
+ "glass_of_milk": "🥛",
+ "glasses": "👓",
+ "globe_showing_americas": "🌎",
+ "globe_showing_asia-australia": "🌏",
+ "globe_showing_europe-africa": "🌍",
+ "globe_with_meridians": "🌐",
+ "gloves": "🧤",
+ "glowing_star": "🌟",
+ "goal_net": "🥅",
+ "goat": "🐐",
+ "goblin": "👺",
+ "goggles": "🥽",
+ "gorilla": "🦍",
+ "graduation_cap": "🎓",
+ "grapes": "🍇",
+ "green_apple": "🍏",
+ "green_book": "📗",
+ "green_circle": "🟢",
+ "green_heart": "💚",
+ "green_salad": "🥗",
+ "green_square": "🟩",
+ "grimacing_face": "😬",
+ "grinning_cat_face": "😺",
+ "grinning_cat_face_with_smiling_eyes": "😸",
+ "grinning_face": "😀",
+ "grinning_face_with_big_eyes": "😃",
+ "grinning_face_with_smiling_eyes": "😄",
+ "grinning_face_with_sweat": "😅",
+ "grinning_squinting_face": "😆",
+ "growing_heart": "💗",
+ "guard": "💂",
+ "guard_dark_skin_tone": "💂🏿",
+ "guard_light_skin_tone": "💂🏻",
+ "guard_medium-dark_skin_tone": "💂🏾",
+ "guard_medium-light_skin_tone": "💂🏼",
+ "guard_medium_skin_tone": "💂🏽",
+ "guide_dog": "🦮",
+ "guitar": "🎸",
+ "hamburger": "🍔",
+ "hammer": "🔨",
+ "hammer_and_pick": "⚒",
+ "hammer_and_wrench": "🛠",
+ "hamster_face": "🐹",
+ "hand_with_fingers_splayed": "🖐",
+ "hand_with_fingers_splayed_dark_skin_tone": "🖐🏿",
+ "hand_with_fingers_splayed_light_skin_tone": "🖐🏻",
+ "hand_with_fingers_splayed_medium-dark_skin_tone": "🖐🏾",
+ "hand_with_fingers_splayed_medium-light_skin_tone": "🖐🏼",
+ "hand_with_fingers_splayed_medium_skin_tone": "🖐🏽",
+ "handbag": "👜",
+ "handshake": "🤝",
+ "hatching_chick": "🐣",
+ "headphone": "🎧",
+ "hear-no-evil_monkey": "🙉",
+ "heart_decoration": "💟",
+ "heart_suit": "♥",
+ "heart_with_arrow": "💘",
+ "heart_with_ribbon": "💝",
+ "heavy_check_mark": "✔",
+ "heavy_division_sign": "➗",
+ "heavy_dollar_sign": "💲",
+ "heavy_heart_exclamation": "❣",
+ "heavy_large_circle": "⭕",
+ "heavy_minus_sign": "➖",
+ "heavy_multiplication_x": "✖",
+ "heavy_plus_sign": "➕",
+ "hedgehog": "🦔",
+ "helicopter": "🚁",
+ "herb": "🌿",
+ "hibiscus": "🌺",
+ "high-heeled_shoe": "👠",
+ "high-speed_train": "🚄",
+ "high_voltage": "⚡",
+ "hiking_boot": "🥾",
+ "hindu_temple": "🛕",
+ "hippopotamus": "🦛",
+ "hole": "🕳",
+ "honey_pot": "🍯",
+ "honeybee": "🐝",
+ "horizontal_traffic_light": "🚥",
+ "horse": "🐴",
+ "horse_face": "🐴",
+ "horse_racing": "🏇",
+ "horse_racing_dark_skin_tone": "🏇🏿",
+ "horse_racing_light_skin_tone": "🏇🏻",
+ "horse_racing_medium-dark_skin_tone": "🏇🏾",
+ "horse_racing_medium-light_skin_tone": "🏇🏼",
+ "horse_racing_medium_skin_tone": "🏇🏽",
+ "hospital": "🏥",
+ "hot_beverage": "☕",
+ "hot_dog": "🌭",
+ "hot_face": "🥵",
+ "hot_pepper": "🌶",
+ "hot_springs": "♨",
+ "hotel": "🏨",
+ "hourglass_done": "⌛",
+ "hourglass_not_done": "⏳",
+ "house": "🏠",
+ "house_with_garden": "🏡",
+ "houses": "🏘",
+ "hugging_face": "🤗",
+ "hundred_points": "💯",
+ "hushed_face": "😯",
+ "ice": "🧊",
+ "ice_cream": "🍨",
+ "ice_hockey": "🏒",
+ "ice_skate": "⛸",
+ "inbox_tray": "📥",
+ "incoming_envelope": "📨",
+ "index_pointing_up": "☝",
+ "index_pointing_up_dark_skin_tone": "☝🏿",
+ "index_pointing_up_light_skin_tone": "☝🏻",
+ "index_pointing_up_medium-dark_skin_tone": "☝🏾",
+ "index_pointing_up_medium-light_skin_tone": "☝🏼",
+ "index_pointing_up_medium_skin_tone": "☝🏽",
+ "infinity": "♾",
+ "information": "ℹ",
+ "input_latin_letters": "🔤",
+ "input_latin_lowercase": "🔡",
+ "input_latin_uppercase": "🔠",
+ "input_numbers": "🔢",
+ "input_symbols": "🔣",
+ "jack-o-lantern": "🎃",
+ "jeans": "👖",
+ "jigsaw": "🧩",
+ "joker": "🃏",
+ "joystick": "🕹",
+ "kaaba": "🕋",
+ "kangaroo": "🦘",
+ "key": "🔑",
+ "keyboard": "⌨",
+ "keycap_#": "#️⃣",
+ "keycap_*": "*️⃣",
+ "keycap_0": "0️⃣",
+ "keycap_1": "1️⃣",
+ "keycap_10": "🔟",
+ "keycap_2": "2️⃣",
+ "keycap_3": "3️⃣",
+ "keycap_4": "4️⃣",
+ "keycap_5": "5️⃣",
+ "keycap_6": "6️⃣",
+ "keycap_7": "7️⃣",
+ "keycap_8": "8️⃣",
+ "keycap_9": "9️⃣",
+ "kick_scooter": "🛴",
+ "kimono": "👘",
+ "kiss": "💋",
+ "kiss_man_man": "👨\u200d❤️\u200d💋\u200d👨",
+ "kiss_mark": "💋",
+ "kiss_woman_man": "👩\u200d❤️\u200d💋\u200d👨",
+ "kiss_woman_woman": "👩\u200d❤️\u200d💋\u200d👩",
+ "kissing_cat_face": "😽",
+ "kissing_face": "😗",
+ "kissing_face_with_closed_eyes": "😚",
+ "kissing_face_with_smiling_eyes": "😙",
+ "kitchen_knife": "🔪",
+ "kite": "🪁",
+ "kiwi_fruit": "🥝",
+ "koala": "🐨",
+ "lab_coat": "🥼",
+ "label": "🏷",
+ "lacrosse": "🥍",
+ "lady_beetle": "🐞",
+ "laptop_computer": "💻",
+ "large_blue_diamond": "🔷",
+ "large_orange_diamond": "🔶",
+ "last_quarter_moon": "🌗",
+ "last_quarter_moon_face": "🌜",
+ "last_track_button": "⏮",
+ "latin_cross": "✝",
+ "leaf_fluttering_in_wind": "🍃",
+ "leafy_green": "🥬",
+ "ledger": "📒",
+ "left-facing_fist": "🤛",
+ "left-facing_fist_dark_skin_tone": "🤛🏿",
+ "left-facing_fist_light_skin_tone": "🤛🏻",
+ "left-facing_fist_medium-dark_skin_tone": "🤛🏾",
+ "left-facing_fist_medium-light_skin_tone": "🤛🏼",
+ "left-facing_fist_medium_skin_tone": "🤛🏽",
+ "left-right_arrow": "↔",
+ "left_arrow": "⬅",
+ "left_arrow_curving_right": "↪",
+ "left_luggage": "🛅",
+ "left_speech_bubble": "🗨",
+ "leg": "🦵",
+ "lemon": "🍋",
+ "leopard": "🐆",
+ "level_slider": "🎚",
+ "light_bulb": "💡",
+ "light_rail": "🚈",
+ "link": "🔗",
+ "linked_paperclips": "🖇",
+ "lion_face": "🦁",
+ "lipstick": "💄",
+ "litter_in_bin_sign": "🚮",
+ "lizard": "🦎",
+ "llama": "🦙",
+ "lobster": "🦞",
+ "locked": "🔒",
+ "locked_with_key": "🔐",
+ "locked_with_pen": "🔏",
+ "locomotive": "🚂",
+ "lollipop": "🍭",
+ "lotion_bottle": "🧴",
+ "loudly_crying_face": "😭",
+ "loudspeaker": "📢",
+ "love-you_gesture": "🤟",
+ "love-you_gesture_dark_skin_tone": "🤟🏿",
+ "love-you_gesture_light_skin_tone": "🤟🏻",
+ "love-you_gesture_medium-dark_skin_tone": "🤟🏾",
+ "love-you_gesture_medium-light_skin_tone": "🤟🏼",
+ "love-you_gesture_medium_skin_tone": "🤟🏽",
+ "love_hotel": "🏩",
+ "love_letter": "💌",
+ "luggage": "🧳",
+ "lying_face": "🤥",
+ "mage": "🧙",
+ "mage_dark_skin_tone": "🧙🏿",
+ "mage_light_skin_tone": "🧙🏻",
+ "mage_medium-dark_skin_tone": "🧙🏾",
+ "mage_medium-light_skin_tone": "🧙🏼",
+ "mage_medium_skin_tone": "🧙🏽",
+ "magnet": "🧲",
+ "magnifying_glass_tilted_left": "🔍",
+ "magnifying_glass_tilted_right": "🔎",
+ "mahjong_red_dragon": "🀄",
+ "male_sign": "♂",
+ "man": "👨",
+ "man_and_woman_holding_hands": "👫",
+ "man_artist": "👨\u200d🎨",
+ "man_artist_dark_skin_tone": "👨🏿\u200d🎨",
+ "man_artist_light_skin_tone": "👨🏻\u200d🎨",
+ "man_artist_medium-dark_skin_tone": "👨🏾\u200d🎨",
+ "man_artist_medium-light_skin_tone": "👨🏼\u200d🎨",
+ "man_artist_medium_skin_tone": "👨🏽\u200d🎨",
+ "man_astronaut": "👨\u200d🚀",
+ "man_astronaut_dark_skin_tone": "👨🏿\u200d🚀",
+ "man_astronaut_light_skin_tone": "👨🏻\u200d🚀",
+ "man_astronaut_medium-dark_skin_tone": "👨🏾\u200d🚀",
+ "man_astronaut_medium-light_skin_tone": "👨🏼\u200d🚀",
+ "man_astronaut_medium_skin_tone": "👨🏽\u200d🚀",
+ "man_biking": "🚴\u200d♂️",
+ "man_biking_dark_skin_tone": "🚴🏿\u200d♂️",
+ "man_biking_light_skin_tone": "🚴🏻\u200d♂️",
+ "man_biking_medium-dark_skin_tone": "🚴🏾\u200d♂️",
+ "man_biking_medium-light_skin_tone": "🚴🏼\u200d♂️",
+ "man_biking_medium_skin_tone": "🚴🏽\u200d♂️",
+ "man_bouncing_ball": "⛹️\u200d♂️",
+ "man_bouncing_ball_dark_skin_tone": "⛹🏿\u200d♂️",
+ "man_bouncing_ball_light_skin_tone": "⛹🏻\u200d♂️",
+ "man_bouncing_ball_medium-dark_skin_tone": "⛹🏾\u200d♂️",
+ "man_bouncing_ball_medium-light_skin_tone": "⛹🏼\u200d♂️",
+ "man_bouncing_ball_medium_skin_tone": "⛹🏽\u200d♂️",
+ "man_bowing": "🙇\u200d♂️",
+ "man_bowing_dark_skin_tone": "🙇🏿\u200d♂️",
+ "man_bowing_light_skin_tone": "🙇🏻\u200d♂️",
+ "man_bowing_medium-dark_skin_tone": "🙇🏾\u200d♂️",
+ "man_bowing_medium-light_skin_tone": "🙇🏼\u200d♂️",
+ "man_bowing_medium_skin_tone": "🙇🏽\u200d♂️",
+ "man_cartwheeling": "🤸\u200d♂️",
+ "man_cartwheeling_dark_skin_tone": "🤸🏿\u200d♂️",
+ "man_cartwheeling_light_skin_tone": "🤸🏻\u200d♂️",
+ "man_cartwheeling_medium-dark_skin_tone": "🤸🏾\u200d♂️",
+ "man_cartwheeling_medium-light_skin_tone": "🤸🏼\u200d♂️",
+ "man_cartwheeling_medium_skin_tone": "🤸🏽\u200d♂️",
+ "man_climbing": "🧗\u200d♂️",
+ "man_climbing_dark_skin_tone": "🧗🏿\u200d♂️",
+ "man_climbing_light_skin_tone": "🧗🏻\u200d♂️",
+ "man_climbing_medium-dark_skin_tone": "🧗🏾\u200d♂️",
+ "man_climbing_medium-light_skin_tone": "🧗🏼\u200d♂️",
+ "man_climbing_medium_skin_tone": "🧗🏽\u200d♂️",
+ "man_construction_worker": "👷\u200d♂️",
+ "man_construction_worker_dark_skin_tone": "👷🏿\u200d♂️",
+ "man_construction_worker_light_skin_tone": "👷🏻\u200d♂️",
+ "man_construction_worker_medium-dark_skin_tone": "👷🏾\u200d♂️",
+ "man_construction_worker_medium-light_skin_tone": "👷🏼\u200d♂️",
+ "man_construction_worker_medium_skin_tone": "👷🏽\u200d♂️",
+ "man_cook": "👨\u200d🍳",
+ "man_cook_dark_skin_tone": "👨🏿\u200d🍳",
+ "man_cook_light_skin_tone": "👨🏻\u200d🍳",
+ "man_cook_medium-dark_skin_tone": "👨🏾\u200d🍳",
+ "man_cook_medium-light_skin_tone": "👨🏼\u200d🍳",
+ "man_cook_medium_skin_tone": "👨🏽\u200d🍳",
+ "man_dancing": "🕺",
+ "man_dancing_dark_skin_tone": "🕺🏿",
+ "man_dancing_light_skin_tone": "🕺🏻",
+ "man_dancing_medium-dark_skin_tone": "🕺🏾",
+ "man_dancing_medium-light_skin_tone": "🕺🏼",
+ "man_dancing_medium_skin_tone": "🕺🏽",
+ "man_dark_skin_tone": "👨🏿",
+ "man_detective": "🕵️\u200d♂️",
+ "man_detective_dark_skin_tone": "🕵🏿\u200d♂️",
+ "man_detective_light_skin_tone": "🕵🏻\u200d♂️",
+ "man_detective_medium-dark_skin_tone": "🕵🏾\u200d♂️",
+ "man_detective_medium-light_skin_tone": "🕵🏼\u200d♂️",
+ "man_detective_medium_skin_tone": "🕵🏽\u200d♂️",
+ "man_elf": "🧝\u200d♂️",
+ "man_elf_dark_skin_tone": "🧝🏿\u200d♂️",
+ "man_elf_light_skin_tone": "🧝🏻\u200d♂️",
+ "man_elf_medium-dark_skin_tone": "🧝🏾\u200d♂️",
+ "man_elf_medium-light_skin_tone": "🧝🏼\u200d♂️",
+ "man_elf_medium_skin_tone": "🧝🏽\u200d♂️",
+ "man_facepalming": "🤦\u200d♂️",
+ "man_facepalming_dark_skin_tone": "🤦🏿\u200d♂️",
+ "man_facepalming_light_skin_tone": "🤦🏻\u200d♂️",
+ "man_facepalming_medium-dark_skin_tone": "🤦🏾\u200d♂️",
+ "man_facepalming_medium-light_skin_tone": "🤦🏼\u200d♂️",
+ "man_facepalming_medium_skin_tone": "🤦🏽\u200d♂️",
+ "man_factory_worker": "👨\u200d🏭",
+ "man_factory_worker_dark_skin_tone": "👨🏿\u200d🏭",
+ "man_factory_worker_light_skin_tone": "👨🏻\u200d🏭",
+ "man_factory_worker_medium-dark_skin_tone": "👨🏾\u200d🏭",
+ "man_factory_worker_medium-light_skin_tone": "👨🏼\u200d🏭",
+ "man_factory_worker_medium_skin_tone": "👨🏽\u200d🏭",
+ "man_fairy": "🧚\u200d♂️",
+ "man_fairy_dark_skin_tone": "🧚🏿\u200d♂️",
+ "man_fairy_light_skin_tone": "🧚🏻\u200d♂️",
+ "man_fairy_medium-dark_skin_tone": "🧚🏾\u200d♂️",
+ "man_fairy_medium-light_skin_tone": "🧚🏼\u200d♂️",
+ "man_fairy_medium_skin_tone": "🧚🏽\u200d♂️",
+ "man_farmer": "👨\u200d🌾",
+ "man_farmer_dark_skin_tone": "👨🏿\u200d🌾",
+ "man_farmer_light_skin_tone": "👨🏻\u200d🌾",
+ "man_farmer_medium-dark_skin_tone": "👨🏾\u200d🌾",
+ "man_farmer_medium-light_skin_tone": "👨🏼\u200d🌾",
+ "man_farmer_medium_skin_tone": "👨🏽\u200d🌾",
+ "man_firefighter": "👨\u200d🚒",
+ "man_firefighter_dark_skin_tone": "👨🏿\u200d🚒",
+ "man_firefighter_light_skin_tone": "👨🏻\u200d🚒",
+ "man_firefighter_medium-dark_skin_tone": "👨🏾\u200d🚒",
+ "man_firefighter_medium-light_skin_tone": "👨🏼\u200d🚒",
+ "man_firefighter_medium_skin_tone": "👨🏽\u200d🚒",
+ "man_frowning": "🙍\u200d♂️",
+ "man_frowning_dark_skin_tone": "🙍🏿\u200d♂️",
+ "man_frowning_light_skin_tone": "🙍🏻\u200d♂️",
+ "man_frowning_medium-dark_skin_tone": "🙍🏾\u200d♂️",
+ "man_frowning_medium-light_skin_tone": "🙍🏼\u200d♂️",
+ "man_frowning_medium_skin_tone": "🙍🏽\u200d♂️",
+ "man_genie": "🧞\u200d♂️",
+ "man_gesturing_no": "🙅\u200d♂️",
+ "man_gesturing_no_dark_skin_tone": "🙅🏿\u200d♂️",
+ "man_gesturing_no_light_skin_tone": "🙅🏻\u200d♂️",
+ "man_gesturing_no_medium-dark_skin_tone": "🙅🏾\u200d♂️",
+ "man_gesturing_no_medium-light_skin_tone": "🙅🏼\u200d♂️",
+ "man_gesturing_no_medium_skin_tone": "🙅🏽\u200d♂️",
+ "man_gesturing_ok": "🙆\u200d♂️",
+ "man_gesturing_ok_dark_skin_tone": "🙆🏿\u200d♂️",
+ "man_gesturing_ok_light_skin_tone": "🙆🏻\u200d♂️",
+ "man_gesturing_ok_medium-dark_skin_tone": "🙆🏾\u200d♂️",
+ "man_gesturing_ok_medium-light_skin_tone": "🙆🏼\u200d♂️",
+ "man_gesturing_ok_medium_skin_tone": "🙆🏽\u200d♂️",
+ "man_getting_haircut": "💇\u200d♂️",
+ "man_getting_haircut_dark_skin_tone": "💇🏿\u200d♂️",
+ "man_getting_haircut_light_skin_tone": "💇🏻\u200d♂️",
+ "man_getting_haircut_medium-dark_skin_tone": "💇🏾\u200d♂️",
+ "man_getting_haircut_medium-light_skin_tone": "💇🏼\u200d♂️",
+ "man_getting_haircut_medium_skin_tone": "💇🏽\u200d♂️",
+ "man_getting_massage": "💆\u200d♂️",
+ "man_getting_massage_dark_skin_tone": "💆🏿\u200d♂️",
+ "man_getting_massage_light_skin_tone": "💆🏻\u200d♂️",
+ "man_getting_massage_medium-dark_skin_tone": "💆🏾\u200d♂️",
+ "man_getting_massage_medium-light_skin_tone": "💆🏼\u200d♂️",
+ "man_getting_massage_medium_skin_tone": "💆🏽\u200d♂️",
+ "man_golfing": "🏌️\u200d♂️",
+ "man_golfing_dark_skin_tone": "🏌🏿\u200d♂️",
+ "man_golfing_light_skin_tone": "🏌🏻\u200d♂️",
+ "man_golfing_medium-dark_skin_tone": "🏌🏾\u200d♂️",
+ "man_golfing_medium-light_skin_tone": "🏌🏼\u200d♂️",
+ "man_golfing_medium_skin_tone": "🏌🏽\u200d♂️",
+ "man_guard": "💂\u200d♂️",
+ "man_guard_dark_skin_tone": "💂🏿\u200d♂️",
+ "man_guard_light_skin_tone": "💂🏻\u200d♂️",
+ "man_guard_medium-dark_skin_tone": "💂🏾\u200d♂️",
+ "man_guard_medium-light_skin_tone": "💂🏼\u200d♂️",
+ "man_guard_medium_skin_tone": "💂🏽\u200d♂️",
+ "man_health_worker": "👨\u200d⚕️",
+ "man_health_worker_dark_skin_tone": "👨🏿\u200d⚕️",
+ "man_health_worker_light_skin_tone": "👨🏻\u200d⚕️",
+ "man_health_worker_medium-dark_skin_tone": "👨🏾\u200d⚕️",
+ "man_health_worker_medium-light_skin_tone": "👨🏼\u200d⚕️",
+ "man_health_worker_medium_skin_tone": "👨🏽\u200d⚕️",
+ "man_in_lotus_position": "🧘\u200d♂️",
+ "man_in_lotus_position_dark_skin_tone": "🧘🏿\u200d♂️",
+ "man_in_lotus_position_light_skin_tone": "🧘🏻\u200d♂️",
+ "man_in_lotus_position_medium-dark_skin_tone": "🧘🏾\u200d♂️",
+ "man_in_lotus_position_medium-light_skin_tone": "🧘🏼\u200d♂️",
+ "man_in_lotus_position_medium_skin_tone": "🧘🏽\u200d♂️",
+ "man_in_manual_wheelchair": "👨\u200d🦽",
+ "man_in_motorized_wheelchair": "👨\u200d🦼",
+ "man_in_steamy_room": "🧖\u200d♂️",
+ "man_in_steamy_room_dark_skin_tone": "🧖🏿\u200d♂️",
+ "man_in_steamy_room_light_skin_tone": "🧖🏻\u200d♂️",
+ "man_in_steamy_room_medium-dark_skin_tone": "🧖🏾\u200d♂️",
+ "man_in_steamy_room_medium-light_skin_tone": "🧖🏼\u200d♂️",
+ "man_in_steamy_room_medium_skin_tone": "🧖🏽\u200d♂️",
+ "man_in_suit_levitating": "🕴",
+ "man_in_suit_levitating_dark_skin_tone": "🕴🏿",
+ "man_in_suit_levitating_light_skin_tone": "🕴🏻",
+ "man_in_suit_levitating_medium-dark_skin_tone": "🕴🏾",
+ "man_in_suit_levitating_medium-light_skin_tone": "🕴🏼",
+ "man_in_suit_levitating_medium_skin_tone": "🕴🏽",
+ "man_in_tuxedo": "🤵",
+ "man_in_tuxedo_dark_skin_tone": "🤵🏿",
+ "man_in_tuxedo_light_skin_tone": "🤵🏻",
+ "man_in_tuxedo_medium-dark_skin_tone": "🤵🏾",
+ "man_in_tuxedo_medium-light_skin_tone": "🤵🏼",
+ "man_in_tuxedo_medium_skin_tone": "🤵🏽",
+ "man_judge": "👨\u200d⚖️",
+ "man_judge_dark_skin_tone": "👨🏿\u200d⚖️",
+ "man_judge_light_skin_tone": "👨🏻\u200d⚖️",
+ "man_judge_medium-dark_skin_tone": "👨🏾\u200d⚖️",
+ "man_judge_medium-light_skin_tone": "👨🏼\u200d⚖️",
+ "man_judge_medium_skin_tone": "👨🏽\u200d⚖️",
+ "man_juggling": "🤹\u200d♂️",
+ "man_juggling_dark_skin_tone": "🤹🏿\u200d♂️",
+ "man_juggling_light_skin_tone": "🤹🏻\u200d♂️",
+ "man_juggling_medium-dark_skin_tone": "🤹🏾\u200d♂️",
+ "man_juggling_medium-light_skin_tone": "🤹🏼\u200d♂️",
+ "man_juggling_medium_skin_tone": "🤹🏽\u200d♂️",
+ "man_lifting_weights": "🏋️\u200d♂️",
+ "man_lifting_weights_dark_skin_tone": "🏋🏿\u200d♂️",
+ "man_lifting_weights_light_skin_tone": "🏋🏻\u200d♂️",
+ "man_lifting_weights_medium-dark_skin_tone": "🏋🏾\u200d♂️",
+ "man_lifting_weights_medium-light_skin_tone": "🏋🏼\u200d♂️",
+ "man_lifting_weights_medium_skin_tone": "🏋🏽\u200d♂️",
+ "man_light_skin_tone": "👨🏻",
+ "man_mage": "🧙\u200d♂️",
+ "man_mage_dark_skin_tone": "🧙🏿\u200d♂️",
+ "man_mage_light_skin_tone": "🧙🏻\u200d♂️",
+ "man_mage_medium-dark_skin_tone": "🧙🏾\u200d♂️",
+ "man_mage_medium-light_skin_tone": "🧙🏼\u200d♂️",
+ "man_mage_medium_skin_tone": "🧙🏽\u200d♂️",
+ "man_mechanic": "👨\u200d🔧",
+ "man_mechanic_dark_skin_tone": "👨🏿\u200d🔧",
+ "man_mechanic_light_skin_tone": "👨🏻\u200d🔧",
+ "man_mechanic_medium-dark_skin_tone": "👨🏾\u200d🔧",
+ "man_mechanic_medium-light_skin_tone": "👨🏼\u200d🔧",
+ "man_mechanic_medium_skin_tone": "👨🏽\u200d🔧",
+ "man_medium-dark_skin_tone": "👨🏾",
+ "man_medium-light_skin_tone": "👨🏼",
+ "man_medium_skin_tone": "👨🏽",
+ "man_mountain_biking": "🚵\u200d♂️",
+ "man_mountain_biking_dark_skin_tone": "🚵🏿\u200d♂️",
+ "man_mountain_biking_light_skin_tone": "🚵🏻\u200d♂️",
+ "man_mountain_biking_medium-dark_skin_tone": "🚵🏾\u200d♂️",
+ "man_mountain_biking_medium-light_skin_tone": "🚵🏼\u200d♂️",
+ "man_mountain_biking_medium_skin_tone": "🚵🏽\u200d♂️",
+ "man_office_worker": "👨\u200d💼",
+ "man_office_worker_dark_skin_tone": "👨🏿\u200d💼",
+ "man_office_worker_light_skin_tone": "👨🏻\u200d💼",
+ "man_office_worker_medium-dark_skin_tone": "👨🏾\u200d💼",
+ "man_office_worker_medium-light_skin_tone": "👨🏼\u200d💼",
+ "man_office_worker_medium_skin_tone": "👨🏽\u200d💼",
+ "man_pilot": "👨\u200d✈️",
+ "man_pilot_dark_skin_tone": "👨🏿\u200d✈️",
+ "man_pilot_light_skin_tone": "👨🏻\u200d✈️",
+ "man_pilot_medium-dark_skin_tone": "👨🏾\u200d✈️",
+ "man_pilot_medium-light_skin_tone": "👨🏼\u200d✈️",
+ "man_pilot_medium_skin_tone": "👨🏽\u200d✈️",
+ "man_playing_handball": "🤾\u200d♂️",
+ "man_playing_handball_dark_skin_tone": "🤾🏿\u200d♂️",
+ "man_playing_handball_light_skin_tone": "🤾🏻\u200d♂️",
+ "man_playing_handball_medium-dark_skin_tone": "🤾🏾\u200d♂️",
+ "man_playing_handball_medium-light_skin_tone": "🤾🏼\u200d♂️",
+ "man_playing_handball_medium_skin_tone": "🤾🏽\u200d♂️",
+ "man_playing_water_polo": "🤽\u200d♂️",
+ "man_playing_water_polo_dark_skin_tone": "🤽🏿\u200d♂️",
+ "man_playing_water_polo_light_skin_tone": "🤽🏻\u200d♂️",
+ "man_playing_water_polo_medium-dark_skin_tone": "🤽🏾\u200d♂️",
+ "man_playing_water_polo_medium-light_skin_tone": "🤽🏼\u200d♂️",
+ "man_playing_water_polo_medium_skin_tone": "🤽🏽\u200d♂️",
+ "man_police_officer": "👮\u200d♂️",
+ "man_police_officer_dark_skin_tone": "👮🏿\u200d♂️",
+ "man_police_officer_light_skin_tone": "👮🏻\u200d♂️",
+ "man_police_officer_medium-dark_skin_tone": "👮🏾\u200d♂️",
+ "man_police_officer_medium-light_skin_tone": "👮🏼\u200d♂️",
+ "man_police_officer_medium_skin_tone": "👮🏽\u200d♂️",
+ "man_pouting": "🙎\u200d♂️",
+ "man_pouting_dark_skin_tone": "🙎🏿\u200d♂️",
+ "man_pouting_light_skin_tone": "🙎🏻\u200d♂️",
+ "man_pouting_medium-dark_skin_tone": "🙎🏾\u200d♂️",
+ "man_pouting_medium-light_skin_tone": "🙎🏼\u200d♂️",
+ "man_pouting_medium_skin_tone": "🙎🏽\u200d♂️",
+ "man_raising_hand": "🙋\u200d♂️",
+ "man_raising_hand_dark_skin_tone": "🙋🏿\u200d♂️",
+ "man_raising_hand_light_skin_tone": "🙋🏻\u200d♂️",
+ "man_raising_hand_medium-dark_skin_tone": "🙋🏾\u200d♂️",
+ "man_raising_hand_medium-light_skin_tone": "🙋🏼\u200d♂️",
+ "man_raising_hand_medium_skin_tone": "🙋🏽\u200d♂️",
+ "man_rowing_boat": "🚣\u200d♂️",
+ "man_rowing_boat_dark_skin_tone": "🚣🏿\u200d♂️",
+ "man_rowing_boat_light_skin_tone": "🚣🏻\u200d♂️",
+ "man_rowing_boat_medium-dark_skin_tone": "🚣🏾\u200d♂️",
+ "man_rowing_boat_medium-light_skin_tone": "🚣🏼\u200d♂️",
+ "man_rowing_boat_medium_skin_tone": "🚣🏽\u200d♂️",
+ "man_running": "🏃\u200d♂️",
+ "man_running_dark_skin_tone": "🏃🏿\u200d♂️",
+ "man_running_light_skin_tone": "🏃🏻\u200d♂️",
+ "man_running_medium-dark_skin_tone": "🏃🏾\u200d♂️",
+ "man_running_medium-light_skin_tone": "🏃🏼\u200d♂️",
+ "man_running_medium_skin_tone": "🏃🏽\u200d♂️",
+ "man_scientist": "👨\u200d🔬",
+ "man_scientist_dark_skin_tone": "👨🏿\u200d🔬",
+ "man_scientist_light_skin_tone": "👨🏻\u200d🔬",
+ "man_scientist_medium-dark_skin_tone": "👨🏾\u200d🔬",
+ "man_scientist_medium-light_skin_tone": "👨🏼\u200d🔬",
+ "man_scientist_medium_skin_tone": "👨🏽\u200d🔬",
+ "man_shrugging": "🤷\u200d♂️",
+ "man_shrugging_dark_skin_tone": "🤷🏿\u200d♂️",
+ "man_shrugging_light_skin_tone": "🤷🏻\u200d♂️",
+ "man_shrugging_medium-dark_skin_tone": "🤷🏾\u200d♂️",
+ "man_shrugging_medium-light_skin_tone": "🤷🏼\u200d♂️",
+ "man_shrugging_medium_skin_tone": "🤷🏽\u200d♂️",
+ "man_singer": "👨\u200d🎤",
+ "man_singer_dark_skin_tone": "👨🏿\u200d🎤",
+ "man_singer_light_skin_tone": "👨🏻\u200d🎤",
+ "man_singer_medium-dark_skin_tone": "👨🏾\u200d🎤",
+ "man_singer_medium-light_skin_tone": "👨🏼\u200d🎤",
+ "man_singer_medium_skin_tone": "👨🏽\u200d🎤",
+ "man_student": "👨\u200d🎓",
+ "man_student_dark_skin_tone": "👨🏿\u200d🎓",
+ "man_student_light_skin_tone": "👨🏻\u200d🎓",
+ "man_student_medium-dark_skin_tone": "👨🏾\u200d🎓",
+ "man_student_medium-light_skin_tone": "👨🏼\u200d🎓",
+ "man_student_medium_skin_tone": "👨🏽\u200d🎓",
+ "man_surfing": "🏄\u200d♂️",
+ "man_surfing_dark_skin_tone": "🏄🏿\u200d♂️",
+ "man_surfing_light_skin_tone": "🏄🏻\u200d♂️",
+ "man_surfing_medium-dark_skin_tone": "🏄🏾\u200d♂️",
+ "man_surfing_medium-light_skin_tone": "🏄🏼\u200d♂️",
+ "man_surfing_medium_skin_tone": "🏄🏽\u200d♂️",
+ "man_swimming": "🏊\u200d♂️",
+ "man_swimming_dark_skin_tone": "🏊🏿\u200d♂️",
+ "man_swimming_light_skin_tone": "🏊🏻\u200d♂️",
+ "man_swimming_medium-dark_skin_tone": "🏊🏾\u200d♂️",
+ "man_swimming_medium-light_skin_tone": "🏊🏼\u200d♂️",
+ "man_swimming_medium_skin_tone": "🏊🏽\u200d♂️",
+ "man_teacher": "👨\u200d🏫",
+ "man_teacher_dark_skin_tone": "👨🏿\u200d🏫",
+ "man_teacher_light_skin_tone": "👨🏻\u200d🏫",
+ "man_teacher_medium-dark_skin_tone": "👨🏾\u200d🏫",
+ "man_teacher_medium-light_skin_tone": "👨🏼\u200d🏫",
+ "man_teacher_medium_skin_tone": "👨🏽\u200d🏫",
+ "man_technologist": "👨\u200d💻",
+ "man_technologist_dark_skin_tone": "👨🏿\u200d💻",
+ "man_technologist_light_skin_tone": "👨🏻\u200d💻",
+ "man_technologist_medium-dark_skin_tone": "👨🏾\u200d💻",
+ "man_technologist_medium-light_skin_tone": "👨🏼\u200d💻",
+ "man_technologist_medium_skin_tone": "👨🏽\u200d💻",
+ "man_tipping_hand": "💁\u200d♂️",
+ "man_tipping_hand_dark_skin_tone": "💁🏿\u200d♂️",
+ "man_tipping_hand_light_skin_tone": "💁🏻\u200d♂️",
+ "man_tipping_hand_medium-dark_skin_tone": "💁🏾\u200d♂️",
+ "man_tipping_hand_medium-light_skin_tone": "💁🏼\u200d♂️",
+ "man_tipping_hand_medium_skin_tone": "💁🏽\u200d♂️",
+ "man_vampire": "🧛\u200d♂️",
+ "man_vampire_dark_skin_tone": "🧛🏿\u200d♂️",
+ "man_vampire_light_skin_tone": "🧛🏻\u200d♂️",
+ "man_vampire_medium-dark_skin_tone": "🧛🏾\u200d♂️",
+ "man_vampire_medium-light_skin_tone": "🧛🏼\u200d♂️",
+ "man_vampire_medium_skin_tone": "🧛🏽\u200d♂️",
+ "man_walking": "🚶\u200d♂️",
+ "man_walking_dark_skin_tone": "🚶🏿\u200d♂️",
+ "man_walking_light_skin_tone": "🚶🏻\u200d♂️",
+ "man_walking_medium-dark_skin_tone": "🚶🏾\u200d♂️",
+ "man_walking_medium-light_skin_tone": "🚶🏼\u200d♂️",
+ "man_walking_medium_skin_tone": "🚶🏽\u200d♂️",
+ "man_wearing_turban": "👳\u200d♂️",
+ "man_wearing_turban_dark_skin_tone": "👳🏿\u200d♂️",
+ "man_wearing_turban_light_skin_tone": "👳🏻\u200d♂️",
+ "man_wearing_turban_medium-dark_skin_tone": "👳🏾\u200d♂️",
+ "man_wearing_turban_medium-light_skin_tone": "👳🏼\u200d♂️",
+ "man_wearing_turban_medium_skin_tone": "👳🏽\u200d♂️",
+ "man_with_probing_cane": "👨\u200d🦯",
+ "man_with_chinese_cap": "👲",
+ "man_with_chinese_cap_dark_skin_tone": "👲🏿",
+ "man_with_chinese_cap_light_skin_tone": "👲🏻",
+ "man_with_chinese_cap_medium-dark_skin_tone": "👲🏾",
+ "man_with_chinese_cap_medium-light_skin_tone": "👲🏼",
+ "man_with_chinese_cap_medium_skin_tone": "👲🏽",
+ "man_zombie": "🧟\u200d♂️",
+ "mango": "🥭",
+ "mantelpiece_clock": "🕰",
+ "manual_wheelchair": "🦽",
+ "man’s_shoe": "👞",
+ "map_of_japan": "🗾",
+ "maple_leaf": "🍁",
+ "martial_arts_uniform": "🥋",
+ "mate": "🧉",
+ "meat_on_bone": "🍖",
+ "mechanical_arm": "🦾",
+ "mechanical_leg": "🦿",
+ "medical_symbol": "⚕",
+ "megaphone": "📣",
+ "melon": "🍈",
+ "memo": "📝",
+ "men_with_bunny_ears": "👯\u200d♂️",
+ "men_wrestling": "🤼\u200d♂️",
+ "menorah": "🕎",
+ "men’s_room": "🚹",
+ "mermaid": "🧜\u200d♀️",
+ "mermaid_dark_skin_tone": "🧜🏿\u200d♀️",
+ "mermaid_light_skin_tone": "🧜🏻\u200d♀️",
+ "mermaid_medium-dark_skin_tone": "🧜🏾\u200d♀️",
+ "mermaid_medium-light_skin_tone": "🧜🏼\u200d♀️",
+ "mermaid_medium_skin_tone": "🧜🏽\u200d♀️",
+ "merman": "🧜\u200d♂️",
+ "merman_dark_skin_tone": "🧜🏿\u200d♂️",
+ "merman_light_skin_tone": "🧜🏻\u200d♂️",
+ "merman_medium-dark_skin_tone": "🧜🏾\u200d♂️",
+ "merman_medium-light_skin_tone": "🧜🏼\u200d♂️",
+ "merman_medium_skin_tone": "🧜🏽\u200d♂️",
+ "merperson": "🧜",
+ "merperson_dark_skin_tone": "🧜🏿",
+ "merperson_light_skin_tone": "🧜🏻",
+ "merperson_medium-dark_skin_tone": "🧜🏾",
+ "merperson_medium-light_skin_tone": "🧜🏼",
+ "merperson_medium_skin_tone": "🧜🏽",
+ "metro": "🚇",
+ "microbe": "🦠",
+ "microphone": "🎤",
+ "microscope": "🔬",
+ "middle_finger": "🖕",
+ "middle_finger_dark_skin_tone": "🖕🏿",
+ "middle_finger_light_skin_tone": "🖕🏻",
+ "middle_finger_medium-dark_skin_tone": "🖕🏾",
+ "middle_finger_medium-light_skin_tone": "🖕🏼",
+ "middle_finger_medium_skin_tone": "🖕🏽",
+ "military_medal": "🎖",
+ "milky_way": "🌌",
+ "minibus": "🚐",
+ "moai": "🗿",
+ "mobile_phone": "📱",
+ "mobile_phone_off": "📴",
+ "mobile_phone_with_arrow": "📲",
+ "money-mouth_face": "🤑",
+ "money_bag": "💰",
+ "money_with_wings": "💸",
+ "monkey": "🐒",
+ "monkey_face": "🐵",
+ "monorail": "🚝",
+ "moon_cake": "🥮",
+ "moon_viewing_ceremony": "🎑",
+ "mosque": "🕌",
+ "mosquito": "🦟",
+ "motor_boat": "🛥",
+ "motor_scooter": "🛵",
+ "motorcycle": "🏍",
+ "motorized_wheelchair": "🦼",
+ "motorway": "🛣",
+ "mount_fuji": "🗻",
+ "mountain": "⛰",
+ "mountain_cableway": "🚠",
+ "mountain_railway": "🚞",
+ "mouse": "🐭",
+ "mouse_face": "🐭",
+ "mouth": "👄",
+ "movie_camera": "🎥",
+ "mushroom": "🍄",
+ "musical_keyboard": "🎹",
+ "musical_note": "🎵",
+ "musical_notes": "🎶",
+ "musical_score": "🎼",
+ "muted_speaker": "🔇",
+ "nail_polish": "💅",
+ "nail_polish_dark_skin_tone": "💅🏿",
+ "nail_polish_light_skin_tone": "💅🏻",
+ "nail_polish_medium-dark_skin_tone": "💅🏾",
+ "nail_polish_medium-light_skin_tone": "💅🏼",
+ "nail_polish_medium_skin_tone": "💅🏽",
+ "name_badge": "📛",
+ "national_park": "🏞",
+ "nauseated_face": "🤢",
+ "nazar_amulet": "🧿",
+ "necktie": "👔",
+ "nerd_face": "🤓",
+ "neutral_face": "😐",
+ "new_moon": "🌑",
+ "new_moon_face": "🌚",
+ "newspaper": "📰",
+ "next_track_button": "⏭",
+ "night_with_stars": "🌃",
+ "nine-thirty": "🕤",
+ "nine_o’clock": "🕘",
+ "no_bicycles": "🚳",
+ "no_entry": "⛔",
+ "no_littering": "🚯",
+ "no_mobile_phones": "📵",
+ "no_one_under_eighteen": "🔞",
+ "no_pedestrians": "🚷",
+ "no_smoking": "🚭",
+ "non-potable_water": "🚱",
+ "nose": "👃",
+ "nose_dark_skin_tone": "👃🏿",
+ "nose_light_skin_tone": "👃🏻",
+ "nose_medium-dark_skin_tone": "👃🏾",
+ "nose_medium-light_skin_tone": "👃🏼",
+ "nose_medium_skin_tone": "👃🏽",
+ "notebook": "📓",
+ "notebook_with_decorative_cover": "📔",
+ "nut_and_bolt": "🔩",
+ "octopus": "🐙",
+ "oden": "🍢",
+ "office_building": "🏢",
+ "ogre": "👹",
+ "oil_drum": "🛢",
+ "old_key": "🗝",
+ "old_man": "👴",
+ "old_man_dark_skin_tone": "👴🏿",
+ "old_man_light_skin_tone": "👴🏻",
+ "old_man_medium-dark_skin_tone": "👴🏾",
+ "old_man_medium-light_skin_tone": "👴🏼",
+ "old_man_medium_skin_tone": "👴🏽",
+ "old_woman": "👵",
+ "old_woman_dark_skin_tone": "👵🏿",
+ "old_woman_light_skin_tone": "👵🏻",
+ "old_woman_medium-dark_skin_tone": "👵🏾",
+ "old_woman_medium-light_skin_tone": "👵🏼",
+ "old_woman_medium_skin_tone": "👵🏽",
+ "older_adult": "🧓",
+ "older_adult_dark_skin_tone": "🧓🏿",
+ "older_adult_light_skin_tone": "🧓🏻",
+ "older_adult_medium-dark_skin_tone": "🧓🏾",
+ "older_adult_medium-light_skin_tone": "🧓🏼",
+ "older_adult_medium_skin_tone": "🧓🏽",
+ "om": "🕉",
+ "oncoming_automobile": "🚘",
+ "oncoming_bus": "🚍",
+ "oncoming_fist": "👊",
+ "oncoming_fist_dark_skin_tone": "👊🏿",
+ "oncoming_fist_light_skin_tone": "👊🏻",
+ "oncoming_fist_medium-dark_skin_tone": "👊🏾",
+ "oncoming_fist_medium-light_skin_tone": "👊🏼",
+ "oncoming_fist_medium_skin_tone": "👊🏽",
+ "oncoming_police_car": "🚔",
+ "oncoming_taxi": "🚖",
+ "one-piece_swimsuit": "🩱",
+ "one-thirty": "🕜",
+ "one_o’clock": "🕐",
+ "onion": "🧅",
+ "open_book": "📖",
+ "open_file_folder": "📂",
+ "open_hands": "👐",
+ "open_hands_dark_skin_tone": "👐🏿",
+ "open_hands_light_skin_tone": "👐🏻",
+ "open_hands_medium-dark_skin_tone": "👐🏾",
+ "open_hands_medium-light_skin_tone": "👐🏼",
+ "open_hands_medium_skin_tone": "👐🏽",
+ "open_mailbox_with_lowered_flag": "📭",
+ "open_mailbox_with_raised_flag": "📬",
+ "optical_disk": "💿",
+ "orange_book": "📙",
+ "orange_circle": "🟠",
+ "orange_heart": "🧡",
+ "orange_square": "🟧",
+ "orangutan": "🦧",
+ "orthodox_cross": "☦",
+ "otter": "🦦",
+ "outbox_tray": "📤",
+ "owl": "🦉",
+ "ox": "🐂",
+ "oyster": "🦪",
+ "package": "📦",
+ "page_facing_up": "📄",
+ "page_with_curl": "📃",
+ "pager": "📟",
+ "paintbrush": "🖌",
+ "palm_tree": "🌴",
+ "palms_up_together": "🤲",
+ "palms_up_together_dark_skin_tone": "🤲🏿",
+ "palms_up_together_light_skin_tone": "🤲🏻",
+ "palms_up_together_medium-dark_skin_tone": "🤲🏾",
+ "palms_up_together_medium-light_skin_tone": "🤲🏼",
+ "palms_up_together_medium_skin_tone": "🤲🏽",
+ "pancakes": "🥞",
+ "panda_face": "🐼",
+ "paperclip": "📎",
+ "parrot": "🦜",
+ "part_alternation_mark": "〽",
+ "party_popper": "🎉",
+ "partying_face": "🥳",
+ "passenger_ship": "🛳",
+ "passport_control": "🛂",
+ "pause_button": "⏸",
+ "paw_prints": "🐾",
+ "peace_symbol": "☮",
+ "peach": "🍑",
+ "peacock": "🦚",
+ "peanuts": "🥜",
+ "pear": "🍐",
+ "pen": "🖊",
+ "pencil": "📝",
+ "penguin": "🐧",
+ "pensive_face": "😔",
+ "people_holding_hands": "🧑\u200d🤝\u200d🧑",
+ "people_with_bunny_ears": "👯",
+ "people_wrestling": "🤼",
+ "performing_arts": "🎭",
+ "persevering_face": "😣",
+ "person_biking": "🚴",
+ "person_biking_dark_skin_tone": "🚴🏿",
+ "person_biking_light_skin_tone": "🚴🏻",
+ "person_biking_medium-dark_skin_tone": "🚴🏾",
+ "person_biking_medium-light_skin_tone": "🚴🏼",
+ "person_biking_medium_skin_tone": "🚴🏽",
+ "person_bouncing_ball": "⛹",
+ "person_bouncing_ball_dark_skin_tone": "⛹🏿",
+ "person_bouncing_ball_light_skin_tone": "⛹🏻",
+ "person_bouncing_ball_medium-dark_skin_tone": "⛹🏾",
+ "person_bouncing_ball_medium-light_skin_tone": "⛹🏼",
+ "person_bouncing_ball_medium_skin_tone": "⛹🏽",
+ "person_bowing": "🙇",
+ "person_bowing_dark_skin_tone": "🙇🏿",
+ "person_bowing_light_skin_tone": "🙇🏻",
+ "person_bowing_medium-dark_skin_tone": "🙇🏾",
+ "person_bowing_medium-light_skin_tone": "🙇🏼",
+ "person_bowing_medium_skin_tone": "🙇🏽",
+ "person_cartwheeling": "🤸",
+ "person_cartwheeling_dark_skin_tone": "🤸🏿",
+ "person_cartwheeling_light_skin_tone": "🤸🏻",
+ "person_cartwheeling_medium-dark_skin_tone": "🤸🏾",
+ "person_cartwheeling_medium-light_skin_tone": "🤸🏼",
+ "person_cartwheeling_medium_skin_tone": "🤸🏽",
+ "person_climbing": "🧗",
+ "person_climbing_dark_skin_tone": "🧗🏿",
+ "person_climbing_light_skin_tone": "🧗🏻",
+ "person_climbing_medium-dark_skin_tone": "🧗🏾",
+ "person_climbing_medium-light_skin_tone": "🧗🏼",
+ "person_climbing_medium_skin_tone": "🧗🏽",
+ "person_facepalming": "🤦",
+ "person_facepalming_dark_skin_tone": "🤦🏿",
+ "person_facepalming_light_skin_tone": "🤦🏻",
+ "person_facepalming_medium-dark_skin_tone": "🤦🏾",
+ "person_facepalming_medium-light_skin_tone": "🤦🏼",
+ "person_facepalming_medium_skin_tone": "🤦🏽",
+ "person_fencing": "🤺",
+ "person_frowning": "🙍",
+ "person_frowning_dark_skin_tone": "🙍🏿",
+ "person_frowning_light_skin_tone": "🙍🏻",
+ "person_frowning_medium-dark_skin_tone": "🙍🏾",
+ "person_frowning_medium-light_skin_tone": "🙍🏼",
+ "person_frowning_medium_skin_tone": "🙍🏽",
+ "person_gesturing_no": "🙅",
+ "person_gesturing_no_dark_skin_tone": "🙅🏿",
+ "person_gesturing_no_light_skin_tone": "🙅🏻",
+ "person_gesturing_no_medium-dark_skin_tone": "🙅🏾",
+ "person_gesturing_no_medium-light_skin_tone": "🙅🏼",
+ "person_gesturing_no_medium_skin_tone": "🙅🏽",
+ "person_gesturing_ok": "🙆",
+ "person_gesturing_ok_dark_skin_tone": "🙆🏿",
+ "person_gesturing_ok_light_skin_tone": "🙆🏻",
+ "person_gesturing_ok_medium-dark_skin_tone": "🙆🏾",
+ "person_gesturing_ok_medium-light_skin_tone": "🙆🏼",
+ "person_gesturing_ok_medium_skin_tone": "🙆🏽",
+ "person_getting_haircut": "💇",
+ "person_getting_haircut_dark_skin_tone": "💇🏿",
+ "person_getting_haircut_light_skin_tone": "💇🏻",
+ "person_getting_haircut_medium-dark_skin_tone": "💇🏾",
+ "person_getting_haircut_medium-light_skin_tone": "💇🏼",
+ "person_getting_haircut_medium_skin_tone": "💇🏽",
+ "person_getting_massage": "💆",
+ "person_getting_massage_dark_skin_tone": "💆🏿",
+ "person_getting_massage_light_skin_tone": "💆🏻",
+ "person_getting_massage_medium-dark_skin_tone": "💆🏾",
+ "person_getting_massage_medium-light_skin_tone": "💆🏼",
+ "person_getting_massage_medium_skin_tone": "💆🏽",
+ "person_golfing": "🏌",
+ "person_golfing_dark_skin_tone": "🏌🏿",
+ "person_golfing_light_skin_tone": "🏌🏻",
+ "person_golfing_medium-dark_skin_tone": "🏌🏾",
+ "person_golfing_medium-light_skin_tone": "🏌🏼",
+ "person_golfing_medium_skin_tone": "🏌🏽",
+ "person_in_bed": "🛌",
+ "person_in_bed_dark_skin_tone": "🛌🏿",
+ "person_in_bed_light_skin_tone": "🛌🏻",
+ "person_in_bed_medium-dark_skin_tone": "🛌🏾",
+ "person_in_bed_medium-light_skin_tone": "🛌🏼",
+ "person_in_bed_medium_skin_tone": "🛌🏽",
+ "person_in_lotus_position": "🧘",
+ "person_in_lotus_position_dark_skin_tone": "🧘🏿",
+ "person_in_lotus_position_light_skin_tone": "🧘🏻",
+ "person_in_lotus_position_medium-dark_skin_tone": "🧘🏾",
+ "person_in_lotus_position_medium-light_skin_tone": "🧘🏼",
+ "person_in_lotus_position_medium_skin_tone": "🧘🏽",
+ "person_in_steamy_room": "🧖",
+ "person_in_steamy_room_dark_skin_tone": "🧖🏿",
+ "person_in_steamy_room_light_skin_tone": "🧖🏻",
+ "person_in_steamy_room_medium-dark_skin_tone": "🧖🏾",
+ "person_in_steamy_room_medium-light_skin_tone": "🧖🏼",
+ "person_in_steamy_room_medium_skin_tone": "🧖🏽",
+ "person_juggling": "🤹",
+ "person_juggling_dark_skin_tone": "🤹🏿",
+ "person_juggling_light_skin_tone": "🤹🏻",
+ "person_juggling_medium-dark_skin_tone": "🤹🏾",
+ "person_juggling_medium-light_skin_tone": "🤹🏼",
+ "person_juggling_medium_skin_tone": "🤹🏽",
+ "person_kneeling": "🧎",
+ "person_lifting_weights": "🏋",
+ "person_lifting_weights_dark_skin_tone": "🏋🏿",
+ "person_lifting_weights_light_skin_tone": "🏋🏻",
+ "person_lifting_weights_medium-dark_skin_tone": "🏋🏾",
+ "person_lifting_weights_medium-light_skin_tone": "🏋🏼",
+ "person_lifting_weights_medium_skin_tone": "🏋🏽",
+ "person_mountain_biking": "🚵",
+ "person_mountain_biking_dark_skin_tone": "🚵🏿",
+ "person_mountain_biking_light_skin_tone": "🚵🏻",
+ "person_mountain_biking_medium-dark_skin_tone": "🚵🏾",
+ "person_mountain_biking_medium-light_skin_tone": "🚵🏼",
+ "person_mountain_biking_medium_skin_tone": "🚵🏽",
+ "person_playing_handball": "🤾",
+ "person_playing_handball_dark_skin_tone": "🤾🏿",
+ "person_playing_handball_light_skin_tone": "🤾🏻",
+ "person_playing_handball_medium-dark_skin_tone": "🤾🏾",
+ "person_playing_handball_medium-light_skin_tone": "🤾🏼",
+ "person_playing_handball_medium_skin_tone": "🤾🏽",
+ "person_playing_water_polo": "🤽",
+ "person_playing_water_polo_dark_skin_tone": "🤽🏿",
+ "person_playing_water_polo_light_skin_tone": "🤽🏻",
+ "person_playing_water_polo_medium-dark_skin_tone": "🤽🏾",
+ "person_playing_water_polo_medium-light_skin_tone": "🤽🏼",
+ "person_playing_water_polo_medium_skin_tone": "🤽🏽",
+ "person_pouting": "🙎",
+ "person_pouting_dark_skin_tone": "🙎🏿",
+ "person_pouting_light_skin_tone": "🙎🏻",
+ "person_pouting_medium-dark_skin_tone": "🙎🏾",
+ "person_pouting_medium-light_skin_tone": "🙎🏼",
+ "person_pouting_medium_skin_tone": "🙎🏽",
+ "person_raising_hand": "🙋",
+ "person_raising_hand_dark_skin_tone": "🙋🏿",
+ "person_raising_hand_light_skin_tone": "🙋🏻",
+ "person_raising_hand_medium-dark_skin_tone": "🙋🏾",
+ "person_raising_hand_medium-light_skin_tone": "🙋🏼",
+ "person_raising_hand_medium_skin_tone": "🙋🏽",
+ "person_rowing_boat": "🚣",
+ "person_rowing_boat_dark_skin_tone": "🚣🏿",
+ "person_rowing_boat_light_skin_tone": "🚣🏻",
+ "person_rowing_boat_medium-dark_skin_tone": "🚣🏾",
+ "person_rowing_boat_medium-light_skin_tone": "🚣🏼",
+ "person_rowing_boat_medium_skin_tone": "🚣🏽",
+ "person_running": "🏃",
+ "person_running_dark_skin_tone": "🏃🏿",
+ "person_running_light_skin_tone": "🏃🏻",
+ "person_running_medium-dark_skin_tone": "🏃🏾",
+ "person_running_medium-light_skin_tone": "🏃🏼",
+ "person_running_medium_skin_tone": "🏃🏽",
+ "person_shrugging": "🤷",
+ "person_shrugging_dark_skin_tone": "🤷🏿",
+ "person_shrugging_light_skin_tone": "🤷🏻",
+ "person_shrugging_medium-dark_skin_tone": "🤷🏾",
+ "person_shrugging_medium-light_skin_tone": "🤷🏼",
+ "person_shrugging_medium_skin_tone": "🤷🏽",
+ "person_standing": "🧍",
+ "person_surfing": "🏄",
+ "person_surfing_dark_skin_tone": "🏄🏿",
+ "person_surfing_light_skin_tone": "🏄🏻",
+ "person_surfing_medium-dark_skin_tone": "🏄🏾",
+ "person_surfing_medium-light_skin_tone": "🏄🏼",
+ "person_surfing_medium_skin_tone": "🏄🏽",
+ "person_swimming": "🏊",
+ "person_swimming_dark_skin_tone": "🏊🏿",
+ "person_swimming_light_skin_tone": "🏊🏻",
+ "person_swimming_medium-dark_skin_tone": "🏊🏾",
+ "person_swimming_medium-light_skin_tone": "🏊🏼",
+ "person_swimming_medium_skin_tone": "🏊🏽",
+ "person_taking_bath": "🛀",
+ "person_taking_bath_dark_skin_tone": "🛀🏿",
+ "person_taking_bath_light_skin_tone": "🛀🏻",
+ "person_taking_bath_medium-dark_skin_tone": "🛀🏾",
+ "person_taking_bath_medium-light_skin_tone": "🛀🏼",
+ "person_taking_bath_medium_skin_tone": "🛀🏽",
+ "person_tipping_hand": "💁",
+ "person_tipping_hand_dark_skin_tone": "💁🏿",
+ "person_tipping_hand_light_skin_tone": "💁🏻",
+ "person_tipping_hand_medium-dark_skin_tone": "💁🏾",
+ "person_tipping_hand_medium-light_skin_tone": "💁🏼",
+ "person_tipping_hand_medium_skin_tone": "💁🏽",
+ "person_walking": "🚶",
+ "person_walking_dark_skin_tone": "🚶🏿",
+ "person_walking_light_skin_tone": "🚶🏻",
+ "person_walking_medium-dark_skin_tone": "🚶🏾",
+ "person_walking_medium-light_skin_tone": "🚶🏼",
+ "person_walking_medium_skin_tone": "🚶🏽",
+ "person_wearing_turban": "👳",
+ "person_wearing_turban_dark_skin_tone": "👳🏿",
+ "person_wearing_turban_light_skin_tone": "👳🏻",
+ "person_wearing_turban_medium-dark_skin_tone": "👳🏾",
+ "person_wearing_turban_medium-light_skin_tone": "👳🏼",
+ "person_wearing_turban_medium_skin_tone": "👳🏽",
+ "petri_dish": "🧫",
+ "pick": "⛏",
+ "pie": "🥧",
+ "pig": "🐷",
+ "pig_face": "🐷",
+ "pig_nose": "🐽",
+ "pile_of_poo": "💩",
+ "pill": "💊",
+ "pinching_hand": "🤏",
+ "pine_decoration": "🎍",
+ "pineapple": "🍍",
+ "ping_pong": "🏓",
+ "pirate_flag": "🏴\u200d☠️",
+ "pistol": "🔫",
+ "pizza": "🍕",
+ "place_of_worship": "🛐",
+ "play_button": "▶",
+ "play_or_pause_button": "⏯",
+ "pleading_face": "🥺",
+ "police_car": "🚓",
+ "police_car_light": "🚨",
+ "police_officer": "👮",
+ "police_officer_dark_skin_tone": "👮🏿",
+ "police_officer_light_skin_tone": "👮🏻",
+ "police_officer_medium-dark_skin_tone": "👮🏾",
+ "police_officer_medium-light_skin_tone": "👮🏼",
+ "police_officer_medium_skin_tone": "👮🏽",
+ "poodle": "🐩",
+ "pool_8_ball": "🎱",
+ "popcorn": "🍿",
+ "post_office": "🏣",
+ "postal_horn": "📯",
+ "postbox": "📮",
+ "pot_of_food": "🍲",
+ "potable_water": "🚰",
+ "potato": "🥔",
+ "poultry_leg": "🍗",
+ "pound_banknote": "💷",
+ "pouting_cat_face": "😾",
+ "pouting_face": "😡",
+ "prayer_beads": "📿",
+ "pregnant_woman": "🤰",
+ "pregnant_woman_dark_skin_tone": "🤰🏿",
+ "pregnant_woman_light_skin_tone": "🤰🏻",
+ "pregnant_woman_medium-dark_skin_tone": "🤰🏾",
+ "pregnant_woman_medium-light_skin_tone": "🤰🏼",
+ "pregnant_woman_medium_skin_tone": "🤰🏽",
+ "pretzel": "🥨",
+ "probing_cane": "🦯",
+ "prince": "🤴",
+ "prince_dark_skin_tone": "🤴🏿",
+ "prince_light_skin_tone": "🤴🏻",
+ "prince_medium-dark_skin_tone": "🤴🏾",
+ "prince_medium-light_skin_tone": "🤴🏼",
+ "prince_medium_skin_tone": "🤴🏽",
+ "princess": "👸",
+ "princess_dark_skin_tone": "👸🏿",
+ "princess_light_skin_tone": "👸🏻",
+ "princess_medium-dark_skin_tone": "👸🏾",
+ "princess_medium-light_skin_tone": "👸🏼",
+ "princess_medium_skin_tone": "👸🏽",
+ "printer": "🖨",
+ "prohibited": "🚫",
+ "purple_circle": "🟣",
+ "purple_heart": "💜",
+ "purple_square": "🟪",
+ "purse": "👛",
+ "pushpin": "📌",
+ "question_mark": "❓",
+ "rabbit": "🐰",
+ "rabbit_face": "🐰",
+ "raccoon": "🦝",
+ "racing_car": "🏎",
+ "radio": "📻",
+ "radio_button": "🔘",
+ "radioactive": "☢",
+ "railway_car": "🚃",
+ "railway_track": "🛤",
+ "rainbow": "🌈",
+ "rainbow_flag": "🏳️\u200d🌈",
+ "raised_back_of_hand": "🤚",
+ "raised_back_of_hand_dark_skin_tone": "🤚🏿",
+ "raised_back_of_hand_light_skin_tone": "🤚🏻",
+ "raised_back_of_hand_medium-dark_skin_tone": "🤚🏾",
+ "raised_back_of_hand_medium-light_skin_tone": "🤚🏼",
+ "raised_back_of_hand_medium_skin_tone": "🤚🏽",
+ "raised_fist": "✊",
+ "raised_fist_dark_skin_tone": "✊🏿",
+ "raised_fist_light_skin_tone": "✊🏻",
+ "raised_fist_medium-dark_skin_tone": "✊🏾",
+ "raised_fist_medium-light_skin_tone": "✊🏼",
+ "raised_fist_medium_skin_tone": "✊🏽",
+ "raised_hand": "✋",
+ "raised_hand_dark_skin_tone": "✋🏿",
+ "raised_hand_light_skin_tone": "✋🏻",
+ "raised_hand_medium-dark_skin_tone": "✋🏾",
+ "raised_hand_medium-light_skin_tone": "✋🏼",
+ "raised_hand_medium_skin_tone": "✋🏽",
+ "raising_hands": "🙌",
+ "raising_hands_dark_skin_tone": "🙌🏿",
+ "raising_hands_light_skin_tone": "🙌🏻",
+ "raising_hands_medium-dark_skin_tone": "🙌🏾",
+ "raising_hands_medium-light_skin_tone": "🙌🏼",
+ "raising_hands_medium_skin_tone": "🙌🏽",
+ "ram": "🐏",
+ "rat": "🐀",
+ "razor": "🪒",
+ "ringed_planet": "🪐",
+ "receipt": "🧾",
+ "record_button": "⏺",
+ "recycling_symbol": "♻",
+ "red_apple": "🍎",
+ "red_circle": "🔴",
+ "red_envelope": "🧧",
+ "red_hair": "🦰",
+ "red-haired_man": "👨\u200d🦰",
+ "red-haired_woman": "👩\u200d🦰",
+ "red_heart": "❤",
+ "red_paper_lantern": "🏮",
+ "red_square": "🟥",
+ "red_triangle_pointed_down": "🔻",
+ "red_triangle_pointed_up": "🔺",
+ "registered": "®",
+ "relieved_face": "😌",
+ "reminder_ribbon": "🎗",
+ "repeat_button": "🔁",
+ "repeat_single_button": "🔂",
+ "rescue_worker’s_helmet": "⛑",
+ "restroom": "🚻",
+ "reverse_button": "◀",
+ "revolving_hearts": "💞",
+ "rhinoceros": "🦏",
+ "ribbon": "🎀",
+ "rice_ball": "🍙",
+ "rice_cracker": "🍘",
+ "right-facing_fist": "🤜",
+ "right-facing_fist_dark_skin_tone": "🤜🏿",
+ "right-facing_fist_light_skin_tone": "🤜🏻",
+ "right-facing_fist_medium-dark_skin_tone": "🤜🏾",
+ "right-facing_fist_medium-light_skin_tone": "🤜🏼",
+ "right-facing_fist_medium_skin_tone": "🤜🏽",
+ "right_anger_bubble": "🗯",
+ "right_arrow": "➡",
+ "right_arrow_curving_down": "⤵",
+ "right_arrow_curving_left": "↩",
+ "right_arrow_curving_up": "⤴",
+ "ring": "💍",
+ "roasted_sweet_potato": "🍠",
+ "robot_face": "🤖",
+ "rocket": "🚀",
+ "roll_of_paper": "🧻",
+ "rolled-up_newspaper": "🗞",
+ "roller_coaster": "🎢",
+ "rolling_on_the_floor_laughing": "🤣",
+ "rooster": "🐓",
+ "rose": "🌹",
+ "rosette": "🏵",
+ "round_pushpin": "📍",
+ "rugby_football": "🏉",
+ "running_shirt": "🎽",
+ "running_shoe": "👟",
+ "sad_but_relieved_face": "😥",
+ "safety_pin": "🧷",
+ "safety_vest": "🦺",
+ "salt": "🧂",
+ "sailboat": "⛵",
+ "sake": "🍶",
+ "sandwich": "🥪",
+ "sari": "🥻",
+ "satellite": "📡",
+ "satellite_antenna": "📡",
+ "sauropod": "🦕",
+ "saxophone": "🎷",
+ "scarf": "🧣",
+ "school": "🏫",
+ "school_backpack": "🎒",
+ "scissors": "✂",
+ "scorpion": "🦂",
+ "scroll": "📜",
+ "seat": "💺",
+ "see-no-evil_monkey": "🙈",
+ "seedling": "🌱",
+ "selfie": "🤳",
+ "selfie_dark_skin_tone": "🤳🏿",
+ "selfie_light_skin_tone": "🤳🏻",
+ "selfie_medium-dark_skin_tone": "🤳🏾",
+ "selfie_medium-light_skin_tone": "🤳🏼",
+ "selfie_medium_skin_tone": "🤳🏽",
+ "service_dog": "🐕\u200d🦺",
+ "seven-thirty": "🕢",
+ "seven_o’clock": "🕖",
+ "shallow_pan_of_food": "🥘",
+ "shamrock": "☘",
+ "shark": "🦈",
+ "shaved_ice": "🍧",
+ "sheaf_of_rice": "🌾",
+ "shield": "🛡",
+ "shinto_shrine": "⛩",
+ "ship": "🚢",
+ "shooting_star": "🌠",
+ "shopping_bags": "🛍",
+ "shopping_cart": "🛒",
+ "shortcake": "🍰",
+ "shorts": "🩳",
+ "shower": "🚿",
+ "shrimp": "🦐",
+ "shuffle_tracks_button": "🔀",
+ "shushing_face": "🤫",
+ "sign_of_the_horns": "🤘",
+ "sign_of_the_horns_dark_skin_tone": "🤘🏿",
+ "sign_of_the_horns_light_skin_tone": "🤘🏻",
+ "sign_of_the_horns_medium-dark_skin_tone": "🤘🏾",
+ "sign_of_the_horns_medium-light_skin_tone": "🤘🏼",
+ "sign_of_the_horns_medium_skin_tone": "🤘🏽",
+ "six-thirty": "🕡",
+ "six_o’clock": "🕕",
+ "skateboard": "🛹",
+ "skier": "⛷",
+ "skis": "🎿",
+ "skull": "💀",
+ "skull_and_crossbones": "☠",
+ "skunk": "🦨",
+ "sled": "🛷",
+ "sleeping_face": "😴",
+ "sleepy_face": "😪",
+ "slightly_frowning_face": "🙁",
+ "slightly_smiling_face": "🙂",
+ "slot_machine": "🎰",
+ "sloth": "🦥",
+ "small_airplane": "🛩",
+ "small_blue_diamond": "🔹",
+ "small_orange_diamond": "🔸",
+ "smiling_cat_face_with_heart-eyes": "😻",
+ "smiling_face": "☺",
+ "smiling_face_with_halo": "😇",
+ "smiling_face_with_3_hearts": "🥰",
+ "smiling_face_with_heart-eyes": "😍",
+ "smiling_face_with_horns": "😈",
+ "smiling_face_with_smiling_eyes": "😊",
+ "smiling_face_with_sunglasses": "😎",
+ "smirking_face": "😏",
+ "snail": "🐌",
+ "snake": "🐍",
+ "sneezing_face": "🤧",
+ "snow-capped_mountain": "🏔",
+ "snowboarder": "🏂",
+ "snowboarder_dark_skin_tone": "🏂🏿",
+ "snowboarder_light_skin_tone": "🏂🏻",
+ "snowboarder_medium-dark_skin_tone": "🏂🏾",
+ "snowboarder_medium-light_skin_tone": "🏂🏼",
+ "snowboarder_medium_skin_tone": "🏂🏽",
+ "snowflake": "❄",
+ "snowman": "☃",
+ "snowman_without_snow": "⛄",
+ "soap": "🧼",
+ "soccer_ball": "⚽",
+ "socks": "🧦",
+ "softball": "🥎",
+ "soft_ice_cream": "🍦",
+ "spade_suit": "♠",
+ "spaghetti": "🍝",
+ "sparkle": "❇",
+ "sparkler": "🎇",
+ "sparkles": "✨",
+ "sparkling_heart": "💖",
+ "speak-no-evil_monkey": "🙊",
+ "speaker_high_volume": "🔊",
+ "speaker_low_volume": "🔈",
+ "speaker_medium_volume": "🔉",
+ "speaking_head": "🗣",
+ "speech_balloon": "💬",
+ "speedboat": "🚤",
+ "spider": "🕷",
+ "spider_web": "🕸",
+ "spiral_calendar": "🗓",
+ "spiral_notepad": "🗒",
+ "spiral_shell": "🐚",
+ "spoon": "🥄",
+ "sponge": "🧽",
+ "sport_utility_vehicle": "🚙",
+ "sports_medal": "🏅",
+ "spouting_whale": "🐳",
+ "squid": "🦑",
+ "squinting_face_with_tongue": "😝",
+ "stadium": "🏟",
+ "star-struck": "🤩",
+ "star_and_crescent": "☪",
+ "star_of_david": "✡",
+ "station": "🚉",
+ "steaming_bowl": "🍜",
+ "stethoscope": "🩺",
+ "stop_button": "⏹",
+ "stop_sign": "🛑",
+ "stopwatch": "⏱",
+ "straight_ruler": "📏",
+ "strawberry": "🍓",
+ "studio_microphone": "🎙",
+ "stuffed_flatbread": "🥙",
+ "sun": "☀",
+ "sun_behind_cloud": "⛅",
+ "sun_behind_large_cloud": "🌥",
+ "sun_behind_rain_cloud": "🌦",
+ "sun_behind_small_cloud": "🌤",
+ "sun_with_face": "🌞",
+ "sunflower": "🌻",
+ "sunglasses": "😎",
+ "sunrise": "🌅",
+ "sunrise_over_mountains": "🌄",
+ "sunset": "🌇",
+ "superhero": "🦸",
+ "supervillain": "🦹",
+ "sushi": "🍣",
+ "suspension_railway": "🚟",
+ "swan": "🦢",
+ "sweat_droplets": "💦",
+ "synagogue": "🕍",
+ "syringe": "💉",
+ "t-shirt": "👕",
+ "taco": "🌮",
+ "takeout_box": "🥡",
+ "tanabata_tree": "🎋",
+ "tangerine": "🍊",
+ "taxi": "🚕",
+ "teacup_without_handle": "🍵",
+ "tear-off_calendar": "📆",
+ "teddy_bear": "🧸",
+ "telephone": "☎",
+ "telephone_receiver": "📞",
+ "telescope": "🔭",
+ "television": "📺",
+ "ten-thirty": "🕥",
+ "ten_o’clock": "🕙",
+ "tennis": "🎾",
+ "tent": "⛺",
+ "test_tube": "🧪",
+ "thermometer": "🌡",
+ "thinking_face": "🤔",
+ "thought_balloon": "💭",
+ "thread": "🧵",
+ "three-thirty": "🕞",
+ "three_o’clock": "🕒",
+ "thumbs_down": "👎",
+ "thumbs_down_dark_skin_tone": "👎🏿",
+ "thumbs_down_light_skin_tone": "👎🏻",
+ "thumbs_down_medium-dark_skin_tone": "👎🏾",
+ "thumbs_down_medium-light_skin_tone": "👎🏼",
+ "thumbs_down_medium_skin_tone": "👎🏽",
+ "thumbs_up": "👍",
+ "thumbs_up_dark_skin_tone": "👍🏿",
+ "thumbs_up_light_skin_tone": "👍🏻",
+ "thumbs_up_medium-dark_skin_tone": "👍🏾",
+ "thumbs_up_medium-light_skin_tone": "👍🏼",
+ "thumbs_up_medium_skin_tone": "👍🏽",
+ "ticket": "🎫",
+ "tiger": "🐯",
+ "tiger_face": "🐯",
+ "timer_clock": "⏲",
+ "tired_face": "😫",
+ "toolbox": "🧰",
+ "toilet": "🚽",
+ "tomato": "🍅",
+ "tongue": "👅",
+ "tooth": "🦷",
+ "top_hat": "🎩",
+ "tornado": "🌪",
+ "trackball": "🖲",
+ "tractor": "🚜",
+ "trade_mark": "™",
+ "train": "🚋",
+ "tram": "🚊",
+ "tram_car": "🚋",
+ "triangular_flag": "🚩",
+ "triangular_ruler": "📐",
+ "trident_emblem": "🔱",
+ "trolleybus": "🚎",
+ "trophy": "🏆",
+ "tropical_drink": "🍹",
+ "tropical_fish": "🐠",
+ "trumpet": "🎺",
+ "tulip": "🌷",
+ "tumbler_glass": "🥃",
+ "turtle": "🐢",
+ "twelve-thirty": "🕧",
+ "twelve_o’clock": "🕛",
+ "two-hump_camel": "🐫",
+ "two-thirty": "🕝",
+ "two_hearts": "💕",
+ "two_men_holding_hands": "👬",
+ "two_o’clock": "🕑",
+ "two_women_holding_hands": "👭",
+ "umbrella": "☂",
+ "umbrella_on_ground": "⛱",
+ "umbrella_with_rain_drops": "☔",
+ "unamused_face": "😒",
+ "unicorn_face": "🦄",
+ "unlocked": "🔓",
+ "up-down_arrow": "↕",
+ "up-left_arrow": "↖",
+ "up-right_arrow": "↗",
+ "up_arrow": "⬆",
+ "upside-down_face": "🙃",
+ "upwards_button": "🔼",
+ "vampire": "🧛",
+ "vampire_dark_skin_tone": "🧛🏿",
+ "vampire_light_skin_tone": "🧛🏻",
+ "vampire_medium-dark_skin_tone": "🧛🏾",
+ "vampire_medium-light_skin_tone": "🧛🏼",
+ "vampire_medium_skin_tone": "🧛🏽",
+ "vertical_traffic_light": "🚦",
+ "vibration_mode": "📳",
+ "victory_hand": "✌",
+ "victory_hand_dark_skin_tone": "✌🏿",
+ "victory_hand_light_skin_tone": "✌🏻",
+ "victory_hand_medium-dark_skin_tone": "✌🏾",
+ "victory_hand_medium-light_skin_tone": "✌🏼",
+ "victory_hand_medium_skin_tone": "✌🏽",
+ "video_camera": "📹",
+ "video_game": "🎮",
+ "videocassette": "📼",
+ "violin": "🎻",
+ "volcano": "🌋",
+ "volleyball": "🏐",
+ "vulcan_salute": "🖖",
+ "vulcan_salute_dark_skin_tone": "🖖🏿",
+ "vulcan_salute_light_skin_tone": "🖖🏻",
+ "vulcan_salute_medium-dark_skin_tone": "🖖🏾",
+ "vulcan_salute_medium-light_skin_tone": "🖖🏼",
+ "vulcan_salute_medium_skin_tone": "🖖🏽",
+ "waffle": "🧇",
+ "waning_crescent_moon": "🌘",
+ "waning_gibbous_moon": "🌖",
+ "warning": "⚠",
+ "wastebasket": "🗑",
+ "watch": "⌚",
+ "water_buffalo": "🐃",
+ "water_closet": "🚾",
+ "water_wave": "🌊",
+ "watermelon": "🍉",
+ "waving_hand": "👋",
+ "waving_hand_dark_skin_tone": "👋🏿",
+ "waving_hand_light_skin_tone": "👋🏻",
+ "waving_hand_medium-dark_skin_tone": "👋🏾",
+ "waving_hand_medium-light_skin_tone": "👋🏼",
+ "waving_hand_medium_skin_tone": "👋🏽",
+ "wavy_dash": "〰",
+ "waxing_crescent_moon": "🌒",
+ "waxing_gibbous_moon": "🌔",
+ "weary_cat_face": "🙀",
+ "weary_face": "😩",
+ "wedding": "💒",
+ "whale": "🐳",
+ "wheel_of_dharma": "☸",
+ "wheelchair_symbol": "♿",
+ "white_circle": "⚪",
+ "white_exclamation_mark": "❕",
+ "white_flag": "🏳",
+ "white_flower": "💮",
+ "white_hair": "🦳",
+ "white-haired_man": "👨\u200d🦳",
+ "white-haired_woman": "👩\u200d🦳",
+ "white_heart": "🤍",
+ "white_heavy_check_mark": "✅",
+ "white_large_square": "⬜",
+ "white_medium-small_square": "◽",
+ "white_medium_square": "◻",
+ "white_medium_star": "⭐",
+ "white_question_mark": "❔",
+ "white_small_square": "▫",
+ "white_square_button": "🔳",
+ "wilted_flower": "🥀",
+ "wind_chime": "🎐",
+ "wind_face": "🌬",
+ "wine_glass": "🍷",
+ "winking_face": "😉",
+ "winking_face_with_tongue": "😜",
+ "wolf_face": "🐺",
+ "woman": "👩",
+ "woman_artist": "👩\u200d🎨",
+ "woman_artist_dark_skin_tone": "👩🏿\u200d🎨",
+ "woman_artist_light_skin_tone": "👩🏻\u200d🎨",
+ "woman_artist_medium-dark_skin_tone": "👩🏾\u200d🎨",
+ "woman_artist_medium-light_skin_tone": "👩🏼\u200d🎨",
+ "woman_artist_medium_skin_tone": "👩🏽\u200d🎨",
+ "woman_astronaut": "👩\u200d🚀",
+ "woman_astronaut_dark_skin_tone": "👩🏿\u200d🚀",
+ "woman_astronaut_light_skin_tone": "👩🏻\u200d🚀",
+ "woman_astronaut_medium-dark_skin_tone": "👩🏾\u200d🚀",
+ "woman_astronaut_medium-light_skin_tone": "👩🏼\u200d🚀",
+ "woman_astronaut_medium_skin_tone": "👩🏽\u200d🚀",
+ "woman_biking": "🚴\u200d♀️",
+ "woman_biking_dark_skin_tone": "🚴🏿\u200d♀️",
+ "woman_biking_light_skin_tone": "🚴🏻\u200d♀️",
+ "woman_biking_medium-dark_skin_tone": "🚴🏾\u200d♀️",
+ "woman_biking_medium-light_skin_tone": "🚴🏼\u200d♀️",
+ "woman_biking_medium_skin_tone": "🚴🏽\u200d♀️",
+ "woman_bouncing_ball": "⛹️\u200d♀️",
+ "woman_bouncing_ball_dark_skin_tone": "⛹🏿\u200d♀️",
+ "woman_bouncing_ball_light_skin_tone": "⛹🏻\u200d♀️",
+ "woman_bouncing_ball_medium-dark_skin_tone": "⛹🏾\u200d♀️",
+ "woman_bouncing_ball_medium-light_skin_tone": "⛹🏼\u200d♀️",
+ "woman_bouncing_ball_medium_skin_tone": "⛹🏽\u200d♀️",
+ "woman_bowing": "🙇\u200d♀️",
+ "woman_bowing_dark_skin_tone": "🙇🏿\u200d♀️",
+ "woman_bowing_light_skin_tone": "🙇🏻\u200d♀️",
+ "woman_bowing_medium-dark_skin_tone": "🙇🏾\u200d♀️",
+ "woman_bowing_medium-light_skin_tone": "🙇🏼\u200d♀️",
+ "woman_bowing_medium_skin_tone": "🙇🏽\u200d♀️",
+ "woman_cartwheeling": "🤸\u200d♀️",
+ "woman_cartwheeling_dark_skin_tone": "🤸🏿\u200d♀️",
+ "woman_cartwheeling_light_skin_tone": "🤸🏻\u200d♀️",
+ "woman_cartwheeling_medium-dark_skin_tone": "🤸🏾\u200d♀️",
+ "woman_cartwheeling_medium-light_skin_tone": "🤸🏼\u200d♀️",
+ "woman_cartwheeling_medium_skin_tone": "🤸🏽\u200d♀️",
+ "woman_climbing": "🧗\u200d♀️",
+ "woman_climbing_dark_skin_tone": "🧗🏿\u200d♀️",
+ "woman_climbing_light_skin_tone": "🧗🏻\u200d♀️",
+ "woman_climbing_medium-dark_skin_tone": "🧗🏾\u200d♀️",
+ "woman_climbing_medium-light_skin_tone": "🧗🏼\u200d♀️",
+ "woman_climbing_medium_skin_tone": "🧗🏽\u200d♀️",
+ "woman_construction_worker": "👷\u200d♀️",
+ "woman_construction_worker_dark_skin_tone": "👷🏿\u200d♀️",
+ "woman_construction_worker_light_skin_tone": "👷🏻\u200d♀️",
+ "woman_construction_worker_medium-dark_skin_tone": "👷🏾\u200d♀️",
+ "woman_construction_worker_medium-light_skin_tone": "👷🏼\u200d♀️",
+ "woman_construction_worker_medium_skin_tone": "👷🏽\u200d♀️",
+ "woman_cook": "👩\u200d🍳",
+ "woman_cook_dark_skin_tone": "👩🏿\u200d🍳",
+ "woman_cook_light_skin_tone": "👩🏻\u200d🍳",
+ "woman_cook_medium-dark_skin_tone": "👩🏾\u200d🍳",
+ "woman_cook_medium-light_skin_tone": "👩🏼\u200d🍳",
+ "woman_cook_medium_skin_tone": "👩🏽\u200d🍳",
+ "woman_dancing": "💃",
+ "woman_dancing_dark_skin_tone": "💃🏿",
+ "woman_dancing_light_skin_tone": "💃🏻",
+ "woman_dancing_medium-dark_skin_tone": "💃🏾",
+ "woman_dancing_medium-light_skin_tone": "💃🏼",
+ "woman_dancing_medium_skin_tone": "💃🏽",
+ "woman_dark_skin_tone": "👩🏿",
+ "woman_detective": "🕵️\u200d♀️",
+ "woman_detective_dark_skin_tone": "🕵🏿\u200d♀️",
+ "woman_detective_light_skin_tone": "🕵🏻\u200d♀️",
+ "woman_detective_medium-dark_skin_tone": "🕵🏾\u200d♀️",
+ "woman_detective_medium-light_skin_tone": "🕵🏼\u200d♀️",
+ "woman_detective_medium_skin_tone": "🕵🏽\u200d♀️",
+ "woman_elf": "🧝\u200d♀️",
+ "woman_elf_dark_skin_tone": "🧝🏿\u200d♀️",
+ "woman_elf_light_skin_tone": "🧝🏻\u200d♀️",
+ "woman_elf_medium-dark_skin_tone": "🧝🏾\u200d♀️",
+ "woman_elf_medium-light_skin_tone": "🧝🏼\u200d♀️",
+ "woman_elf_medium_skin_tone": "🧝🏽\u200d♀️",
+ "woman_facepalming": "🤦\u200d♀️",
+ "woman_facepalming_dark_skin_tone": "🤦🏿\u200d♀️",
+ "woman_facepalming_light_skin_tone": "🤦🏻\u200d♀️",
+ "woman_facepalming_medium-dark_skin_tone": "🤦🏾\u200d♀️",
+ "woman_facepalming_medium-light_skin_tone": "🤦🏼\u200d♀️",
+ "woman_facepalming_medium_skin_tone": "🤦🏽\u200d♀️",
+ "woman_factory_worker": "👩\u200d🏭",
+ "woman_factory_worker_dark_skin_tone": "👩🏿\u200d🏭",
+ "woman_factory_worker_light_skin_tone": "👩🏻\u200d🏭",
+ "woman_factory_worker_medium-dark_skin_tone": "👩🏾\u200d🏭",
+ "woman_factory_worker_medium-light_skin_tone": "👩🏼\u200d🏭",
+ "woman_factory_worker_medium_skin_tone": "👩🏽\u200d🏭",
+ "woman_fairy": "🧚\u200d♀️",
+ "woman_fairy_dark_skin_tone": "🧚🏿\u200d♀️",
+ "woman_fairy_light_skin_tone": "🧚🏻\u200d♀️",
+ "woman_fairy_medium-dark_skin_tone": "🧚🏾\u200d♀️",
+ "woman_fairy_medium-light_skin_tone": "🧚🏼\u200d♀️",
+ "woman_fairy_medium_skin_tone": "🧚🏽\u200d♀️",
+ "woman_farmer": "👩\u200d🌾",
+ "woman_farmer_dark_skin_tone": "👩🏿\u200d🌾",
+ "woman_farmer_light_skin_tone": "👩🏻\u200d🌾",
+ "woman_farmer_medium-dark_skin_tone": "👩🏾\u200d🌾",
+ "woman_farmer_medium-light_skin_tone": "👩🏼\u200d🌾",
+ "woman_farmer_medium_skin_tone": "👩🏽\u200d🌾",
+ "woman_firefighter": "👩\u200d🚒",
+ "woman_firefighter_dark_skin_tone": "👩🏿\u200d🚒",
+ "woman_firefighter_light_skin_tone": "👩🏻\u200d🚒",
+ "woman_firefighter_medium-dark_skin_tone": "👩🏾\u200d🚒",
+ "woman_firefighter_medium-light_skin_tone": "👩🏼\u200d🚒",
+ "woman_firefighter_medium_skin_tone": "👩🏽\u200d🚒",
+ "woman_frowning": "🙍\u200d♀️",
+ "woman_frowning_dark_skin_tone": "🙍🏿\u200d♀️",
+ "woman_frowning_light_skin_tone": "🙍🏻\u200d♀️",
+ "woman_frowning_medium-dark_skin_tone": "🙍🏾\u200d♀️",
+ "woman_frowning_medium-light_skin_tone": "🙍🏼\u200d♀️",
+ "woman_frowning_medium_skin_tone": "🙍🏽\u200d♀️",
+ "woman_genie": "🧞\u200d♀️",
+ "woman_gesturing_no": "🙅\u200d♀️",
+ "woman_gesturing_no_dark_skin_tone": "🙅🏿\u200d♀️",
+ "woman_gesturing_no_light_skin_tone": "🙅🏻\u200d♀️",
+ "woman_gesturing_no_medium-dark_skin_tone": "🙅🏾\u200d♀️",
+ "woman_gesturing_no_medium-light_skin_tone": "🙅🏼\u200d♀️",
+ "woman_gesturing_no_medium_skin_tone": "🙅🏽\u200d♀️",
+ "woman_gesturing_ok": "🙆\u200d♀️",
+ "woman_gesturing_ok_dark_skin_tone": "🙆🏿\u200d♀️",
+ "woman_gesturing_ok_light_skin_tone": "🙆🏻\u200d♀️",
+ "woman_gesturing_ok_medium-dark_skin_tone": "🙆🏾\u200d♀️",
+ "woman_gesturing_ok_medium-light_skin_tone": "🙆🏼\u200d♀️",
+ "woman_gesturing_ok_medium_skin_tone": "🙆🏽\u200d♀️",
+ "woman_getting_haircut": "💇\u200d♀️",
+ "woman_getting_haircut_dark_skin_tone": "💇🏿\u200d♀️",
+ "woman_getting_haircut_light_skin_tone": "💇🏻\u200d♀️",
+ "woman_getting_haircut_medium-dark_skin_tone": "💇🏾\u200d♀️",
+ "woman_getting_haircut_medium-light_skin_tone": "💇🏼\u200d♀️",
+ "woman_getting_haircut_medium_skin_tone": "💇🏽\u200d♀️",
+ "woman_getting_massage": "💆\u200d♀️",
+ "woman_getting_massage_dark_skin_tone": "💆🏿\u200d♀️",
+ "woman_getting_massage_light_skin_tone": "💆🏻\u200d♀️",
+ "woman_getting_massage_medium-dark_skin_tone": "💆🏾\u200d♀️",
+ "woman_getting_massage_medium-light_skin_tone": "💆🏼\u200d♀️",
+ "woman_getting_massage_medium_skin_tone": "💆🏽\u200d♀️",
+ "woman_golfing": "🏌️\u200d♀️",
+ "woman_golfing_dark_skin_tone": "🏌🏿\u200d♀️",
+ "woman_golfing_light_skin_tone": "🏌🏻\u200d♀️",
+ "woman_golfing_medium-dark_skin_tone": "🏌🏾\u200d♀️",
+ "woman_golfing_medium-light_skin_tone": "🏌🏼\u200d♀️",
+ "woman_golfing_medium_skin_tone": "🏌🏽\u200d♀️",
+ "woman_guard": "💂\u200d♀️",
+ "woman_guard_dark_skin_tone": "💂🏿\u200d♀️",
+ "woman_guard_light_skin_tone": "💂🏻\u200d♀️",
+ "woman_guard_medium-dark_skin_tone": "💂🏾\u200d♀️",
+ "woman_guard_medium-light_skin_tone": "💂🏼\u200d♀️",
+ "woman_guard_medium_skin_tone": "💂🏽\u200d♀️",
+ "woman_health_worker": "👩\u200d⚕️",
+ "woman_health_worker_dark_skin_tone": "👩🏿\u200d⚕️",
+ "woman_health_worker_light_skin_tone": "👩🏻\u200d⚕️",
+ "woman_health_worker_medium-dark_skin_tone": "👩🏾\u200d⚕️",
+ "woman_health_worker_medium-light_skin_tone": "👩🏼\u200d⚕️",
+ "woman_health_worker_medium_skin_tone": "👩🏽\u200d⚕️",
+ "woman_in_lotus_position": "🧘\u200d♀️",
+ "woman_in_lotus_position_dark_skin_tone": "🧘🏿\u200d♀️",
+ "woman_in_lotus_position_light_skin_tone": "🧘🏻\u200d♀️",
+ "woman_in_lotus_position_medium-dark_skin_tone": "🧘🏾\u200d♀️",
+ "woman_in_lotus_position_medium-light_skin_tone": "🧘🏼\u200d♀️",
+ "woman_in_lotus_position_medium_skin_tone": "🧘🏽\u200d♀️",
+ "woman_in_manual_wheelchair": "👩\u200d🦽",
+ "woman_in_motorized_wheelchair": "👩\u200d🦼",
+ "woman_in_steamy_room": "🧖\u200d♀️",
+ "woman_in_steamy_room_dark_skin_tone": "🧖🏿\u200d♀️",
+ "woman_in_steamy_room_light_skin_tone": "🧖🏻\u200d♀️",
+ "woman_in_steamy_room_medium-dark_skin_tone": "🧖🏾\u200d♀️",
+ "woman_in_steamy_room_medium-light_skin_tone": "🧖🏼\u200d♀️",
+ "woman_in_steamy_room_medium_skin_tone": "🧖🏽\u200d♀️",
+ "woman_judge": "👩\u200d⚖️",
+ "woman_judge_dark_skin_tone": "👩🏿\u200d⚖️",
+ "woman_judge_light_skin_tone": "👩🏻\u200d⚖️",
+ "woman_judge_medium-dark_skin_tone": "👩🏾\u200d⚖️",
+ "woman_judge_medium-light_skin_tone": "👩🏼\u200d⚖️",
+ "woman_judge_medium_skin_tone": "👩🏽\u200d⚖️",
+ "woman_juggling": "🤹\u200d♀️",
+ "woman_juggling_dark_skin_tone": "🤹🏿\u200d♀️",
+ "woman_juggling_light_skin_tone": "🤹🏻\u200d♀️",
+ "woman_juggling_medium-dark_skin_tone": "🤹🏾\u200d♀️",
+ "woman_juggling_medium-light_skin_tone": "🤹🏼\u200d♀️",
+ "woman_juggling_medium_skin_tone": "🤹🏽\u200d♀️",
+ "woman_lifting_weights": "🏋️\u200d♀️",
+ "woman_lifting_weights_dark_skin_tone": "🏋🏿\u200d♀️",
+ "woman_lifting_weights_light_skin_tone": "🏋🏻\u200d♀️",
+ "woman_lifting_weights_medium-dark_skin_tone": "🏋🏾\u200d♀️",
+ "woman_lifting_weights_medium-light_skin_tone": "🏋🏼\u200d♀️",
+ "woman_lifting_weights_medium_skin_tone": "🏋🏽\u200d♀️",
+ "woman_light_skin_tone": "👩🏻",
+ "woman_mage": "🧙\u200d♀️",
+ "woman_mage_dark_skin_tone": "🧙🏿\u200d♀️",
+ "woman_mage_light_skin_tone": "🧙🏻\u200d♀️",
+ "woman_mage_medium-dark_skin_tone": "🧙🏾\u200d♀️",
+ "woman_mage_medium-light_skin_tone": "🧙🏼\u200d♀️",
+ "woman_mage_medium_skin_tone": "🧙🏽\u200d♀️",
+ "woman_mechanic": "👩\u200d🔧",
+ "woman_mechanic_dark_skin_tone": "👩🏿\u200d🔧",
+ "woman_mechanic_light_skin_tone": "👩🏻\u200d🔧",
+ "woman_mechanic_medium-dark_skin_tone": "👩🏾\u200d🔧",
+ "woman_mechanic_medium-light_skin_tone": "👩🏼\u200d🔧",
+ "woman_mechanic_medium_skin_tone": "👩🏽\u200d🔧",
+ "woman_medium-dark_skin_tone": "👩🏾",
+ "woman_medium-light_skin_tone": "👩🏼",
+ "woman_medium_skin_tone": "👩🏽",
+ "woman_mountain_biking": "🚵\u200d♀️",
+ "woman_mountain_biking_dark_skin_tone": "🚵🏿\u200d♀️",
+ "woman_mountain_biking_light_skin_tone": "🚵🏻\u200d♀️",
+ "woman_mountain_biking_medium-dark_skin_tone": "🚵🏾\u200d♀️",
+ "woman_mountain_biking_medium-light_skin_tone": "🚵🏼\u200d♀️",
+ "woman_mountain_biking_medium_skin_tone": "🚵🏽\u200d♀️",
+ "woman_office_worker": "👩\u200d💼",
+ "woman_office_worker_dark_skin_tone": "👩🏿\u200d💼",
+ "woman_office_worker_light_skin_tone": "👩🏻\u200d💼",
+ "woman_office_worker_medium-dark_skin_tone": "👩🏾\u200d💼",
+ "woman_office_worker_medium-light_skin_tone": "👩🏼\u200d💼",
+ "woman_office_worker_medium_skin_tone": "👩🏽\u200d💼",
+ "woman_pilot": "👩\u200d✈️",
+ "woman_pilot_dark_skin_tone": "👩🏿\u200d✈️",
+ "woman_pilot_light_skin_tone": "👩🏻\u200d✈️",
+ "woman_pilot_medium-dark_skin_tone": "👩🏾\u200d✈️",
+ "woman_pilot_medium-light_skin_tone": "👩🏼\u200d✈️",
+ "woman_pilot_medium_skin_tone": "👩🏽\u200d✈️",
+ "woman_playing_handball": "🤾\u200d♀️",
+ "woman_playing_handball_dark_skin_tone": "🤾🏿\u200d♀️",
+ "woman_playing_handball_light_skin_tone": "🤾🏻\u200d♀️",
+ "woman_playing_handball_medium-dark_skin_tone": "🤾🏾\u200d♀️",
+ "woman_playing_handball_medium-light_skin_tone": "🤾🏼\u200d♀️",
+ "woman_playing_handball_medium_skin_tone": "🤾🏽\u200d♀️",
+ "woman_playing_water_polo": "🤽\u200d♀️",
+ "woman_playing_water_polo_dark_skin_tone": "🤽🏿\u200d♀️",
+ "woman_playing_water_polo_light_skin_tone": "🤽🏻\u200d♀️",
+ "woman_playing_water_polo_medium-dark_skin_tone": "🤽🏾\u200d♀️",
+ "woman_playing_water_polo_medium-light_skin_tone": "🤽🏼\u200d♀️",
+ "woman_playing_water_polo_medium_skin_tone": "🤽🏽\u200d♀️",
+ "woman_police_officer": "👮\u200d♀️",
+ "woman_police_officer_dark_skin_tone": "👮🏿\u200d♀️",
+ "woman_police_officer_light_skin_tone": "👮🏻\u200d♀️",
+ "woman_police_officer_medium-dark_skin_tone": "👮🏾\u200d♀️",
+ "woman_police_officer_medium-light_skin_tone": "👮🏼\u200d♀️",
+ "woman_police_officer_medium_skin_tone": "👮🏽\u200d♀️",
+ "woman_pouting": "🙎\u200d♀️",
+ "woman_pouting_dark_skin_tone": "🙎🏿\u200d♀️",
+ "woman_pouting_light_skin_tone": "🙎🏻\u200d♀️",
+ "woman_pouting_medium-dark_skin_tone": "🙎🏾\u200d♀️",
+ "woman_pouting_medium-light_skin_tone": "🙎🏼\u200d♀️",
+ "woman_pouting_medium_skin_tone": "🙎🏽\u200d♀️",
+ "woman_raising_hand": "🙋\u200d♀️",
+ "woman_raising_hand_dark_skin_tone": "🙋🏿\u200d♀️",
+ "woman_raising_hand_light_skin_tone": "🙋🏻\u200d♀️",
+ "woman_raising_hand_medium-dark_skin_tone": "🙋🏾\u200d♀️",
+ "woman_raising_hand_medium-light_skin_tone": "🙋🏼\u200d♀️",
+ "woman_raising_hand_medium_skin_tone": "🙋🏽\u200d♀️",
+ "woman_rowing_boat": "🚣\u200d♀️",
+ "woman_rowing_boat_dark_skin_tone": "🚣🏿\u200d♀️",
+ "woman_rowing_boat_light_skin_tone": "🚣🏻\u200d♀️",
+ "woman_rowing_boat_medium-dark_skin_tone": "🚣🏾\u200d♀️",
+ "woman_rowing_boat_medium-light_skin_tone": "🚣🏼\u200d♀️",
+ "woman_rowing_boat_medium_skin_tone": "🚣🏽\u200d♀️",
+ "woman_running": "🏃\u200d♀️",
+ "woman_running_dark_skin_tone": "🏃🏿\u200d♀️",
+ "woman_running_light_skin_tone": "🏃🏻\u200d♀️",
+ "woman_running_medium-dark_skin_tone": "🏃🏾\u200d♀️",
+ "woman_running_medium-light_skin_tone": "🏃🏼\u200d♀️",
+ "woman_running_medium_skin_tone": "🏃🏽\u200d♀️",
+ "woman_scientist": "👩\u200d🔬",
+ "woman_scientist_dark_skin_tone": "👩🏿\u200d🔬",
+ "woman_scientist_light_skin_tone": "👩🏻\u200d🔬",
+ "woman_scientist_medium-dark_skin_tone": "👩🏾\u200d🔬",
+ "woman_scientist_medium-light_skin_tone": "👩🏼\u200d🔬",
+ "woman_scientist_medium_skin_tone": "👩🏽\u200d🔬",
+ "woman_shrugging": "🤷\u200d♀️",
+ "woman_shrugging_dark_skin_tone": "🤷🏿\u200d♀️",
+ "woman_shrugging_light_skin_tone": "🤷🏻\u200d♀️",
+ "woman_shrugging_medium-dark_skin_tone": "🤷🏾\u200d♀️",
+ "woman_shrugging_medium-light_skin_tone": "🤷🏼\u200d♀️",
+ "woman_shrugging_medium_skin_tone": "🤷🏽\u200d♀️",
+ "woman_singer": "👩\u200d🎤",
+ "woman_singer_dark_skin_tone": "👩🏿\u200d🎤",
+ "woman_singer_light_skin_tone": "👩🏻\u200d🎤",
+ "woman_singer_medium-dark_skin_tone": "👩🏾\u200d🎤",
+ "woman_singer_medium-light_skin_tone": "👩🏼\u200d🎤",
+ "woman_singer_medium_skin_tone": "👩🏽\u200d🎤",
+ "woman_student": "👩\u200d🎓",
+ "woman_student_dark_skin_tone": "👩🏿\u200d🎓",
+ "woman_student_light_skin_tone": "👩🏻\u200d🎓",
+ "woman_student_medium-dark_skin_tone": "👩🏾\u200d🎓",
+ "woman_student_medium-light_skin_tone": "👩🏼\u200d🎓",
+ "woman_student_medium_skin_tone": "👩🏽\u200d🎓",
+ "woman_surfing": "🏄\u200d♀️",
+ "woman_surfing_dark_skin_tone": "🏄🏿\u200d♀️",
+ "woman_surfing_light_skin_tone": "🏄🏻\u200d♀️",
+ "woman_surfing_medium-dark_skin_tone": "🏄🏾\u200d♀️",
+ "woman_surfing_medium-light_skin_tone": "🏄🏼\u200d♀️",
+ "woman_surfing_medium_skin_tone": "🏄🏽\u200d♀️",
+ "woman_swimming": "🏊\u200d♀️",
+ "woman_swimming_dark_skin_tone": "🏊🏿\u200d♀️",
+ "woman_swimming_light_skin_tone": "🏊🏻\u200d♀️",
+ "woman_swimming_medium-dark_skin_tone": "🏊🏾\u200d♀️",
+ "woman_swimming_medium-light_skin_tone": "🏊🏼\u200d♀️",
+ "woman_swimming_medium_skin_tone": "🏊🏽\u200d♀️",
+ "woman_teacher": "👩\u200d🏫",
+ "woman_teacher_dark_skin_tone": "👩🏿\u200d🏫",
+ "woman_teacher_light_skin_tone": "👩🏻\u200d🏫",
+ "woman_teacher_medium-dark_skin_tone": "👩🏾\u200d🏫",
+ "woman_teacher_medium-light_skin_tone": "👩🏼\u200d🏫",
+ "woman_teacher_medium_skin_tone": "👩🏽\u200d🏫",
+ "woman_technologist": "👩\u200d💻",
+ "woman_technologist_dark_skin_tone": "👩🏿\u200d💻",
+ "woman_technologist_light_skin_tone": "👩🏻\u200d💻",
+ "woman_technologist_medium-dark_skin_tone": "👩🏾\u200d💻",
+ "woman_technologist_medium-light_skin_tone": "👩🏼\u200d💻",
+ "woman_technologist_medium_skin_tone": "👩🏽\u200d💻",
+ "woman_tipping_hand": "💁\u200d♀️",
+ "woman_tipping_hand_dark_skin_tone": "💁🏿\u200d♀️",
+ "woman_tipping_hand_light_skin_tone": "💁🏻\u200d♀️",
+ "woman_tipping_hand_medium-dark_skin_tone": "💁🏾\u200d♀️",
+ "woman_tipping_hand_medium-light_skin_tone": "💁🏼\u200d♀️",
+ "woman_tipping_hand_medium_skin_tone": "💁🏽\u200d♀️",
+ "woman_vampire": "🧛\u200d♀️",
+ "woman_vampire_dark_skin_tone": "🧛🏿\u200d♀️",
+ "woman_vampire_light_skin_tone": "🧛🏻\u200d♀️",
+ "woman_vampire_medium-dark_skin_tone": "🧛🏾\u200d♀️",
+ "woman_vampire_medium-light_skin_tone": "🧛🏼\u200d♀️",
+ "woman_vampire_medium_skin_tone": "🧛🏽\u200d♀️",
+ "woman_walking": "🚶\u200d♀️",
+ "woman_walking_dark_skin_tone": "🚶🏿\u200d♀️",
+ "woman_walking_light_skin_tone": "🚶🏻\u200d♀️",
+ "woman_walking_medium-dark_skin_tone": "🚶🏾\u200d♀️",
+ "woman_walking_medium-light_skin_tone": "🚶🏼\u200d♀️",
+ "woman_walking_medium_skin_tone": "🚶🏽\u200d♀️",
+ "woman_wearing_turban": "👳\u200d♀️",
+ "woman_wearing_turban_dark_skin_tone": "👳🏿\u200d♀️",
+ "woman_wearing_turban_light_skin_tone": "👳🏻\u200d♀️",
+ "woman_wearing_turban_medium-dark_skin_tone": "👳🏾\u200d♀️",
+ "woman_wearing_turban_medium-light_skin_tone": "👳🏼\u200d♀️",
+ "woman_wearing_turban_medium_skin_tone": "👳🏽\u200d♀️",
+ "woman_with_headscarf": "🧕",
+ "woman_with_headscarf_dark_skin_tone": "🧕🏿",
+ "woman_with_headscarf_light_skin_tone": "🧕🏻",
+ "woman_with_headscarf_medium-dark_skin_tone": "🧕🏾",
+ "woman_with_headscarf_medium-light_skin_tone": "🧕🏼",
+ "woman_with_headscarf_medium_skin_tone": "🧕🏽",
+ "woman_with_probing_cane": "👩\u200d🦯",
+ "woman_zombie": "🧟\u200d♀️",
+ "woman’s_boot": "👢",
+ "woman’s_clothes": "👚",
+ "woman’s_hat": "👒",
+ "woman’s_sandal": "👡",
+ "women_with_bunny_ears": "👯\u200d♀️",
+ "women_wrestling": "🤼\u200d♀️",
+ "women’s_room": "🚺",
+ "woozy_face": "🥴",
+ "world_map": "🗺",
+ "worried_face": "😟",
+ "wrapped_gift": "🎁",
+ "wrench": "🔧",
+ "writing_hand": "✍",
+ "writing_hand_dark_skin_tone": "✍🏿",
+ "writing_hand_light_skin_tone": "✍🏻",
+ "writing_hand_medium-dark_skin_tone": "✍🏾",
+ "writing_hand_medium-light_skin_tone": "✍🏼",
+ "writing_hand_medium_skin_tone": "✍🏽",
+ "yarn": "🧶",
+ "yawning_face": "🥱",
+ "yellow_circle": "🟡",
+ "yellow_heart": "💛",
+ "yellow_square": "🟨",
+ "yen_banknote": "💴",
+ "yo-yo": "🪀",
+ "yin_yang": "☯",
+ "zany_face": "🤪",
+ "zebra": "🦓",
+ "zipper-mouth_face": "🤐",
+ "zombie": "🧟",
+ "zzz": "💤",
+ "åland_islands": "🇦🇽",
+ "keycap_asterisk": "*⃣",
+ "keycap_digit_eight": "8⃣",
+ "keycap_digit_five": "5⃣",
+ "keycap_digit_four": "4⃣",
+ "keycap_digit_nine": "9⃣",
+ "keycap_digit_one": "1⃣",
+ "keycap_digit_seven": "7⃣",
+ "keycap_digit_six": "6⃣",
+ "keycap_digit_three": "3⃣",
+ "keycap_digit_two": "2⃣",
+ "keycap_digit_zero": "0⃣",
+ "keycap_number_sign": "#⃣",
+ "light_skin_tone": "🏻",
+ "medium_light_skin_tone": "🏼",
+ "medium_skin_tone": "🏽",
+ "medium_dark_skin_tone": "🏾",
+ "dark_skin_tone": "🏿",
+ "regional_indicator_symbol_letter_a": "🇦",
+ "regional_indicator_symbol_letter_b": "🇧",
+ "regional_indicator_symbol_letter_c": "🇨",
+ "regional_indicator_symbol_letter_d": "🇩",
+ "regional_indicator_symbol_letter_e": "🇪",
+ "regional_indicator_symbol_letter_f": "🇫",
+ "regional_indicator_symbol_letter_g": "🇬",
+ "regional_indicator_symbol_letter_h": "🇭",
+ "regional_indicator_symbol_letter_i": "🇮",
+ "regional_indicator_symbol_letter_j": "🇯",
+ "regional_indicator_symbol_letter_k": "🇰",
+ "regional_indicator_symbol_letter_l": "🇱",
+ "regional_indicator_symbol_letter_m": "🇲",
+ "regional_indicator_symbol_letter_n": "🇳",
+ "regional_indicator_symbol_letter_o": "🇴",
+ "regional_indicator_symbol_letter_p": "🇵",
+ "regional_indicator_symbol_letter_q": "🇶",
+ "regional_indicator_symbol_letter_r": "🇷",
+ "regional_indicator_symbol_letter_s": "🇸",
+ "regional_indicator_symbol_letter_t": "🇹",
+ "regional_indicator_symbol_letter_u": "🇺",
+ "regional_indicator_symbol_letter_v": "🇻",
+ "regional_indicator_symbol_letter_w": "🇼",
+ "regional_indicator_symbol_letter_x": "🇽",
+ "regional_indicator_symbol_letter_y": "🇾",
+ "regional_indicator_symbol_letter_z": "🇿",
+ "airplane_arriving": "🛬",
+ "space_invader": "👾",
+ "football": "🏈",
+ "anger": "💢",
+ "angry": "😠",
+ "anguished": "😧",
+ "signal_strength": "📶",
+ "arrows_counterclockwise": "🔄",
+ "arrow_heading_down": "⤵",
+ "arrow_heading_up": "⤴",
+ "art": "🎨",
+ "astonished": "😲",
+ "athletic_shoe": "👟",
+ "atm": "🏧",
+ "car": "🚗",
+ "red_car": "🚗",
+ "angel": "👼",
+ "back": "🔙",
+ "badminton_racquet_and_shuttlecock": "🏸",
+ "dollar": "💵",
+ "euro": "💶",
+ "pound": "💷",
+ "yen": "💴",
+ "barber": "💈",
+ "bath": "🛀",
+ "bear": "🐻",
+ "heartbeat": "💓",
+ "beer": "🍺",
+ "no_bell": "🔕",
+ "bento": "🍱",
+ "bike": "🚲",
+ "bicyclist": "🚴",
+ "8ball": "🎱",
+ "biohazard_sign": "☣",
+ "birthday": "🎂",
+ "black_circle_for_record": "⏺",
+ "clubs": "♣",
+ "diamonds": "♦",
+ "arrow_double_down": "⏬",
+ "hearts": "♥",
+ "rewind": "⏪",
+ "black_left__pointing_double_triangle_with_vertical_bar": "⏮",
+ "arrow_backward": "◀",
+ "black_medium_small_square": "◾",
+ "question": "❓",
+ "fast_forward": "⏩",
+ "black_right__pointing_double_triangle_with_vertical_bar": "⏭",
+ "arrow_forward": "▶",
+ "black_right__pointing_triangle_with_double_vertical_bar": "⏯",
+ "arrow_right": "➡",
+ "spades": "♠",
+ "black_square_for_stop": "⏹",
+ "sunny": "☀",
+ "phone": "☎",
+ "recycle": "♻",
+ "arrow_double_up": "⏫",
+ "busstop": "🚏",
+ "date": "📅",
+ "flags": "🎏",
+ "cat2": "🐈",
+ "joy_cat": "😹",
+ "smirk_cat": "😼",
+ "chart_with_downwards_trend": "📉",
+ "chart_with_upwards_trend": "📈",
+ "chart": "💹",
+ "mega": "📣",
+ "checkered_flag": "🏁",
+ "accept": "🉑",
+ "ideograph_advantage": "🉐",
+ "congratulations": "㊗",
+ "secret": "㊙",
+ "m": "Ⓜ",
+ "city_sunset": "🌆",
+ "clapper": "🎬",
+ "clap": "👏",
+ "beers": "🍻",
+ "clock830": "🕣",
+ "clock8": "🕗",
+ "clock1130": "🕦",
+ "clock11": "🕚",
+ "clock530": "🕠",
+ "clock5": "🕔",
+ "clock430": "🕟",
+ "clock4": "🕓",
+ "clock930": "🕤",
+ "clock9": "🕘",
+ "clock130": "🕜",
+ "clock1": "🕐",
+ "clock730": "🕢",
+ "clock7": "🕖",
+ "clock630": "🕡",
+ "clock6": "🕕",
+ "clock1030": "🕥",
+ "clock10": "🕙",
+ "clock330": "🕞",
+ "clock3": "🕒",
+ "clock1230": "🕧",
+ "clock12": "🕛",
+ "clock230": "🕝",
+ "clock2": "🕑",
+ "arrows_clockwise": "🔃",
+ "repeat": "🔁",
+ "repeat_one": "🔂",
+ "closed_lock_with_key": "🔐",
+ "mailbox_closed": "📪",
+ "mailbox": "📫",
+ "cloud_with_tornado": "🌪",
+ "cocktail": "🍸",
+ "boom": "💥",
+ "compression": "🗜",
+ "confounded": "😖",
+ "confused": "😕",
+ "rice": "🍚",
+ "cow2": "🐄",
+ "cricket_bat_and_ball": "🏏",
+ "x": "❌",
+ "cry": "😢",
+ "curry": "🍛",
+ "dagger_knife": "🗡",
+ "dancer": "💃",
+ "dark_sunglasses": "🕶",
+ "dash": "💨",
+ "truck": "🚚",
+ "derelict_house_building": "🏚",
+ "diamond_shape_with_a_dot_inside": "💠",
+ "dart": "🎯",
+ "disappointed_relieved": "😥",
+ "disappointed": "😞",
+ "do_not_litter": "🚯",
+ "dog2": "🐕",
+ "flipper": "🐬",
+ "loop": "➿",
+ "bangbang": "‼",
+ "double_vertical_bar": "⏸",
+ "dove_of_peace": "🕊",
+ "small_red_triangle_down": "🔻",
+ "arrow_down_small": "🔽",
+ "arrow_down": "⬇",
+ "dromedary_camel": "🐪",
+ "e__mail": "📧",
+ "corn": "🌽",
+ "ear_of_rice": "🌾",
+ "earth_americas": "🌎",
+ "earth_asia": "🌏",
+ "earth_africa": "🌍",
+ "eight_pointed_black_star": "✴",
+ "eight_spoked_asterisk": "✳",
+ "eject_symbol": "⏏",
+ "bulb": "💡",
+ "emoji_modifier_fitzpatrick_type__1__2": "🏻",
+ "emoji_modifier_fitzpatrick_type__3": "🏼",
+ "emoji_modifier_fitzpatrick_type__4": "🏽",
+ "emoji_modifier_fitzpatrick_type__5": "🏾",
+ "emoji_modifier_fitzpatrick_type__6": "🏿",
+ "end": "🔚",
+ "email": "✉",
+ "european_castle": "🏰",
+ "european_post_office": "🏤",
+ "interrobang": "⁉",
+ "expressionless": "😑",
+ "eyeglasses": "👓",
+ "massage": "💆",
+ "yum": "😋",
+ "scream": "😱",
+ "kissing_heart": "😘",
+ "sweat": "😓",
+ "face_with_head__bandage": "🤕",
+ "triumph": "😤",
+ "mask": "😷",
+ "no_good": "🙅",
+ "ok_woman": "🙆",
+ "open_mouth": "😮",
+ "cold_sweat": "😰",
+ "stuck_out_tongue": "😛",
+ "stuck_out_tongue_closed_eyes": "😝",
+ "stuck_out_tongue_winking_eye": "😜",
+ "joy": "😂",
+ "no_mouth": "😶",
+ "santa": "🎅",
+ "fax": "📠",
+ "fearful": "😨",
+ "field_hockey_stick_and_ball": "🏑",
+ "first_quarter_moon_with_face": "🌛",
+ "fish_cake": "🍥",
+ "fishing_pole_and_fish": "🎣",
+ "facepunch": "👊",
+ "punch": "👊",
+ "flag_for_afghanistan": "🇦🇫",
+ "flag_for_albania": "🇦🇱",
+ "flag_for_algeria": "🇩🇿",
+ "flag_for_american_samoa": "🇦🇸",
+ "flag_for_andorra": "🇦🇩",
+ "flag_for_angola": "🇦🇴",
+ "flag_for_anguilla": "🇦🇮",
+ "flag_for_antarctica": "🇦🇶",
+ "flag_for_antigua_&_barbuda": "🇦🇬",
+ "flag_for_argentina": "🇦🇷",
+ "flag_for_armenia": "🇦🇲",
+ "flag_for_aruba": "🇦🇼",
+ "flag_for_ascension_island": "🇦🇨",
+ "flag_for_australia": "🇦🇺",
+ "flag_for_austria": "🇦🇹",
+ "flag_for_azerbaijan": "🇦🇿",
+ "flag_for_bahamas": "🇧🇸",
+ "flag_for_bahrain": "🇧🇭",
+ "flag_for_bangladesh": "🇧🇩",
+ "flag_for_barbados": "🇧🇧",
+ "flag_for_belarus": "🇧🇾",
+ "flag_for_belgium": "🇧🇪",
+ "flag_for_belize": "🇧🇿",
+ "flag_for_benin": "🇧🇯",
+ "flag_for_bermuda": "🇧🇲",
+ "flag_for_bhutan": "🇧🇹",
+ "flag_for_bolivia": "🇧🇴",
+ "flag_for_bosnia_&_herzegovina": "🇧🇦",
+ "flag_for_botswana": "🇧🇼",
+ "flag_for_bouvet_island": "🇧🇻",
+ "flag_for_brazil": "🇧🇷",
+ "flag_for_british_indian_ocean_territory": "🇮🇴",
+ "flag_for_british_virgin_islands": "🇻🇬",
+ "flag_for_brunei": "🇧🇳",
+ "flag_for_bulgaria": "🇧🇬",
+ "flag_for_burkina_faso": "🇧🇫",
+ "flag_for_burundi": "🇧🇮",
+ "flag_for_cambodia": "🇰🇭",
+ "flag_for_cameroon": "🇨🇲",
+ "flag_for_canada": "🇨🇦",
+ "flag_for_canary_islands": "🇮🇨",
+ "flag_for_cape_verde": "🇨🇻",
+ "flag_for_caribbean_netherlands": "🇧🇶",
+ "flag_for_cayman_islands": "🇰🇾",
+ "flag_for_central_african_republic": "🇨🇫",
+ "flag_for_ceuta_&_melilla": "🇪🇦",
+ "flag_for_chad": "🇹🇩",
+ "flag_for_chile": "🇨🇱",
+ "flag_for_china": "🇨🇳",
+ "flag_for_christmas_island": "🇨🇽",
+ "flag_for_clipperton_island": "🇨🇵",
+ "flag_for_cocos__islands": "🇨🇨",
+ "flag_for_colombia": "🇨🇴",
+ "flag_for_comoros": "🇰🇲",
+ "flag_for_congo____brazzaville": "🇨🇬",
+ "flag_for_congo____kinshasa": "🇨🇩",
+ "flag_for_cook_islands": "🇨🇰",
+ "flag_for_costa_rica": "🇨🇷",
+ "flag_for_croatia": "🇭🇷",
+ "flag_for_cuba": "🇨🇺",
+ "flag_for_curaçao": "🇨🇼",
+ "flag_for_cyprus": "🇨🇾",
+ "flag_for_czech_republic": "🇨🇿",
+ "flag_for_côte_d’ivoire": "🇨🇮",
+ "flag_for_denmark": "🇩🇰",
+ "flag_for_diego_garcia": "🇩🇬",
+ "flag_for_djibouti": "🇩🇯",
+ "flag_for_dominica": "🇩🇲",
+ "flag_for_dominican_republic": "🇩🇴",
+ "flag_for_ecuador": "🇪🇨",
+ "flag_for_egypt": "🇪🇬",
+ "flag_for_el_salvador": "🇸🇻",
+ "flag_for_equatorial_guinea": "🇬🇶",
+ "flag_for_eritrea": "🇪🇷",
+ "flag_for_estonia": "🇪🇪",
+ "flag_for_ethiopia": "🇪🇹",
+ "flag_for_european_union": "🇪🇺",
+ "flag_for_falkland_islands": "🇫🇰",
+ "flag_for_faroe_islands": "🇫🇴",
+ "flag_for_fiji": "🇫🇯",
+ "flag_for_finland": "🇫🇮",
+ "flag_for_france": "🇫🇷",
+ "flag_for_french_guiana": "🇬🇫",
+ "flag_for_french_polynesia": "🇵🇫",
+ "flag_for_french_southern_territories": "🇹🇫",
+ "flag_for_gabon": "🇬🇦",
+ "flag_for_gambia": "🇬🇲",
+ "flag_for_georgia": "🇬🇪",
+ "flag_for_germany": "🇩🇪",
+ "flag_for_ghana": "🇬🇭",
+ "flag_for_gibraltar": "🇬🇮",
+ "flag_for_greece": "🇬🇷",
+ "flag_for_greenland": "🇬🇱",
+ "flag_for_grenada": "🇬🇩",
+ "flag_for_guadeloupe": "🇬🇵",
+ "flag_for_guam": "🇬🇺",
+ "flag_for_guatemala": "🇬🇹",
+ "flag_for_guernsey": "🇬🇬",
+ "flag_for_guinea": "🇬🇳",
+ "flag_for_guinea__bissau": "🇬🇼",
+ "flag_for_guyana": "🇬🇾",
+ "flag_for_haiti": "🇭🇹",
+ "flag_for_heard_&_mcdonald_islands": "🇭🇲",
+ "flag_for_honduras": "🇭🇳",
+ "flag_for_hong_kong": "🇭🇰",
+ "flag_for_hungary": "🇭🇺",
+ "flag_for_iceland": "🇮🇸",
+ "flag_for_india": "🇮🇳",
+ "flag_for_indonesia": "🇮🇩",
+ "flag_for_iran": "🇮🇷",
+ "flag_for_iraq": "🇮🇶",
+ "flag_for_ireland": "🇮🇪",
+ "flag_for_isle_of_man": "🇮🇲",
+ "flag_for_israel": "🇮🇱",
+ "flag_for_italy": "🇮🇹",
+ "flag_for_jamaica": "🇯🇲",
+ "flag_for_japan": "🇯🇵",
+ "flag_for_jersey": "🇯🇪",
+ "flag_for_jordan": "🇯🇴",
+ "flag_for_kazakhstan": "🇰🇿",
+ "flag_for_kenya": "🇰🇪",
+ "flag_for_kiribati": "🇰🇮",
+ "flag_for_kosovo": "🇽🇰",
+ "flag_for_kuwait": "🇰🇼",
+ "flag_for_kyrgyzstan": "🇰🇬",
+ "flag_for_laos": "🇱🇦",
+ "flag_for_latvia": "🇱🇻",
+ "flag_for_lebanon": "🇱🇧",
+ "flag_for_lesotho": "🇱🇸",
+ "flag_for_liberia": "🇱🇷",
+ "flag_for_libya": "🇱🇾",
+ "flag_for_liechtenstein": "🇱🇮",
+ "flag_for_lithuania": "🇱🇹",
+ "flag_for_luxembourg": "🇱🇺",
+ "flag_for_macau": "🇲🇴",
+ "flag_for_macedonia": "🇲🇰",
+ "flag_for_madagascar": "🇲🇬",
+ "flag_for_malawi": "🇲🇼",
+ "flag_for_malaysia": "🇲🇾",
+ "flag_for_maldives": "🇲🇻",
+ "flag_for_mali": "🇲🇱",
+ "flag_for_malta": "🇲🇹",
+ "flag_for_marshall_islands": "🇲🇭",
+ "flag_for_martinique": "🇲🇶",
+ "flag_for_mauritania": "🇲🇷",
+ "flag_for_mauritius": "🇲🇺",
+ "flag_for_mayotte": "🇾🇹",
+ "flag_for_mexico": "🇲🇽",
+ "flag_for_micronesia": "🇫🇲",
+ "flag_for_moldova": "🇲🇩",
+ "flag_for_monaco": "🇲🇨",
+ "flag_for_mongolia": "🇲🇳",
+ "flag_for_montenegro": "🇲🇪",
+ "flag_for_montserrat": "🇲🇸",
+ "flag_for_morocco": "🇲🇦",
+ "flag_for_mozambique": "🇲🇿",
+ "flag_for_myanmar": "🇲🇲",
+ "flag_for_namibia": "🇳🇦",
+ "flag_for_nauru": "🇳🇷",
+ "flag_for_nepal": "🇳🇵",
+ "flag_for_netherlands": "🇳🇱",
+ "flag_for_new_caledonia": "🇳🇨",
+ "flag_for_new_zealand": "🇳🇿",
+ "flag_for_nicaragua": "🇳🇮",
+ "flag_for_niger": "🇳🇪",
+ "flag_for_nigeria": "🇳🇬",
+ "flag_for_niue": "🇳🇺",
+ "flag_for_norfolk_island": "🇳🇫",
+ "flag_for_north_korea": "🇰🇵",
+ "flag_for_northern_mariana_islands": "🇲🇵",
+ "flag_for_norway": "🇳🇴",
+ "flag_for_oman": "🇴🇲",
+ "flag_for_pakistan": "🇵🇰",
+ "flag_for_palau": "🇵🇼",
+ "flag_for_palestinian_territories": "🇵🇸",
+ "flag_for_panama": "🇵🇦",
+ "flag_for_papua_new_guinea": "🇵🇬",
+ "flag_for_paraguay": "🇵🇾",
+ "flag_for_peru": "🇵🇪",
+ "flag_for_philippines": "🇵🇭",
+ "flag_for_pitcairn_islands": "🇵🇳",
+ "flag_for_poland": "🇵🇱",
+ "flag_for_portugal": "🇵🇹",
+ "flag_for_puerto_rico": "🇵🇷",
+ "flag_for_qatar": "🇶🇦",
+ "flag_for_romania": "🇷🇴",
+ "flag_for_russia": "🇷🇺",
+ "flag_for_rwanda": "🇷🇼",
+ "flag_for_réunion": "🇷🇪",
+ "flag_for_samoa": "🇼🇸",
+ "flag_for_san_marino": "🇸🇲",
+ "flag_for_saudi_arabia": "🇸🇦",
+ "flag_for_senegal": "🇸🇳",
+ "flag_for_serbia": "🇷🇸",
+ "flag_for_seychelles": "🇸🇨",
+ "flag_for_sierra_leone": "🇸🇱",
+ "flag_for_singapore": "🇸🇬",
+ "flag_for_sint_maarten": "🇸🇽",
+ "flag_for_slovakia": "🇸🇰",
+ "flag_for_slovenia": "🇸🇮",
+ "flag_for_solomon_islands": "🇸🇧",
+ "flag_for_somalia": "🇸🇴",
+ "flag_for_south_africa": "🇿🇦",
+ "flag_for_south_georgia_&_south_sandwich_islands": "🇬🇸",
+ "flag_for_south_korea": "🇰🇷",
+ "flag_for_south_sudan": "🇸🇸",
+ "flag_for_spain": "🇪🇸",
+ "flag_for_sri_lanka": "🇱🇰",
+ "flag_for_st._barthélemy": "🇧🇱",
+ "flag_for_st._helena": "🇸🇭",
+ "flag_for_st._kitts_&_nevis": "🇰🇳",
+ "flag_for_st._lucia": "🇱🇨",
+ "flag_for_st._martin": "🇲🇫",
+ "flag_for_st._pierre_&_miquelon": "🇵🇲",
+ "flag_for_st._vincent_&_grenadines": "🇻🇨",
+ "flag_for_sudan": "🇸🇩",
+ "flag_for_suriname": "🇸🇷",
+ "flag_for_svalbard_&_jan_mayen": "🇸🇯",
+ "flag_for_swaziland": "🇸🇿",
+ "flag_for_sweden": "🇸🇪",
+ "flag_for_switzerland": "🇨🇭",
+ "flag_for_syria": "🇸🇾",
+ "flag_for_são_tomé_&_príncipe": "🇸🇹",
+ "flag_for_taiwan": "🇹🇼",
+ "flag_for_tajikistan": "🇹🇯",
+ "flag_for_tanzania": "🇹🇿",
+ "flag_for_thailand": "🇹🇭",
+ "flag_for_timor__leste": "🇹🇱",
+ "flag_for_togo": "🇹🇬",
+ "flag_for_tokelau": "🇹🇰",
+ "flag_for_tonga": "🇹🇴",
+ "flag_for_trinidad_&_tobago": "🇹🇹",
+ "flag_for_tristan_da_cunha": "🇹🇦",
+ "flag_for_tunisia": "🇹🇳",
+ "flag_for_turkey": "🇹🇷",
+ "flag_for_turkmenistan": "🇹🇲",
+ "flag_for_turks_&_caicos_islands": "🇹🇨",
+ "flag_for_tuvalu": "🇹🇻",
+ "flag_for_u.s._outlying_islands": "🇺🇲",
+ "flag_for_u.s._virgin_islands": "🇻🇮",
+ "flag_for_uganda": "🇺🇬",
+ "flag_for_ukraine": "🇺🇦",
+ "flag_for_united_arab_emirates": "🇦🇪",
+ "flag_for_united_kingdom": "🇬🇧",
+ "flag_for_united_states": "🇺🇸",
+ "flag_for_uruguay": "🇺🇾",
+ "flag_for_uzbekistan": "🇺🇿",
+ "flag_for_vanuatu": "🇻🇺",
+ "flag_for_vatican_city": "🇻🇦",
+ "flag_for_venezuela": "🇻🇪",
+ "flag_for_vietnam": "🇻🇳",
+ "flag_for_wallis_&_futuna": "🇼🇫",
+ "flag_for_western_sahara": "🇪🇭",
+ "flag_for_yemen": "🇾🇪",
+ "flag_for_zambia": "🇿🇲",
+ "flag_for_zimbabwe": "🇿🇼",
+ "flag_for_åland_islands": "🇦🇽",
+ "golf": "⛳",
+ "fleur__de__lis": "⚜",
+ "muscle": "💪",
+ "flushed": "😳",
+ "frame_with_picture": "🖼",
+ "fries": "🍟",
+ "frog": "🐸",
+ "hatched_chick": "🐥",
+ "frowning": "😦",
+ "fuelpump": "⛽",
+ "full_moon_with_face": "🌝",
+ "gem": "💎",
+ "star2": "🌟",
+ "golfer": "🏌",
+ "mortar_board": "🎓",
+ "grimacing": "😬",
+ "smile_cat": "😸",
+ "grinning": "😀",
+ "grin": "😁",
+ "heartpulse": "💗",
+ "guardsman": "💂",
+ "haircut": "💇",
+ "hamster": "🐹",
+ "raising_hand": "🙋",
+ "headphones": "🎧",
+ "hear_no_evil": "🙉",
+ "cupid": "💘",
+ "gift_heart": "💝",
+ "heart": "❤",
+ "exclamation": "❗",
+ "heavy_exclamation_mark": "❗",
+ "heavy_heart_exclamation_mark_ornament": "❣",
+ "o": "⭕",
+ "helm_symbol": "⎈",
+ "helmet_with_white_cross": "⛑",
+ "high_heel": "👠",
+ "bullettrain_side": "🚄",
+ "bullettrain_front": "🚅",
+ "high_brightness": "🔆",
+ "zap": "⚡",
+ "hocho": "🔪",
+ "knife": "🔪",
+ "bee": "🐝",
+ "traffic_light": "🚥",
+ "racehorse": "🐎",
+ "coffee": "☕",
+ "hotsprings": "♨",
+ "hourglass": "⌛",
+ "hourglass_flowing_sand": "⏳",
+ "house_buildings": "🏘",
+ "100": "💯",
+ "hushed": "😯",
+ "ice_hockey_stick_and_puck": "🏒",
+ "imp": "👿",
+ "information_desk_person": "💁",
+ "information_source": "ℹ",
+ "capital_abcd": "🔠",
+ "abc": "🔤",
+ "abcd": "🔡",
+ "1234": "🔢",
+ "symbols": "🔣",
+ "izakaya_lantern": "🏮",
+ "lantern": "🏮",
+ "jack_o_lantern": "🎃",
+ "dolls": "🎎",
+ "japanese_goblin": "👺",
+ "japanese_ogre": "👹",
+ "beginner": "🔰",
+ "zero": "0️⃣",
+ "one": "1️⃣",
+ "ten": "🔟",
+ "two": "2️⃣",
+ "three": "3️⃣",
+ "four": "4️⃣",
+ "five": "5️⃣",
+ "six": "6️⃣",
+ "seven": "7️⃣",
+ "eight": "8️⃣",
+ "nine": "9️⃣",
+ "couplekiss": "💏",
+ "kissing_cat": "😽",
+ "kissing": "😗",
+ "kissing_closed_eyes": "😚",
+ "kissing_smiling_eyes": "😙",
+ "beetle": "🐞",
+ "large_blue_circle": "🔵",
+ "last_quarter_moon_with_face": "🌜",
+ "leaves": "🍃",
+ "mag": "🔍",
+ "left_right_arrow": "↔",
+ "leftwards_arrow_with_hook": "↩",
+ "arrow_left": "⬅",
+ "lock": "🔒",
+ "lock_with_ink_pen": "🔏",
+ "sob": "😭",
+ "low_brightness": "🔅",
+ "lower_left_ballpoint_pen": "🖊",
+ "lower_left_crayon": "🖍",
+ "lower_left_fountain_pen": "🖋",
+ "lower_left_paintbrush": "🖌",
+ "mahjong": "🀄",
+ "couple": "👫",
+ "man_in_business_suit_levitating": "🕴",
+ "man_with_gua_pi_mao": "👲",
+ "man_with_turban": "👳",
+ "mans_shoe": "👞",
+ "shoe": "👞",
+ "menorah_with_nine_branches": "🕎",
+ "mens": "🚹",
+ "minidisc": "💽",
+ "iphone": "📱",
+ "calling": "📲",
+ "money__mouth_face": "🤑",
+ "moneybag": "💰",
+ "rice_scene": "🎑",
+ "mountain_bicyclist": "🚵",
+ "mouse2": "🐁",
+ "lips": "👄",
+ "moyai": "🗿",
+ "notes": "🎶",
+ "nail_care": "💅",
+ "ab": "🆎",
+ "negative_squared_cross_mark": "❎",
+ "a": "🅰",
+ "b": "🅱",
+ "o2": "🅾",
+ "parking": "🅿",
+ "new_moon_with_face": "🌚",
+ "no_entry_sign": "🚫",
+ "underage": "🔞",
+ "non__potable_water": "🚱",
+ "arrow_upper_right": "↗",
+ "arrow_upper_left": "↖",
+ "office": "🏢",
+ "older_man": "👴",
+ "older_woman": "👵",
+ "om_symbol": "🕉",
+ "on": "🔛",
+ "book": "📖",
+ "unlock": "🔓",
+ "mailbox_with_no_mail": "📭",
+ "mailbox_with_mail": "📬",
+ "cd": "💿",
+ "tada": "🎉",
+ "feet": "🐾",
+ "walking": "🚶",
+ "pencil2": "✏",
+ "pensive": "😔",
+ "persevere": "😣",
+ "bow": "🙇",
+ "raised_hands": "🙌",
+ "person_with_ball": "⛹",
+ "person_with_blond_hair": "👱",
+ "pray": "🙏",
+ "person_with_pouting_face": "🙎",
+ "computer": "💻",
+ "pig2": "🐖",
+ "hankey": "💩",
+ "poop": "💩",
+ "shit": "💩",
+ "bamboo": "🎍",
+ "gun": "🔫",
+ "black_joker": "🃏",
+ "rotating_light": "🚨",
+ "cop": "👮",
+ "stew": "🍲",
+ "pouch": "👝",
+ "pouting_cat": "😾",
+ "rage": "😡",
+ "put_litter_in_its_place": "🚮",
+ "rabbit2": "🐇",
+ "racing_motorcycle": "🏍",
+ "radioactive_sign": "☢",
+ "fist": "✊",
+ "hand": "✋",
+ "raised_hand_with_fingers_splayed": "🖐",
+ "raised_hand_with_part_between_middle_and_ring_fingers": "🖖",
+ "blue_car": "🚙",
+ "apple": "🍎",
+ "relieved": "😌",
+ "reversed_hand_with_middle_finger_extended": "🖕",
+ "mag_right": "🔎",
+ "arrow_right_hook": "↪",
+ "sweet_potato": "🍠",
+ "robot": "🤖",
+ "rolled__up_newspaper": "🗞",
+ "rowboat": "🚣",
+ "runner": "🏃",
+ "running": "🏃",
+ "running_shirt_with_sash": "🎽",
+ "boat": "⛵",
+ "scales": "⚖",
+ "school_satchel": "🎒",
+ "scorpius": "♏",
+ "see_no_evil": "🙈",
+ "sheep": "🐑",
+ "stars": "🌠",
+ "cake": "🍰",
+ "six_pointed_star": "🔯",
+ "ski": "🎿",
+ "sleeping_accommodation": "🛌",
+ "sleeping": "😴",
+ "sleepy": "😪",
+ "sleuth_or_spy": "🕵",
+ "heart_eyes_cat": "😻",
+ "smiley_cat": "😺",
+ "innocent": "😇",
+ "heart_eyes": "😍",
+ "smiling_imp": "😈",
+ "smiley": "😃",
+ "sweat_smile": "😅",
+ "smile": "😄",
+ "laughing": "😆",
+ "satisfied": "😆",
+ "blush": "😊",
+ "smirk": "😏",
+ "smoking": "🚬",
+ "snow_capped_mountain": "🏔",
+ "soccer": "⚽",
+ "icecream": "🍦",
+ "soon": "🔜",
+ "arrow_lower_right": "↘",
+ "arrow_lower_left": "↙",
+ "speak_no_evil": "🙊",
+ "speaker": "🔈",
+ "mute": "🔇",
+ "sound": "🔉",
+ "loud_sound": "🔊",
+ "speaking_head_in_silhouette": "🗣",
+ "spiral_calendar_pad": "🗓",
+ "spiral_note_pad": "🗒",
+ "shell": "🐚",
+ "sweat_drops": "💦",
+ "u5272": "🈹",
+ "u5408": "🈴",
+ "u55b6": "🈺",
+ "u6307": "🈯",
+ "u6708": "🈷",
+ "u6709": "🈶",
+ "u6e80": "🈵",
+ "u7121": "🈚",
+ "u7533": "🈸",
+ "u7981": "🈲",
+ "u7a7a": "🈳",
+ "cl": "🆑",
+ "cool": "🆒",
+ "free": "🆓",
+ "id": "🆔",
+ "koko": "🈁",
+ "sa": "🈂",
+ "new": "🆕",
+ "ng": "🆖",
+ "ok": "🆗",
+ "sos": "🆘",
+ "up": "🆙",
+ "vs": "🆚",
+ "steam_locomotive": "🚂",
+ "ramen": "🍜",
+ "partly_sunny": "⛅",
+ "city_sunrise": "🌇",
+ "surfer": "🏄",
+ "swimmer": "🏊",
+ "shirt": "👕",
+ "tshirt": "👕",
+ "table_tennis_paddle_and_ball": "🏓",
+ "tea": "🍵",
+ "tv": "📺",
+ "three_button_mouse": "🖱",
+ "+1": "👍",
+ "thumbsup": "👍",
+ "__1": "👎",
+ "-1": "👎",
+ "thumbsdown": "👎",
+ "thunder_cloud_and_rain": "⛈",
+ "tiger2": "🐅",
+ "tophat": "🎩",
+ "top": "🔝",
+ "tm": "™",
+ "train2": "🚆",
+ "triangular_flag_on_post": "🚩",
+ "trident": "🔱",
+ "twisted_rightwards_arrows": "🔀",
+ "unamused": "😒",
+ "small_red_triangle": "🔺",
+ "arrow_up_small": "🔼",
+ "arrow_up_down": "↕",
+ "upside__down_face": "🙃",
+ "arrow_up": "⬆",
+ "v": "✌",
+ "vhs": "📼",
+ "wc": "🚾",
+ "ocean": "🌊",
+ "waving_black_flag": "🏴",
+ "wave": "👋",
+ "waving_white_flag": "🏳",
+ "moon": "🌔",
+ "scream_cat": "🙀",
+ "weary": "😩",
+ "weight_lifter": "🏋",
+ "whale2": "🐋",
+ "wheelchair": "♿",
+ "point_down": "👇",
+ "grey_exclamation": "❕",
+ "white_frowning_face": "☹",
+ "white_check_mark": "✅",
+ "point_left": "👈",
+ "white_medium_small_square": "◽",
+ "star": "⭐",
+ "grey_question": "❔",
+ "point_right": "👉",
+ "relaxed": "☺",
+ "white_sun_behind_cloud": "🌥",
+ "white_sun_behind_cloud_with_rain": "🌦",
+ "white_sun_with_small_cloud": "🌤",
+ "point_up_2": "👆",
+ "point_up": "☝",
+ "wind_blowing_face": "🌬",
+ "wink": "😉",
+ "wolf": "🐺",
+ "dancers": "👯",
+ "boot": "👢",
+ "womans_clothes": "👚",
+ "womans_hat": "👒",
+ "sandal": "👡",
+ "womens": "🚺",
+ "worried": "😟",
+ "gift": "🎁",
+ "zipper__mouth_face": "🤐",
+ "regional_indicator_a": "🇦",
+ "regional_indicator_b": "🇧",
+ "regional_indicator_c": "🇨",
+ "regional_indicator_d": "🇩",
+ "regional_indicator_e": "🇪",
+ "regional_indicator_f": "🇫",
+ "regional_indicator_g": "🇬",
+ "regional_indicator_h": "🇭",
+ "regional_indicator_i": "🇮",
+ "regional_indicator_j": "🇯",
+ "regional_indicator_k": "🇰",
+ "regional_indicator_l": "🇱",
+ "regional_indicator_m": "🇲",
+ "regional_indicator_n": "🇳",
+ "regional_indicator_o": "🇴",
+ "regional_indicator_p": "🇵",
+ "regional_indicator_q": "🇶",
+ "regional_indicator_r": "🇷",
+ "regional_indicator_s": "🇸",
+ "regional_indicator_t": "🇹",
+ "regional_indicator_u": "🇺",
+ "regional_indicator_v": "🇻",
+ "regional_indicator_w": "🇼",
+ "regional_indicator_x": "🇽",
+ "regional_indicator_y": "🇾",
+ "regional_indicator_z": "🇿",
+}
diff --git a/src/pip/_vendor/rich/_emoji_replace.py b/src/pip/_vendor/rich/_emoji_replace.py
new file mode 100644
index 000000000..bb2cafa18
--- /dev/null
+++ b/src/pip/_vendor/rich/_emoji_replace.py
@@ -0,0 +1,32 @@
+from typing import Callable, Match, Optional
+import re
+
+from ._emoji_codes import EMOJI
+
+
+_ReStringMatch = Match[str] # regex match object
+_ReSubCallable = Callable[[_ReStringMatch], str] # Callable invoked by re.sub
+_EmojiSubMethod = Callable[[_ReSubCallable, str], str] # Sub method of a compiled re
+
+
+def _emoji_replace(
+ text: str,
+ default_variant: Optional[str] = None,
+ _emoji_sub: _EmojiSubMethod = re.compile(r"(:(\S*?)(?:(?:\-)(emoji|text))?:)").sub,
+) -> str:
+ """Replace emoji code in text."""
+ get_emoji = EMOJI.__getitem__
+ variants = {"text": "\uFE0E", "emoji": "\uFE0F"}
+ get_variant = variants.get
+ default_variant_code = variants.get(default_variant, "") if default_variant else ""
+
+ def do_replace(match: Match[str]) -> str:
+ emoji_code, emoji_name, variant = match.groups()
+ try:
+ return get_emoji(emoji_name.lower()) + get_variant(
+ variant, default_variant_code
+ )
+ except KeyError:
+ return emoji_code
+
+ return _emoji_sub(do_replace, text)
diff --git a/src/pip/_vendor/rich/_extension.py b/src/pip/_vendor/rich/_extension.py
new file mode 100644
index 000000000..cbd6da9be
--- /dev/null
+++ b/src/pip/_vendor/rich/_extension.py
@@ -0,0 +1,10 @@
+from typing import Any
+
+
+def load_ipython_extension(ip: Any) -> None: # pragma: no cover
+ # prevent circular import
+ from pip._vendor.rich.pretty import install
+ from pip._vendor.rich.traceback import install as tr_install
+
+ install()
+ tr_install()
diff --git a/src/pip/_vendor/rich/_inspect.py b/src/pip/_vendor/rich/_inspect.py
new file mode 100644
index 000000000..262695b1c
--- /dev/null
+++ b/src/pip/_vendor/rich/_inspect.py
@@ -0,0 +1,210 @@
+from __future__ import absolute_import
+
+from inspect import cleandoc, getdoc, getfile, isclass, ismodule, signature
+from typing import Any, Iterable, Optional, Tuple
+
+from .console import RenderableType, Group
+from .highlighter import ReprHighlighter
+from .jupyter import JupyterMixin
+from .panel import Panel
+from .pretty import Pretty
+from .table import Table
+from .text import Text, TextType
+
+
+def _first_paragraph(doc: str) -> str:
+ """Get the first paragraph from a docstring."""
+ paragraph, _, _ = doc.partition("\n\n")
+ return paragraph
+
+
+def _reformat_doc(doc: str) -> str:
+ """Reformat docstring."""
+ doc = cleandoc(doc).strip()
+ return doc
+
+
+class Inspect(JupyterMixin):
+ """A renderable to inspect any Python Object.
+
+ Args:
+ obj (Any): An object to inspect.
+ title (str, optional): Title to display over inspect result, or None use type. Defaults to None.
+ help (bool, optional): Show full help text rather than just first paragraph. Defaults to False.
+ methods (bool, optional): Enable inspection of callables. Defaults to False.
+ docs (bool, optional): Also render doc strings. Defaults to True.
+ private (bool, optional): Show private attributes (beginning with underscore). Defaults to False.
+ dunder (bool, optional): Show attributes starting with double underscore. Defaults to False.
+ sort (bool, optional): Sort attributes alphabetically. Defaults to True.
+ all (bool, optional): Show all attributes. Defaults to False.
+ value (bool, optional): Pretty print value of object. Defaults to True.
+ """
+
+ def __init__(
+ self,
+ obj: Any,
+ *,
+ title: Optional[TextType] = None,
+ help: bool = False,
+ methods: bool = False,
+ docs: bool = True,
+ private: bool = False,
+ dunder: bool = False,
+ sort: bool = True,
+ all: bool = True,
+ value: bool = True,
+ ) -> None:
+ self.highlighter = ReprHighlighter()
+ self.obj = obj
+ self.title = title or self._make_title(obj)
+ if all:
+ methods = private = dunder = True
+ self.help = help
+ self.methods = methods
+ self.docs = docs or help
+ self.private = private or dunder
+ self.dunder = dunder
+ self.sort = sort
+ self.value = value
+
+ def _make_title(self, obj: Any) -> Text:
+ """Make a default title."""
+ title_str = (
+ str(obj)
+ if (isclass(obj) or callable(obj) or ismodule(obj))
+ else str(type(obj))
+ )
+ title_text = self.highlighter(title_str)
+ return title_text
+
+ def __rich__(self) -> Panel:
+ return Panel.fit(
+ Group(*self._render()),
+ title=self.title,
+ border_style="scope.border",
+ padding=(0, 1),
+ )
+
+ def _get_signature(self, name: str, obj: Any) -> Optional[Text]:
+ """Get a signature for a callable."""
+ try:
+ _signature = str(signature(obj)) + ":"
+ except ValueError:
+ _signature = "(...)"
+ except TypeError:
+ return None
+
+ source_filename: Optional[str] = None
+ try:
+ source_filename = getfile(obj)
+ except TypeError:
+ pass
+
+ callable_name = Text(name, style="inspect.callable")
+ if source_filename:
+ callable_name.stylize(f"link file://{source_filename}")
+ signature_text = self.highlighter(_signature)
+
+ qualname = name or getattr(obj, "__qualname__", name)
+ qual_signature = Text.assemble(
+ ("def ", "inspect.def"), (qualname, "inspect.callable"), signature_text
+ )
+
+ return qual_signature
+
+ def _render(self) -> Iterable[RenderableType]:
+ """Render object."""
+
+ def sort_items(item: Tuple[str, Any]) -> Tuple[bool, str]:
+ key, (_error, value) = item
+ return (callable(value), key.strip("_").lower())
+
+ def safe_getattr(attr_name: str) -> Tuple[Any, Any]:
+ """Get attribute or any exception."""
+ try:
+ return (None, getattr(obj, attr_name))
+ except Exception as error:
+ return (error, None)
+
+ obj = self.obj
+ keys = dir(obj)
+ total_items = len(keys)
+ if not self.dunder:
+ keys = [key for key in keys if not key.startswith("__")]
+ if not self.private:
+ keys = [key for key in keys if not key.startswith("_")]
+ not_shown_count = total_items - len(keys)
+ items = [(key, safe_getattr(key)) for key in keys]
+ if self.sort:
+ items.sort(key=sort_items)
+
+ items_table = Table.grid(padding=(0, 1), expand=False)
+ items_table.add_column(justify="right")
+ add_row = items_table.add_row
+ highlighter = self.highlighter
+
+ if callable(obj):
+ signature = self._get_signature("", obj)
+ if signature is not None:
+ yield signature
+ yield ""
+
+ if self.docs:
+ _doc = getdoc(obj)
+ if _doc is not None:
+ if not self.help:
+ _doc = _first_paragraph(_doc)
+ doc_text = Text(_reformat_doc(_doc), style="inspect.help")
+ doc_text = highlighter(doc_text)
+ yield doc_text
+ yield ""
+
+ if self.value and not (isclass(obj) or callable(obj) or ismodule(obj)):
+ yield Panel(
+ Pretty(obj, indent_guides=True, max_length=10, max_string=60),
+ border_style="inspect.value.border",
+ )
+ yield ""
+
+ for key, (error, value) in items:
+ key_text = Text.assemble(
+ (
+ key,
+ "inspect.attr.dunder" if key.startswith("__") else "inspect.attr",
+ ),
+ (" =", "inspect.equals"),
+ )
+ if error is not None:
+ warning = key_text.copy()
+ warning.stylize("inspect.error")
+ add_row(warning, highlighter(repr(error)))
+ continue
+
+ if callable(value):
+ if not self.methods:
+ continue
+
+ _signature_text = self._get_signature(key, value)
+ if _signature_text is None:
+ add_row(key_text, Pretty(value, highlighter=highlighter))
+ else:
+ if self.docs:
+ docs = getdoc(value)
+ if docs is not None:
+ _doc = _reformat_doc(str(docs))
+ if not self.help:
+ _doc = _first_paragraph(_doc)
+ _signature_text.append("\n" if "\n" in _doc else " ")
+ doc = highlighter(_doc)
+ doc.stylize("inspect.doc")
+ _signature_text.append(doc)
+
+ add_row(key_text, _signature_text)
+ else:
+ add_row(key_text, Pretty(value, highlighter=highlighter))
+ if items_table.row_count:
+ yield items_table
+ else:
+ yield Text.from_markup(
+ f"[b cyan]{not_shown_count}[/][i] attribute(s) not shown.[/i] Run [b][magenta]inspect[/]([not b]inspect[/])[/b] for options."
+ )
diff --git a/src/pip/_vendor/rich/_log_render.py b/src/pip/_vendor/rich/_log_render.py
new file mode 100644
index 000000000..fc16c8443
--- /dev/null
+++ b/src/pip/_vendor/rich/_log_render.py
@@ -0,0 +1,94 @@
+from datetime import datetime
+from typing import Iterable, List, Optional, TYPE_CHECKING, Union, Callable
+
+
+from .text import Text, TextType
+
+if TYPE_CHECKING:
+ from .console import Console, ConsoleRenderable, RenderableType
+ from .table import Table
+
+FormatTimeCallable = Callable[[datetime], Text]
+
+
+class LogRender:
+ def __init__(
+ self,
+ show_time: bool = True,
+ show_level: bool = False,
+ show_path: bool = True,
+ time_format: Union[str, FormatTimeCallable] = "[%x %X]",
+ omit_repeated_times: bool = True,
+ level_width: Optional[int] = 8,
+ ) -> None:
+ self.show_time = show_time
+ self.show_level = show_level
+ self.show_path = show_path
+ self.time_format = time_format
+ self.omit_repeated_times = omit_repeated_times
+ self.level_width = level_width
+ self._last_time: Optional[Text] = None
+
+ def __call__(
+ self,
+ console: "Console",
+ renderables: Iterable["ConsoleRenderable"],
+ log_time: Optional[datetime] = None,
+ time_format: Optional[Union[str, FormatTimeCallable]] = None,
+ level: TextType = "",
+ path: Optional[str] = None,
+ line_no: Optional[int] = None,
+ link_path: Optional[str] = None,
+ ) -> "Table":
+ from .containers import Renderables
+ from .table import Table
+
+ output = Table.grid(padding=(0, 1))
+ output.expand = True
+ if self.show_time:
+ output.add_column(style="log.time")
+ if self.show_level:
+ output.add_column(style="log.level", width=self.level_width)
+ output.add_column(ratio=1, style="log.message", overflow="fold")
+ if self.show_path and path:
+ output.add_column(style="log.path")
+ row: List["RenderableType"] = []
+ if self.show_time:
+ log_time = log_time or console.get_datetime()
+ time_format = time_format or self.time_format
+ if callable(time_format):
+ log_time_display = time_format(log_time)
+ else:
+ log_time_display = Text(log_time.strftime(time_format))
+ if log_time_display == self._last_time and self.omit_repeated_times:
+ row.append(Text(" " * len(log_time_display)))
+ else:
+ row.append(log_time_display)
+ self._last_time = log_time_display
+ if self.show_level:
+ row.append(level)
+
+ row.append(Renderables(renderables))
+ if self.show_path and path:
+ path_text = Text()
+ path_text.append(
+ path, style=f"link file://{link_path}" if link_path else ""
+ )
+ if line_no:
+ path_text.append(":")
+ path_text.append(
+ f"{line_no}",
+ style=f"link file://{link_path}#{line_no}" if link_path else "",
+ )
+ row.append(path_text)
+
+ output.add_row(*row)
+ return output
+
+
+if __name__ == "__main__": # pragma: no cover
+ from pip._vendor.rich.console import Console
+
+ c = Console()
+ c.print("[on blue]Hello", justify="right")
+ c.log("[on blue]hello", justify="right")
diff --git a/src/pip/_vendor/rich/_loop.py b/src/pip/_vendor/rich/_loop.py
new file mode 100644
index 000000000..01c6cafbe
--- /dev/null
+++ b/src/pip/_vendor/rich/_loop.py
@@ -0,0 +1,43 @@
+from typing import Iterable, Tuple, TypeVar
+
+T = TypeVar("T")
+
+
+def loop_first(values: Iterable[T]) -> Iterable[Tuple[bool, T]]:
+ """Iterate and generate a tuple with a flag for first value."""
+ iter_values = iter(values)
+ try:
+ value = next(iter_values)
+ except StopIteration:
+ return
+ yield True, value
+ for value in iter_values:
+ yield False, value
+
+
+def loop_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]:
+ """Iterate and generate a tuple with a flag for last value."""
+ iter_values = iter(values)
+ try:
+ previous_value = next(iter_values)
+ except StopIteration:
+ return
+ for value in iter_values:
+ yield False, previous_value
+ previous_value = value
+ yield True, previous_value
+
+
+def loop_first_last(values: Iterable[T]) -> Iterable[Tuple[bool, bool, T]]:
+ """Iterate and generate a tuple with a flag for first and last value."""
+ iter_values = iter(values)
+ try:
+ previous_value = next(iter_values)
+ except StopIteration:
+ return
+ first = True
+ for value in iter_values:
+ yield first, False, previous_value
+ first = False
+ previous_value = value
+ yield first, True, previous_value
diff --git a/src/pip/_vendor/rich/_lru_cache.py b/src/pip/_vendor/rich/_lru_cache.py
new file mode 100644
index 000000000..b7bf2ce1a
--- /dev/null
+++ b/src/pip/_vendor/rich/_lru_cache.py
@@ -0,0 +1,34 @@
+from collections import OrderedDict
+from typing import Dict, Generic, TypeVar
+
+
+CacheKey = TypeVar("CacheKey")
+CacheValue = TypeVar("CacheValue")
+
+
+class LRUCache(Generic[CacheKey, CacheValue], OrderedDict): # type: ignore # https://github.com/python/mypy/issues/6904
+ """
+ A dictionary-like container that stores a given maximum items.
+
+ If an additional item is added when the LRUCache is full, the least
+ recently used key is discarded to make room for the new item.
+
+ """
+
+ def __init__(self, cache_size: int) -> None:
+ self.cache_size = cache_size
+ super(LRUCache, self).__init__()
+
+ def __setitem__(self, key: CacheKey, value: CacheValue) -> None:
+ """Store a new views, potentially discarding an old value."""
+ if key not in self:
+ if len(self) >= self.cache_size:
+ self.popitem(last=False)
+ OrderedDict.__setitem__(self, key, value)
+
+ def __getitem__(self: Dict[CacheKey, CacheValue], key: CacheKey) -> CacheValue:
+ """Gets the item, but also makes it most recent."""
+ value: CacheValue = OrderedDict.__getitem__(self, key)
+ OrderedDict.__delitem__(self, key)
+ OrderedDict.__setitem__(self, key, value)
+ return value
diff --git a/src/pip/_vendor/rich/_palettes.py b/src/pip/_vendor/rich/_palettes.py
new file mode 100644
index 000000000..3c748d33e
--- /dev/null
+++ b/src/pip/_vendor/rich/_palettes.py
@@ -0,0 +1,309 @@
+from .palette import Palette
+
+
+# Taken from https://en.wikipedia.org/wiki/ANSI_escape_code (Windows 10 column)
+WINDOWS_PALETTE = Palette(
+ [
+ (12, 12, 12),
+ (197, 15, 31),
+ (19, 161, 14),
+ (193, 156, 0),
+ (0, 55, 218),
+ (136, 23, 152),
+ (58, 150, 221),
+ (204, 204, 204),
+ (118, 118, 118),
+ (231, 72, 86),
+ (22, 198, 12),
+ (249, 241, 165),
+ (59, 120, 255),
+ (180, 0, 158),
+ (97, 214, 214),
+ (242, 242, 242),
+ ]
+)
+
+# # The standard ansi colors (including bright variants)
+STANDARD_PALETTE = Palette(
+ [
+ (0, 0, 0),
+ (170, 0, 0),
+ (0, 170, 0),
+ (170, 85, 0),
+ (0, 0, 170),
+ (170, 0, 170),
+ (0, 170, 170),
+ (170, 170, 170),
+ (85, 85, 85),
+ (255, 85, 85),
+ (85, 255, 85),
+ (255, 255, 85),
+ (85, 85, 255),
+ (255, 85, 255),
+ (85, 255, 255),
+ (255, 255, 255),
+ ]
+)
+
+
+# The 256 color palette
+EIGHT_BIT_PALETTE = Palette(
+ [
+ (0, 0, 0),
+ (128, 0, 0),
+ (0, 128, 0),
+ (128, 128, 0),
+ (0, 0, 128),
+ (128, 0, 128),
+ (0, 128, 128),
+ (192, 192, 192),
+ (128, 128, 128),
+ (255, 0, 0),
+ (0, 255, 0),
+ (255, 255, 0),
+ (0, 0, 255),
+ (255, 0, 255),
+ (0, 255, 255),
+ (255, 255, 255),
+ (0, 0, 0),
+ (0, 0, 95),
+ (0, 0, 135),
+ (0, 0, 175),
+ (0, 0, 215),
+ (0, 0, 255),
+ (0, 95, 0),
+ (0, 95, 95),
+ (0, 95, 135),
+ (0, 95, 175),
+ (0, 95, 215),
+ (0, 95, 255),
+ (0, 135, 0),
+ (0, 135, 95),
+ (0, 135, 135),
+ (0, 135, 175),
+ (0, 135, 215),
+ (0, 135, 255),
+ (0, 175, 0),
+ (0, 175, 95),
+ (0, 175, 135),
+ (0, 175, 175),
+ (0, 175, 215),
+ (0, 175, 255),
+ (0, 215, 0),
+ (0, 215, 95),
+ (0, 215, 135),
+ (0, 215, 175),
+ (0, 215, 215),
+ (0, 215, 255),
+ (0, 255, 0),
+ (0, 255, 95),
+ (0, 255, 135),
+ (0, 255, 175),
+ (0, 255, 215),
+ (0, 255, 255),
+ (95, 0, 0),
+ (95, 0, 95),
+ (95, 0, 135),
+ (95, 0, 175),
+ (95, 0, 215),
+ (95, 0, 255),
+ (95, 95, 0),
+ (95, 95, 95),
+ (95, 95, 135),
+ (95, 95, 175),
+ (95, 95, 215),
+ (95, 95, 255),
+ (95, 135, 0),
+ (95, 135, 95),
+ (95, 135, 135),
+ (95, 135, 175),
+ (95, 135, 215),
+ (95, 135, 255),
+ (95, 175, 0),
+ (95, 175, 95),
+ (95, 175, 135),
+ (95, 175, 175),
+ (95, 175, 215),
+ (95, 175, 255),
+ (95, 215, 0),
+ (95, 215, 95),
+ (95, 215, 135),
+ (95, 215, 175),
+ (95, 215, 215),
+ (95, 215, 255),
+ (95, 255, 0),
+ (95, 255, 95),
+ (95, 255, 135),
+ (95, 255, 175),
+ (95, 255, 215),
+ (95, 255, 255),
+ (135, 0, 0),
+ (135, 0, 95),
+ (135, 0, 135),
+ (135, 0, 175),
+ (135, 0, 215),
+ (135, 0, 255),
+ (135, 95, 0),
+ (135, 95, 95),
+ (135, 95, 135),
+ (135, 95, 175),
+ (135, 95, 215),
+ (135, 95, 255),
+ (135, 135, 0),
+ (135, 135, 95),
+ (135, 135, 135),
+ (135, 135, 175),
+ (135, 135, 215),
+ (135, 135, 255),
+ (135, 175, 0),
+ (135, 175, 95),
+ (135, 175, 135),
+ (135, 175, 175),
+ (135, 175, 215),
+ (135, 175, 255),
+ (135, 215, 0),
+ (135, 215, 95),
+ (135, 215, 135),
+ (135, 215, 175),
+ (135, 215, 215),
+ (135, 215, 255),
+ (135, 255, 0),
+ (135, 255, 95),
+ (135, 255, 135),
+ (135, 255, 175),
+ (135, 255, 215),
+ (135, 255, 255),
+ (175, 0, 0),
+ (175, 0, 95),
+ (175, 0, 135),
+ (175, 0, 175),
+ (175, 0, 215),
+ (175, 0, 255),
+ (175, 95, 0),
+ (175, 95, 95),
+ (175, 95, 135),
+ (175, 95, 175),
+ (175, 95, 215),
+ (175, 95, 255),
+ (175, 135, 0),
+ (175, 135, 95),
+ (175, 135, 135),
+ (175, 135, 175),
+ (175, 135, 215),
+ (175, 135, 255),
+ (175, 175, 0),
+ (175, 175, 95),
+ (175, 175, 135),
+ (175, 175, 175),
+ (175, 175, 215),
+ (175, 175, 255),
+ (175, 215, 0),
+ (175, 215, 95),
+ (175, 215, 135),
+ (175, 215, 175),
+ (175, 215, 215),
+ (175, 215, 255),
+ (175, 255, 0),
+ (175, 255, 95),
+ (175, 255, 135),
+ (175, 255, 175),
+ (175, 255, 215),
+ (175, 255, 255),
+ (215, 0, 0),
+ (215, 0, 95),
+ (215, 0, 135),
+ (215, 0, 175),
+ (215, 0, 215),
+ (215, 0, 255),
+ (215, 95, 0),
+ (215, 95, 95),
+ (215, 95, 135),
+ (215, 95, 175),
+ (215, 95, 215),
+ (215, 95, 255),
+ (215, 135, 0),
+ (215, 135, 95),
+ (215, 135, 135),
+ (215, 135, 175),
+ (215, 135, 215),
+ (215, 135, 255),
+ (215, 175, 0),
+ (215, 175, 95),
+ (215, 175, 135),
+ (215, 175, 175),
+ (215, 175, 215),
+ (215, 175, 255),
+ (215, 215, 0),
+ (215, 215, 95),
+ (215, 215, 135),
+ (215, 215, 175),
+ (215, 215, 215),
+ (215, 215, 255),
+ (215, 255, 0),
+ (215, 255, 95),
+ (215, 255, 135),
+ (215, 255, 175),
+ (215, 255, 215),
+ (215, 255, 255),
+ (255, 0, 0),
+ (255, 0, 95),
+ (255, 0, 135),
+ (255, 0, 175),
+ (255, 0, 215),
+ (255, 0, 255),
+ (255, 95, 0),
+ (255, 95, 95),
+ (255, 95, 135),
+ (255, 95, 175),
+ (255, 95, 215),
+ (255, 95, 255),
+ (255, 135, 0),
+ (255, 135, 95),
+ (255, 135, 135),
+ (255, 135, 175),
+ (255, 135, 215),
+ (255, 135, 255),
+ (255, 175, 0),
+ (255, 175, 95),
+ (255, 175, 135),
+ (255, 175, 175),
+ (255, 175, 215),
+ (255, 175, 255),
+ (255, 215, 0),
+ (255, 215, 95),
+ (255, 215, 135),
+ (255, 215, 175),
+ (255, 215, 215),
+ (255, 215, 255),
+ (255, 255, 0),
+ (255, 255, 95),
+ (255, 255, 135),
+ (255, 255, 175),
+ (255, 255, 215),
+ (255, 255, 255),
+ (8, 8, 8),
+ (18, 18, 18),
+ (28, 28, 28),
+ (38, 38, 38),
+ (48, 48, 48),
+ (58, 58, 58),
+ (68, 68, 68),
+ (78, 78, 78),
+ (88, 88, 88),
+ (98, 98, 98),
+ (108, 108, 108),
+ (118, 118, 118),
+ (128, 128, 128),
+ (138, 138, 138),
+ (148, 148, 148),
+ (158, 158, 158),
+ (168, 168, 168),
+ (178, 178, 178),
+ (188, 188, 188),
+ (198, 198, 198),
+ (208, 208, 208),
+ (218, 218, 218),
+ (228, 228, 228),
+ (238, 238, 238),
+ ]
+)
diff --git a/src/pip/_vendor/rich/_pick.py b/src/pip/_vendor/rich/_pick.py
new file mode 100644
index 000000000..4f6d8b2d7
--- /dev/null
+++ b/src/pip/_vendor/rich/_pick.py
@@ -0,0 +1,17 @@
+from typing import Optional
+
+
+def pick_bool(*values: Optional[bool]) -> bool:
+ """Pick the first non-none bool or return the last value.
+
+ Args:
+ *values (bool): Any number of boolean or None values.
+
+ Returns:
+ bool: First non-none boolean.
+ """
+ assert values, "1 or more values required"
+ for value in values:
+ if value is not None:
+ return value
+ return bool(value)
diff --git a/src/pip/_vendor/rich/_ratio.py b/src/pip/_vendor/rich/_ratio.py
new file mode 100644
index 000000000..e8a3a674e
--- /dev/null
+++ b/src/pip/_vendor/rich/_ratio.py
@@ -0,0 +1,160 @@
+import sys
+from fractions import Fraction
+from math import ceil
+from typing import cast, List, Optional, Sequence
+
+if sys.version_info >= (3, 8):
+ from typing import Protocol
+else:
+ from pip._vendor.typing_extensions import Protocol # pragma: no cover
+
+
+class Edge(Protocol):
+ """Any object that defines an edge (such as Layout)."""
+
+ size: Optional[int] = None
+ ratio: int = 1
+ minimum_size: int = 1
+
+
+def ratio_resolve(total: int, edges: Sequence[Edge]) -> List[int]:
+ """Divide total space to satisfy size, ratio, and minimum_size, constraints.
+
+ The returned list of integers should add up to total in most cases, unless it is
+ impossible to satisfy all the constraints. For instance, if there are two edges
+ with a minimum size of 20 each and `total` is 30 then the returned list will be
+ greater than total. In practice, this would mean that a Layout object would
+ clip the rows that would overflow the screen height.
+
+ Args:
+ total (int): Total number of characters.
+ edges (List[Edge]): Edges within total space.
+
+ Returns:
+ List[int]: Number of characters for each edge.
+ """
+ # Size of edge or None for yet to be determined
+ sizes = [(edge.size or None) for edge in edges]
+
+ _Fraction = Fraction
+
+ # While any edges haven't been calculated
+ while None in sizes:
+ # Get flexible edges and index to map these back on to sizes list
+ flexible_edges = [
+ (index, edge)
+ for index, (size, edge) in enumerate(zip(sizes, edges))
+ if size is None
+ ]
+ # Remaining space in total
+ remaining = total - sum(size or 0 for size in sizes)
+ if remaining <= 0:
+ # No room for flexible edges
+ return [
+ ((edge.minimum_size or 1) if size is None else size)
+ for size, edge in zip(sizes, edges)
+ ]
+ # Calculate number of characters in a ratio portion
+ portion = _Fraction(
+ remaining, sum((edge.ratio or 1) for _, edge in flexible_edges)
+ )
+
+ # If any edges will be less than their minimum, replace size with the minimum
+ for index, edge in flexible_edges:
+ if portion * edge.ratio <= edge.minimum_size:
+ sizes[index] = edge.minimum_size
+ # New fixed size will invalidate calculations, so we need to repeat the process
+ break
+ else:
+ # Distribute flexible space and compensate for rounding error
+ # Since edge sizes can only be integers we need to add the remainder
+ # to the following line
+ remainder = _Fraction(0)
+ for index, edge in flexible_edges:
+ size, remainder = divmod(portion * edge.ratio + remainder, 1)
+ sizes[index] = size
+ break
+ # Sizes now contains integers only
+ return cast(List[int], sizes)
+
+
+def ratio_reduce(
+ total: int, ratios: List[int], maximums: List[int], values: List[int]
+) -> List[int]:
+ """Divide an integer total in to parts based on ratios.
+
+ Args:
+ total (int): The total to divide.
+ ratios (List[int]): A list of integer ratios.
+ maximums (List[int]): List of maximums values for each slot.
+ values (List[int]): List of values
+
+ Returns:
+ List[int]: A list of integers guaranteed to sum to total.
+ """
+ ratios = [ratio if _max else 0 for ratio, _max in zip(ratios, maximums)]
+ total_ratio = sum(ratios)
+ if not total_ratio:
+ return values[:]
+ total_remaining = total
+ result: List[int] = []
+ append = result.append
+ for ratio, maximum, value in zip(ratios, maximums, values):
+ if ratio and total_ratio > 0:
+ distributed = min(maximum, round(ratio * total_remaining / total_ratio))
+ append(value - distributed)
+ total_remaining -= distributed
+ total_ratio -= ratio
+ else:
+ append(value)
+ return result
+
+
+def ratio_distribute(
+ total: int, ratios: List[int], minimums: Optional[List[int]] = None
+) -> List[int]:
+ """Distribute an integer total in to parts based on ratios.
+
+ Args:
+ total (int): The total to divide.
+ ratios (List[int]): A list of integer ratios.
+ minimums (List[int]): List of minimum values for each slot.
+
+ Returns:
+ List[int]: A list of integers guaranteed to sum to total.
+ """
+ if minimums:
+ ratios = [ratio if _min else 0 for ratio, _min in zip(ratios, minimums)]
+ total_ratio = sum(ratios)
+ assert total_ratio > 0, "Sum of ratios must be > 0"
+
+ total_remaining = total
+ distributed_total: List[int] = []
+ append = distributed_total.append
+ if minimums is None:
+ _minimums = [0] * len(ratios)
+ else:
+ _minimums = minimums
+ for ratio, minimum in zip(ratios, _minimums):
+ if total_ratio > 0:
+ distributed = max(minimum, ceil(ratio * total_remaining / total_ratio))
+ else:
+ distributed = total_remaining
+ append(distributed)
+ total_ratio -= ratio
+ total_remaining -= distributed
+ return distributed_total
+
+
+if __name__ == "__main__":
+ from dataclasses import dataclass
+
+ @dataclass
+ class E:
+
+ size: Optional[int] = None
+ ratio: int = 1
+ minimum_size: int = 1
+
+ resolved = ratio_resolve(110, [E(None, 1, 1), E(None, 1, 1), E(None, 1, 1)])
+ print(sum(resolved))
diff --git a/src/pip/_vendor/rich/_spinners.py b/src/pip/_vendor/rich/_spinners.py
new file mode 100644
index 000000000..dc1db0777
--- /dev/null
+++ b/src/pip/_vendor/rich/_spinners.py
@@ -0,0 +1,848 @@
+"""
+Spinners are from:
+* cli-spinners:
+ MIT License
+ Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights to
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ the Software, and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+"""
+
+SPINNERS = {
+ "dots": {
+ "interval": 80,
+ "frames": ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
+ },
+ "dots2": {"interval": 80, "frames": ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"]},
+ "dots3": {
+ "interval": 80,
+ "frames": ["⠋", "⠙", "⠚", "⠞", "⠖", "⠦", "⠴", "⠲", "⠳", "⠓"],
+ },
+ "dots4": {
+ "interval": 80,
+ "frames": [
+ "⠄",
+ "⠆",
+ "⠇",
+ "⠋",
+ "⠙",
+ "⠸",
+ "⠰",
+ "⠠",
+ "⠰",
+ "⠸",
+ "⠙",
+ "⠋",
+ "⠇",
+ "⠆",
+ ],
+ },
+ "dots5": {
+ "interval": 80,
+ "frames": [
+ "⠋",
+ "⠙",
+ "⠚",
+ "⠒",
+ "⠂",
+ "⠂",
+ "⠒",
+ "⠲",
+ "⠴",
+ "⠦",
+ "⠖",
+ "⠒",
+ "⠐",
+ "⠐",
+ "⠒",
+ "⠓",
+ "⠋",
+ ],
+ },
+ "dots6": {
+ "interval": 80,
+ "frames": [
+ "⠁",
+ "⠉",
+ "⠙",
+ "⠚",
+ "⠒",
+ "⠂",
+ "⠂",
+ "⠒",
+ "⠲",
+ "⠴",
+ "⠤",
+ "⠄",
+ "⠄",
+ "⠤",
+ "⠴",
+ "⠲",
+ "⠒",
+ "⠂",
+ "⠂",
+ "⠒",
+ "⠚",
+ "⠙",
+ "⠉",
+ "⠁",
+ ],
+ },
+ "dots7": {
+ "interval": 80,
+ "frames": [
+ "⠈",
+ "⠉",
+ "⠋",
+ "⠓",
+ "⠒",
+ "⠐",
+ "⠐",
+ "⠒",
+ "⠖",
+ "⠦",
+ "⠤",
+ "⠠",
+ "⠠",
+ "⠤",
+ "⠦",
+ "⠖",
+ "⠒",
+ "⠐",
+ "⠐",
+ "⠒",
+ "⠓",
+ "⠋",
+ "⠉",
+ "⠈",
+ ],
+ },
+ "dots8": {
+ "interval": 80,
+ "frames": [
+ "⠁",
+ "⠁",
+ "⠉",
+ "⠙",
+ "⠚",
+ "⠒",
+ "⠂",
+ "⠂",
+ "⠒",
+ "⠲",
+ "⠴",
+ "⠤",
+ "⠄",
+ "⠄",
+ "⠤",
+ "⠠",
+ "⠠",
+ "⠤",
+ "⠦",
+ "⠖",
+ "⠒",
+ "⠐",
+ "⠐",
+ "⠒",
+ "⠓",
+ "⠋",
+ "⠉",
+ "⠈",
+ "⠈",
+ ],
+ },
+ "dots9": {"interval": 80, "frames": ["⢹", "⢺", "⢼", "⣸", "⣇", "⡧", "⡗", "⡏"]},
+ "dots10": {"interval": 80, "frames": ["⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠"]},
+ "dots11": {"interval": 100, "frames": ["⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"]},
+ "dots12": {
+ "interval": 80,
+ "frames": [
+ "⢀⠀",
+ "⡀⠀",
+ "⠄⠀",
+ "⢂⠀",
+ "⡂⠀",
+ "⠅⠀",
+ "⢃⠀",
+ "⡃⠀",
+ "⠍⠀",
+ "⢋⠀",
+ "⡋⠀",
+ "⠍⠁",
+ "⢋⠁",
+ "⡋⠁",
+ "⠍⠉",
+ "⠋⠉",
+ "⠋⠉",
+ "⠉⠙",
+ "⠉⠙",
+ "⠉⠩",
+ "⠈⢙",
+ "⠈⡙",
+ "⢈⠩",
+ "⡀⢙",
+ "⠄⡙",
+ "⢂⠩",
+ "⡂⢘",
+ "⠅⡘",
+ "⢃⠨",
+ "⡃⢐",
+ "⠍⡐",
+ "⢋⠠",
+ "⡋⢀",
+ "⠍⡁",
+ "⢋⠁",
+ "⡋⠁",
+ "⠍⠉",
+ "⠋⠉",
+ "⠋⠉",
+ "⠉⠙",
+ "⠉⠙",
+ "⠉⠩",
+ "⠈⢙",
+ "⠈⡙",
+ "⠈⠩",
+ "⠀⢙",
+ "⠀⡙",
+ "⠀⠩",
+ "⠀⢘",
+ "⠀⡘",
+ "⠀⠨",
+ "⠀⢐",
+ "⠀⡐",
+ "⠀⠠",
+ "⠀⢀",
+ "⠀⡀",
+ ],
+ },
+ "dots8Bit": {
+ "interval": 80,
+ "frames": [
+ "⠀",
+ "⠁",
+ "⠂",
+ "⠃",
+ "⠄",
+ "⠅",
+ "⠆",
+ "⠇",
+ "⡀",
+ "⡁",
+ "⡂",
+ "⡃",
+ "⡄",
+ "⡅",
+ "⡆",
+ "⡇",
+ "⠈",
+ "⠉",
+ "⠊",
+ "⠋",
+ "⠌",
+ "⠍",
+ "⠎",
+ "⠏",
+ "⡈",
+ "⡉",
+ "⡊",
+ "⡋",
+ "⡌",
+ "⡍",
+ "⡎",
+ "⡏",
+ "⠐",
+ "⠑",
+ "⠒",
+ "⠓",
+ "⠔",
+ "⠕",
+ "⠖",
+ "⠗",
+ "⡐",
+ "⡑",
+ "⡒",
+ "⡓",
+ "⡔",
+ "⡕",
+ "⡖",
+ "⡗",
+ "⠘",
+ "⠙",
+ "⠚",
+ "⠛",
+ "⠜",
+ "⠝",
+ "⠞",
+ "⠟",
+ "⡘",
+ "⡙",
+ "⡚",
+ "⡛",
+ "⡜",
+ "⡝",
+ "⡞",
+ "⡟",
+ "⠠",
+ "⠡",
+ "⠢",
+ "⠣",
+ "⠤",
+ "⠥",
+ "⠦",
+ "⠧",
+ "⡠",
+ "⡡",
+ "⡢",
+ "⡣",
+ "⡤",
+ "⡥",
+ "⡦",
+ "⡧",
+ "⠨",
+ "⠩",
+ "⠪",
+ "⠫",
+ "⠬",
+ "⠭",
+ "⠮",
+ "⠯",
+ "⡨",
+ "⡩",
+ "⡪",
+ "⡫",
+ "⡬",
+ "⡭",
+ "⡮",
+ "⡯",
+ "⠰",
+ "⠱",
+ "⠲",
+ "⠳",
+ "⠴",
+ "⠵",
+ "⠶",
+ "⠷",
+ "⡰",
+ "⡱",
+ "⡲",
+ "⡳",
+ "⡴",
+ "⡵",
+ "⡶",
+ "⡷",
+ "⠸",
+ "⠹",
+ "⠺",
+ "⠻",
+ "⠼",
+ "⠽",
+ "⠾",
+ "⠿",
+ "⡸",
+ "⡹",
+ "⡺",
+ "⡻",
+ "⡼",
+ "⡽",
+ "⡾",
+ "⡿",
+ "⢀",
+ "⢁",
+ "⢂",
+ "⢃",
+ "⢄",
+ "⢅",
+ "⢆",
+ "⢇",
+ "⣀",
+ "⣁",
+ "⣂",
+ "⣃",
+ "⣄",
+ "⣅",
+ "⣆",
+ "⣇",
+ "⢈",
+ "⢉",
+ "⢊",
+ "⢋",
+ "⢌",
+ "⢍",
+ "⢎",
+ "⢏",
+ "⣈",
+ "⣉",
+ "⣊",
+ "⣋",
+ "⣌",
+ "⣍",
+ "⣎",
+ "⣏",
+ "⢐",
+ "⢑",
+ "⢒",
+ "⢓",
+ "⢔",
+ "⢕",
+ "⢖",
+ "⢗",
+ "⣐",
+ "⣑",
+ "⣒",
+ "⣓",
+ "⣔",
+ "⣕",
+ "⣖",
+ "⣗",
+ "⢘",
+ "⢙",
+ "⢚",
+ "⢛",
+ "⢜",
+ "⢝",
+ "⢞",
+ "⢟",
+ "⣘",
+ "⣙",
+ "⣚",
+ "⣛",
+ "⣜",
+ "⣝",
+ "⣞",
+ "⣟",
+ "⢠",
+ "⢡",
+ "⢢",
+ "⢣",
+ "⢤",
+ "⢥",
+ "⢦",
+ "⢧",
+ "⣠",
+ "⣡",
+ "⣢",
+ "⣣",
+ "⣤",
+ "⣥",
+ "⣦",
+ "⣧",
+ "⢨",
+ "⢩",
+ "⢪",
+ "⢫",
+ "⢬",
+ "⢭",
+ "⢮",
+ "⢯",
+ "⣨",
+ "⣩",
+ "⣪",
+ "⣫",
+ "⣬",
+ "⣭",
+ "⣮",
+ "⣯",
+ "⢰",
+ "⢱",
+ "⢲",
+ "⢳",
+ "⢴",
+ "⢵",
+ "⢶",
+ "⢷",
+ "⣰",
+ "⣱",
+ "⣲",
+ "⣳",
+ "⣴",
+ "⣵",
+ "⣶",
+ "⣷",
+ "⢸",
+ "⢹",
+ "⢺",
+ "⢻",
+ "⢼",
+ "⢽",
+ "⢾",
+ "⢿",
+ "⣸",
+ "⣹",
+ "⣺",
+ "⣻",
+ "⣼",
+ "⣽",
+ "⣾",
+ "⣿",
+ ],
+ },
+ "line": {"interval": 130, "frames": ["-", "\\", "|", "/"]},
+ "line2": {"interval": 100, "frames": ["⠂", "-", "–", "—", "–", "-"]},
+ "pipe": {"interval": 100, "frames": ["┤", "┘", "┴", "└", "├", "┌", "┬", "┐"]},
+ "simpleDots": {"interval": 400, "frames": [". ", ".. ", "...", " "]},
+ "simpleDotsScrolling": {
+ "interval": 200,
+ "frames": [". ", ".. ", "...", " ..", " .", " "],
+ },
+ "star": {"interval": 70, "frames": ["✶", "✸", "✹", "✺", "✹", "✷"]},
+ "star2": {"interval": 80, "frames": ["+", "x", "*"]},
+ "flip": {
+ "interval": 70,
+ "frames": ["_", "_", "_", "-", "`", "`", "'", "´", "-", "_", "_", "_"],
+ },
+ "hamburger": {"interval": 100, "frames": ["☱", "☲", "☴"]},
+ "growVertical": {
+ "interval": 120,
+ "frames": ["▁", "▃", "▄", "▅", "▆", "▇", "▆", "▅", "▄", "▃"],
+ },
+ "growHorizontal": {
+ "interval": 120,
+ "frames": ["▏", "▎", "▍", "▌", "▋", "▊", "▉", "▊", "▋", "▌", "▍", "▎"],
+ },
+ "balloon": {"interval": 140, "frames": [" ", ".", "o", "O", "@", "*", " "]},
+ "balloon2": {"interval": 120, "frames": [".", "o", "O", "°", "O", "o", "."]},
+ "noise": {"interval": 100, "frames": ["▓", "▒", "░"]},
+ "bounce": {"interval": 120, "frames": ["⠁", "⠂", "⠄", "⠂"]},
+ "boxBounce": {"interval": 120, "frames": ["▖", "▘", "▝", "▗"]},
+ "boxBounce2": {"interval": 100, "frames": ["▌", "▀", "▐", "▄"]},
+ "triangle": {"interval": 50, "frames": ["◢", "◣", "◤", "◥"]},
+ "arc": {"interval": 100, "frames": ["◜", "◠", "◝", "◞", "◡", "◟"]},
+ "circle": {"interval": 120, "frames": ["◡", "⊙", "◠"]},
+ "squareCorners": {"interval": 180, "frames": ["◰", "◳", "◲", "◱"]},
+ "circleQuarters": {"interval": 120, "frames": ["◴", "◷", "◶", "◵"]},
+ "circleHalves": {"interval": 50, "frames": ["◐", "◓", "◑", "◒"]},
+ "squish": {"interval": 100, "frames": ["╫", "╪"]},
+ "toggle": {"interval": 250, "frames": ["⊶", "⊷"]},
+ "toggle2": {"interval": 80, "frames": ["▫", "▪"]},
+ "toggle3": {"interval": 120, "frames": ["□", "■"]},
+ "toggle4": {"interval": 100, "frames": ["■", "□", "▪", "▫"]},
+ "toggle5": {"interval": 100, "frames": ["▮", "▯"]},
+ "toggle6": {"interval": 300, "frames": ["ဝ", "၀"]},
+ "toggle7": {"interval": 80, "frames": ["⦾", "⦿"]},
+ "toggle8": {"interval": 100, "frames": ["◍", "◌"]},
+ "toggle9": {"interval": 100, "frames": ["◉", "◎"]},
+ "toggle10": {"interval": 100, "frames": ["㊂", "㊀", "㊁"]},
+ "toggle11": {"interval": 50, "frames": ["⧇", "⧆"]},
+ "toggle12": {"interval": 120, "frames": ["☗", "☖"]},
+ "toggle13": {"interval": 80, "frames": ["=", "*", "-"]},
+ "arrow": {"interval": 100, "frames": ["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"]},
+ "arrow2": {
+ "interval": 80,
+ "frames": ["⬆️ ", "↗️ ", "➡️ ", "↘️ ", "⬇️ ", "↙️ ", "⬅️ ", "↖️ "],
+ },
+ "arrow3": {
+ "interval": 120,
+ "frames": ["▹▹▹▹▹", "▸▹▹▹▹", "▹▸▹▹▹", "▹▹▸▹▹", "▹▹▹▸▹", "▹▹▹▹▸"],
+ },
+ "bouncingBar": {
+ "interval": 80,
+ "frames": [
+ "[ ]",
+ "[= ]",
+ "[== ]",
+ "[=== ]",
+ "[ ===]",
+ "[ ==]",
+ "[ =]",
+ "[ ]",
+ "[ =]",
+ "[ ==]",
+ "[ ===]",
+ "[====]",
+ "[=== ]",
+ "[== ]",
+ "[= ]",
+ ],
+ },
+ "bouncingBall": {
+ "interval": 80,
+ "frames": [
+ "( ● )",
+ "( ● )",
+ "( ● )",
+ "( ● )",
+ "( ●)",
+ "( ● )",
+ "( ● )",
+ "( ● )",
+ "( ● )",
+ "(● )",
+ ],
+ },
+ "smiley": {"interval": 200, "frames": ["😄 ", "😝 "]},
+ "monkey": {"interval": 300, "frames": ["🙈 ", "🙈 ", "🙉 ", "🙊 "]},
+ "hearts": {"interval": 100, "frames": ["💛 ", "💙 ", "💜 ", "💚 ", "❤️ "]},
+ "clock": {
+ "interval": 100,
+ "frames": [
+ "🕛 ",
+ "🕐 ",
+ "🕑 ",
+ "🕒 ",
+ "🕓 ",
+ "🕔 ",
+ "🕕 ",
+ "🕖 ",
+ "🕗 ",
+ "🕘 ",
+ "🕙 ",
+ "🕚 ",
+ ],
+ },
+ "earth": {"interval": 180, "frames": ["🌍 ", "🌎 ", "🌏 "]},
+ "material": {
+ "interval": 17,
+ "frames": [
+ "█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
+ "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
+ "███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
+ "████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
+ "██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
+ "██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
+ "███████▁▁▁▁▁▁▁▁▁▁▁▁▁",
+ "████████▁▁▁▁▁▁▁▁▁▁▁▁",
+ "█████████▁▁▁▁▁▁▁▁▁▁▁",
+ "█████████▁▁▁▁▁▁▁▁▁▁▁",
+ "██████████▁▁▁▁▁▁▁▁▁▁",
+ "███████████▁▁▁▁▁▁▁▁▁",
+ "█████████████▁▁▁▁▁▁▁",
+ "██████████████▁▁▁▁▁▁",
+ "██████████████▁▁▁▁▁▁",
+ "▁██████████████▁▁▁▁▁",
+ "▁██████████████▁▁▁▁▁",
+ "▁██████████████▁▁▁▁▁",
+ "▁▁██████████████▁▁▁▁",
+ "▁▁▁██████████████▁▁▁",
+ "▁▁▁▁█████████████▁▁▁",
+ "▁▁▁▁██████████████▁▁",
+ "▁▁▁▁██████████████▁▁",
+ "▁▁▁▁▁██████████████▁",
+ "▁▁▁▁▁██████████████▁",
+ "▁▁▁▁▁██████████████▁",
+ "▁▁▁▁▁▁██████████████",
+ "▁▁▁▁▁▁██████████████",
+ "▁▁▁▁▁▁▁█████████████",
+ "▁▁▁▁▁▁▁█████████████",
+ "▁▁▁▁▁▁▁▁████████████",
+ "▁▁▁▁▁▁▁▁████████████",
+ "▁▁▁▁▁▁▁▁▁███████████",
+ "▁▁▁▁▁▁▁▁▁███████████",
+ "▁▁▁▁▁▁▁▁▁▁██████████",
+ "▁▁▁▁▁▁▁▁▁▁██████████",
+ "▁▁▁▁▁▁▁▁▁▁▁▁████████",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁███████",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁██████",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████",
+ "█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████",
+ "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███",
+ "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███",
+ "███▁▁▁▁▁▁▁▁▁▁▁▁▁▁███",
+ "████▁▁▁▁▁▁▁▁▁▁▁▁▁▁██",
+ "█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█",
+ "█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█",
+ "██████▁▁▁▁▁▁▁▁▁▁▁▁▁█",
+ "████████▁▁▁▁▁▁▁▁▁▁▁▁",
+ "█████████▁▁▁▁▁▁▁▁▁▁▁",
+ "█████████▁▁▁▁▁▁▁▁▁▁▁",
+ "█████████▁▁▁▁▁▁▁▁▁▁▁",
+ "█████████▁▁▁▁▁▁▁▁▁▁▁",
+ "███████████▁▁▁▁▁▁▁▁▁",
+ "████████████▁▁▁▁▁▁▁▁",
+ "████████████▁▁▁▁▁▁▁▁",
+ "██████████████▁▁▁▁▁▁",
+ "██████████████▁▁▁▁▁▁",
+ "▁██████████████▁▁▁▁▁",
+ "▁██████████████▁▁▁▁▁",
+ "▁▁▁█████████████▁▁▁▁",
+ "▁▁▁▁▁████████████▁▁▁",
+ "▁▁▁▁▁████████████▁▁▁",
+ "▁▁▁▁▁▁███████████▁▁▁",
+ "▁▁▁▁▁▁▁▁█████████▁▁▁",
+ "▁▁▁▁▁▁▁▁█████████▁▁▁",
+ "▁▁▁▁▁▁▁▁▁█████████▁▁",
+ "▁▁▁▁▁▁▁▁▁█████████▁▁",
+ "▁▁▁▁▁▁▁▁▁▁█████████▁",
+ "▁▁▁▁▁▁▁▁▁▁▁████████▁",
+ "▁▁▁▁▁▁▁▁▁▁▁████████▁",
+ "▁▁▁▁▁▁▁▁▁▁▁▁███████▁",
+ "▁▁▁▁▁▁▁▁▁▁▁▁███████▁",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁███████",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁███████",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
+ ],
+ },
+ "moon": {
+ "interval": 80,
+ "frames": ["🌑 ", "🌒 ", "🌓 ", "🌔 ", "🌕 ", "🌖 ", "🌗 ", "🌘 "],
+ },
+ "runner": {"interval": 140, "frames": ["🚶 ", "🏃 "]},
+ "pong": {
+ "interval": 80,
+ "frames": [
+ "▐⠂ ▌",
+ "▐⠈ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⡀ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠈ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⡀ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠈ ▌",
+ "▐ ⠂▌",
+ "▐ ⠠▌",
+ "▐ ⡀▌",
+ "▐ ⠠ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠈ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⡀ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠈ ▌",
+ "▐ ⠂ ▌",
+ "▐ ⠠ ▌",
+ "▐ ⡀ ▌",
+ "▐⠠ ▌",
+ ],
+ },
+ "shark": {
+ "interval": 120,
+ "frames": [
+ "▐|\\____________▌",
+ "▐_|\\___________▌",
+ "▐__|\\__________▌",
+ "▐___|\\_________▌",
+ "▐____|\\________▌",
+ "▐_____|\\_______▌",
+ "▐______|\\______▌",
+ "▐_______|\\_____▌",
+ "▐________|\\____▌",
+ "▐_________|\\___▌",
+ "▐__________|\\__▌",
+ "▐___________|\\_▌",
+ "▐____________|\\▌",
+ "▐____________/|▌",
+ "▐___________/|_▌",
+ "▐__________/|__▌",
+ "▐_________/|___▌",
+ "▐________/|____▌",
+ "▐_______/|_____▌",
+ "▐______/|______▌",
+ "▐_____/|_______▌",
+ "▐____/|________▌",
+ "▐___/|_________▌",
+ "▐__/|__________▌",
+ "▐_/|___________▌",
+ "▐/|____________▌",
+ ],
+ },
+ "dqpb": {"interval": 100, "frames": ["d", "q", "p", "b"]},
+ "weather": {
+ "interval": 100,
+ "frames": [
+ "☀️ ",
+ "☀️ ",
+ "☀️ ",
+ "🌤 ",
+ "⛅️ ",
+ "🌥 ",
+ "☁️ ",
+ "🌧 ",
+ "🌨 ",
+ "🌧 ",
+ "🌨 ",
+ "🌧 ",
+ "🌨 ",
+ "⛈ ",
+ "🌨 ",
+ "🌧 ",
+ "🌨 ",
+ "☁️ ",
+ "🌥 ",
+ "⛅️ ",
+ "🌤 ",
+ "☀️ ",
+ "☀️ ",
+ ],
+ },
+ "christmas": {"interval": 400, "frames": ["🌲", "🎄"]},
+ "grenade": {
+ "interval": 80,
+ "frames": [
+ "، ",
+ "′ ",
+ " ´ ",
+ " ‾ ",
+ " ⸌",
+ " ⸊",
+ " |",
+ " ⁎",
+ " ⁕",
+ " ෴ ",
+ " ⁓",
+ " ",
+ " ",
+ " ",
+ ],
+ },
+ "point": {"interval": 125, "frames": ["∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"]},
+ "layer": {"interval": 150, "frames": ["-", "=", "≡"]},
+ "betaWave": {
+ "interval": 80,
+ "frames": [
+ "ρββββββ",
+ "βρβββββ",
+ "ββρββββ",
+ "βββρβββ",
+ "ββββρββ",
+ "βββββρβ",
+ "ββββββρ",
+ ],
+ },
+ "aesthetic": {
+ "interval": 80,
+ "frames": [
+ "▰▱▱▱▱▱▱",
+ "▰▰▱▱▱▱▱",
+ "▰▰▰▱▱▱▱",
+ "▰▰▰▰▱▱▱",
+ "▰▰▰▰▰▱▱",
+ "▰▰▰▰▰▰▱",
+ "▰▰▰▰▰▰▰",
+ "▰▱▱▱▱▱▱",
+ ],
+ },
+}
diff --git a/src/pip/_vendor/rich/_stack.py b/src/pip/_vendor/rich/_stack.py
new file mode 100644
index 000000000..194564e76
--- /dev/null
+++ b/src/pip/_vendor/rich/_stack.py
@@ -0,0 +1,16 @@
+from typing import List, TypeVar
+
+T = TypeVar("T")
+
+
+class Stack(List[T]):
+ """A small shim over builtin list."""
+
+ @property
+ def top(self) -> T:
+ """Get top of stack."""
+ return self[-1]
+
+ def push(self, item: T) -> None:
+ """Push an item on to the stack (append in stack nomenclature)."""
+ self.append(item)
diff --git a/src/pip/_vendor/rich/_timer.py b/src/pip/_vendor/rich/_timer.py
new file mode 100644
index 000000000..a2ca6be03
--- /dev/null
+++ b/src/pip/_vendor/rich/_timer.py
@@ -0,0 +1,19 @@
+"""
+Timer context manager, only used in debug.
+
+"""
+
+from time import time
+
+import contextlib
+from typing import Generator
+
+
+@contextlib.contextmanager
+def timer(subject: str = "time") -> Generator[None, None, None]:
+ """print the elapsed time. (only used in debugging)"""
+ start = time()
+ yield
+ elapsed = time() - start
+ elapsed_ms = elapsed * 1000
+ print(f"{subject} elapsed {elapsed_ms:.1f}ms")
diff --git a/src/pip/_vendor/rich/_windows.py b/src/pip/_vendor/rich/_windows.py
new file mode 100644
index 000000000..0fdbc4539
--- /dev/null
+++ b/src/pip/_vendor/rich/_windows.py
@@ -0,0 +1,75 @@
+import sys
+
+from dataclasses import dataclass
+
+
+@dataclass
+class WindowsConsoleFeatures:
+ """Windows features available."""
+
+ vt: bool = False
+ """The console supports VT codes."""
+ truecolor: bool = False
+ """The console supports truecolor."""
+
+
+try:
+ import ctypes
+ from ctypes import wintypes
+ from ctypes import LibraryLoader
+
+ if sys.platform == "win32":
+ windll = LibraryLoader(ctypes.WinDLL)
+ else:
+ windll = None
+ raise ImportError("Not windows")
+except (AttributeError, ImportError, ValueError):
+
+ # Fallback if we can't load the Windows DLL
+ def get_windows_console_features() -> WindowsConsoleFeatures:
+ features = WindowsConsoleFeatures()
+ return features
+
+
+else:
+
+ STDOUT = -11
+ ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
+ _GetConsoleMode = windll.kernel32.GetConsoleMode
+ _GetConsoleMode.argtypes = [wintypes.HANDLE, wintypes.LPDWORD]
+ _GetConsoleMode.restype = wintypes.BOOL
+
+ _GetStdHandle = windll.kernel32.GetStdHandle
+ _GetStdHandle.argtypes = [
+ wintypes.DWORD,
+ ]
+ _GetStdHandle.restype = wintypes.HANDLE
+
+ def get_windows_console_features() -> WindowsConsoleFeatures:
+ """Get windows console features.
+
+ Returns:
+ WindowsConsoleFeatures: An instance of WindowsConsoleFeatures.
+ """
+ handle = _GetStdHandle(STDOUT)
+ console_mode = wintypes.DWORD()
+ result = _GetConsoleMode(handle, console_mode)
+ vt = bool(result and console_mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+ truecolor = False
+ if vt:
+ win_version = sys.getwindowsversion()
+ truecolor = win_version.major > 10 or (
+ win_version.major == 10 and win_version.build >= 15063
+ )
+ features = WindowsConsoleFeatures(vt=vt, truecolor=truecolor)
+ return features
+
+
+if __name__ == "__main__":
+ import platform
+
+ features = get_windows_console_features()
+ from pip._vendor.rich import print
+
+ print(f'platform="{platform.system()}"')
+ print(repr(features))
diff --git a/src/pip/_vendor/rich/_wrap.py b/src/pip/_vendor/rich/_wrap.py
new file mode 100644
index 000000000..b537757a5
--- /dev/null
+++ b/src/pip/_vendor/rich/_wrap.py
@@ -0,0 +1,55 @@
+import re
+from typing import Iterable, List, Tuple
+
+from .cells import cell_len, chop_cells
+from ._loop import loop_last
+
+re_word = re.compile(r"\s*\S+\s*")
+
+
+def words(text: str) -> Iterable[Tuple[int, int, str]]:
+ position = 0
+ word_match = re_word.match(text, position)
+ while word_match is not None:
+ start, end = word_match.span()
+ word = word_match.group(0)
+ yield start, end, word
+ word_match = re_word.match(text, end)
+
+
+def divide_line(text: str, width: int, fold: bool = True) -> List[int]:
+ divides: List[int] = []
+ append = divides.append
+ line_position = 0
+ _cell_len = cell_len
+ for start, _end, word in words(text):
+ word_length = _cell_len(word.rstrip())
+ if line_position + word_length > width:
+ if word_length > width:
+ if fold:
+ for last, line in loop_last(
+ chop_cells(word, width, position=line_position)
+ ):
+ if last:
+ line_position = _cell_len(line)
+ else:
+ start += len(line)
+ append(start)
+ else:
+ if start:
+ append(start)
+ line_position = _cell_len(word)
+ elif line_position and start:
+ append(start)
+ line_position = _cell_len(word)
+ else:
+ line_position += _cell_len(word)
+ return divides
+
+
+if __name__ == "__main__": # pragma: no cover
+ from .console import Console
+
+ console = Console(width=10)
+ console.print("12345 abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ 12345")
+ print(chop_cells("abcdefghijklmnopqrstuvwxyz", 10, position=2))
diff --git a/src/pip/_vendor/rich/abc.py b/src/pip/_vendor/rich/abc.py
new file mode 100644
index 000000000..e6e498efa
--- /dev/null
+++ b/src/pip/_vendor/rich/abc.py
@@ -0,0 +1,33 @@
+from abc import ABC
+
+
+class RichRenderable(ABC):
+ """An abstract base class for Rich renderables.
+
+ Note that there is no need to extend this class, the intended use is to check if an
+ object supports the Rich renderable protocol. For example::
+
+ if isinstance(my_object, RichRenderable):
+ console.print(my_object)
+
+ """
+
+ @classmethod
+ def __subclasshook__(cls, other: type) -> bool:
+ """Check if this class supports the rich render protocol."""
+ return hasattr(other, "__rich_console__") or hasattr(other, "__rich__")
+
+
+if __name__ == "__main__": # pragma: no cover
+ from pip._vendor.rich.text import Text
+
+ t = Text()
+ print(isinstance(Text, RichRenderable))
+ print(isinstance(t, RichRenderable))
+
+ class Foo:
+ pass
+
+ f = Foo()
+ print(isinstance(f, RichRenderable))
+ print(isinstance("", RichRenderable))
diff --git a/src/pip/_vendor/rich/align.py b/src/pip/_vendor/rich/align.py
new file mode 100644
index 000000000..4344ae141
--- /dev/null
+++ b/src/pip/_vendor/rich/align.py
@@ -0,0 +1,312 @@
+import sys
+from itertools import chain
+from typing import TYPE_CHECKING, Iterable, Optional
+
+if sys.version_info >= (3, 8):
+ from typing import Literal
+else:
+ from pip._vendor.typing_extensions import Literal # pragma: no cover
+
+from .constrain import Constrain
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .segment import Segment
+from .style import StyleType
+
+if TYPE_CHECKING:
+ from .console import Console, ConsoleOptions, RenderableType, RenderResult
+
+AlignMethod = Literal["left", "center", "right"]
+VerticalAlignMethod = Literal["top", "middle", "bottom"]
+AlignValues = AlignMethod # TODO: deprecate AlignValues
+
+
+class Align(JupyterMixin):
+ """Align a renderable by adding spaces if necessary.
+
+ Args:
+ renderable (RenderableType): A console renderable.
+ align (AlignMethod): One of "left", "center", or "right""
+ style (StyleType, optional): An optional style to apply to the background.
+ vertical (Optional[VerticalAlginMethod], optional): Optional vertical align, one of "top", "middle", or "bottom". Defaults to None.
+ pad (bool, optional): Pad the right with spaces. Defaults to True.
+ width (int, optional): Restrict contents to given width, or None to use default width. Defaults to None.
+ height (int, optional): Set height of align renderable, or None to fit to contents. Defaults to None.
+
+ Raises:
+ ValueError: if ``align`` is not one of the expected values.
+ """
+
+ def __init__(
+ self,
+ renderable: "RenderableType",
+ align: AlignMethod = "left",
+ style: Optional[StyleType] = None,
+ *,
+ vertical: Optional[VerticalAlignMethod] = None,
+ pad: bool = True,
+ width: Optional[int] = None,
+ height: Optional[int] = None,
+ ) -> None:
+ if align not in ("left", "center", "right"):
+ raise ValueError(
+ f'invalid value for align, expected "left", "center", or "right" (not {align!r})'
+ )
+ if vertical is not None and vertical not in ("top", "middle", "bottom"):
+ raise ValueError(
+ f'invalid value for vertical, expected "top", "middle", or "bottom" (not {vertical!r})'
+ )
+ self.renderable = renderable
+ self.align = align
+ self.style = style
+ self.vertical = vertical
+ self.pad = pad
+ self.width = width
+ self.height = height
+
+ def __repr__(self) -> str:
+ return f"Align({self.renderable!r}, {self.align!r})"
+
+ @classmethod
+ def left(
+ cls,
+ renderable: "RenderableType",
+ style: Optional[StyleType] = None,
+ *,
+ vertical: Optional[VerticalAlignMethod] = None,
+ pad: bool = True,
+ width: Optional[int] = None,
+ height: Optional[int] = None,
+ ) -> "Align":
+ """Align a renderable to the left."""
+ return cls(
+ renderable,
+ "left",
+ style=style,
+ vertical=vertical,
+ pad=pad,
+ width=width,
+ height=height,
+ )
+
+ @classmethod
+ def center(
+ cls,
+ renderable: "RenderableType",
+ style: Optional[StyleType] = None,
+ *,
+ vertical: Optional[VerticalAlignMethod] = None,
+ pad: bool = True,
+ width: Optional[int] = None,
+ height: Optional[int] = None,
+ ) -> "Align":
+ """Align a renderable to the center."""
+ return cls(
+ renderable,
+ "center",
+ style=style,
+ vertical=vertical,
+ pad=pad,
+ width=width,
+ height=height,
+ )
+
+ @classmethod
+ def right(
+ cls,
+ renderable: "RenderableType",
+ style: Optional[StyleType] = None,
+ *,
+ vertical: Optional[VerticalAlignMethod] = None,
+ pad: bool = True,
+ width: Optional[int] = None,
+ height: Optional[int] = None,
+ ) -> "Align":
+ """Align a renderable to the right."""
+ return cls(
+ renderable,
+ "right",
+ style=style,
+ vertical=vertical,
+ pad=pad,
+ width=width,
+ height=height,
+ )
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+ align = self.align
+ width = console.measure(self.renderable, options=options).maximum
+ rendered = console.render(
+ Constrain(
+ self.renderable, width if self.width is None else min(width, self.width)
+ ),
+ options.update(height=None),
+ )
+ lines = list(Segment.split_lines(rendered))
+ width, height = Segment.get_shape(lines)
+ lines = Segment.set_shape(lines, width, height)
+ new_line = Segment.line()
+ excess_space = options.max_width - width
+ style = console.get_style(self.style) if self.style is not None else None
+
+ def generate_segments() -> Iterable[Segment]:
+ if excess_space <= 0:
+ # Exact fit
+ for line in lines:
+ yield from line
+ yield new_line
+
+ elif align == "left":
+ # Pad on the right
+ pad = Segment(" " * excess_space, style) if self.pad else None
+ for line in lines:
+ yield from line
+ if pad:
+ yield pad
+ yield new_line
+
+ elif align == "center":
+ # Pad left and right
+ left = excess_space // 2
+ pad = Segment(" " * left, style)
+ pad_right = (
+ Segment(" " * (excess_space - left), style) if self.pad else None
+ )
+ for line in lines:
+ if left:
+ yield pad
+ yield from line
+ if pad_right:
+ yield pad_right
+ yield new_line
+
+ elif align == "right":
+ # Padding on left
+ pad = Segment(" " * excess_space, style)
+ for line in lines:
+ yield pad
+ yield from line
+ yield new_line
+
+ blank_line = (
+ Segment(f"{' ' * (self.width or options.max_width)}\n", style)
+ if self.pad
+ else Segment("\n")
+ )
+
+ def blank_lines(count: int) -> Iterable[Segment]:
+ if count > 0:
+ for _ in range(count):
+ yield blank_line
+
+ vertical_height = self.height or options.height
+ iter_segments: Iterable[Segment]
+ if self.vertical and vertical_height is not None:
+ if self.vertical == "top":
+ bottom_space = vertical_height - height
+ iter_segments = chain(generate_segments(), blank_lines(bottom_space))
+ elif self.vertical == "middle":
+ top_space = (vertical_height - height) // 2
+ bottom_space = vertical_height - top_space - height
+ iter_segments = chain(
+ blank_lines(top_space),
+ generate_segments(),
+ blank_lines(bottom_space),
+ )
+ else: # self.vertical == "bottom":
+ top_space = vertical_height - height
+ iter_segments = chain(blank_lines(top_space), generate_segments())
+ else:
+ iter_segments = generate_segments()
+ if self.style:
+ style = console.get_style(self.style)
+ iter_segments = Segment.apply_style(iter_segments, style)
+ yield from iter_segments
+
+ def __rich_measure__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> Measurement:
+ measurement = Measurement.get(console, options, self.renderable)
+ return measurement
+
+
+class VerticalCenter(JupyterMixin):
+ """Vertically aligns a renderable.
+
+ Warn:
+ This class is deprecated and may be removed in a future version. Use Align class with
+ `vertical="middle"`.
+
+ Args:
+ renderable (RenderableType): A renderable object.
+ """
+
+ def __init__(
+ self,
+ renderable: "RenderableType",
+ style: Optional[StyleType] = None,
+ ) -> None:
+ self.renderable = renderable
+ self.style = style
+
+ def __repr__(self) -> str:
+ return f"VerticalCenter({self.renderable!r})"
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+ style = console.get_style(self.style) if self.style is not None else None
+ lines = console.render_lines(
+ self.renderable, options.update(height=None), pad=False
+ )
+ width, _height = Segment.get_shape(lines)
+ new_line = Segment.line()
+ height = options.height or options.size.height
+ top_space = (height - len(lines)) // 2
+ bottom_space = height - top_space - len(lines)
+ blank_line = Segment(f"{' ' * width}", style)
+
+ def blank_lines(count: int) -> Iterable[Segment]:
+ for _ in range(count):
+ yield blank_line
+ yield new_line
+
+ if top_space > 0:
+ yield from blank_lines(top_space)
+ for line in lines:
+ yield from line
+ yield new_line
+ if bottom_space > 0:
+ yield from blank_lines(bottom_space)
+
+ def __rich_measure__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> Measurement:
+ measurement = Measurement.get(console, options, self.renderable)
+ return measurement
+
+
+if __name__ == "__main__": # pragma: no cover
+ from pip._vendor.rich.console import Console, Group
+ from pip._vendor.rich.highlighter import ReprHighlighter
+ from pip._vendor.rich.panel import Panel
+
+ highlighter = ReprHighlighter()
+ console = Console()
+
+ panel = Panel(
+ Group(
+ Align.left(highlighter("align='left'")),
+ Align.center(highlighter("align='center'")),
+ Align.right(highlighter("align='right'")),
+ ),
+ width=60,
+ style="on dark_blue",
+ title="Algin",
+ )
+
+ console.print(
+ Align.center(panel, vertical="middle", style="on red", height=console.height)
+ )
diff --git a/src/pip/_vendor/rich/ansi.py b/src/pip/_vendor/rich/ansi.py
new file mode 100644
index 000000000..92e4772ed
--- /dev/null
+++ b/src/pip/_vendor/rich/ansi.py
@@ -0,0 +1,228 @@
+from contextlib import suppress
+import re
+from typing import Iterable, NamedTuple
+
+from .color import Color
+from .style import Style
+from .text import Text
+
+re_ansi = re.compile(r"(?:\x1b\[(.*?)m)|(?:\x1b\](.*?)\x1b\\)")
+re_csi = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
+
+
+class _AnsiToken(NamedTuple):
+ """Result of ansi tokenized string."""
+
+ plain: str = ""
+ sgr: str = ""
+ osc: str = ""
+
+
+def _ansi_tokenize(ansi_text: str) -> Iterable[_AnsiToken]:
+ """Tokenize a string in to plain text and ANSI codes.
+
+ Args:
+ ansi_text (str): A String containing ANSI codes.
+
+ Yields:
+ AnsiToken: A named tuple of (plain, sgr, osc)
+ """
+
+ def remove_csi(ansi_text: str) -> str:
+ """Remove unknown CSI sequences."""
+ return re_csi.sub("", ansi_text)
+
+ position = 0
+ for match in re_ansi.finditer(ansi_text):
+ start, end = match.span(0)
+ sgr, osc = match.groups()
+ if start > position:
+ yield _AnsiToken(remove_csi(ansi_text[position:start]))
+ yield _AnsiToken("", sgr, osc)
+ position = end
+ if position < len(ansi_text):
+ yield _AnsiToken(remove_csi(ansi_text[position:]))
+
+
+SGR_STYLE_MAP = {
+ 1: "bold",
+ 2: "dim",
+ 3: "italic",
+ 4: "underline",
+ 5: "blink",
+ 6: "blink2",
+ 7: "reverse",
+ 8: "conceal",
+ 9: "strike",
+ 21: "underline2",
+ 22: "not dim not bold",
+ 23: "not italic",
+ 24: "not underline",
+ 25: "not blink",
+ 26: "not blink2",
+ 27: "not reverse",
+ 28: "not conceal",
+ 29: "not strike",
+ 30: "color(0)",
+ 31: "color(1)",
+ 32: "color(2)",
+ 33: "color(3)",
+ 34: "color(4)",
+ 35: "color(5)",
+ 36: "color(6)",
+ 37: "color(7)",
+ 39: "default",
+ 40: "on color(0)",
+ 41: "on color(1)",
+ 42: "on color(2)",
+ 43: "on color(3)",
+ 44: "on color(4)",
+ 45: "on color(5)",
+ 46: "on color(6)",
+ 47: "on color(7)",
+ 49: "on default",
+ 51: "frame",
+ 52: "encircle",
+ 53: "overline",
+ 54: "not frame not encircle",
+ 55: "not overline",
+ 90: "color(8)",
+ 91: "color(9)",
+ 92: "color(10)",
+ 93: "color(11)",
+ 94: "color(12)",
+ 95: "color(13)",
+ 96: "color(14)",
+ 97: "color(15)",
+ 100: "on color(8)",
+ 101: "on color(9)",
+ 102: "on color(10)",
+ 103: "on color(11)",
+ 104: "on color(12)",
+ 105: "on color(13)",
+ 106: "on color(14)",
+ 107: "on color(15)",
+}
+
+
+class AnsiDecoder:
+ """Translate ANSI code in to styled Text."""
+
+ def __init__(self) -> None:
+ self.style = Style.null()
+
+ def decode(self, terminal_text: str) -> Iterable[Text]:
+ """Decode ANSI codes in an interable of lines.
+
+ Args:
+ lines (Iterable[str]): An iterable of lines of terminal output.
+
+ Yields:
+ Text: Marked up Text.
+ """
+ for line in terminal_text.splitlines():
+ yield self.decode_line(line)
+
+ def decode_line(self, line: str) -> Text:
+ """Decode a line containing ansi codes.
+
+ Args:
+ line (str): A line of terminal output.
+
+ Returns:
+ Text: A Text instance marked up according to ansi codes.
+ """
+ from_ansi = Color.from_ansi
+ from_rgb = Color.from_rgb
+ _Style = Style
+ text = Text()
+ append = text.append
+ line = line.rsplit("\r", 1)[-1]
+ for token in _ansi_tokenize(line):
+ plain_text, sgr, osc = token
+ if plain_text:
+ append(plain_text, self.style or None)
+ elif osc:
+ if osc.startswith("8;"):
+ _params, semicolon, link = osc[2:].partition(";")
+ if semicolon:
+ self.style = self.style.update_link(link or None)
+ elif sgr:
+ # Translate in to semi-colon separated codes
+ # Ignore invalid codes, because we want to be lenient
+ codes = [
+ min(255, int(_code)) for _code in sgr.split(";") if _code.isdigit()
+ ]
+ iter_codes = iter(codes)
+ for code in iter_codes:
+ if code == 0:
+ # reset
+ self.style = _Style.null()
+ elif code in SGR_STYLE_MAP:
+ # styles
+ self.style += _Style.parse(SGR_STYLE_MAP[code])
+ elif code == 38:
+ #  Foreground
+ with suppress(StopIteration):
+ color_type = next(iter_codes)
+ if color_type == 5:
+ self.style += _Style.from_color(
+ from_ansi(next(iter_codes))
+ )
+ elif color_type == 2:
+ self.style += _Style.from_color(
+ from_rgb(
+ next(iter_codes),
+ next(iter_codes),
+ next(iter_codes),
+ )
+ )
+ elif code == 48:
+ # Background
+ with suppress(StopIteration):
+ color_type = next(iter_codes)
+ if color_type == 5:
+ self.style += _Style.from_color(
+ None, from_ansi(next(iter_codes))
+ )
+ elif color_type == 2:
+ self.style += _Style.from_color(
+ None,
+ from_rgb(
+ next(iter_codes),
+ next(iter_codes),
+ next(iter_codes),
+ ),
+ )
+
+ return text
+
+
+if __name__ == "__main__": # pragma: no cover
+ import pty
+ import io
+ import os
+ import sys
+
+ decoder = AnsiDecoder()
+
+ stdout = io.BytesIO()
+
+ def read(fd: int) -> bytes:
+ data = os.read(fd, 1024)
+ stdout.write(data)
+ return data
+
+ pty.spawn(sys.argv[1:], read)
+
+ from .console import Console
+
+ console = Console(record=True)
+
+ stdout_result = stdout.getvalue().decode("utf-8")
+ print(stdout_result)
+
+ for line in decoder.decode(stdout_result):
+ console.print(line)
+
+ console.save_html("stdout.html")
diff --git a/src/pip/_vendor/rich/bar.py b/src/pip/_vendor/rich/bar.py
new file mode 100644
index 000000000..ed86a552d
--- /dev/null
+++ b/src/pip/_vendor/rich/bar.py
@@ -0,0 +1,94 @@
+from typing import Optional, Union
+
+from .color import Color
+from .console import Console, ConsoleOptions, RenderResult
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .segment import Segment
+from .style import Style
+
+# There are left-aligned characters for 1/8 to 7/8, but
+# the right-aligned characters exist only for 1/8 and 4/8.
+BEGIN_BLOCK_ELEMENTS = ["█", "█", "█", "▐", "▐", "▐", "▕", "▕"]
+END_BLOCK_ELEMENTS = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"]
+FULL_BLOCK = "█"
+
+
+class Bar(JupyterMixin):
+ """Renders a solid block bar.
+
+ Args:
+ size (float): Value for the end of the bar.
+ begin (float): Begin point (between 0 and size, inclusive).
+ end (float): End point (between 0 and size, inclusive).
+ width (int, optional): Width of the bar, or ``None`` for maximum width. Defaults to None.
+ color (Union[Color, str], optional): Color of the bar. Defaults to "default".
+ bgcolor (Union[Color, str], optional): Color of bar background. Defaults to "default".
+ """
+
+ def __init__(
+ self,
+ size: float,
+ begin: float,
+ end: float,
+ *,
+ width: Optional[int] = None,
+ color: Union[Color, str] = "default",
+ bgcolor: Union[Color, str] = "default",
+ ):
+ self.size = size
+ self.begin = max(begin, 0)
+ self.end = min(end, size)
+ self.width = width
+ self.style = Style(color=color, bgcolor=bgcolor)
+
+ def __repr__(self) -> str:
+ return f"Bar({self.size}, {self.begin}, {self.end})"
+
+ def __rich_console__(
+ self, console: Console, options: ConsoleOptions
+ ) -> RenderResult:
+
+ width = min(
+ self.width if self.width is not None else options.max_width,
+ options.max_width,
+ )
+
+ if self.begin >= self.end:
+ yield Segment(" " * width, self.style)
+ yield Segment.line()
+ return
+
+ prefix_complete_eights = int(width * 8 * self.begin / self.size)
+ prefix_bar_count = prefix_complete_eights // 8
+ prefix_eights_count = prefix_complete_eights % 8
+
+ body_complete_eights = int(width * 8 * self.end / self.size)
+ body_bar_count = body_complete_eights // 8
+ body_eights_count = body_complete_eights % 8
+
+ # When start and end fall into the same cell, we ideally should render
+ # a symbol that's "center-aligned", but there is no good symbol in Unicode.
+ # In this case, we fall back to right-aligned block symbol for simplicity.
+
+ prefix = " " * prefix_bar_count
+ if prefix_eights_count:
+ prefix += BEGIN_BLOCK_ELEMENTS[prefix_eights_count]
+
+ body = FULL_BLOCK * body_bar_count
+ if body_eights_count:
+ body += END_BLOCK_ELEMENTS[body_eights_count]
+
+ suffix = " " * (width - len(body))
+
+ yield Segment(prefix + body[len(prefix) :] + suffix, self.style)
+ yield Segment.line()
+
+ def __rich_measure__(
+ self, console: Console, options: ConsoleOptions
+ ) -> Measurement:
+ return (
+ Measurement(self.width, self.width)
+ if self.width is not None
+ else Measurement(4, options.max_width)
+ )
diff --git a/src/pip/_vendor/rich/box.py b/src/pip/_vendor/rich/box.py
new file mode 100644
index 000000000..aec2926be
--- /dev/null
+++ b/src/pip/_vendor/rich/box.py
@@ -0,0 +1,483 @@
+import sys
+from typing import TYPE_CHECKING, Iterable, List
+
+if sys.version_info >= (3, 8):
+ from typing import Literal
+else:
+ from pip._vendor.typing_extensions import Literal # pragma: no cover
+
+
+from ._loop import loop_last
+
+if TYPE_CHECKING:
+ from pip._vendor.rich.console import ConsoleOptions
+
+
+class Box:
+ """Defines characters to render boxes.
+
+ ┌─┬┐ top
+ │ ││ head
+ ├─┼┤ head_row
+ │ ││ mid
+ ├─┼┤ row
+ ├─┼┤ foot_row
+ │ ││ foot
+ └─┴┘ bottom
+
+ Args:
+ box (str): Characters making up box.
+ ascii (bool, optional): True if this box uses ascii characters only. Default is False.
+ """
+
+ def __init__(self, box: str, *, ascii: bool = False) -> None:
+ self._box = box
+ self.ascii = ascii
+ line1, line2, line3, line4, line5, line6, line7, line8 = box.splitlines()
+ # top
+ self.top_left, self.top, self.top_divider, self.top_right = iter(line1)
+ # head
+ self.head_left, _, self.head_vertical, self.head_right = iter(line2)
+ # head_row
+ (
+ self.head_row_left,
+ self.head_row_horizontal,
+ self.head_row_cross,
+ self.head_row_right,
+ ) = iter(line3)
+
+ # mid
+ self.mid_left, _, self.mid_vertical, self.mid_right = iter(line4)
+ # row
+ self.row_left, self.row_horizontal, self.row_cross, self.row_right = iter(line5)
+ # foot_row
+ (
+ self.foot_row_left,
+ self.foot_row_horizontal,
+ self.foot_row_cross,
+ self.foot_row_right,
+ ) = iter(line6)
+ # foot
+ self.foot_left, _, self.foot_vertical, self.foot_right = iter(line7)
+ # bottom
+ self.bottom_left, self.bottom, self.bottom_divider, self.bottom_right = iter(
+ line8
+ )
+
+ def __repr__(self) -> str:
+ return "Box(...)"
+
+ def __str__(self) -> str:
+ return self._box
+
+ def substitute(self, options: "ConsoleOptions", safe: bool = True) -> "Box":
+ """Substitute this box for another if it won't render due to platform issues.
+
+ Args:
+ options (ConsoleOptions): Console options used in rendering.
+ safe (bool, optional): Substitute this for another Box if there are known problems
+ displaying on the platform (currently only relevant on Windows). Default is True.
+
+ Returns:
+ Box: A different Box or the same Box.
+ """
+ box = self
+ if options.legacy_windows and safe:
+ box = LEGACY_WINDOWS_SUBSTITUTIONS.get(box, box)
+ if options.ascii_only and not box.ascii:
+ box = ASCII
+ return box
+
+ def get_top(self, widths: Iterable[int]) -> str:
+ """Get the top of a simple box.
+
+ Args:
+ widths (List[int]): Widths of columns.
+
+ Returns:
+ str: A string of box characters.
+ """
+
+ parts: List[str] = []
+ append = parts.append
+ append(self.top_left)
+ for last, width in loop_last(widths):
+ append(self.top * width)
+ if not last:
+ append(self.top_divider)
+ append(self.top_right)
+ return "".join(parts)
+
+ def get_row(
+ self,
+ widths: Iterable[int],
+ level: Literal["head", "row", "foot", "mid"] = "row",
+ edge: bool = True,
+ ) -> str:
+ """Get the top of a simple box.
+
+ Args:
+ width (List[int]): Widths of columns.
+
+ Returns:
+ str: A string of box characters.
+ """
+ if level == "head":
+ left = self.head_row_left
+ horizontal = self.head_row_horizontal
+ cross = self.head_row_cross
+ right = self.head_row_right
+ elif level == "row":
+ left = self.row_left
+ horizontal = self.row_horizontal
+ cross = self.row_cross
+ right = self.row_right
+ elif level == "mid":
+ left = self.mid_left
+ horizontal = " "
+ cross = self.mid_vertical
+ right = self.mid_right
+ elif level == "foot":
+ left = self.foot_row_left
+ horizontal = self.foot_row_horizontal
+ cross = self.foot_row_cross
+ right = self.foot_row_right
+ else:
+ raise ValueError("level must be 'head', 'row' or 'foot'")
+
+ parts: List[str] = []
+ append = parts.append
+ if edge:
+ append(left)
+ for last, width in loop_last(widths):
+ append(horizontal * width)
+ if not last:
+ append(cross)
+ if edge:
+ append(right)
+ return "".join(parts)
+
+ def get_bottom(self, widths: Iterable[int]) -> str:
+ """Get the bottom of a simple box.
+
+ Args:
+ widths (List[int]): Widths of columns.
+
+ Returns:
+ str: A string of box characters.
+ """
+
+ parts: List[str] = []
+ append = parts.append
+ append(self.bottom_left)
+ for last, width in loop_last(widths):
+ append(self.bottom * width)
+ if not last:
+ append(self.bottom_divider)
+ append(self.bottom_right)
+ return "".join(parts)
+
+
+ASCII: Box = Box(
+ """\
++--+
+| ||
+|-+|
+| ||
+|-+|
+|-+|
+| ||
++--+
+""",
+ ascii=True,
+)
+
+ASCII2: Box = Box(
+ """\
++-++
+| ||
++-++
+| ||
++-++
++-++
+| ||
++-++
+""",
+ ascii=True,
+)
+
+ASCII_DOUBLE_HEAD: Box = Box(
+ """\
++-++
+| ||
++=++
+| ||
++-++
++-++
+| ||
++-++
+""",
+ ascii=True,
+)
+
+SQUARE: Box = Box(
+ """\
+┌─┬┐
+│ ││
+├─┼┤
+│ ││
+├─┼┤
+├─┼┤
+│ ││
+└─┴┘
+"""
+)
+
+SQUARE_DOUBLE_HEAD: Box = Box(
+ """\
+┌─┬┐
+│ ││
+╞═╪╡
+│ ││
+├─┼┤
+├─┼┤
+│ ││
+└─┴┘
+"""
+)
+
+MINIMAL: Box = Box(
+ """\
+ ╷
+ │
+╶─┼╴
+ │
+╶─┼╴
+╶─┼╴
+ │
+ ╵
+"""
+)
+
+
+MINIMAL_HEAVY_HEAD: Box = Box(
+ """\
+ ╷
+ │
+╺━┿╸
+ │
+╶─┼╴
+╶─┼╴
+ │
+ ╵
+"""
+)
+
+MINIMAL_DOUBLE_HEAD: Box = Box(
+ """\
+ ╷
+ │
+ ═╪
+ │
+ ─┼
+ ─┼
+ │
+ ╵
+"""
+)
+
+
+SIMPLE: Box = Box(
+ """\
+
+
+ ──
+
+
+ ──
+
+
+"""
+)
+
+SIMPLE_HEAD: Box = Box(
+ """\
+
+
+ ──
+
+
+
+
+
+"""
+)
+
+
+SIMPLE_HEAVY: Box = Box(
+ """\
+
+
+ ━━
+
+
+ ━━
+
+
+"""
+)
+
+
+HORIZONTALS: Box = Box(
+ """\
+ ──
+
+ ──
+
+ ──
+ ──
+
+ ──
+"""
+)
+
+ROUNDED: Box = Box(
+ """\
+╭─┬╮
+│ ││
+├─┼┤
+│ ││
+├─┼┤
+├─┼┤
+│ ││
+╰─┴╯
+"""
+)
+
+HEAVY: Box = Box(
+ """\
+┏━┳┓
+┃ ┃┃
+┣━╋┫
+┃ ┃┃
+┣━╋┫
+┣━╋┫
+┃ ┃┃
+┗━┻┛
+"""
+)
+
+HEAVY_EDGE: Box = Box(
+ """\
+┏━┯┓
+┃ │┃
+┠─┼┨
+┃ │┃
+┠─┼┨
+┠─┼┨
+┃ │┃
+┗━┷┛
+"""
+)
+
+HEAVY_HEAD: Box = Box(
+ """\
+┏━┳┓
+┃ ┃┃
+┡━╇┩
+│ ││
+├─┼┤
+├─┼┤
+│ ││
+└─┴┘
+"""
+)
+
+DOUBLE: Box = Box(
+ """\
+╔═╦╗
+║ ║║
+╠═╬╣
+║ ║║
+╠═╬╣
+╠═╬╣
+║ ║║
+╚═╩╝
+"""
+)
+
+DOUBLE_EDGE: Box = Box(
+ """\
+╔═╤╗
+║ │║
+╟─┼╢
+║ │║
+╟─┼╢
+╟─┼╢
+║ │║
+╚═╧╝
+"""
+)
+
+# Map Boxes that don't render with raster fonts on to equivalent that do
+LEGACY_WINDOWS_SUBSTITUTIONS = {
+ ROUNDED: SQUARE,
+ MINIMAL_HEAVY_HEAD: MINIMAL,
+ SIMPLE_HEAVY: SIMPLE,
+ HEAVY: SQUARE,
+ HEAVY_EDGE: SQUARE,
+ HEAVY_HEAD: SQUARE,
+}
+
+
+if __name__ == "__main__": # pragma: no cover
+
+ from pip._vendor.rich.columns import Columns
+ from pip._vendor.rich.panel import Panel
+
+ from . import box as box
+ from .console import Console
+ from .table import Table
+ from .text import Text
+
+ console = Console(record=True)
+
+ BOXES = [
+ "ASCII",
+ "ASCII2",
+ "ASCII_DOUBLE_HEAD",
+ "SQUARE",
+ "SQUARE_DOUBLE_HEAD",
+ "MINIMAL",
+ "MINIMAL_HEAVY_HEAD",
+ "MINIMAL_DOUBLE_HEAD",
+ "SIMPLE",
+ "SIMPLE_HEAD",
+ "SIMPLE_HEAVY",
+ "HORIZONTALS",
+ "ROUNDED",
+ "HEAVY",
+ "HEAVY_EDGE",
+ "HEAVY_HEAD",
+ "DOUBLE",
+ "DOUBLE_EDGE",
+ ]
+
+ console.print(Panel("[bold green]Box Constants", style="green"), justify="center")
+ console.print()
+
+ columns = Columns(expand=True, padding=2)
+ for box_name in sorted(BOXES):
+ table = Table(
+ show_footer=True, style="dim", border_style="not dim", expand=True
+ )
+ table.add_column("Header 1", "Footer 1")
+ table.add_column("Header 2", "Footer 2")
+ table.add_row("Cell", "Cell")
+ table.add_row("Cell", "Cell")
+ table.box = getattr(box, box_name)
+ table.title = Text(f"box.{box_name}", style="magenta")
+ columns.add_renderable(table)
+ console.print(columns)
+
+ # console.save_html("box.html", inline_styles=True)
diff --git a/src/pip/_vendor/rich/cells.py b/src/pip/_vendor/rich/cells.py
new file mode 100644
index 000000000..7f9c0c16c
--- /dev/null
+++ b/src/pip/_vendor/rich/cells.py
@@ -0,0 +1,133 @@
+from functools import lru_cache
+from typing import Dict, List
+
+from ._cell_widths import CELL_WIDTHS
+from ._lru_cache import LRUCache
+
+
+def cell_len(text: str, _cache: Dict[str, int] = LRUCache(1024 * 4)) -> int:
+ """Get the number of cells required to display text.
+
+ Args:
+ text (str): Text to display.
+
+ Returns:
+ int: Number of cells required to display the text.
+ """
+ cached_result = _cache.get(text, None)
+ if cached_result is not None:
+ return cached_result
+
+ _get_size = get_character_cell_size
+ total_size = sum(_get_size(character) for character in text)
+ if len(text) <= 64:
+ _cache[text] = total_size
+ return total_size
+
+
+@lru_cache(maxsize=4096)
+def get_character_cell_size(character: str) -> int:
+ """Get the cell size of a character.
+
+ Args:
+ character (str): A single character.
+
+ Returns:
+ int: Number of cells (0, 1 or 2) occupied by that character.
+ """
+
+ codepoint = ord(character)
+ if 127 > codepoint > 31:
+ # Shortcut for ascii
+ return 1
+ return _get_codepoint_cell_size(codepoint)
+
+
+@lru_cache(maxsize=4096)
+def _get_codepoint_cell_size(codepoint: int) -> int:
+ """Get the cell size of a character.
+
+ Args:
+ character (str): A single character.
+
+ Returns:
+ int: Number of cells (0, 1 or 2) occupied by that character.
+ """
+
+ _table = CELL_WIDTHS
+ lower_bound = 0
+ upper_bound = len(_table) - 1
+ index = (lower_bound + upper_bound) // 2
+ while True:
+ start, end, width = _table[index]
+ if codepoint < start:
+ upper_bound = index - 1
+ elif codepoint > end:
+ lower_bound = index + 1
+ else:
+ return 0 if width == -1 else width
+ if upper_bound < lower_bound:
+ break
+ index = (lower_bound + upper_bound) // 2
+ return 1
+
+
+def set_cell_size(text: str, total: int) -> str:
+ """Set the length of a string to fit within given number of cells."""
+ cell_size = cell_len(text)
+ if cell_size == total:
+ return text
+ if cell_size < total:
+ return text + " " * (total - cell_size)
+
+ start = 0
+ end = cell_size
+
+ # Binary search until we find the right size
+ while True:
+ pos = (start + end) // 2
+ before = text[:pos]
+ before_len = cell_len(before)
+ if before_len == total + 1 and cell_len(before[-1]) == 2:
+ return before[:-1] + " "
+ if before_len == total:
+ return before
+ if before_len > total:
+ end = pos
+ else:
+ start = pos
+
+
+# TODO: This is inefficient
+# TODO: This might not work with CWJ type characters
+def chop_cells(text: str, max_size: int, position: int = 0) -> List[str]:
+ """Break text in to equal (cell) length strings."""
+ _get_character_cell_size = get_character_cell_size
+ characters = [
+ (character, _get_character_cell_size(character)) for character in text
+ ][::-1]
+ total_size = position
+ lines: List[List[str]] = [[]]
+ append = lines[-1].append
+
+ pop = characters.pop
+ while characters:
+ character, size = pop()
+ if total_size + size > max_size:
+ lines.append([character])
+ append = lines[-1].append
+ total_size = size
+ else:
+ total_size += size
+ append(character)
+ return ["".join(line) for line in lines]
+
+
+if __name__ == "__main__": # pragma: no cover
+
+ print(get_character_cell_size("😽"))
+ for line in chop_cells("""这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑。""", 8):
+ print(line)
+ for n in range(80, 1, -1):
+ print(set_cell_size("""这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑。""", n) + "|")
+ print("x" * n)
diff --git a/src/pip/_vendor/rich/color.py b/src/pip/_vendor/rich/color.py
new file mode 100644
index 000000000..f0fa026d6
--- /dev/null
+++ b/src/pip/_vendor/rich/color.py
@@ -0,0 +1,581 @@
+import platform
+import re
+from colorsys import rgb_to_hls
+from enum import IntEnum
+from functools import lru_cache
+from typing import TYPE_CHECKING, NamedTuple, Optional, Tuple
+
+from ._palettes import EIGHT_BIT_PALETTE, STANDARD_PALETTE, WINDOWS_PALETTE
+from .color_triplet import ColorTriplet
+from .repr import rich_repr, Result
+from .terminal_theme import DEFAULT_TERMINAL_THEME
+
+if TYPE_CHECKING: # pragma: no cover
+ from .terminal_theme import TerminalTheme
+ from .text import Text
+
+
+WINDOWS = platform.system() == "Windows"
+
+
+class ColorSystem(IntEnum):
+ """One of the 3 color system supported by terminals."""
+
+ STANDARD = 1
+ EIGHT_BIT = 2
+ TRUECOLOR = 3
+ WINDOWS = 4
+
+ def __repr__(self) -> str:
+ return f"ColorSystem.{self.name}"
+
+
+class ColorType(IntEnum):
+ """Type of color stored in Color class."""
+
+ DEFAULT = 0
+ STANDARD = 1
+ EIGHT_BIT = 2
+ TRUECOLOR = 3
+ WINDOWS = 4
+
+ def __repr__(self) -> str:
+ return f"ColorType.{self.name}"
+
+
+ANSI_COLOR_NAMES = {
+ "black": 0,
+ "red": 1,
+ "green": 2,
+ "yellow": 3,
+ "blue": 4,
+ "magenta": 5,
+ "cyan": 6,
+ "white": 7,
+ "bright_black": 8,
+ "bright_red": 9,
+ "bright_green": 10,
+ "bright_yellow": 11,
+ "bright_blue": 12,
+ "bright_magenta": 13,
+ "bright_cyan": 14,
+ "bright_white": 15,
+ "grey0": 16,
+ "navy_blue": 17,
+ "dark_blue": 18,
+ "blue3": 20,
+ "blue1": 21,
+ "dark_green": 22,
+ "deep_sky_blue4": 25,
+ "dodger_blue3": 26,
+ "dodger_blue2": 27,
+ "green4": 28,
+ "spring_green4": 29,
+ "turquoise4": 30,
+ "deep_sky_blue3": 32,
+ "dodger_blue1": 33,
+ "green3": 40,
+ "spring_green3": 41,
+ "dark_cyan": 36,
+ "light_sea_green": 37,
+ "deep_sky_blue2": 38,
+ "deep_sky_blue1": 39,
+ "spring_green2": 47,
+ "cyan3": 43,
+ "dark_turquoise": 44,
+ "turquoise2": 45,
+ "green1": 46,
+ "spring_green1": 48,
+ "medium_spring_green": 49,
+ "cyan2": 50,
+ "cyan1": 51,
+ "dark_red": 88,
+ "deep_pink4": 125,
+ "purple4": 55,
+ "purple3": 56,
+ "blue_violet": 57,
+ "orange4": 94,
+ "grey37": 59,
+ "medium_purple4": 60,
+ "slate_blue3": 62,
+ "royal_blue1": 63,
+ "chartreuse4": 64,
+ "dark_sea_green4": 71,
+ "pale_turquoise4": 66,
+ "steel_blue": 67,
+ "steel_blue3": 68,
+ "cornflower_blue": 69,
+ "chartreuse3": 76,
+ "cadet_blue": 73,
+ "sky_blue3": 74,
+ "steel_blue1": 81,
+ "pale_green3": 114,
+ "sea_green3": 78,
+ "aquamarine3": 79,
+ "medium_turquoise": 80,
+ "chartreuse2": 112,
+ "sea_green2": 83,
+ "sea_green1": 85,
+ "aquamarine1": 122,
+ "dark_slate_gray2": 87,
+ "dark_magenta": 91,
+ "dark_violet": 128,
+ "purple": 129,
+ "light_pink4": 95,
+ "plum4": 96,
+ "medium_purple3": 98,
+ "slate_blue1": 99,
+ "yellow4": 106,
+ "wheat4": 101,
+ "grey53": 102,
+ "light_slate_grey": 103,
+ "medium_purple": 104,
+ "light_slate_blue": 105,
+ "dark_olive_green3": 149,
+ "dark_sea_green": 108,
+ "light_sky_blue3": 110,
+ "sky_blue2": 111,
+ "dark_sea_green3": 150,
+ "dark_slate_gray3": 116,
+ "sky_blue1": 117,
+ "chartreuse1": 118,
+ "light_green": 120,
+ "pale_green1": 156,
+ "dark_slate_gray1": 123,
+ "red3": 160,
+ "medium_violet_red": 126,
+ "magenta3": 164,
+ "dark_orange3": 166,
+ "indian_red": 167,
+ "hot_pink3": 168,
+ "medium_orchid3": 133,
+ "medium_orchid": 134,
+ "medium_purple2": 140,
+ "dark_goldenrod": 136,
+ "light_salmon3": 173,
+ "rosy_brown": 138,
+ "grey63": 139,
+ "medium_purple1": 141,
+ "gold3": 178,
+ "dark_khaki": 143,
+ "navajo_white3": 144,
+ "grey69": 145,
+ "light_steel_blue3": 146,
+ "light_steel_blue": 147,
+ "yellow3": 184,
+ "dark_sea_green2": 157,
+ "light_cyan3": 152,
+ "light_sky_blue1": 153,
+ "green_yellow": 154,
+ "dark_olive_green2": 155,
+ "dark_sea_green1": 193,
+ "pale_turquoise1": 159,
+ "deep_pink3": 162,
+ "magenta2": 200,
+ "hot_pink2": 169,
+ "orchid": 170,
+ "medium_orchid1": 207,
+ "orange3": 172,
+ "light_pink3": 174,
+ "pink3": 175,
+ "plum3": 176,
+ "violet": 177,
+ "light_goldenrod3": 179,
+ "tan": 180,
+ "misty_rose3": 181,
+ "thistle3": 182,
+ "plum2": 183,
+ "khaki3": 185,
+ "light_goldenrod2": 222,
+ "light_yellow3": 187,
+ "grey84": 188,
+ "light_steel_blue1": 189,
+ "yellow2": 190,
+ "dark_olive_green1": 192,
+ "honeydew2": 194,
+ "light_cyan1": 195,
+ "red1": 196,
+ "deep_pink2": 197,
+ "deep_pink1": 199,
+ "magenta1": 201,
+ "orange_red1": 202,
+ "indian_red1": 204,
+ "hot_pink": 206,
+ "dark_orange": 208,
+ "salmon1": 209,
+ "light_coral": 210,
+ "pale_violet_red1": 211,
+ "orchid2": 212,
+ "orchid1": 213,
+ "orange1": 214,
+ "sandy_brown": 215,
+ "light_salmon1": 216,
+ "light_pink1": 217,
+ "pink1": 218,
+ "plum1": 219,
+ "gold1": 220,
+ "navajo_white1": 223,
+ "misty_rose1": 224,
+ "thistle1": 225,
+ "yellow1": 226,
+ "light_goldenrod1": 227,
+ "khaki1": 228,
+ "wheat1": 229,
+ "cornsilk1": 230,
+ "grey100": 231,
+ "grey3": 232,
+ "grey7": 233,
+ "grey11": 234,
+ "grey15": 235,
+ "grey19": 236,
+ "grey23": 237,
+ "grey27": 238,
+ "grey30": 239,
+ "grey35": 240,
+ "grey39": 241,
+ "grey42": 242,
+ "grey46": 243,
+ "grey50": 244,
+ "grey54": 245,
+ "grey58": 246,
+ "grey62": 247,
+ "grey66": 248,
+ "grey70": 249,
+ "grey74": 250,
+ "grey78": 251,
+ "grey82": 252,
+ "grey85": 253,
+ "grey89": 254,
+ "grey93": 255,
+}
+
+
+class ColorParseError(Exception):
+ """The color could not be parsed."""
+
+
+RE_COLOR = re.compile(
+ r"""^
+\#([0-9a-f]{6})$|
+color\(([0-9]{1,3})\)$|
+rgb\(([\d\s,]+)\)$
+""",
+ re.VERBOSE,
+)
+
+
+@rich_repr
+class Color(NamedTuple):
+ """Terminal color definition."""
+
+ name: str
+ """The name of the color (typically the input to Color.parse)."""
+ type: ColorType
+ """The type of the color."""
+ number: Optional[int] = None
+ """The color number, if a standard color, or None."""
+ triplet: Optional[ColorTriplet] = None
+ """A triplet of color components, if an RGB color."""
+
+ def __rich__(self) -> "Text":
+ """Dispays the actual color if Rich printed."""
+ from .text import Text
+ from .style import Style
+
+ return Text.assemble(
+ f"<color {self.name!r} ({self.type.name.lower()})",
+ ("⬤", Style(color=self)),
+ " >",
+ )
+
+ def __rich_repr__(self) -> Result:
+ yield self.name
+ yield self.type
+ yield "number", self.number, None
+ yield "triplet", self.triplet, None
+
+ @property
+ def system(self) -> ColorSystem:
+ """Get the native color system for this color."""
+ if self.type == ColorType.DEFAULT:
+ return ColorSystem.STANDARD
+ return ColorSystem(int(self.type))
+
+ @property
+ def is_system_defined(self) -> bool:
+ """Check if the color is ultimately defined by the system."""
+ return self.system not in (ColorSystem.EIGHT_BIT, ColorSystem.TRUECOLOR)
+
+ @property
+ def is_default(self) -> bool:
+ """Check if the color is a default color."""
+ return self.type == ColorType.DEFAULT
+
+ def get_truecolor(
+ self, theme: Optional["TerminalTheme"] = None, foreground: bool = True
+ ) -> ColorTriplet:
+ """Get an equivalent color triplet for this color.
+
+ Args:
+ theme (TerminalTheme, optional): Optional terminal theme, or None to use default. Defaults to None.
+ foreground (bool, optional): True for a foreground color, or False for background. Defaults to True.
+
+ Returns:
+ ColorTriplet: A color triplet containing RGB components.
+ """
+
+ if theme is None:
+ theme = DEFAULT_TERMINAL_THEME
+ if self.type == ColorType.TRUECOLOR:
+ assert self.triplet is not None
+ return self.triplet
+ elif self.type == ColorType.EIGHT_BIT:
+ assert self.number is not None
+ return EIGHT_BIT_PALETTE[self.number]
+ elif self.type == ColorType.STANDARD:
+ assert self.number is not None
+ return theme.ansi_colors[self.number]
+ elif self.type == ColorType.WINDOWS:
+ assert self.number is not None
+ return WINDOWS_PALETTE[self.number]
+ else: # self.type == ColorType.DEFAULT:
+ assert self.number is None
+ return theme.foreground_color if foreground else theme.background_color
+
+ @classmethod
+ def from_ansi(cls, number: int) -> "Color":
+ """Create a Color number from it's 8-bit ansi number.
+
+ Args:
+ number (int): A number between 0-255 inclusive.
+
+ Returns:
+ Color: A new Color instance.
+ """
+ return cls(
+ name=f"color({number})",
+ type=(ColorType.STANDARD if number < 16 else ColorType.EIGHT_BIT),
+ number=number,
+ )
+
+ @classmethod
+ def from_triplet(cls, triplet: "ColorTriplet") -> "Color":
+ """Create a truecolor RGB color from a triplet of values.
+
+ Args:
+ triplet (ColorTriplet): A color triplet containing red, green and blue components.
+
+ Returns:
+ Color: A new color object.
+ """
+ return cls(name=triplet.hex, type=ColorType.TRUECOLOR, triplet=triplet)
+
+ @classmethod
+ def from_rgb(cls, red: float, green: float, blue: float) -> "Color":
+ """Create a truecolor from three color components in the range(0->255).
+
+ Args:
+ red (float): Red component in range 0-255.
+ green (float): Green component in range 0-255.
+ blue (float): Blue component in range 0-255.
+
+ Returns:
+ Color: A new color object.
+ """
+ return cls.from_triplet(ColorTriplet(int(red), int(green), int(blue)))
+
+ @classmethod
+ def default(cls) -> "Color":
+ """Get a Color instance representing the default color.
+
+ Returns:
+ Color: Default color.
+ """
+ return cls(name="default", type=ColorType.DEFAULT)
+
+ @classmethod
+ @lru_cache(maxsize=1024)
+ def parse(cls, color: str) -> "Color":
+ """Parse a color definition."""
+ original_color = color
+ color = color.lower().strip()
+
+ if color == "default":
+ return cls(color, type=ColorType.DEFAULT)
+
+ color_number = ANSI_COLOR_NAMES.get(color)
+ if color_number is not None:
+ return cls(
+ color,
+ type=(ColorType.STANDARD if color_number < 16 else ColorType.EIGHT_BIT),
+ number=color_number,
+ )
+
+ color_match = RE_COLOR.match(color)
+ if color_match is None:
+ raise ColorParseError(f"{original_color!r} is not a valid color")
+
+ color_24, color_8, color_rgb = color_match.groups()
+ if color_24:
+ triplet = ColorTriplet(
+ int(color_24[0:2], 16), int(color_24[2:4], 16), int(color_24[4:6], 16)
+ )
+ return cls(color, ColorType.TRUECOLOR, triplet=triplet)
+
+ elif color_8:
+ number = int(color_8)
+ if number > 255:
+ raise ColorParseError(f"color number must be <= 255 in {color!r}")
+ return cls(
+ color,
+ type=(ColorType.STANDARD if number < 16 else ColorType.EIGHT_BIT),
+ number=number,
+ )
+
+ else: # color_rgb:
+ components = color_rgb.split(",")
+ if len(components) != 3:
+ raise ColorParseError(
+ f"expected three components in {original_color!r}"
+ )
+ red, green, blue = components
+ triplet = ColorTriplet(int(red), int(green), int(blue))
+ if not all(component <= 255 for component in triplet):
+ raise ColorParseError(
+ f"color components must be <= 255 in {original_color!r}"
+ )
+ return cls(color, ColorType.TRUECOLOR, triplet=triplet)
+
+ @lru_cache(maxsize=1024)
+ def get_ansi_codes(self, foreground: bool = True) -> Tuple[str, ...]:
+ """Get the ANSI escape codes for this color."""
+ _type = self.type
+ if _type == ColorType.DEFAULT:
+ return ("39" if foreground else "49",)
+
+ elif _type == ColorType.WINDOWS:
+ number = self.number
+ assert number is not None
+ fore, back = (30, 40) if number < 8 else (82, 92)
+ return (str(fore + number if foreground else back + number),)
+
+ elif _type == ColorType.STANDARD:
+ number = self.number
+ assert number is not None
+ fore, back = (30, 40) if number < 8 else (82, 92)
+ return (str(fore + number if foreground else back + number),)
+
+ elif _type == ColorType.EIGHT_BIT:
+ assert self.number is not None
+ return ("38" if foreground else "48", "5", str(self.number))
+
+ else: # self.standard == ColorStandard.TRUECOLOR:
+ assert self.triplet is not None
+ red, green, blue = self.triplet
+ return ("38" if foreground else "48", "2", str(red), str(green), str(blue))
+
+ @lru_cache(maxsize=1024)
+ def downgrade(self, system: ColorSystem) -> "Color":
+ """Downgrade a color system to a system with fewer colors."""
+
+ 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:
+ gray = round(l * 25.0)
+ if gray == 0:
+ color_number = 16
+ elif gray == 25:
+ color_number = 231
+ else:
+ color_number = 231 + gray
+ return Color(self.name, ColorType.EIGHT_BIT, number=color_number)
+
+ color_number = (
+ 16 + 36 * round(red * 5.0) + 6 * round(green * 5.0) + round(blue * 5.0)
+ )
+ return Color(self.name, ColorType.EIGHT_BIT, number=color_number)
+
+ # Convert to standard from truecolor or 8-bit
+ elif system == ColorSystem.STANDARD:
+ if self.system == ColorSystem.TRUECOLOR:
+ assert self.triplet is not None
+ triplet = self.triplet
+ else: # self.system == ColorSystem.EIGHT_BIT
+ assert self.number is not None
+ triplet = ColorTriplet(*EIGHT_BIT_PALETTE[self.number])
+
+ color_number = STANDARD_PALETTE.match(triplet)
+ return Color(self.name, ColorType.STANDARD, number=color_number)
+
+ elif system == ColorSystem.WINDOWS:
+ if self.system == ColorSystem.TRUECOLOR:
+ assert self.triplet is not None
+ triplet = self.triplet
+ else: # self.system == ColorSystem.EIGHT_BIT
+ assert self.number is not None
+ if self.number < 16:
+ return Color(self.name, ColorType.WINDOWS, number=self.number)
+ triplet = ColorTriplet(*EIGHT_BIT_PALETTE[self.number])
+
+ color_number = WINDOWS_PALETTE.match(triplet)
+ return Color(self.name, ColorType.WINDOWS, number=color_number)
+
+ return self
+
+
+def parse_rgb_hex(hex_color: str) -> ColorTriplet:
+ """Parse six hex characters in to RGB triplet."""
+ assert len(hex_color) == 6, "must be 6 characters"
+ color = ColorTriplet(
+ int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16)
+ )
+ return color
+
+
+def blend_rgb(
+ color1: ColorTriplet, color2: ColorTriplet, cross_fade: float = 0.5
+) -> ColorTriplet:
+ """Blend one RGB color in to another."""
+ r1, g1, b1 = color1
+ r2, g2, b2 = color2
+ new_color = ColorTriplet(
+ int(r1 + (r2 - r1) * cross_fade),
+ int(g1 + (g2 - g1) * cross_fade),
+ int(b1 + (b2 - b1) * cross_fade),
+ )
+ return new_color
+
+
+if __name__ == "__main__": # pragma: no cover
+
+ from .console import Console
+ from .table import Table
+ from .text import Text
+
+ console = Console()
+
+ table = Table(show_footer=False, show_edge=True)
+ table.add_column("Color", width=10, overflow="ellipsis")
+ table.add_column("Number", justify="right", style="yellow")
+ table.add_column("Name", style="green")
+ table.add_column("Hex", style="blue")
+ table.add_column("RGB", style="magenta")
+
+ colors = sorted((v, k) for k, v in ANSI_COLOR_NAMES.items())
+ for color_number, name in colors:
+ color_cell = Text(" " * 10, style=f"on {name}")
+ if color_number < 16:
+ table.add_row(color_cell, f"{color_number}", Text(f'"{name}"'))
+ else:
+ color = EIGHT_BIT_PALETTE[color_number] # type: ignore
+ table.add_row(
+ color_cell, str(color_number), Text(f'"{name}"'), color.hex, color.rgb
+ )
+
+ console.print(table)
diff --git a/src/pip/_vendor/rich/color_triplet.py b/src/pip/_vendor/rich/color_triplet.py
new file mode 100644
index 000000000..02cab3282
--- /dev/null
+++ b/src/pip/_vendor/rich/color_triplet.py
@@ -0,0 +1,38 @@
+from typing import NamedTuple, Tuple
+
+
+class ColorTriplet(NamedTuple):
+ """The red, green, and blue components of a color."""
+
+ red: int
+ """Red component in 0 to 255 range."""
+ green: int
+ """Green component in 0 to 255 range."""
+ blue: int
+ """Blue component in 0 to 255 range."""
+
+ @property
+ def hex(self) -> str:
+ """get the color triplet in CSS style."""
+ red, green, blue = self
+ return f"#{red:02x}{green:02x}{blue:02x}"
+
+ @property
+ def rgb(self) -> str:
+ """The color in RGB format.
+
+ Returns:
+ str: An rgb color, e.g. ``"rgb(100,23,255)"``.
+ """
+ red, green, blue = self
+ return f"rgb({red},{green},{blue})"
+
+ @property
+ def normalized(self) -> Tuple[float, float, float]:
+ """Convert components into floats between 0 and 1.
+
+ Returns:
+ Tuple[float, float, float]: A tuple of three normalized colour components.
+ """
+ red, green, blue = self
+ return red / 255.0, green / 255.0, blue / 255.0
diff --git a/src/pip/_vendor/rich/columns.py b/src/pip/_vendor/rich/columns.py
new file mode 100644
index 000000000..669a3a707
--- /dev/null
+++ b/src/pip/_vendor/rich/columns.py
@@ -0,0 +1,187 @@
+from collections import defaultdict
+from itertools import chain
+from operator import itemgetter
+from typing import Dict, Iterable, List, Optional, Tuple
+
+from .align import Align, AlignMethod
+from .console import Console, ConsoleOptions, RenderableType, RenderResult
+from .constrain import Constrain
+from .measure import Measurement
+from .padding import Padding, PaddingDimensions
+from .table import Table
+from .text import TextType
+from .jupyter import JupyterMixin
+
+
+class Columns(JupyterMixin):
+ """Display renderables in neat columns.
+
+ Args:
+ renderables (Iterable[RenderableType]): Any number of Rich renderables (including str).
+ width (int, optional): The desired width of the columns, or None to auto detect. Defaults to None.
+ padding (PaddingDimensions, optional): Optional padding around cells. Defaults to (0, 1).
+ expand (bool, optional): Expand columns to full width. Defaults to False.
+ equal (bool, optional): Arrange in to equal sized columns. Defaults to False.
+ column_first (bool, optional): Align items from top to bottom (rather than left to right). Defaults to False.
+ right_to_left (bool, optional): Start column from right hand side. Defaults to False.
+ align (str, optional): Align value ("left", "right", or "center") or None for default. Defaults to None.
+ title (TextType, optional): Optional title for Columns.
+ """
+
+ def __init__(
+ self,
+ renderables: Optional[Iterable[RenderableType]] = None,
+ padding: PaddingDimensions = (0, 1),
+ *,
+ width: Optional[int] = None,
+ expand: bool = False,
+ equal: bool = False,
+ column_first: bool = False,
+ right_to_left: bool = False,
+ align: Optional[AlignMethod] = None,
+ title: Optional[TextType] = None,
+ ) -> None:
+ self.renderables = list(renderables or [])
+ self.width = width
+ self.padding = padding
+ self.expand = expand
+ self.equal = equal
+ self.column_first = column_first
+ self.right_to_left = right_to_left
+ self.align: Optional[AlignMethod] = align
+ self.title = title
+
+ def add_renderable(self, renderable: RenderableType) -> None:
+ """Add a renderable to the columns.
+
+ Args:
+ renderable (RenderableType): Any renderable object.
+ """
+ self.renderables.append(renderable)
+
+ def __rich_console__(
+ self, console: Console, options: ConsoleOptions
+ ) -> RenderResult:
+ render_str = console.render_str
+ renderables = [
+ render_str(renderable) if isinstance(renderable, str) else renderable
+ for renderable in self.renderables
+ ]
+ if not renderables:
+ return
+ _top, right, _bottom, left = Padding.unpack(self.padding)
+ width_padding = max(left, right)
+ max_width = options.max_width
+ widths: Dict[int, int] = defaultdict(int)
+ column_count = len(renderables)
+
+ get_measurement = Measurement.get
+ renderable_widths = [
+ get_measurement(console, options, renderable).maximum
+ for renderable in renderables
+ ]
+ if self.equal:
+ renderable_widths = [max(renderable_widths)] * len(renderable_widths)
+
+ def iter_renderables(
+ column_count: int,
+ ) -> Iterable[Tuple[int, Optional[RenderableType]]]:
+ item_count = len(renderables)
+ if self.column_first:
+ width_renderables = list(zip(renderable_widths, renderables))
+
+ column_lengths: List[int] = [item_count // column_count] * column_count
+ for col_no in range(item_count % column_count):
+ column_lengths[col_no] += 1
+
+ row_count = (item_count + column_count - 1) // column_count
+ cells = [[-1] * column_count for _ in range(row_count)]
+ row = col = 0
+ for index in range(item_count):
+ cells[row][col] = index
+ column_lengths[col] -= 1
+ if column_lengths[col]:
+ row += 1
+ else:
+ col += 1
+ row = 0
+ for index in chain.from_iterable(cells):
+ if index == -1:
+ break
+ yield width_renderables[index]
+ else:
+ yield from zip(renderable_widths, renderables)
+ # Pad odd elements with spaces
+ if item_count % column_count:
+ for _ in range(column_count - (item_count % column_count)):
+ yield 0, None
+
+ table = Table.grid(padding=self.padding, collapse_padding=True, pad_edge=False)
+ table.expand = self.expand
+ table.title = self.title
+
+ if self.width is not None:
+ column_count = (max_width) // (self.width + width_padding)
+ for _ in range(column_count):
+ table.add_column(width=self.width)
+ else:
+ while column_count > 1:
+ widths.clear()
+ column_no = 0
+ for renderable_width, _ in iter_renderables(column_count):
+ widths[column_no] = max(widths[column_no], renderable_width)
+ total_width = sum(widths.values()) + width_padding * (
+ len(widths) - 1
+ )
+ if total_width > max_width:
+ column_count = len(widths) - 1
+ break
+ else:
+ column_no = (column_no + 1) % column_count
+ else:
+ break
+
+ get_renderable = itemgetter(1)
+ _renderables = [
+ get_renderable(_renderable)
+ for _renderable in iter_renderables(column_count)
+ ]
+ if self.equal:
+ _renderables = [
+ None
+ if renderable is None
+ else Constrain(renderable, renderable_widths[0])
+ for renderable in _renderables
+ ]
+ if self.align:
+ align = self.align
+ _Align = Align
+ _renderables = [
+ None if renderable is None else _Align(renderable, align)
+ for renderable in _renderables
+ ]
+
+ right_to_left = self.right_to_left
+ add_row = table.add_row
+ for start in range(0, len(_renderables), column_count):
+ row = _renderables[start : start + column_count]
+ if right_to_left:
+ row = row[::-1]
+ add_row(*row)
+ yield table
+
+
+if __name__ == "__main__": # pragma: no cover
+ import os
+
+ console = Console()
+
+ files = [f"{i} {s}" for i, s in enumerate(sorted(os.listdir()))]
+ columns = Columns(files, padding=(0, 1), expand=False, equal=False)
+ console.print(columns)
+ console.rule()
+ columns.column_first = True
+ console.print(columns)
+ columns.right_to_left = True
+ console.rule()
+ console.print(columns)
diff --git a/src/pip/_vendor/rich/console.py b/src/pip/_vendor/rich/console.py
new file mode 100644
index 000000000..b7ae13574
--- /dev/null
+++ b/src/pip/_vendor/rich/console.py
@@ -0,0 +1,2191 @@
+import inspect
+import os
+import platform
+import shutil
+import sys
+import threading
+from abc import ABC, abstractmethod
+from dataclasses import dataclass, field
+from datetime import datetime
+from functools import wraps
+from getpass import getpass
+from html import escape
+from inspect import isclass
+from itertools import islice
+from time import monotonic
+from types import FrameType, TracebackType, ModuleType
+from typing import (
+ IO,
+ TYPE_CHECKING,
+ Any,
+ Callable,
+ Dict,
+ Iterable,
+ List,
+ Mapping,
+ NamedTuple,
+ Optional,
+ TextIO,
+ Tuple,
+ Type,
+ Union,
+ cast,
+)
+
+if sys.version_info >= (3, 8):
+ from typing import Literal, Protocol, runtime_checkable
+else:
+ from pip._vendor.typing_extensions import (
+ Literal,
+ Protocol,
+ runtime_checkable,
+ ) # pragma: no cover
+
+from . import errors, themes
+from ._emoji_replace import _emoji_replace
+from ._log_render import FormatTimeCallable, LogRender
+from .align import Align, AlignMethod
+from .color import ColorSystem
+from .control import Control
+from .emoji import EmojiVariant
+from .highlighter import NullHighlighter, ReprHighlighter
+from .markup import render as render_markup
+from .measure import Measurement, measure_renderables
+from .pager import Pager, SystemPager
+from .pretty import Pretty, is_expandable
+from .region import Region
+from .scope import render_scope
+from .screen import Screen
+from .segment import Segment
+from .style import Style, StyleType
+from .styled import Styled
+from .terminal_theme import DEFAULT_TERMINAL_THEME, TerminalTheme
+from .text import Text, TextType
+from .theme import Theme, ThemeStack
+
+if TYPE_CHECKING:
+ from ._windows import WindowsConsoleFeatures
+ from .live import Live
+ from .status import Status
+
+WINDOWS = platform.system() == "Windows"
+
+HighlighterType = Callable[[Union[str, "Text"]], "Text"]
+JustifyMethod = Literal["default", "left", "center", "right", "full"]
+OverflowMethod = Literal["fold", "crop", "ellipsis", "ignore"]
+
+
+class NoChange:
+ pass
+
+
+NO_CHANGE = NoChange()
+
+
+CONSOLE_HTML_FORMAT = """\
+<!DOCTYPE html>
+<head>
+<meta charset="UTF-8">
+<style>
+{stylesheet}
+body {{
+ color: {foreground};
+ background-color: {background};
+}}
+</style>
+</head>
+<html>
+<body>
+ <code>
+ <pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">{code}</pre>
+ </code>
+</body>
+</html>
+"""
+
+_TERM_COLORS = {"256color": ColorSystem.EIGHT_BIT, "16color": ColorSystem.STANDARD}
+
+
+class ConsoleDimensions(NamedTuple):
+ """Size of the terminal."""
+
+ width: int
+ """The width of the console in 'cells'."""
+ height: int
+ """The height of the console in lines."""
+
+
+@dataclass
+class ConsoleOptions:
+ """Options for __rich_console__ method."""
+
+ size: ConsoleDimensions
+ """Size of console."""
+ legacy_windows: bool
+ """legacy_windows: flag for legacy windows."""
+ min_width: int
+ """Minimum width of renderable."""
+ max_width: int
+ """Maximum width of renderable."""
+ is_terminal: bool
+ """True if the target is a terminal, otherwise False."""
+ encoding: str
+ """Encoding of terminal."""
+ max_height: int
+ """Height of container (starts as terminal)"""
+ justify: Optional[JustifyMethod] = None
+ """Justify value override for renderable."""
+ overflow: Optional[OverflowMethod] = None
+ """Overflow value override for renderable."""
+ no_wrap: Optional[bool] = False
+ """Disable wrapping for text."""
+ highlight: Optional[bool] = None
+ """Highlight override for render_str."""
+ markup: Optional[bool] = None
+ """Enable markup when rendering strings."""
+ height: Optional[int] = None
+
+ @property
+ def ascii_only(self) -> bool:
+ """Check if renderables should use ascii only."""
+ return not self.encoding.startswith("utf")
+
+ def copy(self) -> "ConsoleOptions":
+ """Return a copy of the options.
+
+ Returns:
+ ConsoleOptions: a copy of self.
+ """
+ options: ConsoleOptions = ConsoleOptions.__new__(ConsoleOptions)
+ options.__dict__ = self.__dict__.copy()
+ return options
+
+ def update(
+ self,
+ *,
+ width: Union[int, NoChange] = NO_CHANGE,
+ min_width: Union[int, NoChange] = NO_CHANGE,
+ max_width: Union[int, NoChange] = NO_CHANGE,
+ justify: Union[Optional[JustifyMethod], NoChange] = NO_CHANGE,
+ overflow: Union[Optional[OverflowMethod], NoChange] = NO_CHANGE,
+ no_wrap: Union[Optional[bool], NoChange] = NO_CHANGE,
+ highlight: Union[Optional[bool], NoChange] = NO_CHANGE,
+ markup: Union[Optional[bool], NoChange] = NO_CHANGE,
+ height: Union[Optional[int], NoChange] = NO_CHANGE,
+ ) -> "ConsoleOptions":
+ """Update values, return a copy."""
+ options = self.copy()
+ if not isinstance(width, NoChange):
+ options.min_width = options.max_width = max(0, width)
+ if not isinstance(min_width, NoChange):
+ options.min_width = min_width
+ if not isinstance(max_width, NoChange):
+ options.max_width = max_width
+ if not isinstance(justify, NoChange):
+ options.justify = justify
+ if not isinstance(overflow, NoChange):
+ options.overflow = overflow
+ if not isinstance(no_wrap, NoChange):
+ options.no_wrap = no_wrap
+ if not isinstance(highlight, NoChange):
+ options.highlight = highlight
+ if not isinstance(markup, NoChange):
+ options.markup = markup
+ if not isinstance(height, NoChange):
+ if height is not None:
+ options.max_height = height
+ options.height = None if height is None else max(0, height)
+ return options
+
+ def update_width(self, width: int) -> "ConsoleOptions":
+ """Update just the width, return a copy.
+
+ Args:
+ width (int): New width (sets both min_width and max_width)
+
+ Returns:
+ ~ConsoleOptions: New console options instance.
+ """
+ options = self.copy()
+ options.min_width = options.max_width = max(0, width)
+ return options
+
+ def update_dimensions(self, width: int, height: int) -> "ConsoleOptions":
+ """Update the width and height, and return a copy.
+
+ Args:
+ width (int): New width (sets both min_width and max_width).
+ height (int): New height.
+
+ Returns:
+ ~ConsoleOptions: New console options instance.
+ """
+ options = self.copy()
+ options.min_width = options.max_width = max(0, width)
+ options.height = height
+ options.max_height = height
+ return options
+
+
+@runtime_checkable
+class RichCast(Protocol):
+ """An object that may be 'cast' to a console renderable."""
+
+ def __rich__(self) -> Union["ConsoleRenderable", str]: # pragma: no cover
+ ...
+
+
+@runtime_checkable
+class ConsoleRenderable(Protocol):
+ """An object that supports the console protocol."""
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult": # pragma: no cover
+ ...
+
+
+RenderableType = Union[ConsoleRenderable, RichCast, str]
+"""A type that may be rendered by Console."""
+
+RenderResult = Iterable[Union[RenderableType, Segment]]
+"""The result of calling a __rich_console__ method."""
+
+
+_null_highlighter = NullHighlighter()
+
+
+class CaptureError(Exception):
+ """An error in the Capture context manager."""
+
+
+class NewLine:
+ """A renderable to generate new line(s)"""
+
+ def __init__(self, count: int = 1) -> None:
+ self.count = count
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> Iterable[Segment]:
+ yield Segment("\n" * self.count)
+
+
+class ScreenUpdate:
+ """Render a list of lines at a given offset."""
+
+ def __init__(self, lines: List[List[Segment]], x: int, y: int) -> None:
+ self._lines = lines
+ self.x = x
+ self.y = y
+
+ def __rich_console__(
+ self, console: "Console", options: ConsoleOptions
+ ) -> RenderResult:
+ x = self.x
+ move_to = Control.move_to
+ for offset, line in enumerate(self._lines, self.y):
+ yield move_to(x, offset)
+ yield from line
+
+
+class Capture:
+ """Context manager to capture the result of printing to the console.
+ See :meth:`~rich.console.Console.capture` for how to use.
+
+ Args:
+ console (Console): A console instance to capture output.
+ """
+
+ def __init__(self, console: "Console") -> None:
+ self._console = console
+ self._result: Optional[str] = None
+
+ def __enter__(self) -> "Capture":
+ self._console.begin_capture()
+ return self
+
+ def __exit__(
+ self,
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[TracebackType],
+ ) -> None:
+ self._result = self._console.end_capture()
+
+ def get(self) -> str:
+ """Get the result of the capture."""
+ if self._result is None:
+ raise CaptureError(
+ "Capture result is not available until context manager exits."
+ )
+ return self._result
+
+
+class ThemeContext:
+ """A context manager to use a temporary theme. See :meth:`~rich.console.Console.use_theme` for usage."""
+
+ def __init__(self, console: "Console", theme: Theme, inherit: bool = True) -> None:
+ self.console = console
+ self.theme = theme
+ self.inherit = inherit
+
+ def __enter__(self) -> "ThemeContext":
+ self.console.push_theme(self.theme)
+ return self
+
+ def __exit__(
+ self,
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[TracebackType],
+ ) -> None:
+ self.console.pop_theme()
+
+
+class PagerContext:
+ """A context manager that 'pages' content. See :meth:`~rich.console.Console.pager` for usage."""
+
+ def __init__(
+ self,
+ console: "Console",
+ pager: Optional[Pager] = None,
+ styles: bool = False,
+ links: bool = False,
+ ) -> None:
+ self._console = console
+ self.pager = SystemPager() if pager is None else pager
+ self.styles = styles
+ self.links = links
+
+ def __enter__(self) -> "PagerContext":
+ self._console._enter_buffer()
+ return self
+
+ def __exit__(
+ self,
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[TracebackType],
+ ) -> None:
+ if exc_type is None:
+ with self._console._lock:
+ buffer: List[Segment] = self._console._buffer[:]
+ del self._console._buffer[:]
+ segments: Iterable[Segment] = buffer
+ if not self.styles:
+ segments = Segment.strip_styles(segments)
+ elif not self.links:
+ segments = Segment.strip_links(segments)
+ content = self._console._render_buffer(segments)
+ self.pager.show(content)
+ self._console._exit_buffer()
+
+
+class ScreenContext:
+ """A context manager that enables an alternative screen. See :meth:`~rich.console.Console.screen` for usage."""
+
+ def __init__(
+ self, console: "Console", hide_cursor: bool, style: StyleType = ""
+ ) -> None:
+ self.console = console
+ self.hide_cursor = hide_cursor
+ self.screen = Screen(style=style)
+ self._changed = False
+
+ def update(
+ self, *renderables: RenderableType, style: Optional[StyleType] = None
+ ) -> None:
+ """Update the screen.
+
+ Args:
+ renderable (RenderableType, optional): Optional renderable to replace current renderable,
+ or None for no change. Defaults to None.
+ style: (Style, optional): Replacement style, or None for no change. Defaults to None.
+ """
+ if renderables:
+ self.screen.renderable = (
+ Group(*renderables) if len(renderables) > 1 else renderables[0]
+ )
+ if style is not None:
+ self.screen.style = style
+ self.console.print(self.screen, end="")
+
+ def __enter__(self) -> "ScreenContext":
+ self._changed = self.console.set_alt_screen(True)
+ if self._changed and self.hide_cursor:
+ self.console.show_cursor(False)
+ return self
+
+ def __exit__(
+ self,
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[TracebackType],
+ ) -> None:
+ if self._changed:
+ self.console.set_alt_screen(False)
+ if self.hide_cursor:
+ self.console.show_cursor(True)
+
+
+class Group:
+ """Takes a group of renderables and returns a renderable object that renders the group.
+
+ Args:
+ renderables (Iterable[RenderableType]): An iterable of renderable objects.
+ fit (bool, optional): Fit dimension of group to contents, or fill available space. Defaults to True.
+ """
+
+ def __init__(self, *renderables: "RenderableType", fit: bool = True) -> None:
+ self._renderables = renderables
+ self.fit = fit
+ self._render: Optional[List[RenderableType]] = None
+
+ @property
+ def renderables(self) -> List["RenderableType"]:
+ if self._render is None:
+ self._render = list(self._renderables)
+ return self._render
+
+ def __rich_measure__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "Measurement":
+ if self.fit:
+ return measure_renderables(console, options, self.renderables)
+ else:
+ return Measurement(options.max_width, options.max_width)
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> RenderResult:
+ yield from self.renderables
+
+
+RenderGroup = Group # TODO: deprecate at some point
+
+
+def group(fit: bool = True) -> Callable[..., Callable[..., Group]]:
+ """A decorator that turns an iterable of renderables in to a group.
+
+ Args:
+ fit (bool, optional): Fit dimension of group to contents, or fill available space. Defaults to True.
+ """
+
+ def decorator(
+ method: Callable[..., Iterable[RenderableType]]
+ ) -> Callable[..., Group]:
+ """Convert a method that returns an iterable of renderables in to a RenderGroup."""
+
+ @wraps(method)
+ def _replace(*args: Any, **kwargs: Any) -> Group:
+ renderables = method(*args, **kwargs)
+ return Group(*renderables, fit=fit)
+
+ return _replace
+
+ return decorator
+
+
+render_group = group
+
+
+def _is_jupyter() -> bool: # pragma: no cover
+ """Check if we're running in a Jupyter notebook."""
+ try:
+ get_ipython # type: ignore
+ except NameError:
+ return False
+ ipython = get_ipython() # type: ignore
+ shell = ipython.__class__.__name__
+ if "google.colab" in str(ipython.__class__) or shell == "ZMQInteractiveShell":
+ return True # Jupyter notebook or qtconsole
+ elif shell == "TerminalInteractiveShell":
+ return False # Terminal running IPython
+ else:
+ return False # Other type (?)
+
+
+COLOR_SYSTEMS = {
+ "standard": ColorSystem.STANDARD,
+ "256": ColorSystem.EIGHT_BIT,
+ "truecolor": ColorSystem.TRUECOLOR,
+ "windows": ColorSystem.WINDOWS,
+}
+
+
+_COLOR_SYSTEMS_NAMES = {system: name for name, system in COLOR_SYSTEMS.items()}
+
+
+@dataclass
+class ConsoleThreadLocals(threading.local):
+ """Thread local values for Console context."""
+
+ theme_stack: ThemeStack
+ buffer: List[Segment] = field(default_factory=list)
+ buffer_index: int = 0
+
+
+class RenderHook(ABC):
+ """Provides hooks in to the render process."""
+
+ @abstractmethod
+ def process_renderables(
+ self, renderables: List[ConsoleRenderable]
+ ) -> List[ConsoleRenderable]:
+ """Called with a list of objects to render.
+
+ This method can return a new list of renderables, or modify and return the same list.
+
+ Args:
+ renderables (List[ConsoleRenderable]): A number of renderable objects.
+
+ Returns:
+ List[ConsoleRenderable]: A replacement list of renderables.
+ """
+
+
+_windows_console_features: Optional["WindowsConsoleFeatures"] = None
+
+
+def get_windows_console_features() -> "WindowsConsoleFeatures": # pragma: no cover
+ global _windows_console_features
+ if _windows_console_features is not None:
+ return _windows_console_features
+ from ._windows import get_windows_console_features
+
+ _windows_console_features = get_windows_console_features()
+ return _windows_console_features
+
+
+def detect_legacy_windows() -> bool:
+ """Detect legacy Windows."""
+ return WINDOWS and not get_windows_console_features().vt
+
+
+if detect_legacy_windows(): # pragma: no cover
+ from pip._vendor.colorama import init
+
+ init(strip=False)
+
+
+class Console:
+ """A high level console interface.
+
+ Args:
+ color_system (str, optional): The color system supported by your terminal,
+ either ``"standard"``, ``"256"`` or ``"truecolor"``. Leave as ``"auto"`` to autodetect.
+ force_terminal (Optional[bool], optional): Enable/disable terminal control codes, or None to auto-detect terminal. Defaults to None.
+ force_jupyter (Optional[bool], optional): Enable/disable Jupyter rendering, or None to auto-detect Jupyter. Defaults to None.
+ force_interactive (Optional[bool], optional): Enable/disable interactive mode, or None to auto detect. Defaults to None.
+ soft_wrap (Optional[bool], optional): Set soft wrap default on print method. Defaults to False.
+ theme (Theme, optional): An optional style theme object, or ``None`` for default theme.
+ stderr (bool, optional): Use stderr rather than stdout if ``file`` is not specified. Defaults to False.
+ file (IO, optional): A file object where the console should write to. Defaults to stdout.
+ quiet (bool, Optional): Boolean to suppress all output. Defaults to False.
+ width (int, optional): The width of the terminal. Leave as default to auto-detect width.
+ height (int, optional): The height of the terminal. Leave as default to auto-detect height.
+ style (StyleType, optional): Style to apply to all output, or None for no style. Defaults to None.
+ no_color (Optional[bool], optional): Enabled no color mode, or None to auto detect. Defaults to None.
+ tab_size (int, optional): Number of spaces used to replace a tab character. Defaults to 8.
+ record (bool, optional): Boolean to enable recording of terminal output,
+ required to call :meth:`export_html` and :meth:`export_text`. Defaults to False.
+ markup (bool, optional): Boolean to enable :ref:`console_markup`. Defaults to True.
+ emoji (bool, optional): Enable emoji code. Defaults to True.
+ emoji_variant (str, optional): Optional emoji variant, either "text" or "emoji". Defaults to None.
+ highlight (bool, optional): Enable automatic highlighting. Defaults to True.
+ log_time (bool, optional): Boolean to enable logging of time by :meth:`log` methods. Defaults to True.
+ log_path (bool, optional): Boolean to enable the logging of the caller by :meth:`log`. Defaults to True.
+ log_time_format (Union[str, TimeFormatterCallable], optional): If ``log_time`` is enabled, either string for strftime or callable that formats the time. Defaults to "[%X] ".
+ highlighter (HighlighterType, optional): Default highlighter.
+ legacy_windows (bool, optional): Enable legacy Windows mode, or ``None`` to auto detect. Defaults to ``None``.
+ safe_box (bool, optional): Restrict box options that don't render on legacy Windows.
+ get_datetime (Callable[[], datetime], optional): Callable that gets the current time as a datetime.datetime object (used by Console.log),
+ or None for datetime.now.
+ get_time (Callable[[], time], optional): Callable that gets the current time in seconds, default uses time.monotonic.
+ """
+
+ _environ: Mapping[str, str] = os.environ
+
+ def __init__(
+ self,
+ *,
+ color_system: Optional[
+ Literal["auto", "standard", "256", "truecolor", "windows"]
+ ] = "auto",
+ force_terminal: Optional[bool] = None,
+ force_jupyter: Optional[bool] = None,
+ force_interactive: Optional[bool] = None,
+ soft_wrap: bool = False,
+ theme: Optional[Theme] = None,
+ stderr: bool = False,
+ file: Optional[IO[str]] = None,
+ quiet: bool = False,
+ width: Optional[int] = None,
+ height: Optional[int] = None,
+ style: Optional[StyleType] = None,
+ no_color: Optional[bool] = None,
+ tab_size: int = 8,
+ record: bool = False,
+ markup: bool = True,
+ emoji: bool = True,
+ emoji_variant: Optional[EmojiVariant] = None,
+ highlight: bool = True,
+ log_time: bool = True,
+ log_path: bool = True,
+ log_time_format: Union[str, FormatTimeCallable] = "[%X]",
+ highlighter: Optional["HighlighterType"] = ReprHighlighter(),
+ legacy_windows: Optional[bool] = None,
+ safe_box: bool = True,
+ get_datetime: Optional[Callable[[], datetime]] = None,
+ get_time: Optional[Callable[[], float]] = None,
+ _environ: Optional[Mapping[str, str]] = None,
+ ):
+ # Copy of os.environ allows us to replace it for testing
+ if _environ is not None:
+ self._environ = _environ
+
+ self.is_jupyter = _is_jupyter() if force_jupyter is None else force_jupyter
+ if self.is_jupyter:
+ width = width or 93
+ height = height or 100
+
+ if width is None:
+ columns = self._environ.get("COLUMNS")
+ if columns is not None and columns.isdigit():
+ width = int(columns)
+ if height is None:
+ lines = self._environ.get("LINES")
+ if lines is not None and lines.isdigit():
+ height = int(lines)
+
+ self.soft_wrap = soft_wrap
+ self._width = width
+ self._height = height
+ self.tab_size = tab_size
+ self.record = record
+ self._markup = markup
+ self._emoji = emoji
+ self._emoji_variant: Optional[EmojiVariant] = emoji_variant
+ self._highlight = highlight
+ self.legacy_windows: bool = (
+ (detect_legacy_windows() and not self.is_jupyter)
+ if legacy_windows is None
+ else legacy_windows
+ )
+
+ self._color_system: Optional[ColorSystem]
+ self._force_terminal = force_terminal
+ self._file = file
+ self.quiet = quiet
+ self.stderr = stderr
+
+ if color_system is None:
+ self._color_system = None
+ elif color_system == "auto":
+ self._color_system = self._detect_color_system()
+ else:
+ self._color_system = COLOR_SYSTEMS[color_system]
+
+ self._lock = threading.RLock()
+ self._log_render = LogRender(
+ show_time=log_time,
+ show_path=log_path,
+ time_format=log_time_format,
+ )
+ self.highlighter: HighlighterType = highlighter or _null_highlighter
+ self.safe_box = safe_box
+ self.get_datetime = get_datetime or datetime.now
+ self.get_time = get_time or monotonic
+ self.style = style
+ self.no_color = (
+ no_color if no_color is not None else "NO_COLOR" in self._environ
+ )
+ self.is_interactive = (
+ (self.is_terminal and not self.is_dumb_terminal)
+ if force_interactive is None
+ else force_interactive
+ )
+
+ self._record_buffer_lock = threading.RLock()
+ self._thread_locals = ConsoleThreadLocals(
+ theme_stack=ThemeStack(themes.DEFAULT if theme is None else theme)
+ )
+ self._record_buffer: List[Segment] = []
+ self._render_hooks: List[RenderHook] = []
+ self._live: Optional["Live"] = None
+ self._is_alt_screen = False
+
+ def __repr__(self) -> str:
+ return f"<console width={self.width} {str(self._color_system)}>"
+
+ @property
+ def file(self) -> IO[str]:
+ """Get the file object to write to."""
+ file = self._file or (sys.stderr if self.stderr else sys.stdout)
+ file = getattr(file, "rich_proxied_file", file)
+ return file
+
+ @file.setter
+ def file(self, new_file: IO[str]) -> None:
+ """Set a new file object."""
+ self._file = new_file
+
+ @property
+ def _buffer(self) -> List[Segment]:
+ """Get a thread local buffer."""
+ return self._thread_locals.buffer
+
+ @property
+ def _buffer_index(self) -> int:
+ """Get a thread local buffer."""
+ return self._thread_locals.buffer_index
+
+ @_buffer_index.setter
+ def _buffer_index(self, value: int) -> None:
+ self._thread_locals.buffer_index = value
+
+ @property
+ def _theme_stack(self) -> ThemeStack:
+ """Get the thread local theme stack."""
+ return self._thread_locals.theme_stack
+
+ def _detect_color_system(self) -> Optional[ColorSystem]:
+ """Detect color system from env vars."""
+ if self.is_jupyter:
+ return ColorSystem.TRUECOLOR
+ if not self.is_terminal or self.is_dumb_terminal:
+ return None
+ if WINDOWS: # pragma: no cover
+ if self.legacy_windows: # pragma: no cover
+ return ColorSystem.WINDOWS
+ windows_console_features = get_windows_console_features()
+ return (
+ ColorSystem.TRUECOLOR
+ if windows_console_features.truecolor
+ else ColorSystem.EIGHT_BIT
+ )
+ else:
+ color_term = self._environ.get("COLORTERM", "").strip().lower()
+ if color_term in ("truecolor", "24bit"):
+ return ColorSystem.TRUECOLOR
+ term = self._environ.get("TERM", "").strip().lower()
+ _term_name, _hyphen, colors = term.rpartition("-")
+ color_system = _TERM_COLORS.get(colors, ColorSystem.STANDARD)
+ return color_system
+
+ def _enter_buffer(self) -> None:
+ """Enter in to a buffer context, and buffer all output."""
+ self._buffer_index += 1
+
+ def _exit_buffer(self) -> None:
+ """Leave buffer context, and render content if required."""
+ self._buffer_index -= 1
+ self._check_buffer()
+
+ def set_live(self, live: "Live") -> None:
+ """Set Live instance. Used by Live context manager.
+
+ Args:
+ live (Live): Live instance using this Console.
+
+ Raises:
+ errors.LiveError: If this Console has a Live context currently active.
+ """
+ with self._lock:
+ if self._live is not None:
+ raise errors.LiveError("Only one live display may be active at once")
+ self._live = live
+
+ def clear_live(self) -> None:
+ """Clear the Live instance."""
+ with self._lock:
+ self._live = None
+
+ def push_render_hook(self, hook: RenderHook) -> None:
+ """Add a new render hook to the stack.
+
+ Args:
+ hook (RenderHook): Render hook instance.
+ """
+
+ self._render_hooks.append(hook)
+
+ def pop_render_hook(self) -> None:
+ """Pop the last renderhook from the stack."""
+ self._render_hooks.pop()
+
+ def __enter__(self) -> "Console":
+ """Own context manager to enter buffer context."""
+ self._enter_buffer()
+ return self
+
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
+ """Exit buffer context."""
+ self._exit_buffer()
+
+ def begin_capture(self) -> None:
+ """Begin capturing console output. Call :meth:`end_capture` to exit capture mode and return output."""
+ self._enter_buffer()
+
+ def end_capture(self) -> str:
+ """End capture mode and return captured string.
+
+ Returns:
+ str: Console output.
+ """
+ render_result = self._render_buffer(self._buffer)
+ del self._buffer[:]
+ self._exit_buffer()
+ return render_result
+
+ def push_theme(self, theme: Theme, *, inherit: bool = True) -> None:
+ """Push a new theme on to the top of the stack, replacing the styles from the previous theme.
+ Generally speaking, you should call :meth:`~rich.console.Console.use_theme` to get a context manager, rather
+ than calling this method directly.
+
+ Args:
+ theme (Theme): A theme instance.
+ inherit (bool, optional): Inherit existing styles. Defaults to True.
+ """
+ self._theme_stack.push_theme(theme, inherit=inherit)
+
+ def pop_theme(self) -> None:
+ """Remove theme from top of stack, restoring previous theme."""
+ self._theme_stack.pop_theme()
+
+ def use_theme(self, theme: Theme, *, inherit: bool = True) -> ThemeContext:
+ """Use a different theme for the duration of the context manager.
+
+ Args:
+ theme (Theme): Theme instance to user.
+ inherit (bool, optional): Inherit existing console styles. Defaults to True.
+
+ Returns:
+ ThemeContext: [description]
+ """
+ return ThemeContext(self, theme, inherit)
+
+ @property
+ def color_system(self) -> Optional[str]:
+ """Get color system string.
+
+ Returns:
+ Optional[str]: "standard", "256" or "truecolor".
+ """
+
+ if self._color_system is not None:
+ return _COLOR_SYSTEMS_NAMES[self._color_system]
+ else:
+ return None
+
+ @property
+ def encoding(self) -> str:
+ """Get the encoding of the console file, e.g. ``"utf-8"``.
+
+ Returns:
+ str: A standard encoding string.
+ """
+ return (getattr(self.file, "encoding", "utf-8") or "utf-8").lower()
+
+ @property
+ def is_terminal(self) -> bool:
+ """Check if the console is writing to a terminal.
+
+ Returns:
+ bool: True if the console writing to a device capable of
+ understanding terminal codes, otherwise False.
+ """
+ if self._force_terminal is not None:
+ return self._force_terminal
+ isatty: Optional[Callable[[], bool]] = getattr(self.file, "isatty", None)
+ try:
+ return False if isatty is None else isatty()
+ except ValueError:
+ # in some situation (at the end of a pytest run for example) isatty() can raise
+ # ValueError: I/O operation on closed file
+ # return False because we aren't in a terminal anymore
+ return False
+
+ @property
+ def is_dumb_terminal(self) -> bool:
+ """Detect dumb terminal.
+
+ Returns:
+ bool: True if writing to a dumb terminal, otherwise False.
+
+ """
+ _term = self._environ.get("TERM", "")
+ is_dumb = _term.lower() in ("dumb", "unknown")
+ return self.is_terminal and is_dumb
+
+ @property
+ def options(self) -> ConsoleOptions:
+ """Get default console options."""
+ return ConsoleOptions(
+ max_height=self.size.height,
+ size=self.size,
+ legacy_windows=self.legacy_windows,
+ min_width=1,
+ max_width=self.width,
+ encoding=self.encoding,
+ is_terminal=self.is_terminal,
+ )
+
+ @property
+ def size(self) -> ConsoleDimensions:
+ """Get the size of the console.
+
+ Returns:
+ ConsoleDimensions: A named tuple containing the dimensions.
+ """
+
+ if self._width is not None and self._height is not None:
+ return ConsoleDimensions(self._width, self._height)
+
+ if self.is_dumb_terminal:
+ return ConsoleDimensions(80, 25)
+
+ width: Optional[int] = None
+ height: Optional[int] = None
+ if WINDOWS: # pragma: no cover
+ width, height = shutil.get_terminal_size()
+ else:
+ try:
+ width, height = os.get_terminal_size(sys.__stdin__.fileno())
+ except (AttributeError, ValueError, OSError):
+ try:
+ width, height = os.get_terminal_size(sys.__stdout__.fileno())
+ except (AttributeError, ValueError, OSError):
+ pass
+
+ # get_terminal_size can report 0, 0 if run from pseudo-terminal
+ width = width or 80
+ height = height or 25
+ return ConsoleDimensions(
+ ((width - self.legacy_windows) if self._width is None else self._width),
+ height if self._height is None else self._height,
+ )
+
+ @size.setter
+ def size(self, new_size: Tuple[int, int]) -> None:
+ """Set a new size for the terminal.
+
+ Args:
+ new_size (Tuple[int, int]): New width and height.
+ """
+ width, height = new_size
+ self._width = width
+ self._height = height
+
+ @property
+ def width(self) -> int:
+ """Get the width of the console.
+
+ Returns:
+ int: The width (in characters) of the console.
+ """
+ return self.size.width
+
+ @width.setter
+ def width(self, width: int) -> None:
+ """Set width.
+
+ Args:
+ width (int): New width.
+ """
+ self._width = width
+
+ @property
+ def height(self) -> int:
+ """Get the height of the console.
+
+ Returns:
+ int: The height (in lines) of the console.
+ """
+ return self.size.height
+
+ @height.setter
+ def height(self, height: int) -> None:
+ """Set height.
+
+ Args:
+ height (int): new height.
+ """
+ self._height = height
+
+ def bell(self) -> None:
+ """Play a 'bell' sound (if supported by the terminal)."""
+ self.control(Control.bell())
+
+ def capture(self) -> Capture:
+ """A context manager to *capture* the result of print() or log() in a string,
+ rather than writing it to the console.
+
+ Example:
+ >>> from rich.console import Console
+ >>> console = Console()
+ >>> with console.capture() as capture:
+ ... console.print("[bold magenta]Hello World[/]")
+ >>> print(capture.get())
+
+ Returns:
+ Capture: Context manager with disables writing to the terminal.
+ """
+ capture = Capture(self)
+ return capture
+
+ def pager(
+ self, pager: Optional[Pager] = None, styles: bool = False, links: bool = False
+ ) -> PagerContext:
+ """A context manager to display anything printed within a "pager". The pager application
+ is defined by the system and will typically support at least pressing a key to scroll.
+
+ Args:
+ pager (Pager, optional): A pager object, or None to use :class:~rich.pager.SystemPager`. Defaults to None.
+ styles (bool, optional): Show styles in pager. Defaults to False.
+ links (bool, optional): Show links in pager. Defaults to False.
+
+ Example:
+ >>> from rich.console import Console
+ >>> from rich.__main__ import make_test_card
+ >>> console = Console()
+ >>> with console.pager():
+ console.print(make_test_card())
+
+ Returns:
+ PagerContext: A context manager.
+ """
+ return PagerContext(self, pager=pager, styles=styles, links=links)
+
+ def line(self, count: int = 1) -> None:
+ """Write new line(s).
+
+ Args:
+ count (int, optional): Number of new lines. Defaults to 1.
+ """
+
+ assert count >= 0, "count must be >= 0"
+ self.print(NewLine(count))
+
+ def clear(self, home: bool = True) -> None:
+ """Clear the screen.
+
+ Args:
+ home (bool, optional): Also move the cursor to 'home' position. Defaults to True.
+ """
+ if home:
+ self.control(Control.clear(), Control.home())
+ else:
+ self.control(Control.clear())
+
+ def status(
+ self,
+ status: RenderableType,
+ *,
+ spinner: str = "dots",
+ spinner_style: str = "status.spinner",
+ speed: float = 1.0,
+ refresh_per_second: float = 12.5,
+ ) -> "Status":
+ """Display a status and spinner.
+
+ Args:
+ status (RenderableType): A status renderable (str or Text typically).
+ spinner (str, optional): Name of spinner animation (see python -m rich.spinner). Defaults to "dots".
+ spinner_style (StyleType, optional): Style of spinner. Defaults to "status.spinner".
+ speed (float, optional): Speed factor for spinner animation. Defaults to 1.0.
+ refresh_per_second (float, optional): Number of refreshes per second. Defaults to 12.5.
+
+ Returns:
+ Status: A Status object that may be used as a context manager.
+ """
+ from .status import Status
+
+ status_renderable = Status(
+ status,
+ console=self,
+ spinner=spinner,
+ spinner_style=spinner_style,
+ speed=speed,
+ refresh_per_second=refresh_per_second,
+ )
+ return status_renderable
+
+ def show_cursor(self, show: bool = True) -> bool:
+ """Show or hide the cursor.
+
+ Args:
+ show (bool, optional): Set visibility of the cursor.
+ """
+ if self.is_terminal and not self.legacy_windows:
+ self.control(Control.show_cursor(show))
+ return True
+ return False
+
+ def set_alt_screen(self, enable: bool = True) -> bool:
+ """Enables alternative screen mode.
+
+ Note, if you enable this mode, you should ensure that is disabled before
+ the application exits. See :meth:`~rich.Console.screen` for a context manager
+ that handles this for you.
+
+ Args:
+ enable (bool, optional): Enable (True) or disable (False) alternate screen. Defaults to True.
+
+ Returns:
+ bool: True if the control codes were written.
+
+ """
+ changed = False
+ if self.is_terminal and not self.legacy_windows:
+ self.control(Control.alt_screen(enable))
+ changed = True
+ self._is_alt_screen = enable
+ return changed
+
+ @property
+ def is_alt_screen(self) -> bool:
+ """Check if the alt screen was enabled.
+
+ Returns:
+ bool: True if the alt screen was enabled, otherwise False.
+ """
+ return self._is_alt_screen
+
+ def screen(
+ self, hide_cursor: bool = True, style: Optional[StyleType] = None
+ ) -> "ScreenContext":
+ """Context manager to enable and disable 'alternative screen' mode.
+
+ Args:
+ hide_cursor (bool, optional): Also hide the cursor. Defaults to False.
+ style (Style, optional): Optional style for screen. Defaults to None.
+
+ Returns:
+ ~ScreenContext: Context which enables alternate screen on enter, and disables it on exit.
+ """
+ return ScreenContext(self, hide_cursor=hide_cursor, style=style or "")
+
+ def measure(
+ self, renderable: RenderableType, *, options: Optional[ConsoleOptions] = None
+ ) -> Measurement:
+ """Measure a renderable. Returns a :class:`~rich.measure.Measurement` object which contains
+ information regarding the number of characters required to print the renderable.
+
+ Args:
+ renderable (RenderableType): Any renderable or string.
+ options (Optional[ConsoleOptions], optional): Options to use when measuring, or None
+ to use default options. Defaults to None.
+
+ Returns:
+ Measurement: A measurement of the renderable.
+ """
+ measurement = Measurement.get(self, options or self.options, renderable)
+ return measurement
+
+ def render(
+ self, renderable: RenderableType, options: Optional[ConsoleOptions] = None
+ ) -> Iterable[Segment]:
+ """Render an object in to an iterable of `Segment` instances.
+
+ This method contains the logic for rendering objects with the console protocol.
+ You are unlikely to need to use it directly, unless you are extending the library.
+
+ Args:
+ renderable (RenderableType): An object supporting the console protocol, or
+ an object that may be converted to a string.
+ options (ConsoleOptions, optional): An options object, or None to use self.options. Defaults to None.
+
+ Returns:
+ Iterable[Segment]: An iterable of segments that may be rendered.
+ """
+
+ _options = options or self.options
+ if _options.max_width < 1:
+ # No space to render anything. This prevents potential recursion errors.
+ return
+ render_iterable: RenderResult
+ if hasattr(renderable, "__rich__") and not isclass(renderable):
+ renderable = renderable.__rich__() # type: ignore
+ if hasattr(renderable, "__rich_console__") and not isclass(renderable):
+ render_iterable = renderable.__rich_console__(self, _options) # type: ignore
+ elif isinstance(renderable, str):
+ text_renderable = self.render_str(
+ renderable, highlight=_options.highlight, markup=_options.markup
+ )
+ render_iterable = text_renderable.__rich_console__(self, _options)
+ else:
+ raise errors.NotRenderableError(
+ f"Unable to render {renderable!r}; "
+ "A str, Segment or object with __rich_console__ method is required"
+ )
+
+ try:
+ iter_render = iter(render_iterable)
+ except TypeError:
+ raise errors.NotRenderableError(
+ f"object {render_iterable!r} is not renderable"
+ )
+ _Segment = Segment
+ for render_output in iter_render:
+ if isinstance(render_output, _Segment):
+ yield render_output
+ else:
+ yield from self.render(render_output, _options)
+
+ def render_lines(
+ self,
+ renderable: RenderableType,
+ options: Optional[ConsoleOptions] = None,
+ *,
+ style: Optional[Style] = None,
+ pad: bool = True,
+ new_lines: bool = False,
+ ) -> List[List[Segment]]:
+ """Render objects in to a list of lines.
+
+ The output of render_lines is useful when further formatting of rendered console text
+ is required, such as the Panel class which draws a border around any renderable object.
+
+ Args:
+ renderable (RenderableType): Any object renderable in the console.
+ options (Optional[ConsoleOptions], optional): Console options, or None to use self.options. Default to ``None``.
+ style (Style, optional): Optional style to apply to renderables. Defaults to ``None``.
+ pad (bool, optional): Pad lines shorter than render width. Defaults to ``True``.
+ new_lines (bool, optional): Include "\n" characters at end of lines.
+
+ Returns:
+ List[List[Segment]]: A list of lines, where a line is a list of Segment objects.
+ """
+ with self._lock:
+ render_options = options or self.options
+ _rendered = self.render(renderable, render_options)
+ if style:
+ _rendered = Segment.apply_style(_rendered, style)
+ lines = list(
+ islice(
+ Segment.split_and_crop_lines(
+ _rendered,
+ render_options.max_width,
+ include_new_lines=new_lines,
+ pad=pad,
+ ),
+ None,
+ render_options.height,
+ )
+ )
+ if render_options.height is not None:
+ extra_lines = render_options.height - len(lines)
+ if extra_lines > 0:
+ pad_line = [
+ [Segment(" " * render_options.max_width, style), Segment("\n")]
+ if new_lines
+ else [Segment(" " * render_options.max_width, style)]
+ ]
+ lines.extend(pad_line * extra_lines)
+
+ return lines
+
+ def render_str(
+ self,
+ text: str,
+ *,
+ style: Union[str, Style] = "",
+ justify: Optional[JustifyMethod] = None,
+ overflow: Optional[OverflowMethod] = None,
+ emoji: Optional[bool] = None,
+ markup: Optional[bool] = None,
+ highlight: Optional[bool] = None,
+ highlighter: Optional[HighlighterType] = None,
+ ) -> "Text":
+ """Convert a string to a Text instance. This is is called automatically if
+ you print or log a string.
+
+ Args:
+ text (str): Text to render.
+ style (Union[str, Style], optional): Style to apply to rendered text.
+ justify (str, optional): Justify method: "default", "left", "center", "full", or "right". Defaults to ``None``.
+ overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to ``None``.
+ emoji (Optional[bool], optional): Enable emoji, or ``None`` to use Console default.
+ markup (Optional[bool], optional): Enable markup, or ``None`` to use Console default.
+ highlight (Optional[bool], optional): Enable highlighting, or ``None`` to use Console default.
+ highlighter (HighlighterType, optional): Optional highlighter to apply.
+ Returns:
+ ConsoleRenderable: Renderable object.
+
+ """
+ emoji_enabled = emoji or (emoji is None and self._emoji)
+ markup_enabled = markup or (markup is None and self._markup)
+ highlight_enabled = highlight or (highlight is None and self._highlight)
+
+ if markup_enabled:
+ rich_text = render_markup(
+ text,
+ style=style,
+ emoji=emoji_enabled,
+ emoji_variant=self._emoji_variant,
+ )
+ rich_text.justify = justify
+ rich_text.overflow = overflow
+ else:
+ rich_text = Text(
+ _emoji_replace(text, default_variant=self._emoji_variant)
+ if emoji_enabled
+ else text,
+ justify=justify,
+ overflow=overflow,
+ style=style,
+ )
+
+ _highlighter = (highlighter or self.highlighter) if highlight_enabled else None
+ if _highlighter is not None:
+ highlight_text = _highlighter(str(rich_text))
+ highlight_text.copy_styles(rich_text)
+ return highlight_text
+
+ return rich_text
+
+ def get_style(
+ self, name: Union[str, Style], *, default: Optional[Union[Style, str]] = None
+ ) -> Style:
+ """Get a Style instance by it's theme name or parse a definition.
+
+ Args:
+ name (str): The name of a style or a style definition.
+
+ Returns:
+ Style: A Style object.
+
+ Raises:
+ MissingStyle: If no style could be parsed from name.
+
+ """
+ if isinstance(name, Style):
+ return name
+
+ try:
+ style = self._theme_stack.get(name)
+ if style is None:
+ style = Style.parse(name)
+ return style.copy() if style.link else style
+ except errors.StyleSyntaxError as error:
+ if default is not None:
+ return self.get_style(default)
+ raise errors.MissingStyle(
+ f"Failed to get style {name!r}; {error}"
+ ) from None
+
+ def _collect_renderables(
+ self,
+ objects: Iterable[Any],
+ sep: str,
+ end: str,
+ *,
+ justify: Optional[JustifyMethod] = None,
+ emoji: Optional[bool] = None,
+ markup: Optional[bool] = None,
+ highlight: Optional[bool] = None,
+ ) -> List[ConsoleRenderable]:
+ """Combine a number of renderables and text into one renderable.
+
+ Args:
+ objects (Iterable[Any]): Anything that Rich can render.
+ sep (str): String to write between print data.
+ end (str): String to write at end of print data.
+ justify (str, optional): One of "left", "right", "center", or "full". Defaults to ``None``.
+ emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default.
+ markup (Optional[bool], optional): Enable markup, or ``None`` to use console default.
+ highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default.
+
+ Returns:
+ List[ConsoleRenderable]: A list of things to render.
+ """
+ renderables: List[ConsoleRenderable] = []
+ _append = renderables.append
+ text: List[Text] = []
+ append_text = text.append
+
+ append = _append
+ if justify in ("left", "center", "right"):
+
+ def align_append(renderable: RenderableType) -> None:
+ _append(Align(renderable, cast(AlignMethod, justify)))
+
+ append = align_append
+
+ _highlighter: HighlighterType = _null_highlighter
+ if highlight or (highlight is None and self._highlight):
+ _highlighter = self.highlighter
+
+ def check_text() -> None:
+ if text:
+ sep_text = Text(sep, justify=justify, end=end)
+ append(sep_text.join(text))
+ del text[:]
+
+ for renderable in objects:
+ # I promise this is sane
+ # This detects an object which claims to have all attributes, such as MagicMock.mock_calls
+ if hasattr(
+ renderable, "jwevpw_eors4dfo6mwo345ermk7kdnfnwerwer"
+ ): # pragma: no cover
+ renderable = repr(renderable)
+ rich_cast = getattr(renderable, "__rich__", None)
+ if rich_cast:
+ renderable = rich_cast()
+ if isinstance(renderable, str):
+ append_text(
+ self.render_str(
+ renderable, emoji=emoji, markup=markup, highlighter=_highlighter
+ )
+ )
+ elif isinstance(renderable, ConsoleRenderable):
+ check_text()
+ append(renderable)
+ elif is_expandable(renderable):
+ check_text()
+ append(Pretty(renderable, highlighter=_highlighter))
+ else:
+ append_text(_highlighter(str(renderable)))
+
+ check_text()
+
+ if self.style is not None:
+ style = self.get_style(self.style)
+ renderables = [Styled(renderable, style) for renderable in renderables]
+
+ return renderables
+
+ def rule(
+ self,
+ title: TextType = "",
+ *,
+ characters: str = "─",
+ style: Union[str, Style] = "rule.line",
+ align: AlignMethod = "center",
+ ) -> None:
+ """Draw a line with optional centered title.
+
+ Args:
+ title (str, optional): Text to render over the rule. Defaults to "".
+ characters (str, optional): Character(s) to form the line. Defaults to "─".
+ style (str, optional): Style of line. Defaults to "rule.line".
+ align (str, optional): How to align the title, one of "left", "center", or "right". Defaults to "center".
+ """
+ from .rule import Rule
+
+ rule = Rule(title=title, characters=characters, style=style, align=align)
+ self.print(rule)
+
+ def control(self, *control: Control) -> None:
+ """Insert non-printing control codes.
+
+ Args:
+ control_codes (str): Control codes, such as those that may move the cursor.
+ """
+ if not self.is_dumb_terminal:
+ for _control in control:
+ self._buffer.append(_control.segment)
+ self._check_buffer()
+
+ def out(
+ self,
+ *objects: Any,
+ sep: str = " ",
+ end: str = "\n",
+ style: Optional[Union[str, Style]] = None,
+ highlight: Optional[bool] = None,
+ ) -> None:
+ """Output to the terminal. This is a low-level way of writing to the terminal which unlike
+ :meth:`~rich.console.Console.print` won't pretty print, wrap text, or apply markup, but will
+ optionally apply highlighting and a basic style.
+
+ Args:
+ sep (str, optional): String to write between print data. Defaults to " ".
+ end (str, optional): String to write at end of print data. Defaults to "\\\\n".
+ style (Union[str, Style], optional): A style to apply to output. Defaults to None.
+ highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use
+ console default. Defaults to ``None``.
+ """
+ raw_output: str = sep.join(str(_object) for _object in objects)
+ self.print(
+ raw_output,
+ style=style,
+ highlight=highlight,
+ emoji=False,
+ markup=False,
+ no_wrap=True,
+ overflow="ignore",
+ crop=False,
+ end=end,
+ )
+
+ def print(
+ self,
+ *objects: Any,
+ sep: str = " ",
+ end: str = "\n",
+ style: Optional[Union[str, Style]] = None,
+ justify: Optional[JustifyMethod] = None,
+ overflow: Optional[OverflowMethod] = None,
+ no_wrap: Optional[bool] = None,
+ emoji: Optional[bool] = None,
+ markup: Optional[bool] = None,
+ highlight: Optional[bool] = None,
+ width: Optional[int] = None,
+ height: Optional[int] = None,
+ crop: bool = True,
+ soft_wrap: Optional[bool] = None,
+ new_line_start: bool = False,
+ ) -> None:
+ """Print to the console.
+
+ Args:
+ objects (positional args): Objects to log to the terminal.
+ sep (str, optional): String to write between print data. Defaults to " ".
+ end (str, optional): String to write at end of print data. Defaults to "\\\\n".
+ style (Union[str, Style], optional): A style to apply to output. Defaults to None.
+ justify (str, optional): Justify method: "default", "left", "right", "center", or "full". Defaults to ``None``.
+ overflow (str, optional): Overflow method: "ignore", "crop", "fold", or "ellipsis". Defaults to None.
+ no_wrap (Optional[bool], optional): Disable word wrapping. Defaults to None.
+ emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to ``None``.
+ markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to ``None``.
+ highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to ``None``.
+ width (Optional[int], optional): Width of output, or ``None`` to auto-detect. Defaults to ``None``.
+ crop (Optional[bool], optional): Crop output to width of terminal. Defaults to True.
+ soft_wrap (bool, optional): Enable soft wrap mode which disables word wrapping and cropping of text or ``None`` for
+ Console default. Defaults to ``None``.
+ new_line_start (bool, False): Insert a new line at the start if the output contains more than one line. Defaults to ``False``.
+ """
+ if not objects:
+ objects = (NewLine(),)
+
+ if soft_wrap is None:
+ soft_wrap = self.soft_wrap
+ if soft_wrap:
+ if no_wrap is None:
+ no_wrap = True
+ if overflow is None:
+ overflow = "ignore"
+ crop = False
+
+ with self:
+ renderables = self._collect_renderables(
+ objects,
+ sep,
+ end,
+ justify=justify,
+ emoji=emoji,
+ markup=markup,
+ highlight=highlight,
+ )
+ for hook in self._render_hooks:
+ renderables = hook.process_renderables(renderables)
+ render_options = self.options.update(
+ justify=justify,
+ overflow=overflow,
+ width=min(width, self.width) if width is not None else NO_CHANGE,
+ height=height,
+ no_wrap=no_wrap,
+ markup=markup,
+ highlight=highlight,
+ )
+
+ new_segments: List[Segment] = []
+ extend = new_segments.extend
+ render = self.render
+ if style is None:
+ for renderable in renderables:
+ extend(render(renderable, render_options))
+ else:
+ for renderable in renderables:
+ extend(
+ Segment.apply_style(
+ render(renderable, render_options), self.get_style(style)
+ )
+ )
+ if new_line_start:
+ if (
+ len("".join(segment.text for segment in new_segments).splitlines())
+ > 1
+ ):
+ new_segments.insert(0, Segment.line())
+ if crop:
+ buffer_extend = self._buffer.extend
+ for line in Segment.split_and_crop_lines(
+ new_segments, self.width, pad=False
+ ):
+ buffer_extend(line)
+ else:
+ self._buffer.extend(new_segments)
+
+ def print_json(
+ self,
+ json: Optional[str] = None,
+ *,
+ data: Any = None,
+ indent: int = 2,
+ highlight: bool = True,
+ skip_keys: bool = False,
+ ensure_ascii: bool = True,
+ check_circular: bool = True,
+ allow_nan: bool = True,
+ default: Optional[Callable[[Any], Any]] = None,
+ sort_keys: bool = False,
+ ) -> None:
+ """Pretty prints JSON. Output will be valid JSON.
+
+ Args:
+ json (Optional[str]): A string containing JSON.
+ data (Any): If json is not supplied, then encode this data.
+ indent (int, optional): Number of spaces to indent. Defaults to 2.
+ highlight (bool, optional): Enable highlighting of output: Defaults to True.
+ skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False.
+ ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False.
+ check_circular (bool, optional): Check for circular references. Defaults to True.
+ allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True.
+ default (Callable, optional): A callable that converts values that can not be encoded
+ in to something that can be JSON encoded. Defaults to None.
+ sort_keys (bool, optional): Sort dictionary keys. Defaults to False.
+ """
+ from pip._vendor.rich.json import JSON
+
+ if json is None:
+ json_renderable = JSON.from_data(
+ data,
+ indent=indent,
+ highlight=highlight,
+ skip_keys=skip_keys,
+ ensure_ascii=ensure_ascii,
+ check_circular=check_circular,
+ allow_nan=allow_nan,
+ default=default,
+ sort_keys=sort_keys,
+ )
+ else:
+ if not isinstance(json, str):
+ raise TypeError(
+ f"json must be str. Did you mean print_json(data={json!r}) ?"
+ )
+ json_renderable = JSON(
+ json,
+ indent=indent,
+ highlight=highlight,
+ skip_keys=skip_keys,
+ ensure_ascii=ensure_ascii,
+ check_circular=check_circular,
+ allow_nan=allow_nan,
+ default=default,
+ sort_keys=sort_keys,
+ )
+ self.print(json_renderable, soft_wrap=True)
+
+ def update_screen(
+ self,
+ renderable: RenderableType,
+ *,
+ region: Optional[Region] = None,
+ options: Optional[ConsoleOptions] = None,
+ ) -> None:
+ """Update the screen at a given offset.
+
+ Args:
+ renderable (RenderableType): A Rich renderable.
+ region (Region, optional): Region of screen to update, or None for entire screen. Defaults to None.
+ x (int, optional): x offset. Defaults to 0.
+ y (int, optional): y offset. Defaults to 0.
+
+ Raises:
+ errors.NoAltScreen: If the Console isn't in alt screen mode.
+
+ """
+ if not self.is_alt_screen:
+ raise errors.NoAltScreen("Alt screen must be enabled to call update_screen")
+ render_options = options or self.options
+ if region is None:
+ x = y = 0
+ render_options = render_options.update_dimensions(
+ render_options.max_width, render_options.height or self.height
+ )
+ else:
+ x, y, width, height = region
+ render_options = render_options.update_dimensions(width, height)
+
+ lines = self.render_lines(renderable, options=render_options)
+ self.update_screen_lines(lines, x, y)
+
+ def update_screen_lines(
+ self, lines: List[List[Segment]], x: int = 0, y: int = 0
+ ) -> None:
+ """Update lines of the screen at a given offset.
+
+ Args:
+ lines (List[List[Segment]]): Rendered lines (as produced by :meth:`~rich.Console.render_lines`).
+ x (int, optional): x offset (column no). Defaults to 0.
+ y (int, optional): y offset (column no). Defaults to 0.
+
+ Raises:
+ errors.NoAltScreen: If the Console isn't in alt screen mode.
+ """
+ if not self.is_alt_screen:
+ raise errors.NoAltScreen("Alt screen must be enabled to call update_screen")
+ screen_update = ScreenUpdate(lines, x, y)
+ segments = self.render(screen_update)
+ self._buffer.extend(segments)
+ self._check_buffer()
+
+ def print_exception(
+ self,
+ *,
+ width: Optional[int] = 100,
+ extra_lines: int = 3,
+ theme: Optional[str] = None,
+ word_wrap: bool = False,
+ show_locals: bool = False,
+ suppress: Iterable[Union[str, ModuleType]] = (),
+ max_frames: int = 100,
+ ) -> None:
+ """Prints a rich render of the last exception and traceback.
+
+ Args:
+ width (Optional[int], optional): Number of characters used to render code. Defaults to 88.
+ extra_lines (int, optional): Additional lines of code to render. Defaults to 3.
+ theme (str, optional): Override pygments theme used in traceback
+ 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.
+ 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.
+ """
+ from .traceback import Traceback
+
+ traceback = Traceback(
+ width=width,
+ extra_lines=extra_lines,
+ theme=theme,
+ word_wrap=word_wrap,
+ show_locals=show_locals,
+ suppress=suppress,
+ max_frames=max_frames,
+ )
+ self.print(traceback)
+
+ @staticmethod
+ def _caller_frame_info(
+ offset: int,
+ currentframe: Callable[[], Optional[FrameType]] = inspect.currentframe,
+ ) -> Tuple[str, int, Dict[str, Any]]:
+ """Get caller frame information.
+
+ Args:
+ offset (int): the caller offset within the current frame stack.
+ currentframe (Callable[[], Optional[FrameType]], optional): the callable to use to
+ retrieve the current frame. Defaults to ``inspect.currentframe``.
+
+ Returns:
+ Tuple[str, int, Dict[str, Any]]: A tuple containing the filename, the line number and
+ the dictionary of local variables associated with the caller frame.
+
+ Raises:
+ RuntimeError: If the stack offset is invalid.
+ """
+ # Ignore the frame of this local helper
+ offset += 1
+
+ frame = currentframe()
+ if frame is not None:
+ # Use the faster currentframe where implemented
+ while offset and frame:
+ frame = frame.f_back
+ offset -= 1
+ assert frame is not None
+ return frame.f_code.co_filename, frame.f_lineno, frame.f_locals
+ else:
+ # Fallback to the slower stack
+ frame_info = inspect.stack()[offset]
+ return frame_info.filename, frame_info.lineno, frame_info.frame.f_locals
+
+ def log(
+ self,
+ *objects: Any,
+ sep: str = " ",
+ end: str = "\n",
+ style: Optional[Union[str, Style]] = None,
+ justify: Optional[JustifyMethod] = None,
+ emoji: Optional[bool] = None,
+ markup: Optional[bool] = None,
+ highlight: Optional[bool] = None,
+ log_locals: bool = False,
+ _stack_offset: int = 1,
+ ) -> None:
+ """Log rich content to the terminal.
+
+ Args:
+ objects (positional args): Objects to log to the terminal.
+ sep (str, optional): String to write between print data. Defaults to " ".
+ end (str, optional): String to write at end of print data. Defaults to "\\\\n".
+ style (Union[str, Style], optional): A style to apply to output. Defaults to None.
+ justify (str, optional): One of "left", "right", "center", or "full". Defaults to ``None``.
+ overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to None.
+ emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to None.
+ markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to None.
+ highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to None.
+ log_locals (bool, optional): Boolean to enable logging of locals where ``log()``
+ was called. Defaults to False.
+ _stack_offset (int, optional): Offset of caller from end of call stack. Defaults to 1.
+ """
+ if not objects:
+ objects = (NewLine(),)
+
+ with self:
+ renderables = self._collect_renderables(
+ objects,
+ sep,
+ end,
+ justify=justify,
+ emoji=emoji,
+ markup=markup,
+ highlight=highlight,
+ )
+ if style is not None:
+ renderables = [Styled(renderable, style) for renderable in renderables]
+
+ filename, line_no, locals = self._caller_frame_info(_stack_offset)
+ link_path = None if filename.startswith("<") else os.path.abspath(filename)
+ path = filename.rpartition(os.sep)[-1]
+ if log_locals:
+ locals_map = {
+ key: value
+ for key, value in locals.items()
+ if not key.startswith("__")
+ }
+ renderables.append(render_scope(locals_map, title="[i]locals"))
+
+ renderables = [
+ self._log_render(
+ self,
+ renderables,
+ log_time=self.get_datetime(),
+ path=path,
+ line_no=line_no,
+ link_path=link_path,
+ )
+ ]
+ for hook in self._render_hooks:
+ renderables = hook.process_renderables(renderables)
+ new_segments: List[Segment] = []
+ extend = new_segments.extend
+ render = self.render
+ render_options = self.options
+ for renderable in renderables:
+ extend(render(renderable, render_options))
+ buffer_extend = self._buffer.extend
+ for line in Segment.split_and_crop_lines(
+ new_segments, self.width, pad=False
+ ):
+ buffer_extend(line)
+
+ def _check_buffer(self) -> None:
+ """Check if the buffer may be rendered."""
+ if self.quiet:
+ del self._buffer[:]
+ return
+ with self._lock:
+ if self._buffer_index == 0:
+ if self.is_jupyter: # pragma: no cover
+ from .jupyter import display
+
+ display(self._buffer, self._render_buffer(self._buffer[:]))
+ del self._buffer[:]
+ else:
+ text = self._render_buffer(self._buffer[:])
+ del self._buffer[:]
+ if text:
+ try:
+ if WINDOWS: # pragma: no cover
+ # https://bugs.python.org/issue37871
+ self.file.writelines(text.splitlines(True))
+ else:
+ self.file.write(text)
+ self.file.flush()
+ except UnicodeEncodeError as error:
+ error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***"
+ raise
+
+ def _render_buffer(self, buffer: Iterable[Segment]) -> str:
+ """Render buffered output, and clear buffer."""
+ output: List[str] = []
+ append = output.append
+ color_system = self._color_system
+ legacy_windows = self.legacy_windows
+ if self.record:
+ with self._record_buffer_lock:
+ self._record_buffer.extend(buffer)
+ not_terminal = not self.is_terminal
+ if self.no_color and color_system:
+ buffer = Segment.remove_color(buffer)
+ for text, style, control in buffer:
+ if style:
+ append(
+ style.render(
+ text,
+ color_system=color_system,
+ legacy_windows=legacy_windows,
+ )
+ )
+ elif not (not_terminal and control):
+ append(text)
+
+ rendered = "".join(output)
+ return rendered
+
+ def input(
+ self,
+ prompt: TextType = "",
+ *,
+ markup: bool = True,
+ emoji: bool = True,
+ password: bool = False,
+ stream: Optional[TextIO] = None,
+ ) -> str:
+ """Displays a prompt and waits for input from the user. The prompt may contain color / style.
+
+ It works in the same way as Python's builtin :func:`input` function and provides elaborate line editing and history features if Python's builtin :mod:`readline` module is previously loaded.
+
+ Args:
+ prompt (Union[str, Text]): Text to render in the prompt.
+ markup (bool, optional): Enable console markup (requires a str prompt). Defaults to True.
+ emoji (bool, optional): Enable emoji (requires a str prompt). Defaults to True.
+ password: (bool, optional): Hide typed text. Defaults to False.
+ stream: (TextIO, optional): Optional file to read input from (rather than stdin). Defaults to None.
+
+ Returns:
+ str: Text read from stdin.
+ """
+ prompt_str = ""
+ if prompt:
+ with self.capture() as capture:
+ self.print(prompt, markup=markup, emoji=emoji, end="")
+ prompt_str = capture.get()
+ if self.legacy_windows:
+ # Legacy windows doesn't like ANSI codes in getpass or input (colorama bug)?
+ self.file.write(prompt_str)
+ prompt_str = ""
+ if password:
+ result = getpass(prompt_str, stream=stream)
+ else:
+ if stream:
+ self.file.write(prompt_str)
+ result = stream.readline()
+ else:
+ result = input(prompt_str)
+ return result
+
+ def export_text(self, *, clear: bool = True, styles: bool = False) -> str:
+ """Generate text from console contents (requires record=True argument in constructor).
+
+ Args:
+ clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``.
+ styles (bool, optional): If ``True``, ansi escape codes will be included. ``False`` for plain text.
+ Defaults to ``False``.
+
+ Returns:
+ str: String containing console contents.
+
+ """
+ assert (
+ self.record
+ ), "To export console contents set record=True in the constructor or instance"
+
+ with self._record_buffer_lock:
+ if styles:
+ text = "".join(
+ (style.render(text) if style else text)
+ for text, style, _ in self._record_buffer
+ )
+ else:
+ text = "".join(
+ segment.text
+ for segment in self._record_buffer
+ if not segment.control
+ )
+ if clear:
+ del self._record_buffer[:]
+ return text
+
+ def save_text(self, path: str, *, clear: bool = True, styles: bool = False) -> None:
+ """Generate text from console and save to a given location (requires record=True argument in constructor).
+
+ Args:
+ path (str): Path to write text files.
+ clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``.
+ styles (bool, optional): If ``True``, ansi style codes will be included. ``False`` for plain text.
+ Defaults to ``False``.
+
+ """
+ text = self.export_text(clear=clear, styles=styles)
+ with open(path, "wt", encoding="utf-8") as write_file:
+ write_file.write(text)
+
+ def export_html(
+ self,
+ *,
+ theme: Optional[TerminalTheme] = None,
+ clear: bool = True,
+ code_format: Optional[str] = None,
+ inline_styles: bool = False,
+ ) -> str:
+ """Generate HTML from console contents (requires record=True argument in constructor).
+
+ Args:
+ theme (TerminalTheme, optional): TerminalTheme object containing console colors.
+ clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``.
+ code_format (str, optional): Format string to render HTML, should contain {foreground}
+ {background} and {code}.
+ inline_styles (bool, optional): If ``True`` styles will be inlined in to spans, which makes files
+ larger but easier to cut and paste markup. If ``False``, styles will be embedded in a style tag.
+ Defaults to False.
+
+ Returns:
+ str: String containing console contents as HTML.
+ """
+ assert (
+ self.record
+ ), "To export console contents set record=True in the constructor or instance"
+ fragments: List[str] = []
+ append = fragments.append
+ _theme = theme or DEFAULT_TERMINAL_THEME
+ stylesheet = ""
+
+ render_code_format = CONSOLE_HTML_FORMAT if code_format is None else code_format
+
+ with self._record_buffer_lock:
+ if inline_styles:
+ for text, style, _ in Segment.filter_control(
+ Segment.simplify(self._record_buffer)
+ ):
+ text = escape(text)
+ if style:
+ rule = style.get_html_style(_theme)
+ if style.link:
+ text = f'<a href="{style.link}">{text}</a>'
+ text = f'<span style="{rule}">{text}</span>' if rule else text
+ append(text)
+ else:
+ styles: Dict[str, int] = {}
+ for text, style, _ in Segment.filter_control(
+ Segment.simplify(self._record_buffer)
+ ):
+ text = escape(text)
+ if style:
+ rule = style.get_html_style(_theme)
+ style_number = styles.setdefault(rule, len(styles) + 1)
+ if style.link:
+ text = f'<a class="r{style_number}" href="{style.link}">{text}</a>'
+ else:
+ text = f'<span class="r{style_number}">{text}</span>'
+ append(text)
+ stylesheet_rules: List[str] = []
+ stylesheet_append = stylesheet_rules.append
+ for style_rule, style_number in styles.items():
+ if style_rule:
+ stylesheet_append(f".r{style_number} {{{style_rule}}}")
+ stylesheet = "\n".join(stylesheet_rules)
+
+ rendered_code = render_code_format.format(
+ code="".join(fragments),
+ stylesheet=stylesheet,
+ foreground=_theme.foreground_color.hex,
+ background=_theme.background_color.hex,
+ )
+ if clear:
+ del self._record_buffer[:]
+ return rendered_code
+
+ def save_html(
+ self,
+ path: str,
+ *,
+ theme: Optional[TerminalTheme] = None,
+ clear: bool = True,
+ code_format: str = CONSOLE_HTML_FORMAT,
+ inline_styles: bool = False,
+ ) -> None:
+ """Generate HTML from console contents and write to a file (requires record=True argument in constructor).
+
+ Args:
+ path (str): Path to write html file.
+ theme (TerminalTheme, optional): TerminalTheme object containing console colors.
+ clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``.
+ code_format (str, optional): Format string to render HTML, should contain {foreground}
+ {background} and {code}.
+ inline_styles (bool, optional): If ``True`` styles will be inlined in to spans, which makes files
+ larger but easier to cut and paste markup. If ``False``, styles will be embedded in a style tag.
+ Defaults to False.
+
+ """
+ html = self.export_html(
+ theme=theme,
+ clear=clear,
+ code_format=code_format,
+ inline_styles=inline_styles,
+ )
+ with open(path, "wt", encoding="utf-8") as write_file:
+ write_file.write(html)
+
+
+if __name__ == "__main__": # pragma: no cover
+ console = Console()
+
+ console.log(
+ "JSONRPC [i]request[/i]",
+ 5,
+ 1.3,
+ True,
+ False,
+ None,
+ {
+ "jsonrpc": "2.0",
+ "method": "subtract",
+ "params": {"minuend": 42, "subtrahend": 23},
+ "id": 3,
+ },
+ )
+
+ console.log("Hello, World!", "{'a': 1}", repr(console))
+
+ console.print(
+ {
+ "name": None,
+ "empty": [],
+ "quiz": {
+ "sport": {
+ "answered": True,
+ "q1": {
+ "question": "Which one is correct team name in NBA?",
+ "options": [
+ "New York Bulls",
+ "Los Angeles Kings",
+ "Golden State Warriors",
+ "Huston Rocket",
+ ],
+ "answer": "Huston Rocket",
+ },
+ },
+ "maths": {
+ "answered": False,
+ "q1": {
+ "question": "5 + 7 = ?",
+ "options": [10, 11, 12, 13],
+ "answer": 12,
+ },
+ "q2": {
+ "question": "12 - 8 = ?",
+ "options": [1, 2, 3, 4],
+ "answer": 4,
+ },
+ },
+ },
+ }
+ )
+ console.log("foo")
diff --git a/src/pip/_vendor/rich/constrain.py b/src/pip/_vendor/rich/constrain.py
new file mode 100644
index 000000000..65fdf5634
--- /dev/null
+++ b/src/pip/_vendor/rich/constrain.py
@@ -0,0 +1,37 @@
+from typing import Optional, TYPE_CHECKING
+
+from .jupyter import JupyterMixin
+from .measure import Measurement
+
+if TYPE_CHECKING:
+ from .console import Console, ConsoleOptions, RenderableType, RenderResult
+
+
+class Constrain(JupyterMixin):
+ """Constrain the width of a renderable to a given number of characters.
+
+ Args:
+ renderable (RenderableType): A renderable object.
+ width (int, optional): The maximum width (in characters) to render. Defaults to 80.
+ """
+
+ def __init__(self, renderable: "RenderableType", width: Optional[int] = 80) -> None:
+ self.renderable = renderable
+ self.width = width
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+ if self.width is None:
+ yield self.renderable
+ else:
+ child_options = options.update_width(min(self.width, options.max_width))
+ yield from console.render(self.renderable, child_options)
+
+ def __rich_measure__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "Measurement":
+ if self.width is not None:
+ options = options.update_width(self.width)
+ measurement = Measurement.get(console, options, self.renderable)
+ return measurement
diff --git a/src/pip/_vendor/rich/containers.py b/src/pip/_vendor/rich/containers.py
new file mode 100644
index 000000000..e29cf3689
--- /dev/null
+++ b/src/pip/_vendor/rich/containers.py
@@ -0,0 +1,167 @@
+from itertools import zip_longest
+from typing import (
+ Iterator,
+ Iterable,
+ List,
+ Optional,
+ Union,
+ overload,
+ TypeVar,
+ TYPE_CHECKING,
+)
+
+if TYPE_CHECKING:
+ from .console import (
+ Console,
+ ConsoleOptions,
+ JustifyMethod,
+ OverflowMethod,
+ RenderResult,
+ RenderableType,
+ )
+ from .text import Text
+
+from .cells import cell_len
+from .measure import Measurement
+
+T = TypeVar("T")
+
+
+class Renderables:
+ """A list subclass which renders its contents to the console."""
+
+ def __init__(
+ self, renderables: Optional[Iterable["RenderableType"]] = None
+ ) -> None:
+ self._renderables: List["RenderableType"] = (
+ list(renderables) if renderables is not None else []
+ )
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+ """Console render method to insert line-breaks."""
+ yield from self._renderables
+
+ def __rich_measure__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "Measurement":
+ dimensions = [
+ Measurement.get(console, options, renderable)
+ for renderable in self._renderables
+ ]
+ if not dimensions:
+ return Measurement(1, 1)
+ _min = max(dimension.minimum for dimension in dimensions)
+ _max = max(dimension.maximum for dimension in dimensions)
+ return Measurement(_min, _max)
+
+ def append(self, renderable: "RenderableType") -> None:
+ self._renderables.append(renderable)
+
+ def __iter__(self) -> Iterable["RenderableType"]:
+ return iter(self._renderables)
+
+
+class Lines:
+ """A list subclass which can render to the console."""
+
+ def __init__(self, lines: Iterable["Text"] = ()) -> None:
+ self._lines: List["Text"] = list(lines)
+
+ def __repr__(self) -> str:
+ return f"Lines({self._lines!r})"
+
+ def __iter__(self) -> Iterator["Text"]:
+ return iter(self._lines)
+
+ @overload
+ def __getitem__(self, index: int) -> "Text":
+ ...
+
+ @overload
+ def __getitem__(self, index: slice) -> List["Text"]:
+ ...
+
+ def __getitem__(self, index: Union[slice, int]) -> Union["Text", List["Text"]]:
+ return self._lines[index]
+
+ def __setitem__(self, index: int, value: "Text") -> "Lines":
+ self._lines[index] = value
+ return self
+
+ def __len__(self) -> int:
+ return self._lines.__len__()
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+ """Console render method to insert line-breaks."""
+ yield from self._lines
+
+ def append(self, line: "Text") -> None:
+ self._lines.append(line)
+
+ def extend(self, lines: Iterable["Text"]) -> None:
+ self._lines.extend(lines)
+
+ def pop(self, index: int = -1) -> "Text":
+ return self._lines.pop(index)
+
+ def justify(
+ self,
+ console: "Console",
+ width: int,
+ justify: "JustifyMethod" = "left",
+ overflow: "OverflowMethod" = "fold",
+ ) -> None:
+ """Justify and overflow text to a given width.
+
+ Args:
+ console (Console): Console instance.
+ width (int): Number of characters per line.
+ justify (str, optional): Default justify method for text: "left", "center", "full" or "right". Defaults to "left".
+ overflow (str, optional): Default overflow for text: "crop", "fold", or "ellipsis". Defaults to "fold".
+
+ """
+ from .text import Text
+
+ if justify == "left":
+ for line in self._lines:
+ line.truncate(width, overflow=overflow, pad=True)
+ elif justify == "center":
+ for line in self._lines:
+ line.rstrip()
+ line.truncate(width, overflow=overflow)
+ line.pad_left((width - cell_len(line.plain)) // 2)
+ line.pad_right(width - cell_len(line.plain))
+ elif justify == "right":
+ for line in self._lines:
+ line.rstrip()
+ line.truncate(width, overflow=overflow)
+ line.pad_left(width - cell_len(line.plain))
+ elif justify == "full":
+ for line_index, line in enumerate(self._lines):
+ if line_index == len(self._lines) - 1:
+ break
+ words = line.split(" ")
+ words_size = sum(cell_len(word.plain) for word in words)
+ num_spaces = len(words) - 1
+ spaces = [1 for _ in range(num_spaces)]
+ index = 0
+ if spaces:
+ while words_size + num_spaces < width:
+ spaces[len(spaces) - index - 1] += 1
+ num_spaces += 1
+ index = (index + 1) % len(spaces)
+ tokens: List[Text] = []
+ for index, (word, next_word) in enumerate(
+ zip_longest(words, words[1:])
+ ):
+ tokens.append(word)
+ if index < len(spaces):
+ style = word.get_style_at_offset(console, -1)
+ next_style = next_word.get_style_at_offset(console, 0)
+ space_style = style if style == next_style else line.style
+ tokens.append(Text(" " * spaces[index], style=space_style))
+ self[line_index] = Text("").join(tokens)
diff --git a/src/pip/_vendor/rich/control.py b/src/pip/_vendor/rich/control.py
new file mode 100644
index 000000000..c98d0d7d9
--- /dev/null
+++ b/src/pip/_vendor/rich/control.py
@@ -0,0 +1,175 @@
+from typing import Any, Callable, Dict, Iterable, List, TYPE_CHECKING, Union
+
+from .segment import ControlCode, ControlType, Segment
+
+if TYPE_CHECKING:
+ from .console import Console, ConsoleOptions, RenderResult
+
+STRIP_CONTROL_CODES = [
+ 8, # Backspace
+ 11, # Vertical tab
+ 12, # Form feed
+ 13, # Carriage return
+]
+_CONTROL_TRANSLATE = {_codepoint: None for _codepoint in STRIP_CONTROL_CODES}
+
+
+CONTROL_CODES_FORMAT: Dict[int, Callable[..., str]] = {
+ ControlType.BELL: lambda: "\x07",
+ ControlType.CARRIAGE_RETURN: lambda: "\r",
+ ControlType.HOME: lambda: "\x1b[H",
+ ControlType.CLEAR: lambda: "\x1b[2J",
+ ControlType.ENABLE_ALT_SCREEN: lambda: "\x1b[?1049h",
+ ControlType.DISABLE_ALT_SCREEN: lambda: "\x1b[?1049l",
+ ControlType.SHOW_CURSOR: lambda: "\x1b[?25h",
+ ControlType.HIDE_CURSOR: lambda: "\x1b[?25l",
+ ControlType.CURSOR_UP: lambda param: f"\x1b[{param}A",
+ ControlType.CURSOR_DOWN: lambda param: f"\x1b[{param}B",
+ ControlType.CURSOR_FORWARD: lambda param: f"\x1b[{param}C",
+ ControlType.CURSOR_BACKWARD: lambda param: f"\x1b[{param}D",
+ ControlType.CURSOR_MOVE_TO_COLUMN: lambda param: f"\x1b[{param+1}G",
+ ControlType.ERASE_IN_LINE: lambda param: f"\x1b[{param}K",
+ ControlType.CURSOR_MOVE_TO: lambda x, y: f"\x1b[{y+1};{x+1}H",
+}
+
+
+class Control:
+ """A renderable that inserts a control code (non printable but may move cursor).
+
+ Args:
+ *codes (str): Positional arguments are either a :class:`~rich.segment.ControlType` enum or a
+ tuple of ControlType and an integer parameter
+ """
+
+ __slots__ = ["segment"]
+
+ def __init__(self, *codes: Union[ControlType, ControlCode]) -> None:
+ control_codes: List[ControlCode] = [
+ (code,) if isinstance(code, ControlType) else code for code in codes
+ ]
+ _format_map = CONTROL_CODES_FORMAT
+ rendered_codes = "".join(
+ _format_map[code](*parameters) for code, *parameters in control_codes
+ )
+ self.segment = Segment(rendered_codes, None, control_codes)
+
+ @classmethod
+ def bell(cls) -> "Control":
+ """Ring the 'bell'."""
+ return cls(ControlType.BELL)
+
+ @classmethod
+ def home(cls) -> "Control":
+ """Move cursor to 'home' position."""
+ return cls(ControlType.HOME)
+
+ @classmethod
+ def move(cls, x: int = 0, y: int = 0) -> "Control":
+ """Move cursor relative to current position.
+
+ Args:
+ x (int): X offset.
+ y (int): Y offset.
+
+ Returns:
+ ~Control: Control object.
+
+ """
+
+ def get_codes() -> Iterable[ControlCode]:
+ control = ControlType
+ if x:
+ yield (
+ control.CURSOR_FORWARD if x > 0 else control.CURSOR_BACKWARD,
+ abs(x),
+ )
+ if y:
+ yield (
+ control.CURSOR_DOWN if y > 0 else control.CURSOR_UP,
+ abs(y),
+ )
+
+ control = cls(*get_codes())
+ return control
+
+ @classmethod
+ def move_to_column(cls, x: int, y: int = 0) -> "Control":
+ """Move to the given column, optionally add offset to row.
+
+ Returns:
+ x (int): absolute x (column)
+ y (int): optional y offset (row)
+
+ Returns:
+ ~Control: Control object.
+ """
+
+ return (
+ cls(
+ (ControlType.CURSOR_MOVE_TO_COLUMN, x),
+ (
+ ControlType.CURSOR_DOWN if y > 0 else ControlType.CURSOR_UP,
+ abs(y),
+ ),
+ )
+ if y
+ else cls((ControlType.CURSOR_MOVE_TO_COLUMN, x))
+ )
+
+ @classmethod
+ def move_to(cls, x: int, y: int) -> "Control":
+ """Move cursor to absolute position.
+
+ Args:
+ x (int): x offset (column)
+ y (int): y offset (row)
+
+ Returns:
+ ~Control: Control object.
+ """
+ return cls((ControlType.CURSOR_MOVE_TO, x, y))
+
+ @classmethod
+ def clear(cls) -> "Control":
+ """Clear the screen."""
+ return cls(ControlType.CLEAR)
+
+ @classmethod
+ def show_cursor(cls, show: bool) -> "Control":
+ """Show or hide the cursor."""
+ return cls(ControlType.SHOW_CURSOR if show else ControlType.HIDE_CURSOR)
+
+ @classmethod
+ def alt_screen(cls, enable: bool) -> "Control":
+ """Enable or disable alt screen."""
+ if enable:
+ return cls(ControlType.ENABLE_ALT_SCREEN, ControlType.HOME)
+ else:
+ return cls(ControlType.DISABLE_ALT_SCREEN)
+
+ def __str__(self) -> str:
+ return self.segment.text
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+ if self.segment.text:
+ yield self.segment
+
+
+def strip_control_codes(
+ text: str, _translate_table: Dict[int, None] = _CONTROL_TRANSLATE
+) -> str:
+ """Remove control codes from text.
+
+ Args:
+ text (str): A string possibly contain control codes.
+
+ Returns:
+ str: String with control codes removed.
+ """
+ return text.translate(_translate_table)
+
+
+if __name__ == "__main__": # pragma: no cover
+ print(strip_control_codes("hello\rWorld"))
diff --git a/src/pip/_vendor/rich/default_styles.py b/src/pip/_vendor/rich/default_styles.py
new file mode 100644
index 000000000..355f9df85
--- /dev/null
+++ b/src/pip/_vendor/rich/default_styles.py
@@ -0,0 +1,159 @@
+from typing import Dict
+
+from .style import Style
+
+
+DEFAULT_STYLES: Dict[str, Style] = {
+ "none": Style.null(),
+ "reset": Style(
+ color="default",
+ bgcolor="default",
+ dim=False,
+ bold=False,
+ italic=False,
+ underline=False,
+ blink=False,
+ blink2=False,
+ reverse=False,
+ conceal=False,
+ strike=False,
+ ),
+ "dim": Style(dim=True),
+ "bright": Style(dim=False),
+ "bold": Style(bold=True),
+ "strong": Style(bold=True),
+ "code": Style(reverse=True, bold=True),
+ "italic": Style(italic=True),
+ "emphasize": Style(italic=True),
+ "underline": Style(underline=True),
+ "blink": Style(blink=True),
+ "blink2": Style(blink2=True),
+ "reverse": Style(reverse=True),
+ "strike": Style(strike=True),
+ "black": Style(color="black"),
+ "red": Style(color="red"),
+ "green": Style(color="green"),
+ "yellow": Style(color="yellow"),
+ "magenta": Style(color="magenta"),
+ "cyan": Style(color="cyan"),
+ "white": Style(color="white"),
+ "inspect.attr": Style(color="yellow", italic=True),
+ "inspect.attr.dunder": Style(color="yellow", italic=True, dim=True),
+ "inspect.callable": Style(bold=True, color="red"),
+ "inspect.def": Style(italic=True, color="bright_cyan"),
+ "inspect.error": Style(bold=True, color="red"),
+ "inspect.equals": Style(),
+ "inspect.help": Style(color="cyan"),
+ "inspect.doc": Style(dim=True),
+ "inspect.value.border": Style(color="green"),
+ "live.ellipsis": Style(bold=True, color="red"),
+ "layout.tree.row": Style(dim=False, color="red"),
+ "layout.tree.column": Style(dim=False, color="blue"),
+ "logging.keyword": Style(bold=True, color="yellow"),
+ "logging.level.notset": Style(dim=True),
+ "logging.level.debug": Style(color="green"),
+ "logging.level.info": Style(color="blue"),
+ "logging.level.warning": Style(color="red"),
+ "logging.level.error": Style(color="red", bold=True),
+ "logging.level.critical": Style(color="red", bold=True, reverse=True),
+ "log.level": Style.null(),
+ "log.time": Style(color="cyan", dim=True),
+ "log.message": Style.null(),
+ "log.path": Style(dim=True),
+ "repr.ellipsis": Style(color="yellow"),
+ "repr.indent": Style(color="green", dim=True),
+ "repr.error": Style(color="red", bold=True),
+ "repr.str": Style(color="green", italic=False, bold=False),
+ "repr.brace": Style(bold=True),
+ "repr.comma": Style(bold=True),
+ "repr.ipv4": Style(bold=True, color="bright_green"),
+ "repr.ipv6": Style(bold=True, color="bright_green"),
+ "repr.eui48": Style(bold=True, color="bright_green"),
+ "repr.eui64": Style(bold=True, color="bright_green"),
+ "repr.tag_start": Style(bold=True),
+ "repr.tag_name": Style(color="bright_magenta", bold=True),
+ "repr.tag_contents": Style(color="default"),
+ "repr.tag_end": Style(bold=True),
+ "repr.attrib_name": Style(color="yellow", italic=False),
+ "repr.attrib_equal": Style(bold=True),
+ "repr.attrib_value": Style(color="magenta", italic=False),
+ "repr.number": Style(color="cyan", bold=True, italic=False),
+ "repr.bool_true": Style(color="bright_green", italic=True),
+ "repr.bool_false": Style(color="bright_red", italic=True),
+ "repr.none": Style(color="magenta", italic=True),
+ "repr.url": Style(underline=True, color="bright_blue", italic=False, bold=False),
+ "repr.uuid": Style(color="bright_yellow", bold=False),
+ "repr.call": Style(color="magenta", bold=True),
+ "repr.path": Style(color="magenta"),
+ "repr.filename": Style(color="bright_magenta"),
+ "rule.line": Style(color="bright_green"),
+ "rule.text": Style.null(),
+ "json.brace": Style(bold=True),
+ "json.bool_true": Style(color="bright_green", italic=True),
+ "json.bool_false": Style(color="bright_red", italic=True),
+ "json.null": Style(color="magenta", italic=True),
+ "json.number": Style(color="cyan", bold=True, italic=False),
+ "json.str": Style(color="green", italic=False, bold=False),
+ "json.key": Style(color="blue", bold=True),
+ "prompt": Style.null(),
+ "prompt.choices": Style(color="magenta", bold=True),
+ "prompt.default": Style(color="cyan", bold=True),
+ "prompt.invalid": Style(color="red"),
+ "prompt.invalid.choice": Style(color="red"),
+ "pretty": Style.null(),
+ "scope.border": Style(color="blue"),
+ "scope.key": Style(color="yellow", italic=True),
+ "scope.key.special": Style(color="yellow", italic=True, dim=True),
+ "scope.equals": Style(color="red"),
+ "table.header": Style(bold=True),
+ "table.footer": Style(bold=True),
+ "table.cell": Style.null(),
+ "table.title": Style(italic=True),
+ "table.caption": Style(italic=True, dim=True),
+ "traceback.error": Style(color="red", italic=True),
+ "traceback.border.syntax_error": Style(color="bright_red"),
+ "traceback.border": Style(color="red"),
+ "traceback.text": Style.null(),
+ "traceback.title": Style(color="red", bold=True),
+ "traceback.exc_type": Style(color="bright_red", bold=True),
+ "traceback.exc_value": Style.null(),
+ "traceback.offset": Style(color="bright_red", bold=True),
+ "bar.back": Style(color="grey23"),
+ "bar.complete": Style(color="rgb(249,38,114)"),
+ "bar.finished": Style(color="rgb(114,156,31)"),
+ "bar.pulse": Style(color="rgb(249,38,114)"),
+ "progress.description": Style.null(),
+ "progress.filesize": Style(color="green"),
+ "progress.filesize.total": Style(color="green"),
+ "progress.download": Style(color="green"),
+ "progress.elapsed": Style(color="yellow"),
+ "progress.percentage": Style(color="magenta"),
+ "progress.remaining": Style(color="cyan"),
+ "progress.data.speed": Style(color="red"),
+ "progress.spinner": Style(color="green"),
+ "status.spinner": Style(color="green"),
+ "tree": Style(),
+ "tree.line": Style(),
+ "markdown.paragraph": Style(),
+ "markdown.text": Style(),
+ "markdown.emph": Style(italic=True),
+ "markdown.strong": Style(bold=True),
+ "markdown.code": Style(bgcolor="black", color="bright_white"),
+ "markdown.code_block": Style(dim=True, color="cyan", bgcolor="black"),
+ "markdown.block_quote": Style(color="magenta"),
+ "markdown.list": Style(color="cyan"),
+ "markdown.item": Style(),
+ "markdown.item.bullet": Style(color="yellow", bold=True),
+ "markdown.item.number": Style(color="yellow", bold=True),
+ "markdown.hr": Style(color="yellow"),
+ "markdown.h1.border": Style(),
+ "markdown.h1": Style(bold=True),
+ "markdown.h2": Style(bold=True, underline=True),
+ "markdown.h3": Style(bold=True),
+ "markdown.h4": Style(bold=True, dim=True),
+ "markdown.h5": Style(underline=True),
+ "markdown.h6": Style(italic=True),
+ "markdown.h7": Style(italic=True, dim=True),
+ "markdown.link": Style(color="bright_blue"),
+ "markdown.link_url": Style(color="blue"),
+}
diff --git a/src/pip/_vendor/rich/diagnose.py b/src/pip/_vendor/rich/diagnose.py
new file mode 100644
index 000000000..38728da2a
--- /dev/null
+++ b/src/pip/_vendor/rich/diagnose.py
@@ -0,0 +1,6 @@
+if __name__ == "__main__": # pragma: no cover
+ from pip._vendor.rich.console import Console
+ from pip._vendor.rich import inspect
+
+ console = Console()
+ inspect(console)
diff --git a/src/pip/_vendor/rich/emoji.py b/src/pip/_vendor/rich/emoji.py
new file mode 100644
index 000000000..791f0465d
--- /dev/null
+++ b/src/pip/_vendor/rich/emoji.py
@@ -0,0 +1,96 @@
+import sys
+from typing import TYPE_CHECKING, Optional, Union
+
+from .jupyter import JupyterMixin
+from .segment import Segment
+from .style import Style
+from ._emoji_codes import EMOJI
+from ._emoji_replace import _emoji_replace
+
+if sys.version_info >= (3, 8):
+ from typing import Literal
+else:
+ from pip._vendor.typing_extensions import Literal # pragma: no cover
+
+
+if TYPE_CHECKING:
+ from .console import Console, ConsoleOptions, RenderResult
+
+
+EmojiVariant = Literal["emoji", "text"]
+
+
+class NoEmoji(Exception):
+ """No emoji by that name."""
+
+
+class Emoji(JupyterMixin):
+ __slots__ = ["name", "style", "_char", "variant"]
+
+ VARIANTS = {"text": "\uFE0E", "emoji": "\uFE0F"}
+
+ def __init__(
+ self,
+ name: str,
+ style: Union[str, Style] = "none",
+ variant: Optional[EmojiVariant] = None,
+ ) -> None:
+ """A single emoji character.
+
+ Args:
+ name (str): Name of emoji.
+ style (Union[str, Style], optional): Optional style. Defaults to None.
+
+ Raises:
+ NoEmoji: If the emoji doesn't exist.
+ """
+ self.name = name
+ self.style = style
+ self.variant = variant
+ try:
+ self._char = EMOJI[name]
+ except KeyError:
+ raise NoEmoji(f"No emoji called {name!r}")
+ if variant is not None:
+ self._char += self.VARIANTS.get(variant, "")
+
+ @classmethod
+ def replace(cls, text: str) -> str:
+ """Replace emoji markup with corresponding unicode characters.
+
+ Args:
+ text (str): A string with emojis codes, e.g. "Hello :smiley:!"
+
+ Returns:
+ str: A string with emoji codes replaces with actual emoji.
+ """
+ return _emoji_replace(text)
+
+ def __repr__(self) -> str:
+ return f"<emoji {self.name!r}>"
+
+ def __str__(self) -> str:
+ return self._char
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+ yield Segment(self._char, console.get_style(self.style))
+
+
+if __name__ == "__main__": # pragma: no cover
+ import sys
+
+ from pip._vendor.rich.columns import Columns
+ from pip._vendor.rich.console import Console
+
+ console = Console(record=True)
+
+ columns = Columns(
+ (f":{name}: {name}" for name in sorted(EMOJI.keys()) if "\u200D" not in name),
+ column_first=True,
+ )
+
+ console.print(columns)
+ if len(sys.argv) > 1:
+ console.save_html(sys.argv[1])
diff --git a/src/pip/_vendor/rich/errors.py b/src/pip/_vendor/rich/errors.py
new file mode 100644
index 000000000..0bcbe53ef
--- /dev/null
+++ b/src/pip/_vendor/rich/errors.py
@@ -0,0 +1,34 @@
+class ConsoleError(Exception):
+ """An error in console operation."""
+
+
+class StyleError(Exception):
+ """An error in styles."""
+
+
+class StyleSyntaxError(ConsoleError):
+ """Style was badly formatted."""
+
+
+class MissingStyle(StyleError):
+ """No such style."""
+
+
+class StyleStackError(ConsoleError):
+ """Style stack is invalid."""
+
+
+class NotRenderableError(ConsoleError):
+ """Object is not renderable."""
+
+
+class MarkupError(ConsoleError):
+ """Markup was badly formatted."""
+
+
+class LiveError(ConsoleError):
+ """Error related to Live display."""
+
+
+class NoAltScreen(ConsoleError):
+ """Alt screen mode was required."""
diff --git a/src/pip/_vendor/rich/file_proxy.py b/src/pip/_vendor/rich/file_proxy.py
new file mode 100644
index 000000000..99a6922cb
--- /dev/null
+++ b/src/pip/_vendor/rich/file_proxy.py
@@ -0,0 +1,54 @@
+import io
+from typing import List, Any, IO, TYPE_CHECKING
+
+from .ansi import AnsiDecoder
+from .text import Text
+
+if TYPE_CHECKING:
+ from .console import Console
+
+
+class FileProxy(io.TextIOBase):
+ """Wraps a file (e.g. sys.stdout) and redirects writes to a console."""
+
+ def __init__(self, console: "Console", file: IO[str]) -> None:
+ self.__console = console
+ self.__file = file
+ self.__buffer: List[str] = []
+ self.__ansi_decoder = AnsiDecoder()
+
+ @property
+ def rich_proxied_file(self) -> IO[str]:
+ """Get proxied file."""
+ return self.__file
+
+ def __getattr__(self, name: str) -> Any:
+ return getattr(self.__file, name)
+
+ def write(self, text: str) -> int:
+ if not isinstance(text, str):
+ raise TypeError(f"write() argument must be str, not {type(text).__name__}")
+ buffer = self.__buffer
+ lines: List[str] = []
+ while text:
+ line, new_line, text = text.partition("\n")
+ if new_line:
+ lines.append("".join(buffer) + line)
+ del buffer[:]
+ else:
+ buffer.append(line)
+ break
+ if lines:
+ console = self.__console
+ with console:
+ output = Text("\n").join(
+ self.__ansi_decoder.decode_line(line) for line in lines
+ )
+ console.print(output, markup=False, emoji=False, highlight=False)
+ return len(text)
+
+ def flush(self) -> None:
+ buffer = self.__buffer
+ if buffer:
+ self.__console.print("".join(buffer))
+ del buffer[:]
diff --git a/src/pip/_vendor/rich/filesize.py b/src/pip/_vendor/rich/filesize.py
new file mode 100644
index 000000000..b3a0996b0
--- /dev/null
+++ b/src/pip/_vendor/rich/filesize.py
@@ -0,0 +1,89 @@
+# coding: utf-8
+"""Functions for reporting filesizes. Borrowed from https://github.com/PyFilesystem/pyfilesystem2
+
+The functions declared in this module should cover the different
+usecases needed to generate a string representation of a file size
+using several different units. Since there are many standards regarding
+file size units, three different functions have been implemented.
+
+See Also:
+ * `Wikipedia: Binary prefix <https://en.wikipedia.org/wiki/Binary_prefix>`_
+
+"""
+
+__all__ = ["decimal"]
+
+from typing import Iterable, List, Tuple, Optional
+
+
+def _to_str(
+ size: int,
+ suffixes: Iterable[str],
+ base: int,
+ *,
+ precision: Optional[int] = 1,
+ separator: Optional[str] = " ",
+) -> str:
+ if size == 1:
+ return "1 byte"
+ elif size < base:
+ return "{:,} bytes".format(size)
+
+ for i, suffix in enumerate(suffixes, 2): # noqa: B007
+ unit = base ** i
+ if size < unit:
+ break
+ return "{:,.{precision}f}{separator}{}".format(
+ (base * size / unit),
+ suffix,
+ precision=precision,
+ separator=separator,
+ )
+
+
+def pick_unit_and_suffix(size: int, suffixes: List[str], base: int) -> Tuple[int, str]:
+ """Pick a suffix and base for the given size."""
+ for i, suffix in enumerate(suffixes):
+ unit = base ** i
+ if size < unit * base:
+ break
+ return unit, suffix
+
+
+def decimal(
+ size: int,
+ *,
+ precision: Optional[int] = 1,
+ separator: Optional[str] = " ",
+) -> str:
+ """Convert a filesize in to a string (powers of 1000, SI prefixes).
+
+ In this convention, ``1000 B = 1 kB``.
+
+ This is typically the format used to advertise the storage
+ capacity of USB flash drives and the like (*256 MB* meaning
+ actually a storage capacity of more than *256 000 000 B*),
+ or used by **Mac OS X** since v10.6 to report file sizes.
+
+ Arguments:
+ int (size): A file size.
+ int (precision): The number of decimal places to include (default = 1).
+ str (separator): The string to separate the value from the units (default = " ").
+
+ Returns:
+ `str`: A string containing a abbreviated file size and units.
+
+ Example:
+ >>> filesize.decimal(30000)
+ '30.0 kB'
+ >>> filesize.decimal(30000, precision=2, separator="")
+ '30.00kB'
+
+ """
+ return _to_str(
+ size,
+ ("kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"),
+ 1000,
+ precision=precision,
+ separator=separator,
+ )
diff --git a/src/pip/_vendor/rich/highlighter.py b/src/pip/_vendor/rich/highlighter.py
new file mode 100644
index 000000000..9e71d0053
--- /dev/null
+++ b/src/pip/_vendor/rich/highlighter.py
@@ -0,0 +1,147 @@
+from abc import ABC, abstractmethod
+from typing import List, Union
+
+from .text import Text
+
+
+def _combine_regex(*regexes: str) -> str:
+ """Combine a number of regexes in to a single regex.
+
+ Returns:
+ str: New regex with all regexes ORed together.
+ """
+ return "|".join(regexes)
+
+
+class Highlighter(ABC):
+ """Abstract base class for highlighters."""
+
+ def __call__(self, text: Union[str, Text]) -> Text:
+ """Highlight a str or Text instance.
+
+ Args:
+ text (Union[str, ~Text]): Text to highlight.
+
+ Raises:
+ TypeError: If not called with text or str.
+
+ Returns:
+ Text: A test instance with highlighting applied.
+ """
+ if isinstance(text, str):
+ highlight_text = Text(text)
+ elif isinstance(text, Text):
+ highlight_text = text.copy()
+ else:
+ raise TypeError(f"str or Text instance required, not {text!r}")
+ self.highlight(highlight_text)
+ return highlight_text
+
+ @abstractmethod
+ def highlight(self, text: Text) -> None:
+ """Apply highlighting in place to text.
+
+ Args:
+ text (~Text): A text object highlight.
+ """
+
+
+class NullHighlighter(Highlighter):
+ """A highlighter object that doesn't highlight.
+
+ May be used to disable highlighting entirely.
+
+ """
+
+ def highlight(self, text: Text) -> None:
+ """Nothing to do"""
+
+
+class RegexHighlighter(Highlighter):
+ """Applies highlighting from a list of regular expressions."""
+
+ highlights: List[str] = []
+ base_style: str = ""
+
+ def highlight(self, text: Text) -> None:
+ """Highlight :class:`rich.text.Text` using regular expressions.
+
+ Args:
+ text (~Text): Text to highlighted.
+
+ """
+
+ highlight_regex = text.highlight_regex
+ for re_highlight in self.highlights:
+ highlight_regex(re_highlight, style_prefix=self.base_style)
+
+
+class ReprHighlighter(RegexHighlighter):
+ """Highlights the text typically produced from ``__repr__`` methods."""
+
+ base_style = "repr."
+ highlights = [
+ 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(
+ r"(?P<ipv4>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})",
+ r"(?P<ipv6>([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})",
+ r"(?P<eui64>(?:[0-9A-Fa-f]{1,2}-){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){3}[0-9A-Fa-f]{4})",
+ r"(?P<eui48>(?:[0-9A-Fa-f]{1,2}-){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){2}[0-9A-Fa-f]{4})",
+ r"(?P<call>[\w\.]*?)\(",
+ r"\b(?P<bool_true>True)\b|\b(?P<bool_false>False)\b|\b(?P<none>None)\b",
+ r"(?P<ellipsis>\.\.\.)",
+ r"(?P<number>(?<!\w)\-?[0-9]+\.?[0-9]*(e[\-\+]?\d+?)?\b|0x[0-9a-fA-F]*)",
+ r"(?P<path>\B(\/[\w\.\-\_\+]+)*\/)(?P<filename>[\w\.\-\_\+]*)?",
+ r"(?<![\\\w])(?P<str>b?\'\'\'.*?(?<!\\)\'\'\'|b?\'.*?(?<!\\)\'|b?\"\"\".*?(?<!\\)\"\"\"|b?\".*?(?<!\\)\")",
+ r"(?P<uuid>[a-fA-F0-9]{8}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{12})",
+ r"(?P<url>(https|http|ws|wss):\/\/[0-9a-zA-Z\$\-\_\+\!`\(\)\,\.\?\/\;\:\&\=\%\#]*)",
+ ),
+ ]
+
+
+class JSONHighlighter(RegexHighlighter):
+ """Highlights JSON"""
+
+ base_style = "json."
+ highlights = [
+ _combine_regex(
+ r"(?P<brace>[\{\[\(\)\]\}])",
+ r"\b(?P<bool_true>true)\b|\b(?P<bool_false>false)\b|\b(?P<null>null)\b",
+ r"(?P<number>(?<!\w)\-?[0-9]+\.?[0-9]*(e[\-\+]?\d+?)?\b|0x[0-9a-fA-F]*)",
+ r"(?<![\\\w])(?P<str>b?\".*?(?<!\\)\")",
+ ),
+ r"(?<![\\\w])(?P<key>b?\".*?(?<!\\)\")\:",
+ ]
+
+
+if __name__ == "__main__": # pragma: no cover
+ from .console import Console
+
+ console = Console()
+ console.print("[bold green]hello world![/bold green]")
+ console.print("'[bold green]hello world![/bold green]'")
+
+ console.print(" /foo")
+ console.print("/foo/")
+ console.print("/foo/bar")
+ console.print("foo/bar/baz")
+
+ console.print("/foo/bar/baz?foo=bar+egg&egg=baz")
+ console.print("/foo/bar/baz/")
+ console.print("/foo/bar/baz/egg")
+ console.print("/foo/bar/baz/egg.py")
+ console.print("/foo/bar/baz/egg.py word")
+ console.print(" /foo/bar/baz/egg.py word")
+ console.print("foo /foo/bar/baz/egg.py word")
+ console.print("foo /foo/bar/ba._++z/egg+.py word")
+ console.print("https://example.org?foo=bar#header")
+
+ console.print(1234567.34)
+ console.print(1 / 2)
+ console.print(-1 / 123123123123)
+
+ console.print(
+ "127.0.1.1 bar 192.168.1.4 2001:0db8:85a3:0000:0000:8a2e:0370:7334 foo"
+ )
diff --git a/src/pip/_vendor/rich/json.py b/src/pip/_vendor/rich/json.py
new file mode 100644
index 000000000..7cf6b5fb2
--- /dev/null
+++ b/src/pip/_vendor/rich/json.py
@@ -0,0 +1,140 @@
+from json import loads, dumps
+from typing import Any, Callable, Optional
+
+from .text import Text
+from .highlighter import JSONHighlighter, NullHighlighter
+
+
+class JSON:
+ """A renderable which pretty prints JSON.
+
+ Args:
+ json (str): JSON encoded data.
+ indent (int, optional): Number of characters to indent by. Defaults to 2.
+ highlight (bool, optional): Enable highlighting. Defaults to True.
+ skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False.
+ ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False.
+ check_circular (bool, optional): Check for circular references. Defaults to True.
+ allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True.
+ default (Callable, optional): A callable that converts values that can not be encoded
+ in to something that can be JSON encoded. Defaults to None.
+ sort_keys (bool, optional): Sort dictionary keys. Defaults to False.
+ """
+
+ def __init__(
+ self,
+ json: str,
+ indent: int = 2,
+ highlight: bool = True,
+ skip_keys: bool = False,
+ ensure_ascii: bool = True,
+ check_circular: bool = True,
+ allow_nan: bool = True,
+ default: Optional[Callable[[Any], Any]] = None,
+ sort_keys: bool = False,
+ ) -> None:
+ data = loads(json)
+ json = dumps(
+ data,
+ indent=indent,
+ skipkeys=skip_keys,
+ ensure_ascii=ensure_ascii,
+ check_circular=check_circular,
+ allow_nan=allow_nan,
+ default=default,
+ sort_keys=sort_keys,
+ )
+ highlighter = JSONHighlighter() if highlight else NullHighlighter()
+ self.text = highlighter(json)
+ self.text.no_wrap = True
+ self.text.overflow = None
+
+ @classmethod
+ def from_data(
+ cls,
+ data: Any,
+ indent: int = 2,
+ highlight: bool = True,
+ skip_keys: bool = False,
+ ensure_ascii: bool = True,
+ check_circular: bool = True,
+ allow_nan: bool = True,
+ default: Optional[Callable[[Any], Any]] = None,
+ sort_keys: bool = False,
+ ) -> "JSON":
+ """Encodes a JSON object from arbitrary data.
+
+ Args:
+ data (Any): An object that may be encoded in to JSON
+ indent (int, optional): Number of characters to indent by. Defaults to 2.
+ highlight (bool, optional): Enable highlighting. Defaults to True.
+ default (Callable, optional): Optional callable which will be called for objects that cannot be serialized. Defaults to None.
+ skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False.
+ ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False.
+ check_circular (bool, optional): Check for circular references. Defaults to True.
+ allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True.
+ default (Callable, optional): A callable that converts values that can not be encoded
+ in to something that can be JSON encoded. Defaults to None.
+ sort_keys (bool, optional): Sort dictionary keys. Defaults to False.
+
+ Returns:
+ JSON: New JSON object from the given data.
+ """
+ json_instance: "JSON" = cls.__new__(cls)
+ json = dumps(
+ data,
+ indent=indent,
+ skipkeys=skip_keys,
+ ensure_ascii=ensure_ascii,
+ check_circular=check_circular,
+ allow_nan=allow_nan,
+ default=default,
+ sort_keys=sort_keys,
+ )
+ highlighter = JSONHighlighter() if highlight else NullHighlighter()
+ json_instance.text = highlighter(json)
+ json_instance.text.no_wrap = True
+ json_instance.text.overflow = None
+ return json_instance
+
+ def __rich__(self) -> Text:
+ return self.text
+
+
+if __name__ == "__main__":
+
+ import argparse
+ import sys
+
+ parser = argparse.ArgumentParser(description="Pretty print json")
+ parser.add_argument(
+ "path",
+ metavar="PATH",
+ help="path to file, or - for stdin",
+ )
+ parser.add_argument(
+ "-i",
+ "--indent",
+ metavar="SPACES",
+ type=int,
+ help="Number of spaces in an indent",
+ default=2,
+ )
+ args = parser.parse_args()
+
+ from pip._vendor.rich.console import Console
+
+ console = Console()
+ error_console = Console(stderr=True)
+
+ try:
+ if args.path == "-":
+ json_data = sys.stdin.read()
+ else:
+ with open(args.path, "rt") as json_file:
+ json_data = json_file.read()
+ except Exception as error:
+ error_console.print(f"Unable to read {args.path!r}; {error}")
+ sys.exit(-1)
+
+ console.print(JSON(json_data, indent=args.indent), soft_wrap=True)
diff --git a/src/pip/_vendor/rich/jupyter.py b/src/pip/_vendor/rich/jupyter.py
new file mode 100644
index 000000000..7cdcc9cab
--- /dev/null
+++ b/src/pip/_vendor/rich/jupyter.py
@@ -0,0 +1,88 @@
+from typing import Any, Dict, Iterable, List
+
+from . import get_console
+from .segment import Segment
+from .terminal_theme import DEFAULT_TERMINAL_THEME
+
+
+JUPYTER_HTML_FORMAT = """\
+<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">{code}</pre>
+"""
+
+
+class JupyterRenderable:
+ """A shim to write html to Jupyter notebook."""
+
+ def __init__(self, html: str, text: str) -> None:
+ self.html = html
+ self.text = text
+
+ def _repr_mimebundle_(
+ self, include: Iterable[str], exclude: Iterable[str], **kwargs: Any
+ ) -> Dict[str, str]:
+ data = {"text/plain": self.text, "text/html": self.html}
+ if include:
+ data = {k: v for (k, v) in data.items() if k in include}
+ if exclude:
+ data = {k: v for (k, v) in data.items() if k not in exclude}
+ return data
+
+
+class JupyterMixin:
+ """Add to an Rich renderable to make it render in Jupyter notebook."""
+
+ __slots__ = ()
+
+ def _repr_mimebundle_(
+ self, include: Iterable[str], exclude: Iterable[str], **kwargs: Any
+ ) -> Dict[str, str]:
+ console = get_console()
+ segments = list(console.render(self, console.options)) # type: ignore
+ html = _render_segments(segments)
+ text = console._render_buffer(segments)
+ data = {"text/plain": text, "text/html": html}
+ if include:
+ data = {k: v for (k, v) in data.items() if k in include}
+ if exclude:
+ data = {k: v for (k, v) in data.items() if k not in exclude}
+ return data
+
+
+def _render_segments(segments: Iterable[Segment]) -> str:
+ def escape(text: str) -> str:
+ """Escape html."""
+ return text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
+
+ fragments: List[str] = []
+ append_fragment = fragments.append
+ theme = DEFAULT_TERMINAL_THEME
+ for text, style, control in Segment.simplify(segments):
+ if control:
+ continue
+ text = escape(text)
+ if style:
+ rule = style.get_html_style(theme)
+ text = f'<span style="{rule}">{text}</span>' if rule else text
+ if style.link:
+ text = f'<a href="{style.link}">{text}</a>'
+ append_fragment(text)
+
+ code = "".join(fragments)
+ html = JUPYTER_HTML_FORMAT.format(code=code)
+
+ return html
+
+
+def display(segments: Iterable[Segment], text: str) -> None:
+ """Render segments to Jupyter."""
+ from IPython.display import display as ipython_display
+
+ html = _render_segments(segments)
+ jupyter_renderable = JupyterRenderable(html, text)
+ ipython_display(jupyter_renderable)
+
+
+def print(*args: Any, **kwargs: Any) -> None:
+ """Proxy for Console print."""
+ console = get_console()
+ return console.print(*args, **kwargs)
diff --git a/src/pip/_vendor/rich/layout.py b/src/pip/_vendor/rich/layout.py
new file mode 100644
index 000000000..22a4c5478
--- /dev/null
+++ b/src/pip/_vendor/rich/layout.py
@@ -0,0 +1,444 @@
+from abc import ABC, abstractmethod
+from itertools import islice
+from operator import itemgetter
+from threading import RLock
+from typing import (
+ TYPE_CHECKING,
+ Dict,
+ Iterable,
+ List,
+ NamedTuple,
+ Optional,
+ Sequence,
+ Tuple,
+ Union,
+)
+
+from ._ratio import ratio_resolve
+from .align import Align
+from .console import Console, ConsoleOptions, RenderableType, RenderResult
+from .highlighter import ReprHighlighter
+from .panel import Panel
+from .pretty import Pretty
+from .repr import rich_repr, Result
+from .region import Region
+from .segment import Segment
+from .style import StyleType
+
+if TYPE_CHECKING:
+ from pip._vendor.rich.tree import Tree
+
+
+class LayoutRender(NamedTuple):
+ """An individual layout render."""
+
+ region: Region
+ render: List[List[Segment]]
+
+
+RegionMap = Dict["Layout", Region]
+RenderMap = Dict["Layout", LayoutRender]
+
+
+class LayoutError(Exception):
+ """Layout related error."""
+
+
+class NoSplitter(LayoutError):
+ """Requested splitter does not exist."""
+
+
+class _Placeholder:
+ """An internal renderable used as a Layout placeholder."""
+
+ highlighter = ReprHighlighter()
+
+ def __init__(self, layout: "Layout", style: StyleType = "") -> None:
+ self.layout = layout
+ self.style = style
+
+ def __rich_console__(
+ self, console: Console, options: ConsoleOptions
+ ) -> RenderResult:
+ width = options.max_width
+ height = options.height or options.size.height
+ layout = self.layout
+ title = (
+ f"{layout.name!r} ({width} x {height})"
+ if layout.name
+ else f"({width} x {height})"
+ )
+ yield Panel(
+ Align.center(Pretty(layout), vertical="middle"),
+ style=self.style,
+ title=self.highlighter(title),
+ border_style="blue",
+ )
+
+
+class Splitter(ABC):
+ """Base class for a splitter."""
+
+ name: str = ""
+
+ @abstractmethod
+ def get_tree_icon(self) -> str:
+ """Get the icon (emoji) used in layout.tree"""
+
+ @abstractmethod
+ def divide(
+ self, children: Sequence["Layout"], region: Region
+ ) -> Iterable[Tuple["Layout", Region]]:
+ """Divide a region amongst several child layouts.
+
+ Args:
+ children (Sequence(Layout)): A number of child layouts.
+ region (Region): A rectangular region to divide.
+ """
+
+
+class RowSplitter(Splitter):
+ """Split a layout region in to rows."""
+
+ name = "row"
+
+ def get_tree_icon(self) -> str:
+ return "[layout.tree.row]⬌"
+
+ def divide(
+ self, children: Sequence["Layout"], region: Region
+ ) -> Iterable[Tuple["Layout", Region]]:
+ x, y, width, height = region
+ render_widths = ratio_resolve(width, children)
+ offset = 0
+ _Region = Region
+ for child, child_width in zip(children, render_widths):
+ yield child, _Region(x + offset, y, child_width, height)
+ offset += child_width
+
+
+class ColumnSplitter(Splitter):
+ """Split a layout region in to columns."""
+
+ name = "column"
+
+ def get_tree_icon(self) -> str:
+ return "[layout.tree.column]⬍"
+
+ def divide(
+ self, children: Sequence["Layout"], region: Region
+ ) -> Iterable[Tuple["Layout", Region]]:
+ x, y, width, height = region
+ render_heights = ratio_resolve(height, children)
+ offset = 0
+ _Region = Region
+ for child, child_height in zip(children, render_heights):
+ yield child, _Region(x, y + offset, width, child_height)
+ offset += child_height
+
+
+@rich_repr
+class Layout:
+ """A renderable to divide a fixed height in to rows or columns.
+
+ Args:
+ renderable (RenderableType, optional): Renderable content, or None for placeholder. Defaults to None.
+ name (str, optional): Optional identifier for Layout. Defaults to None.
+ size (int, optional): Optional fixed size of layout. Defaults to None.
+ minimum_size (int, optional): Minimum size of layout. Defaults to 1.
+ ratio (int, optional): Optional ratio for flexible layout. Defaults to 1.
+ visible (bool, optional): Visibility of layout. Defaults to True.
+ """
+
+ splitters = {"row": RowSplitter, "column": ColumnSplitter}
+
+ def __init__(
+ self,
+ renderable: Optional[RenderableType] = None,
+ *,
+ name: Optional[str] = None,
+ size: Optional[int] = None,
+ minimum_size: int = 1,
+ ratio: int = 1,
+ visible: bool = True,
+ height: Optional[int] = None,
+ ) -> None:
+ self._renderable = renderable or _Placeholder(self)
+ self.size = size
+ self.minimum_size = minimum_size
+ self.ratio = ratio
+ self.name = name
+ self.visible = visible
+ self.height = height
+ self.splitter: Splitter = self.splitters["column"]()
+ self._children: List[Layout] = []
+ self._render_map: RenderMap = {}
+ self._lock = RLock()
+
+ def __rich_repr__(self) -> Result:
+ yield "name", self.name, None
+ yield "size", self.size, None
+ yield "minimum_size", self.minimum_size, 1
+ yield "ratio", self.ratio, 1
+
+ @property
+ def renderable(self) -> RenderableType:
+ """Layout renderable."""
+ return self if self._children else self._renderable
+
+ @property
+ def children(self) -> List["Layout"]:
+ """Gets (visible) layout children."""
+ return [child for child in self._children if child.visible]
+
+ @property
+ def map(self) -> RenderMap:
+ """Get a map of the last render."""
+ return self._render_map
+
+ def get(self, name: str) -> Optional["Layout"]:
+ """Get a named layout, or None if it doesn't exist.
+
+ Args:
+ name (str): Name of layout.
+
+ Returns:
+ Optional[Layout]: Layout instance or None if no layout was found.
+ """
+ if self.name == name:
+ return self
+ else:
+ for child in self._children:
+ named_layout = child.get(name)
+ if named_layout is not None:
+ return named_layout
+ return None
+
+ def __getitem__(self, name: str) -> "Layout":
+ layout = self.get(name)
+ if layout is None:
+ raise KeyError(f"No layout with name {name!r}")
+ return layout
+
+ @property
+ def tree(self) -> "Tree":
+ """Get a tree renderable to show layout structure."""
+ from pip._vendor.rich.styled import Styled
+ from pip._vendor.rich.table import Table
+ from pip._vendor.rich.tree import Tree
+
+ def summary(layout: "Layout") -> Table:
+
+ icon = layout.splitter.get_tree_icon()
+
+ table = Table.grid(padding=(0, 1, 0, 0))
+
+ text: RenderableType = (
+ Pretty(layout) if layout.visible else Styled(Pretty(layout), "dim")
+ )
+ table.add_row(icon, text)
+ _summary = table
+ return _summary
+
+ layout = self
+ tree = Tree(
+ summary(layout),
+ guide_style=f"layout.tree.{layout.splitter.name}",
+ highlight=True,
+ )
+
+ def recurse(tree: "Tree", layout: "Layout") -> None:
+ for child in layout._children:
+ recurse(
+ tree.add(
+ summary(child),
+ guide_style=f"layout.tree.{child.splitter.name}",
+ ),
+ child,
+ )
+
+ recurse(tree, self)
+ return tree
+
+ def split(
+ self,
+ *layouts: Union["Layout", RenderableType],
+ splitter: Union[Splitter, str] = "column",
+ ) -> None:
+ """Split the layout in to multiple sub-layouts.
+
+ Args:
+ *layouts (Layout): Positional arguments should be (sub) Layout instances.
+ splitter (Union[Splitter, str]): Splitter instance or name of splitter.
+ """
+ _layouts = [
+ layout if isinstance(layout, Layout) else Layout(layout)
+ for layout in layouts
+ ]
+ try:
+ self.splitter = (
+ splitter
+ if isinstance(splitter, Splitter)
+ else self.splitters[splitter]()
+ )
+ except KeyError:
+ raise NoSplitter(f"No splitter called {splitter!r}")
+ self._children[:] = _layouts
+
+ def add_split(self, *layouts: Union["Layout", RenderableType]) -> None:
+ """Add a new layout(s) to existing split.
+
+ Args:
+ *layouts (Union[Layout, RenderableType]): Positional arguments should be renderables or (sub) Layout instances.
+
+ """
+ _layouts = (
+ layout if isinstance(layout, Layout) else Layout(layout)
+ for layout in layouts
+ )
+ self._children.extend(_layouts)
+
+ def split_row(self, *layouts: Union["Layout", RenderableType]) -> None:
+ """Split the layout in tow a row (Layouts side by side).
+
+ Args:
+ *layouts (Layout): Positional arguments should be (sub) Layout instances.
+ """
+ self.split(*layouts, splitter="row")
+
+ def split_column(self, *layouts: Union["Layout", RenderableType]) -> None:
+ """Split the layout in to a column (layouts stacked on top of each other).
+
+ Args:
+ *layouts (Layout): Positional arguments should be (sub) Layout instances.
+ """
+ self.split(*layouts, splitter="column")
+
+ def unsplit(self) -> None:
+ """Reset splits to initial state."""
+ del self._children[:]
+
+ def update(self, renderable: RenderableType) -> None:
+ """Update renderable.
+
+ Args:
+ renderable (RenderableType): New renderable object.
+ """
+ with self._lock:
+ self._renderable = renderable
+
+ def refresh_screen(self, console: "Console", layout_name: str) -> None:
+ """Refresh a sub-layout.
+
+ Args:
+ console (Console): Console instance where Layout is to be rendered.
+ layout_name (str): Name of layout.
+ """
+ with self._lock:
+ layout = self[layout_name]
+ region, _lines = self._render_map[layout]
+ (x, y, width, height) = region
+ lines = console.render_lines(
+ layout, console.options.update_dimensions(width, height)
+ )
+ self._render_map[layout] = LayoutRender(region, lines)
+ console.update_screen_lines(lines, x, y)
+
+ def _make_region_map(self, width: int, height: int) -> RegionMap:
+ """Create a dict that maps layout on to Region."""
+ stack: List[Tuple[Layout, Region]] = [(self, Region(0, 0, width, height))]
+ push = stack.append
+ pop = stack.pop
+ layout_regions: List[Tuple[Layout, Region]] = []
+ append_layout_region = layout_regions.append
+ while stack:
+ append_layout_region(pop())
+ layout, region = layout_regions[-1]
+ children = layout.children
+ if children:
+ for child_and_region in layout.splitter.divide(children, region):
+ push(child_and_region)
+
+ region_map = {
+ layout: region
+ for layout, region in sorted(layout_regions, key=itemgetter(1))
+ }
+ return region_map
+
+ def render(self, console: Console, options: ConsoleOptions) -> RenderMap:
+ """Render the sub_layouts.
+
+ Args:
+ console (Console): Console instance.
+ options (ConsoleOptions): Console options.
+
+ Returns:
+ RenderMap: A dict that maps Layout on to a tuple of Region, lines
+ """
+ render_width = options.max_width
+ render_height = options.height or console.height
+ region_map = self._make_region_map(render_width, render_height)
+ layout_regions = [
+ (layout, region)
+ for layout, region in region_map.items()
+ if not layout.children
+ ]
+ render_map: Dict["Layout", "LayoutRender"] = {}
+ render_lines = console.render_lines
+ update_dimensions = options.update_dimensions
+
+ for layout, region in layout_regions:
+ lines = render_lines(
+ layout.renderable, update_dimensions(region.width, region.height)
+ )
+ render_map[layout] = LayoutRender(region, lines)
+ return render_map
+
+ def __rich_console__(
+ self, console: Console, options: ConsoleOptions
+ ) -> RenderResult:
+ with self._lock:
+ width = options.max_width or console.width
+ height = options.height or console.height
+ render_map = self.render(console, options.update_dimensions(width, height))
+ self._render_map = render_map
+ layout_lines: List[List[Segment]] = [[] for _ in range(height)]
+ _islice = islice
+ for (region, lines) in render_map.values():
+ _x, y, _layout_width, layout_height = region
+ for row, line in zip(
+ _islice(layout_lines, y, y + layout_height), lines
+ ):
+ row.extend(line)
+
+ new_line = Segment.line()
+ for layout_row in layout_lines:
+ yield from layout_row
+ yield new_line
+
+
+if __name__ == "__main__":
+ from pip._vendor.rich.console import Console
+
+ console = Console()
+ layout = Layout()
+
+ layout.split_column(
+ Layout(name="header", size=3),
+ Layout(ratio=1, name="main"),
+ Layout(size=10, name="footer"),
+ )
+
+ layout["main"].split_row(Layout(name="side"), Layout(name="body", ratio=2))
+
+ layout["body"].split_row(Layout(name="content", ratio=2), Layout(name="s2"))
+
+ layout["s2"].split_column(
+ Layout(name="top"), Layout(name="middle"), Layout(name="bottom")
+ )
+
+ layout["side"].split_column(Layout(layout.tree, name="left1"), Layout(name="left2"))
+
+ layout["content"].update("foo")
+
+ console.print(layout)
diff --git a/src/pip/_vendor/rich/live.py b/src/pip/_vendor/rich/live.py
new file mode 100644
index 000000000..cb36ddb8d
--- /dev/null
+++ b/src/pip/_vendor/rich/live.py
@@ -0,0 +1,377 @@
+import sys
+from threading import Event, RLock, Thread
+from types import TracebackType
+from typing import IO, Any, Callable, List, Optional, TextIO, Type, cast
+
+from . import get_console
+from .console import Console, ConsoleRenderable, RenderableType, RenderHook
+from .control import Control
+from .file_proxy import FileProxy
+from .jupyter import JupyterMixin
+from .live_render import LiveRender, VerticalOverflowMethod
+from .screen import Screen
+from .text import Text
+
+
+class _RefreshThread(Thread):
+ """A thread that calls refresh() at regular intervals."""
+
+ def __init__(self, live: "Live", refresh_per_second: float) -> None:
+ self.live = live
+ self.refresh_per_second = refresh_per_second
+ self.done = Event()
+ super().__init__(daemon=True)
+
+ def stop(self) -> None:
+ self.done.set()
+
+ def run(self) -> None:
+ while not self.done.wait(1 / self.refresh_per_second):
+ with self.live._lock:
+ if not self.done.is_set():
+ self.live.refresh()
+
+
+class Live(JupyterMixin, RenderHook):
+ """Renders an auto-updating live display of any given renderable.
+
+ Args:
+ renderable (RenderableType, optional): The renderable to live display. Defaults to displaying nothing.
+ console (Console, optional): Optional Console instance. Default will an internal Console instance writing to stdout.
+ screen (bool, optional): Enable alternate screen mode. Defaults to False.
+ auto_refresh (bool, optional): Enable auto refresh. If disabled, you will need to call `refresh()` or `update()` with refresh flag. Defaults to True
+ refresh_per_second (float, optional): Number of times per second to refresh the live display. Defaults to 4.
+ transient (bool, optional): Clear the renderable on exit (has no effect when screen=True). Defaults to False.
+ redirect_stdout (bool, optional): Enable redirection of stdout, so ``print`` may be used. Defaults to True.
+ redirect_stderr (bool, optional): Enable redirection of stderr. Defaults to True.
+ vertical_overflow (VerticalOverflowMethod, optional): How to handle renderable when it is too tall for the console. Defaults to "ellipsis".
+ get_renderable (Callable[[], RenderableType], optional): Optional callable to get renderable. Defaults to None.
+ """
+
+ def __init__(
+ self,
+ renderable: Optional[RenderableType] = None,
+ *,
+ console: Optional[Console] = None,
+ screen: bool = False,
+ auto_refresh: bool = True,
+ refresh_per_second: float = 4,
+ transient: bool = False,
+ redirect_stdout: bool = True,
+ redirect_stderr: bool = True,
+ vertical_overflow: VerticalOverflowMethod = "ellipsis",
+ get_renderable: Optional[Callable[[], RenderableType]] = None,
+ ) -> None:
+ assert refresh_per_second > 0, "refresh_per_second must be > 0"
+ self._renderable = renderable
+ self.console = console if console is not None else get_console()
+ self._screen = screen
+ self._alt_screen = False
+
+ self._redirect_stdout = redirect_stdout
+ self._redirect_stderr = redirect_stderr
+ self._restore_stdout: Optional[IO[str]] = None
+ self._restore_stderr: Optional[IO[str]] = None
+
+ self._lock = RLock()
+ self.ipy_widget: Optional[Any] = None
+ self.auto_refresh = auto_refresh
+ self._started: bool = False
+ self.transient = True if screen else transient
+
+ self._refresh_thread: Optional[_RefreshThread] = None
+ self.refresh_per_second = refresh_per_second
+
+ self.vertical_overflow = vertical_overflow
+ self._get_renderable = get_renderable
+ self._live_render = LiveRender(
+ self.get_renderable(), vertical_overflow=vertical_overflow
+ )
+
+ @property
+ def is_started(self) -> bool:
+ """Check if live display has been started."""
+ return self._started
+
+ def get_renderable(self) -> RenderableType:
+ renderable = (
+ self._get_renderable()
+ if self._get_renderable is not None
+ else self._renderable
+ )
+ return renderable or ""
+
+ def start(self, refresh: bool = False) -> None:
+ """Start live rendering display.
+
+ Args:
+ refresh (bool, optional): Also refresh. Defaults to False.
+ """
+ with self._lock:
+ if self._started:
+ return
+ self.console.set_live(self)
+ self._started = True
+ if self._screen:
+ self._alt_screen = self.console.set_alt_screen(True)
+ self.console.show_cursor(False)
+ self._enable_redirect_io()
+ self.console.push_render_hook(self)
+ if refresh:
+ self.refresh()
+ if self.auto_refresh:
+ self._refresh_thread = _RefreshThread(self, self.refresh_per_second)
+ self._refresh_thread.start()
+
+ def stop(self) -> None:
+ """Stop live rendering display."""
+ with self._lock:
+ if not self._started:
+ return
+ self.console.clear_live()
+ self._started = False
+ try:
+ if self.auto_refresh and self._refresh_thread is not None:
+ self._refresh_thread.stop()
+ # allow it to fully render on the last even if overflow
+ self.vertical_overflow = "visible"
+ if not self._alt_screen and not self.console.is_jupyter:
+ self.refresh()
+
+ finally:
+ self._disable_redirect_io()
+ self.console.pop_render_hook()
+ if not self._alt_screen and self.console.is_terminal:
+ self.console.line()
+ self.console.show_cursor(True)
+ if self._alt_screen:
+ self.console.set_alt_screen(False)
+
+ if self._refresh_thread is not None:
+ self._refresh_thread.join()
+ self._refresh_thread = None
+ if self.transient and not self._alt_screen:
+ self.console.control(self._live_render.restore_cursor())
+ if self.ipy_widget is not None: # pragma: no cover
+ if self.transient:
+ self.ipy_widget.close()
+ else:
+ # jupyter last refresh must occur after console pop render hook
+ # i am not sure why this is needed
+ self.refresh()
+ if not self.console.is_interactive:
+ return self.console.print()
+
+ def __enter__(self) -> "Live":
+ self.start(refresh=self._renderable is not None)
+ return self
+
+ def __exit__(
+ self,
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[TracebackType],
+ ) -> None:
+ self.stop()
+
+ def _enable_redirect_io(self) -> None:
+ """Enable redirecting of stdout / stderr."""
+ if self.console.is_terminal:
+ if self._redirect_stdout and not isinstance(sys.stdout, FileProxy):
+ self._restore_stdout = sys.stdout
+ sys.stdout = cast("TextIO", FileProxy(self.console, sys.stdout))
+ if self._redirect_stderr and not isinstance(sys.stderr, FileProxy):
+ self._restore_stderr = sys.stderr
+ sys.stderr = cast("TextIO", FileProxy(self.console, sys.stderr))
+
+ def _disable_redirect_io(self) -> None:
+ """Disable redirecting of stdout / stderr."""
+ if self._restore_stdout:
+ sys.stdout = cast("TextIO", self._restore_stdout)
+ self._restore_stdout = None
+ if self._restore_stderr:
+ sys.stderr = cast("TextIO", self._restore_stderr)
+ self._restore_stderr = None
+
+ @property
+ def renderable(self) -> RenderableType:
+ """Get the renderable that is being displayed
+
+ Returns:
+ RenderableType: Displayed renderable.
+ """
+ renderable = self.get_renderable()
+ return Screen(renderable) if self._alt_screen else renderable
+
+ def update(self, renderable: RenderableType, *, refresh: bool = False) -> None:
+ """Update the renderable that is being displayed
+
+ Args:
+ renderable (RenderableType): New renderable to use.
+ refresh (bool, optional): Refresh the display. Defaults to False.
+ """
+ with self._lock:
+ self._renderable = renderable
+ if refresh:
+ self.refresh()
+
+ def refresh(self) -> None:
+ """Update the display of the Live Render."""
+ with self._lock:
+ self._live_render.set_renderable(self.renderable)
+ if self.console.is_jupyter: # pragma: no cover
+ try:
+ from IPython.display import display
+ from ipywidgets import Output
+ except ImportError:
+ import warnings
+
+ warnings.warn('install "ipywidgets" for Jupyter support')
+ else:
+ if self.ipy_widget is None:
+ self.ipy_widget = Output()
+ display(self.ipy_widget)
+
+ with self.ipy_widget:
+ self.ipy_widget.clear_output(wait=True)
+ self.console.print(self._live_render.renderable)
+ elif self.console.is_terminal and not self.console.is_dumb_terminal:
+ with self.console:
+ self.console.print(Control())
+ elif (
+ not self._started and not self.transient
+ ): # if it is finished allow files or dumb-terminals to see final result
+ with self.console:
+ self.console.print(Control())
+
+ def process_renderables(
+ self, renderables: List[ConsoleRenderable]
+ ) -> List[ConsoleRenderable]:
+ """Process renderables to restore cursor and display progress."""
+ self._live_render.vertical_overflow = self.vertical_overflow
+ if self.console.is_interactive:
+ # lock needs acquiring as user can modify live_render renderable at any time unlike in Progress.
+ with self._lock:
+ reset = (
+ Control.home()
+ if self._alt_screen
+ else self._live_render.position_cursor()
+ )
+ renderables = [
+ reset,
+ *renderables,
+ self._live_render,
+ ]
+ elif (
+ not self._started and not self.transient
+ ): # if it is finished render the final output for files or dumb_terminals
+ renderables = [*renderables, self._live_render]
+
+ return renderables
+
+
+if __name__ == "__main__": # pragma: no cover
+ import random
+ import time
+ from itertools import cycle
+ from typing import Dict, List, Tuple
+
+ from .align import Align
+ from .console import Console
+ from .live import Live as Live
+ from .panel import Panel
+ from .rule import Rule
+ from .syntax import Syntax
+ from .table import Table
+
+ console = Console()
+
+ syntax = Syntax(
+ '''def loop_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]:
+ """Iterate and generate a tuple with a flag for last value."""
+ iter_values = iter(values)
+ try:
+ previous_value = next(iter_values)
+ except StopIteration:
+ return
+ for value in iter_values:
+ yield False, previous_value
+ previous_value = value
+ yield True, previous_value''',
+ "python",
+ line_numbers=True,
+ )
+
+ table = Table("foo", "bar", "baz")
+ table.add_row("1", "2", "3")
+
+ progress_renderables = [
+ "You can make the terminal shorter and taller to see the live table hide"
+ "Text may be printed while the progress bars are rendering.",
+ Panel("In fact, [i]any[/i] renderable will work"),
+ "Such as [magenta]tables[/]...",
+ table,
+ "Pretty printed structures...",
+ {"type": "example", "text": "Pretty printed"},
+ "Syntax...",
+ syntax,
+ Rule("Give it a try!"),
+ ]
+
+ examples = cycle(progress_renderables)
+
+ exchanges = [
+ "SGD",
+ "MYR",
+ "EUR",
+ "USD",
+ "AUD",
+ "JPY",
+ "CNH",
+ "HKD",
+ "CAD",
+ "INR",
+ "DKK",
+ "GBP",
+ "RUB",
+ "NZD",
+ "MXN",
+ "IDR",
+ "TWD",
+ "THB",
+ "VND",
+ ]
+ with Live(console=console) as live_table:
+ exchange_rate_dict: Dict[Tuple[str, str], float] = {}
+
+ for index in range(100):
+ select_exchange = exchanges[index % len(exchanges)]
+
+ for exchange in exchanges:
+ if exchange == select_exchange:
+ continue
+ time.sleep(0.4)
+ if random.randint(0, 10) < 1:
+ console.log(next(examples))
+ exchange_rate_dict[(select_exchange, exchange)] = 200 / (
+ (random.random() * 320) + 1
+ )
+ if len(exchange_rate_dict) > len(exchanges) - 1:
+ exchange_rate_dict.pop(list(exchange_rate_dict.keys())[0])
+ table = Table(title="Exchange Rates")
+
+ table.add_column("Source Currency")
+ table.add_column("Destination Currency")
+ table.add_column("Exchange Rate")
+
+ for ((source, dest), exchange_rate) in exchange_rate_dict.items():
+ table.add_row(
+ source,
+ dest,
+ Text(
+ f"{exchange_rate:.4f}",
+ style="red" if exchange_rate < 1.0 else "green",
+ ),
+ )
+
+ live_table.update(Align.center(table))
diff --git a/src/pip/_vendor/rich/live_render.py b/src/pip/_vendor/rich/live_render.py
new file mode 100644
index 000000000..b02fd3176
--- /dev/null
+++ b/src/pip/_vendor/rich/live_render.py
@@ -0,0 +1,113 @@
+import sys
+from typing import Optional, Tuple
+
+if sys.version_info >= (3, 8):
+ from typing import Literal
+else:
+ from pip._vendor.typing_extensions import Literal # pragma: no cover
+
+
+from ._loop import loop_last
+from .console import Console, ConsoleOptions, RenderableType, RenderResult
+from .control import Control
+from .segment import ControlType, Segment
+from .style import StyleType
+from .text import Text
+
+VerticalOverflowMethod = Literal["crop", "ellipsis", "visible"]
+
+
+class LiveRender:
+ """Creates a renderable that may be updated.
+
+ Args:
+ renderable (RenderableType): Any renderable object.
+ style (StyleType, optional): An optional style to apply to the renderable. Defaults to "".
+ """
+
+ def __init__(
+ self,
+ renderable: RenderableType,
+ style: StyleType = "",
+ vertical_overflow: VerticalOverflowMethod = "ellipsis",
+ ) -> None:
+ self.renderable = renderable
+ self.style = style
+ self.vertical_overflow = vertical_overflow
+ self._shape: Optional[Tuple[int, int]] = None
+
+ def set_renderable(self, renderable: RenderableType) -> None:
+ """Set a new renderable.
+
+ Args:
+ renderable (RenderableType): Any renderable object, including str.
+ """
+ self.renderable = renderable
+
+ def position_cursor(self) -> Control:
+ """Get control codes to move cursor to beginning of live render.
+
+ Returns:
+ Control: A control instance that may be printed.
+ """
+ if self._shape is not None:
+ _, height = self._shape
+ return Control(
+ ControlType.CARRIAGE_RETURN,
+ (ControlType.ERASE_IN_LINE, 2),
+ *(
+ (
+ (ControlType.CURSOR_UP, 1),
+ (ControlType.ERASE_IN_LINE, 2),
+ )
+ * (height - 1)
+ )
+ )
+ return Control()
+
+ def restore_cursor(self) -> Control:
+ """Get control codes to clear the render and restore the cursor to its previous position.
+
+ Returns:
+ Control: A Control instance that may be printed.
+ """
+ if self._shape is not None:
+ _, height = self._shape
+ return Control(
+ ControlType.CARRIAGE_RETURN,
+ *((ControlType.CURSOR_UP, 1), (ControlType.ERASE_IN_LINE, 2)) * height
+ )
+ return Control()
+
+ def __rich_console__(
+ self, console: Console, options: ConsoleOptions
+ ) -> RenderResult:
+
+ renderable = self.renderable
+ _Segment = Segment
+ style = console.get_style(self.style)
+ lines = console.render_lines(renderable, options, style=style, pad=False)
+ shape = _Segment.get_shape(lines)
+
+ _, height = shape
+ if height > options.size.height:
+ if self.vertical_overflow == "crop":
+ lines = lines[: options.size.height]
+ shape = _Segment.get_shape(lines)
+ elif self.vertical_overflow == "ellipsis":
+ lines = lines[: (options.size.height - 1)]
+ overflow_text = Text(
+ "...",
+ overflow="crop",
+ justify="center",
+ end="",
+ style="live.ellipsis",
+ )
+ lines.append(list(console.render(overflow_text)))
+ shape = _Segment.get_shape(lines)
+ self._shape = shape
+
+ for last, line in loop_last(lines):
+ yield from line
+ if not last:
+ yield _Segment.line()
diff --git a/src/pip/_vendor/rich/logging.py b/src/pip/_vendor/rich/logging.py
new file mode 100644
index 000000000..47ca7d42d
--- /dev/null
+++ b/src/pip/_vendor/rich/logging.py
@@ -0,0 +1,267 @@
+import logging
+from datetime import datetime
+from logging import Handler, LogRecord
+from pathlib import Path
+from typing import ClassVar, List, Optional, Type, Union
+
+from . import get_console
+from ._log_render import LogRender, FormatTimeCallable
+from .console import Console, ConsoleRenderable
+from .highlighter import Highlighter, ReprHighlighter
+from .text import Text
+from .traceback import Traceback
+
+
+class RichHandler(Handler):
+ """A logging handler that renders output with Rich. The time / level / message and file are displayed in columns.
+ The level is color coded, and the message is syntax highlighted.
+
+ Note:
+ Be careful when enabling console markup in log messages if you have configured logging for libraries not
+ under your control. If a dependency writes messages containing square brackets, it may not produce the intended output.
+
+ Args:
+ level (Union[int, str], optional): Log level. Defaults to logging.NOTSET.
+ console (:class:`~rich.console.Console`, optional): Optional console instance to write logs.
+ Default will use a global console instance writing to stdout.
+ show_time (bool, optional): Show a column for the time. Defaults to True.
+ omit_repeated_times (bool, optional): Omit repetition of the same time. Defaults to True.
+ show_level (bool, optional): Show a column for the level. Defaults to True.
+ show_path (bool, optional): Show the path to the original log call. Defaults to True.
+ enable_link_path (bool, optional): Enable terminal link of path column to file. Defaults to True.
+ highlighter (Highlighter, optional): Highlighter to style log messages, or None to use ReprHighlighter. Defaults to None.
+ markup (bool, optional): Enable console markup in log messages. Defaults to False.
+ rich_tracebacks (bool, optional): Enable rich tracebacks with syntax highlighting and formatting. Defaults to False.
+ tracebacks_width (Optional[int], optional): Number of characters used to render tracebacks, or None for full width. Defaults to None.
+ tracebacks_extra_lines (int, optional): Additional lines of code to render tracebacks, or None for full width. Defaults to None.
+ tracebacks_theme (str, optional): Override pygments theme used in traceback.
+ tracebacks_word_wrap (bool, optional): Enable word wrapping of long tracebacks lines. Defaults to True.
+ tracebacks_show_locals (bool, optional): Enable display of locals in tracebacks. 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.
+ log_time_format (Union[str, TimeFormatterCallable], optional): If ``log_time`` is enabled, either string for strftime or callable that formats the time. Defaults to "[%x %X] ".
+ """
+
+ KEYWORDS: ClassVar[Optional[List[str]]] = [
+ "GET",
+ "POST",
+ "HEAD",
+ "PUT",
+ "DELETE",
+ "OPTIONS",
+ "TRACE",
+ "PATCH",
+ ]
+ HIGHLIGHTER_CLASS: ClassVar[Type[Highlighter]] = ReprHighlighter
+
+ def __init__(
+ self,
+ level: Union[int, str] = logging.NOTSET,
+ console: Optional[Console] = None,
+ *,
+ show_time: bool = True,
+ omit_repeated_times: bool = True,
+ show_level: bool = True,
+ show_path: bool = True,
+ enable_link_path: bool = True,
+ highlighter: Optional[Highlighter] = None,
+ markup: bool = False,
+ rich_tracebacks: bool = False,
+ tracebacks_width: Optional[int] = None,
+ tracebacks_extra_lines: int = 3,
+ tracebacks_theme: Optional[str] = None,
+ tracebacks_word_wrap: bool = True,
+ tracebacks_show_locals: bool = False,
+ locals_max_length: int = 10,
+ locals_max_string: int = 80,
+ log_time_format: Union[str, FormatTimeCallable] = "[%x %X]",
+ ) -> None:
+ super().__init__(level=level)
+ self.console = console or get_console()
+ self.highlighter = highlighter or self.HIGHLIGHTER_CLASS()
+ self._log_render = LogRender(
+ show_time=show_time,
+ show_level=show_level,
+ show_path=show_path,
+ time_format=log_time_format,
+ omit_repeated_times=omit_repeated_times,
+ level_width=None,
+ )
+ self.enable_link_path = enable_link_path
+ self.markup = markup
+ self.rich_tracebacks = rich_tracebacks
+ self.tracebacks_width = tracebacks_width
+ self.tracebacks_extra_lines = tracebacks_extra_lines
+ self.tracebacks_theme = tracebacks_theme
+ self.tracebacks_word_wrap = tracebacks_word_wrap
+ self.tracebacks_show_locals = tracebacks_show_locals
+ self.locals_max_length = locals_max_length
+ self.locals_max_string = locals_max_string
+
+ def get_level_text(self, record: LogRecord) -> Text:
+ """Get the level name from the record.
+
+ Args:
+ record (LogRecord): LogRecord instance.
+
+ Returns:
+ Text: A tuple of the style and level name.
+ """
+ level_name = record.levelname
+ level_text = Text.styled(
+ level_name.ljust(8), f"logging.level.{level_name.lower()}"
+ )
+ return level_text
+
+ def emit(self, record: LogRecord) -> None:
+ """Invoked by logging."""
+ message = self.format(record)
+ traceback = None
+ if (
+ self.rich_tracebacks
+ and record.exc_info
+ and record.exc_info != (None, None, None)
+ ):
+ exc_type, exc_value, exc_traceback = record.exc_info
+ assert exc_type is not None
+ assert exc_value is not None
+ traceback = Traceback.from_exception(
+ exc_type,
+ exc_value,
+ exc_traceback,
+ width=self.tracebacks_width,
+ extra_lines=self.tracebacks_extra_lines,
+ theme=self.tracebacks_theme,
+ word_wrap=self.tracebacks_word_wrap,
+ show_locals=self.tracebacks_show_locals,
+ locals_max_length=self.locals_max_length,
+ locals_max_string=self.locals_max_string,
+ )
+ message = record.getMessage()
+ if self.formatter:
+ record.message = record.getMessage()
+ formatter = self.formatter
+ if hasattr(formatter, "usesTime") and formatter.usesTime():
+ record.asctime = formatter.formatTime(record, formatter.datefmt)
+ message = formatter.formatMessage(record)
+
+ message_renderable = self.render_message(record, message)
+ log_renderable = self.render(
+ record=record, traceback=traceback, message_renderable=message_renderable
+ )
+ try:
+ self.console.print(log_renderable)
+ except Exception:
+ self.handleError(record)
+
+ def render_message(self, record: LogRecord, message: str) -> "ConsoleRenderable":
+ """Render message text in to Text.
+
+ record (LogRecord): logging Record.
+ message (str): String containing log message.
+
+ Returns:
+ ConsoleRenderable: Renderable to display log message.
+ """
+ use_markup = (
+ getattr(record, "markup") if hasattr(record, "markup") else self.markup
+ )
+ message_text = Text.from_markup(message) if use_markup else Text(message)
+ if self.highlighter:
+ message_text = self.highlighter(message_text)
+ if self.KEYWORDS:
+ message_text.highlight_words(self.KEYWORDS, "logging.keyword")
+ return message_text
+
+ def render(
+ self,
+ *,
+ record: LogRecord,
+ traceback: Optional[Traceback],
+ message_renderable: "ConsoleRenderable",
+ ) -> "ConsoleRenderable":
+ """Render log for display.
+
+ Args:
+ record (LogRecord): logging Record.
+ traceback (Optional[Traceback]): Traceback instance or None for no Traceback.
+ message_renderable (ConsoleRenderable): Renderable (typically Text) containing log message contents.
+
+ Returns:
+ ConsoleRenderable: Renderable to display log.
+ """
+ path = Path(record.pathname).name
+ level = self.get_level_text(record)
+ time_format = None if self.formatter is None else self.formatter.datefmt
+ log_time = datetime.fromtimestamp(record.created)
+
+ log_renderable = self._log_render(
+ self.console,
+ [message_renderable] if not traceback else [message_renderable, traceback],
+ log_time=log_time,
+ time_format=time_format,
+ level=level,
+ path=path,
+ line_no=record.lineno,
+ link_path=record.pathname if self.enable_link_path else None,
+ )
+ return log_renderable
+
+
+if __name__ == "__main__": # pragma: no cover
+ from time import sleep
+
+ FORMAT = "%(message)s"
+ # FORMAT = "%(asctime)-15s - %(levelname)s - %(message)s"
+ logging.basicConfig(
+ level="NOTSET",
+ format=FORMAT,
+ datefmt="[%X]",
+ handlers=[RichHandler(rich_tracebacks=True, tracebacks_show_locals=True)],
+ )
+ log = logging.getLogger("rich")
+
+ log.info("Server starting...")
+ log.info("Listening on http://127.0.0.1:8080")
+ sleep(1)
+
+ log.info("GET /index.html 200 1298")
+ log.info("GET /imgs/backgrounds/back1.jpg 200 54386")
+ log.info("GET /css/styles.css 200 54386")
+ log.warning("GET /favicon.ico 404 242")
+ sleep(1)
+
+ log.debug(
+ "JSONRPC request\n--> %r\n<-- %r",
+ {
+ "version": "1.1",
+ "method": "confirmFruitPurchase",
+ "params": [["apple", "orange", "mangoes", "pomelo"], 1.123],
+ "id": "194521489",
+ },
+ {"version": "1.1", "result": True, "error": None, "id": "194521489"},
+ )
+ log.debug(
+ "Loading configuration file /adasd/asdasd/qeqwe/qwrqwrqwr/sdgsdgsdg/werwerwer/dfgerert/ertertert/ertetert/werwerwer"
+ )
+ log.error("Unable to find 'pomelo' in database!")
+ log.info("POST /jsonrpc/ 200 65532")
+ log.info("POST /admin/ 401 42234")
+ log.warning("password was rejected for admin site.")
+
+ def divide() -> None:
+ number = 1
+ divisor = 0
+ foos = ["foo"] * 100
+ log.debug("in divide")
+ try:
+ number / divisor
+ except:
+ log.exception("An error of some kind occurred!")
+
+ divide()
+ sleep(1)
+ log.critical("Out of memory!")
+ log.info("Server exited with code=-1")
+ log.info("[bold]EXITING...[/bold]", extra=dict(markup=True))
diff --git a/src/pip/_vendor/rich/markup.py b/src/pip/_vendor/rich/markup.py
new file mode 100644
index 000000000..ff861a354
--- /dev/null
+++ b/src/pip/_vendor/rich/markup.py
@@ -0,0 +1,244 @@
+from ast import literal_eval
+from operator import attrgetter
+import re
+from typing import Callable, Iterable, List, Match, NamedTuple, Optional, Tuple, Union
+
+from .errors import MarkupError
+from .style import Style
+from .text import Span, Text
+from .emoji import EmojiVariant
+from ._emoji_replace import _emoji_replace
+
+
+RE_TAGS = re.compile(
+ r"""((\\*)\[([a-z#\/@].*?)\])""",
+ re.VERBOSE,
+)
+
+RE_HANDLER = re.compile(r"^([\w\.]*?)(\(.*?\))?$")
+
+
+class Tag(NamedTuple):
+ """A tag in console markup."""
+
+ name: str
+ """The tag name. e.g. 'bold'."""
+ parameters: Optional[str]
+ """Any additional parameters after the name."""
+
+ def __str__(self) -> str:
+ return (
+ self.name if self.parameters is None else f"{self.name} {self.parameters}"
+ )
+
+ @property
+ def markup(self) -> str:
+ """Get the string representation of this tag."""
+ return (
+ f"[{self.name}]"
+ if self.parameters is None
+ else f"[{self.name}={self.parameters}]"
+ )
+
+
+_ReStringMatch = Match[str] # regex match object
+_ReSubCallable = Callable[[_ReStringMatch], str] # Callable invoked by re.sub
+_EscapeSubMethod = Callable[[_ReSubCallable, str], str] # Sub method of a compiled re
+
+
+def escape(
+ markup: str, _escape: _EscapeSubMethod = re.compile(r"(\\*)(\[[a-z#\/].*?\])").sub
+) -> str:
+ """Escapes text so that it won't be interpreted as markup.
+
+ Args:
+ markup (str): Content to be inserted in to markup.
+
+ Returns:
+ str: Markup with square brackets escaped.
+ """
+
+ def escape_backslashes(match: Match[str]) -> str:
+ """Called by re.sub replace matches."""
+ backslashes, text = match.groups()
+ return f"{backslashes}{backslashes}\\{text}"
+
+ markup = _escape(escape_backslashes, markup)
+ return markup
+
+
+def _parse(markup: str) -> Iterable[Tuple[int, Optional[str], Optional[Tag]]]:
+ """Parse markup in to an iterable of tuples of (position, text, tag).
+
+ Args:
+ markup (str): A string containing console markup
+
+ """
+ position = 0
+ _divmod = divmod
+ _Tag = Tag
+ for match in RE_TAGS.finditer(markup):
+ full_text, escapes, tag_text = match.groups()
+ start, end = match.span()
+ if start > position:
+ yield start, markup[position:start], None
+ if escapes:
+ backslashes, escaped = _divmod(len(escapes), 2)
+ if backslashes:
+ # Literal backslashes
+ yield start, "\\" * backslashes, None
+ start += backslashes * 2
+ if escaped:
+ # Escape of tag
+ yield start, full_text[len(escapes) :], None
+ position = end
+ continue
+ text, equals, parameters = tag_text.partition("=")
+ yield start, None, _Tag(text, parameters if equals else None)
+ position = end
+ if position < len(markup):
+ yield position, markup[position:], None
+
+
+def render(
+ markup: str,
+ style: Union[str, Style] = "",
+ emoji: bool = True,
+ emoji_variant: Optional[EmojiVariant] = None,
+) -> Text:
+ """Render console markup in to a Text instance.
+
+ Args:
+ markup (str): A string containing console markup.
+ emoji (bool, optional): Also render emoji code. Defaults to True.
+
+ Raises:
+ MarkupError: If there is a syntax error in the markup.
+
+ Returns:
+ Text: A test instance.
+ """
+ emoji_replace = _emoji_replace
+ if "[" not in markup:
+ return Text(
+ emoji_replace(markup, default_variant=emoji_variant) if emoji else markup,
+ style=style,
+ )
+ text = Text(style=style)
+ append = text.append
+ normalize = Style.normalize
+
+ style_stack: List[Tuple[int, Tag]] = []
+ pop = style_stack.pop
+
+ spans: List[Span] = []
+ append_span = spans.append
+
+ _Span = Span
+ _Tag = Tag
+
+ def pop_style(style_name: str) -> Tuple[int, Tag]:
+ """Pop tag matching given style name."""
+ for index, (_, tag) in enumerate(reversed(style_stack), 1):
+ if tag.name == style_name:
+ return pop(-index)
+ raise KeyError(style_name)
+
+ for position, plain_text, tag in _parse(markup):
+ if plain_text is not None:
+ append(emoji_replace(plain_text) if emoji else plain_text)
+ elif tag is not None:
+ if tag.name.startswith("/"): # Closing tag
+ style_name = tag.name[1:].strip()
+
+ if style_name: # explicit close
+ style_name = normalize(style_name)
+ try:
+ start, open_tag = pop_style(style_name)
+ except KeyError:
+ raise MarkupError(
+ f"closing tag '{tag.markup}' at position {position} doesn't match any open tag"
+ ) from None
+ else: # implicit close
+ try:
+ start, open_tag = pop()
+ except IndexError:
+ raise MarkupError(
+ f"closing tag '[/]' at position {position} has nothing to close"
+ ) from None
+
+ if open_tag.name.startswith("@"):
+ if open_tag.parameters:
+ handler_name = ""
+ parameters = open_tag.parameters.strip()
+ handler_match = RE_HANDLER.match(parameters)
+ if handler_match is not None:
+ handler_name, match_parameters = handler_match.groups()
+ parameters = (
+ "()" if match_parameters is None else match_parameters
+ )
+
+ try:
+ meta_params = literal_eval(parameters)
+ except SyntaxError as error:
+ raise MarkupError(
+ f"error parsing {parameters!r} in {open_tag.parameters!r}; {error.msg}"
+ )
+ except Exception as error:
+ raise MarkupError(
+ f"error parsing {open_tag.parameters!r}; {error}"
+ ) from None
+
+ if handler_name:
+ meta_params = (
+ handler_name,
+ meta_params
+ if isinstance(meta_params, tuple)
+ else (meta_params,),
+ )
+
+ else:
+ meta_params = ()
+
+ append_span(
+ _Span(
+ start, len(text), Style(meta={open_tag.name: meta_params})
+ )
+ )
+ else:
+ append_span(_Span(start, len(text), str(open_tag)))
+
+ else: # Opening tag
+ normalized_tag = _Tag(normalize(tag.name), tag.parameters)
+ style_stack.append((len(text), normalized_tag))
+
+ text_length = len(text)
+ while style_stack:
+ start, tag = style_stack.pop()
+ style = str(tag)
+ if style:
+ append_span(_Span(start, text_length, style))
+
+ text.spans = sorted(spans[::-1], key=attrgetter("start"))
+ return text
+
+
+if __name__ == "__main__": # pragma: no cover
+
+ MARKUP = [
+ "[red]Hello World[/red]",
+ "[magenta]Hello [b]World[/b]",
+ "[bold]Bold[italic] bold and italic [/bold]italic[/italic]",
+ "Click [link=https://www.willmcgugan.com]here[/link] to visit my Blog",
+ ":warning-emoji: [bold red blink] DANGER![/]",
+ ]
+
+ from pip._vendor.rich.table import Table
+ from pip._vendor.rich import print
+
+ grid = Table("Markup", "Result", padding=(0, 1))
+
+ for markup in MARKUP:
+ grid.add_row(Text(markup), markup)
+
+ print(grid)
diff --git a/src/pip/_vendor/rich/measure.py b/src/pip/_vendor/rich/measure.py
new file mode 100644
index 000000000..b57aa9c85
--- /dev/null
+++ b/src/pip/_vendor/rich/measure.py
@@ -0,0 +1,150 @@
+from operator import itemgetter
+from typing import Callable, Iterable, NamedTuple, Optional, TYPE_CHECKING
+
+from . import errors
+from .protocol import is_renderable
+
+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)
+ if hasattr(renderable, "__rich__"):
+ renderable = renderable.__rich__() # type: ignore
+ 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: Iterable["RenderableType"],
+) -> "Measurement":
+ """Get a measurement that would fit a number of renderables.
+
+ Args:
+ console (~rich.console.Console): Console instance.
+ renderables (Iterable[RenderableType]): One or more renderable objects.
+ max_width (int): The maximum width available.
+
+ 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/src/pip/_vendor/rich/padding.py b/src/pip/_vendor/rich/padding.py
new file mode 100644
index 000000000..f024e95e0
--- /dev/null
+++ b/src/pip/_vendor/rich/padding.py
@@ -0,0 +1,139 @@
+from typing import cast, List, Optional, Tuple, TYPE_CHECKING, Union
+
+if TYPE_CHECKING:
+ from .console import (
+ Console,
+ ConsoleOptions,
+ RenderableType,
+ RenderResult,
+ )
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .style import Style
+from .segment import Segment
+
+
+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 = cast(Tuple[int, int], pad)
+ return (pad_top, pad_right, pad_top, pad_right)
+ if len(pad) == 4:
+ top, right, bottom, left = cast(Tuple[int, int, int, int], 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,
+ )
+ lines = console.render_lines(
+ self.renderable,
+ options.update_width(width - self.left - self.right),
+ 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/src/pip/_vendor/rich/pager.py b/src/pip/_vendor/rich/pager.py
new file mode 100644
index 000000000..ea9bdf08f
--- /dev/null
+++ b/src/pip/_vendor/rich/pager.py
@@ -0,0 +1,34 @@
+from abc import ABC, abstractmethod
+from typing import Any, Callable
+
+
+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:
+ 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/src/pip/_vendor/rich/palette.py b/src/pip/_vendor/rich/palette.py
new file mode 100644
index 000000000..fa0c4dd40
--- /dev/null
+++ b/src/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/src/pip/_vendor/rich/panel.py b/src/pip/_vendor/rich/panel.py
new file mode 100644
index 000000000..151fe5f01
--- /dev/null
+++ b/src/pip/_vendor/rich/panel.py
@@ -0,0 +1,250 @@
+from typing import Optional, TYPE_CHECKING
+
+from .box import Box, ROUNDED
+
+from .align import AlignMethod
+from .jupyter import JupyterMixin
+from .measure import Measurement, measure_renderables
+from .padding import Padding, PaddingDimensions
+from .style import StyleType
+from .text import Text, TextType
+from .segment import Segment
+
+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, optional): A Box instance that defines the look of the border (see :ref:`appendix_box`.
+ Defaults to box.ROUNDED.
+ 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,
+ padding: PaddingDimensions = (0, 1),
+ ) -> "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,
+ padding=padding,
+ 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)
+
+ title_text = self._title
+ if title_text is not None:
+ title_text.style = 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(self.title_align, width - 4, character=box.top)
+ yield Segment(box.top_left + box.top, border_style)
+ yield from console.render(title_text)
+ 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.style = border_style
+
+ if subtitle_text is None or width <= 4:
+ yield Segment(box.get_bottom([width - 2]), border_style)
+ else:
+ subtitle_text.align(self.subtitle_align, width - 4, character=box.bottom)
+ yield Segment(box.bottom_left + box.bottom, border_style)
+ yield from console.render(subtitle_text)
+ 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 .padding import Padding
+ from .box import ROUNDED, DOUBLE
+
+ p = Panel(
+ "Hello, World!",
+ title="rich.Panel",
+ style="white on blue",
+ box=DOUBLE,
+ padding=1,
+ )
+
+ c.print()
+ c.print(p)
diff --git a/src/pip/_vendor/rich/pretty.py b/src/pip/_vendor/rich/pretty.py
new file mode 100644
index 000000000..ae9fb5cfc
--- /dev/null
+++ b/src/pip/_vendor/rich/pretty.py
@@ -0,0 +1,834 @@
+import builtins
+import os
+from pip._vendor.rich.repr import RichReprResult
+import sys
+from array import array
+from collections import Counter, defaultdict, deque, UserDict, UserList
+import dataclasses
+from dataclasses import dataclass, fields, is_dataclass
+from inspect import isclass
+from itertools import islice
+import re
+from typing import (
+ DefaultDict,
+ TYPE_CHECKING,
+ Any,
+ Callable,
+ Dict,
+ Iterable,
+ List,
+ Optional,
+ Set,
+ Union,
+ Tuple,
+)
+from types import MappingProxyType
+
+try:
+ import attr as _attr_module
+except ImportError: # pragma: no cover
+ _attr_module = None # type: ignore
+
+
+from .highlighter import ReprHighlighter
+from . import get_console
+from ._loop import loop_last
+from ._pick import pick_bool
+from .abc import RichRenderable
+from .cells import cell_len
+from .highlighter import ReprHighlighter
+from .jupyter import JupyterMixin, JupyterRenderable
+from .measure import Measurement
+from .text import Text
+
+if TYPE_CHECKING:
+ from .console import (
+ Console,
+ ConsoleOptions,
+ HighlighterType,
+ JustifyMethod,
+ OverflowMethod,
+ RenderResult,
+ )
+
+# Matches Jupyter's special methods
+_re_jupyter_repr = re.compile(f"^_repr_.+_$")
+
+
+def _is_attr_object(obj: Any) -> bool:
+ """Check if an object was created with attrs module."""
+ return _attr_module is not None and _attr_module.has(type(obj))
+
+
+def _get_attr_fields(obj: Any) -> Iterable["_attr_module.Attribute[Any]"]:
+ """Get fields for an attrs object."""
+ return _attr_module.fields(type(obj)) if _attr_module is not None else []
+
+
+def _is_dataclass_repr(obj: object) -> bool:
+ """Check if an instance of a dataclass contains the default repr.
+
+ Args:
+ obj (object): A dataclass instance.
+
+ Returns:
+ bool: True if the default repr is used, False if there is a custom repr.
+ """
+ # Digging in to a lot of internals here
+ # Catching all exceptions in case something is missing on a non CPython implementation
+ try:
+ return obj.__repr__.__code__.co_filename == dataclasses.__file__
+ except Exception:
+ return False
+
+
+def install(
+ console: Optional["Console"] = None,
+ overflow: "OverflowMethod" = "ignore",
+ crop: bool = False,
+ indent_guides: bool = False,
+ max_length: Optional[int] = None,
+ max_string: Optional[int] = None,
+ expand_all: bool = False,
+) -> None:
+ """Install automatic pretty printing in the Python REPL.
+
+ Args:
+ console (Console, optional): Console instance or ``None`` to use global console. Defaults to None.
+ overflow (Optional[OverflowMethod], optional): Overflow method. Defaults to "ignore".
+ crop (Optional[bool], optional): Enable cropping of long lines. Defaults to False.
+ indent_guides (bool, optional): Enable indentation guides. Defaults to False.
+ max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+ Defaults to None.
+ max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to None.
+ expand_all (bool, optional): Expand all containers. Defaults to False.
+ max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
+ """
+ from pip._vendor.rich import get_console
+
+ from .console import ConsoleRenderable # needed here to prevent circular import
+
+ console = console or get_console()
+ assert console is not None
+
+ def display_hook(value: Any) -> None:
+ """Replacement sys.displayhook which prettifies objects with Rich."""
+ if value is not None:
+ assert console is not None
+ builtins._ = None # type: ignore
+ console.print(
+ value
+ if isinstance(value, RichRenderable)
+ else Pretty(
+ value,
+ overflow=overflow,
+ indent_guides=indent_guides,
+ max_length=max_length,
+ max_string=max_string,
+ expand_all=expand_all,
+ ),
+ crop=crop,
+ )
+ builtins._ = value # type: ignore
+
+ def ipy_display_hook(value: Any) -> None: # pragma: no cover
+ assert console is not None
+ # always skip rich generated jupyter renderables or None values
+ if isinstance(value, JupyterRenderable) or value is None:
+ return
+ # on jupyter rich display, if using one of the special representations don't use rich
+ if console.is_jupyter and any(
+ _re_jupyter_repr.match(attr) for attr in dir(value)
+ ):
+ return
+
+ # certain renderables should start on a new line
+ if isinstance(value, ConsoleRenderable):
+ console.line()
+
+ console.print(
+ value
+ if isinstance(value, RichRenderable)
+ else Pretty(
+ value,
+ overflow=overflow,
+ indent_guides=indent_guides,
+ max_length=max_length,
+ max_string=max_string,
+ expand_all=expand_all,
+ margin=12,
+ ),
+ crop=crop,
+ new_line_start=True,
+ )
+
+ try: # pragma: no cover
+ ip = get_ipython() # type: ignore
+ from IPython.core.formatters import BaseFormatter
+
+ class RichFormatter(BaseFormatter): # type: ignore
+ pprint: bool = True
+
+ def __call__(self, value: Any) -> Any:
+ if self.pprint:
+ return ipy_display_hook(value)
+ else:
+ return repr(value)
+
+ # replace plain text formatter with rich formatter
+ rich_formatter = RichFormatter()
+ ip.display_formatter.formatters["text/plain"] = rich_formatter
+ except Exception:
+ sys.displayhook = display_hook
+
+
+class Pretty(JupyterMixin):
+ """A rich renderable that pretty prints an object.
+
+ Args:
+ _object (Any): An object to pretty print.
+ highlighter (HighlighterType, optional): Highlighter object to apply to result, or None for ReprHighlighter. Defaults to None.
+ indent_size (int, optional): Number of spaces in indent. Defaults to 4.
+ justify (JustifyMethod, optional): Justify method, or None for default. Defaults to None.
+ overflow (OverflowMethod, optional): Overflow method, or None for default. Defaults to None.
+ no_wrap (Optional[bool], optional): Disable word wrapping. Defaults to False.
+ indent_guides (bool, optional): Enable indentation guides. Defaults to False.
+ max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+ Defaults to None.
+ max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to None.
+ expand_all (bool, optional): Expand all containers. Defaults to False.
+ margin (int, optional): Subtrace a margin from width to force containers to expand earlier. Defaults to 0.
+ insert_line (bool, optional): Insert a new line if the output has multiple new lines. Defaults to False.
+ """
+
+ def __init__(
+ self,
+ _object: Any,
+ highlighter: Optional["HighlighterType"] = None,
+ *,
+ indent_size: int = 4,
+ justify: Optional["JustifyMethod"] = None,
+ overflow: Optional["OverflowMethod"] = None,
+ no_wrap: Optional[bool] = False,
+ indent_guides: bool = False,
+ max_length: Optional[int] = None,
+ max_string: Optional[int] = None,
+ expand_all: bool = False,
+ margin: int = 0,
+ insert_line: bool = False,
+ ) -> None:
+ self._object = _object
+ self.highlighter = highlighter or ReprHighlighter()
+ self.indent_size = indent_size
+ self.justify: Optional["JustifyMethod"] = justify
+ self.overflow: Optional["OverflowMethod"] = overflow
+ self.no_wrap = no_wrap
+ self.indent_guides = indent_guides
+ self.max_length = max_length
+ self.max_string = max_string
+ self.expand_all = expand_all
+ self.margin = margin
+ self.insert_line = insert_line
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+ pretty_str = pretty_repr(
+ self._object,
+ max_width=options.max_width - self.margin,
+ indent_size=self.indent_size,
+ max_length=self.max_length,
+ max_string=self.max_string,
+ expand_all=self.expand_all,
+ )
+ pretty_text = Text(
+ pretty_str,
+ justify=self.justify or options.justify,
+ overflow=self.overflow or options.overflow,
+ no_wrap=pick_bool(self.no_wrap, options.no_wrap),
+ style="pretty",
+ )
+ pretty_text = (
+ self.highlighter(pretty_text)
+ if pretty_text
+ else Text(
+ f"{type(self._object)}.__repr__ returned empty string",
+ style="dim italic",
+ )
+ )
+ if self.indent_guides and not options.ascii_only:
+ pretty_text = pretty_text.with_indent_guides(
+ self.indent_size, style="repr.indent"
+ )
+ if self.insert_line and "\n" in pretty_text:
+ yield ""
+ yield pretty_text
+
+ def __rich_measure__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "Measurement":
+ pretty_str = pretty_repr(
+ self._object,
+ max_width=options.max_width,
+ indent_size=self.indent_size,
+ max_length=self.max_length,
+ max_string=self.max_string,
+ )
+ text_width = (
+ max(cell_len(line) for line in pretty_str.splitlines()) if pretty_str else 0
+ )
+ return Measurement(text_width, text_width)
+
+
+def _get_braces_for_defaultdict(_object: DefaultDict[Any, Any]) -> Tuple[str, str, str]:
+ return (
+ f"defaultdict({_object.default_factory!r}, {{",
+ "})",
+ f"defaultdict({_object.default_factory!r}, {{}})",
+ )
+
+
+def _get_braces_for_array(_object: "array[Any]") -> Tuple[str, str, str]:
+ return (f"array({_object.typecode!r}, [", "])", "array({_object.typecode!r})")
+
+
+_BRACES: Dict[type, Callable[[Any], Tuple[str, str, str]]] = {
+ os._Environ: lambda _object: ("environ({", "})", "environ({})"),
+ array: _get_braces_for_array,
+ defaultdict: _get_braces_for_defaultdict,
+ Counter: lambda _object: ("Counter({", "})", "Counter()"),
+ deque: lambda _object: ("deque([", "])", "deque()"),
+ dict: lambda _object: ("{", "}", "{}"),
+ UserDict: lambda _object: ("{", "}", "{}"),
+ frozenset: lambda _object: ("frozenset({", "})", "frozenset()"),
+ list: lambda _object: ("[", "]", "[]"),
+ UserList: lambda _object: ("[", "]", "[]"),
+ set: lambda _object: ("{", "}", "set()"),
+ tuple: lambda _object: ("(", ")", "()"),
+ MappingProxyType: lambda _object: ("mappingproxy({", "})", "mappingproxy({})"),
+}
+_CONTAINERS = tuple(_BRACES.keys())
+_MAPPING_CONTAINERS = (dict, os._Environ, MappingProxyType, UserDict)
+
+
+def is_expandable(obj: Any) -> bool:
+ """Check if an object may be expanded by pretty print."""
+ return (
+ isinstance(obj, _CONTAINERS)
+ or (is_dataclass(obj))
+ or (hasattr(obj, "__rich_repr__"))
+ or _is_attr_object(obj)
+ ) and not isclass(obj)
+
+
+@dataclass
+class Node:
+ """A node in a repr tree. May be atomic or a container."""
+
+ key_repr: str = ""
+ value_repr: str = ""
+ open_brace: str = ""
+ close_brace: str = ""
+ empty: str = ""
+ last: bool = False
+ is_tuple: bool = False
+ children: Optional[List["Node"]] = None
+ key_separator = ": "
+ separator: str = ", "
+
+ def iter_tokens(self) -> Iterable[str]:
+ """Generate tokens for this node."""
+ if self.key_repr:
+ yield self.key_repr
+ yield self.key_separator
+ if self.value_repr:
+ yield self.value_repr
+ elif self.children is not None:
+ if self.children:
+ yield self.open_brace
+ if self.is_tuple and len(self.children) == 1:
+ yield from self.children[0].iter_tokens()
+ yield ","
+ else:
+ for child in self.children:
+ yield from child.iter_tokens()
+ if not child.last:
+ yield self.separator
+ yield self.close_brace
+ else:
+ yield self.empty
+
+ def check_length(self, start_length: int, max_length: int) -> bool:
+ """Check the length fits within a limit.
+
+ Args:
+ start_length (int): Starting length of the line (indent, prefix, suffix).
+ max_length (int): Maximum length.
+
+ Returns:
+ bool: True if the node can be rendered within max length, otherwise False.
+ """
+ total_length = start_length
+ for token in self.iter_tokens():
+ total_length += cell_len(token)
+ if total_length > max_length:
+ return False
+ return True
+
+ def __str__(self) -> str:
+ repr_text = "".join(self.iter_tokens())
+ return repr_text
+
+ def render(
+ self, max_width: int = 80, indent_size: int = 4, expand_all: bool = False
+ ) -> str:
+ """Render the node to a pretty repr.
+
+ Args:
+ max_width (int, optional): Maximum width of the repr. Defaults to 80.
+ indent_size (int, optional): Size of indents. Defaults to 4.
+ expand_all (bool, optional): Expand all levels. Defaults to False.
+
+ Returns:
+ str: A repr string of the original object.
+ """
+ lines = [_Line(node=self, is_root=True)]
+ line_no = 0
+ while line_no < len(lines):
+ line = lines[line_no]
+ if line.expandable and not line.expanded:
+ if expand_all or not line.check_length(max_width):
+ lines[line_no : line_no + 1] = line.expand(indent_size)
+ line_no += 1
+
+ repr_str = "\n".join(str(line) for line in lines)
+ return repr_str
+
+
+@dataclass
+class _Line:
+ """A line in repr output."""
+
+ parent: Optional["_Line"] = None
+ is_root: bool = False
+ node: Optional[Node] = None
+ text: str = ""
+ suffix: str = ""
+ whitespace: str = ""
+ expanded: bool = False
+ last: bool = False
+
+ @property
+ def expandable(self) -> bool:
+ """Check if the line may be expanded."""
+ return bool(self.node is not None and self.node.children)
+
+ def check_length(self, max_length: int) -> bool:
+ """Check this line fits within a given number of cells."""
+ start_length = (
+ len(self.whitespace) + cell_len(self.text) + cell_len(self.suffix)
+ )
+ assert self.node is not None
+ return self.node.check_length(start_length, max_length)
+
+ def expand(self, indent_size: int) -> Iterable["_Line"]:
+ """Expand this line by adding children on their own line."""
+ node = self.node
+ assert node is not None
+ whitespace = self.whitespace
+ assert node.children
+ if node.key_repr:
+ new_line = yield _Line(
+ text=f"{node.key_repr}{node.key_separator}{node.open_brace}",
+ whitespace=whitespace,
+ )
+ else:
+ new_line = yield _Line(text=node.open_brace, whitespace=whitespace)
+ child_whitespace = self.whitespace + " " * indent_size
+ tuple_of_one = node.is_tuple and len(node.children) == 1
+ for last, child in loop_last(node.children):
+ separator = "," if tuple_of_one else node.separator
+ line = _Line(
+ parent=new_line,
+ node=child,
+ whitespace=child_whitespace,
+ suffix=separator,
+ last=last and not tuple_of_one,
+ )
+ yield line
+
+ yield _Line(
+ text=node.close_brace,
+ whitespace=whitespace,
+ suffix=self.suffix,
+ last=self.last,
+ )
+
+ def __str__(self) -> str:
+ if self.last:
+ return f"{self.whitespace}{self.text}{self.node or ''}"
+ else:
+ return (
+ f"{self.whitespace}{self.text}{self.node or ''}{self.suffix.rstrip()}"
+ )
+
+
+def traverse(
+ _object: Any, max_length: Optional[int] = None, max_string: Optional[int] = None
+) -> Node:
+ """Traverse object and generate a tree.
+
+ Args:
+ _object (Any): Object to be traversed.
+ max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+ Defaults to None.
+ max_string (int, optional): Maximum length of string before truncating, or None to disable truncating.
+ Defaults to None.
+
+ Returns:
+ Node: The root of a tree structure which can be used to render a pretty repr.
+ """
+
+ def to_repr(obj: Any) -> str:
+ """Get repr string for an object, but catch errors."""
+ if (
+ max_string is not None
+ and isinstance(obj, (bytes, str))
+ and len(obj) > max_string
+ ):
+ truncated = len(obj) - max_string
+ obj_repr = f"{obj[:max_string]!r}+{truncated}"
+ else:
+ try:
+ obj_repr = repr(obj)
+ except Exception as error:
+ obj_repr = f"<repr-error {str(error)!r}>"
+ return obj_repr
+
+ visited_ids: Set[int] = set()
+ push_visited = visited_ids.add
+ pop_visited = visited_ids.remove
+
+ def _traverse(obj: Any, root: bool = False) -> Node:
+ """Walk the object depth first."""
+ obj_type = type(obj)
+ py_version = (sys.version_info.major, sys.version_info.minor)
+ children: List[Node]
+
+ def iter_rich_args(rich_args: Any) -> Iterable[Union[Any, Tuple[str, Any]]]:
+ for arg in rich_args:
+ if isinstance(arg, tuple):
+ if len(arg) == 3:
+ key, child, default = arg
+ if default == child:
+ continue
+ yield key, child
+ elif len(arg) == 2:
+ key, child = arg
+ yield key, child
+ elif len(arg) == 1:
+ yield arg[0]
+ else:
+ yield arg
+
+ try:
+ fake_attributes = hasattr(
+ obj, "awehoi234_wdfjwljet234_234wdfoijsdfmmnxpi492"
+ )
+ except Exception:
+ fake_attributes = False
+
+ rich_repr_result: Optional[RichReprResult] = None
+ if not fake_attributes:
+ try:
+ if hasattr(obj, "__rich_repr__") and not isclass(obj):
+ rich_repr_result = obj.__rich_repr__()
+ except Exception:
+ pass
+
+ if rich_repr_result is not None:
+ angular = getattr(obj.__rich_repr__, "angular", False)
+ args = list(iter_rich_args(rich_repr_result))
+ class_name = obj.__class__.__name__
+
+ if args:
+ children = []
+ append = children.append
+ if angular:
+ node = Node(
+ open_brace=f"<{class_name} ",
+ close_brace=">",
+ children=children,
+ last=root,
+ separator=" ",
+ )
+ else:
+ node = Node(
+ open_brace=f"{class_name}(",
+ close_brace=")",
+ children=children,
+ last=root,
+ )
+ for last, arg in loop_last(args):
+ if isinstance(arg, tuple):
+ key, child = arg
+ child_node = _traverse(child)
+ child_node.last = last
+ child_node.key_repr = key
+ child_node.key_separator = "="
+ append(child_node)
+ else:
+ child_node = _traverse(arg)
+ child_node.last = last
+ append(child_node)
+ else:
+ node = Node(
+ value_repr=f"<{class_name}>" if angular else f"{class_name}()",
+ children=[],
+ last=root,
+ )
+ elif _is_attr_object(obj) and not fake_attributes:
+ children = []
+ append = children.append
+
+ attr_fields = _get_attr_fields(obj)
+ if attr_fields:
+ node = Node(
+ open_brace=f"{obj.__class__.__name__}(",
+ close_brace=")",
+ children=children,
+ last=root,
+ )
+
+ def iter_attrs() -> Iterable[
+ Tuple[str, Any, Optional[Callable[[Any], str]]]
+ ]:
+ """Iterate over attr fields and values."""
+ for attr in attr_fields:
+ if attr.repr:
+ try:
+ value = getattr(obj, attr.name)
+ except Exception as error:
+ # Can happen, albeit rarely
+ yield (attr.name, error, None)
+ else:
+ yield (
+ attr.name,
+ value,
+ attr.repr if callable(attr.repr) else None,
+ )
+
+ for last, (name, value, repr_callable) in loop_last(iter_attrs()):
+ if repr_callable:
+ child_node = Node(value_repr=str(repr_callable(value)))
+ else:
+ child_node = _traverse(value)
+ child_node.last = last
+ child_node.key_repr = name
+ child_node.key_separator = "="
+ append(child_node)
+ else:
+ node = Node(
+ value_repr=f"{obj.__class__.__name__}()", children=[], last=root
+ )
+
+ elif (
+ is_dataclass(obj)
+ and not isinstance(obj, type)
+ and not fake_attributes
+ and (_is_dataclass_repr(obj) or py_version == (3, 6))
+ ):
+ obj_id = id(obj)
+ if obj_id in visited_ids:
+ # Recursion detected
+ return Node(value_repr="...")
+ push_visited(obj_id)
+
+ children = []
+ append = children.append
+ node = Node(
+ open_brace=f"{obj.__class__.__name__}(",
+ close_brace=")",
+ children=children,
+ last=root,
+ )
+
+ for last, field in loop_last(field for field in fields(obj) if field.repr):
+ if field.repr:
+ child_node = _traverse(getattr(obj, field.name))
+ child_node.key_repr = field.name
+ child_node.last = last
+ child_node.key_separator = "="
+ append(child_node)
+
+ pop_visited(obj_id)
+
+ elif isinstance(obj, _CONTAINERS):
+ for container_type in _CONTAINERS:
+ if isinstance(obj, container_type):
+ obj_type = container_type
+ break
+
+ obj_id = id(obj)
+ if obj_id in visited_ids:
+ # Recursion detected
+ return Node(value_repr="...")
+ push_visited(obj_id)
+
+ open_brace, close_brace, empty = _BRACES[obj_type](obj)
+
+ if obj_type.__repr__ != type(obj).__repr__:
+ node = Node(value_repr=to_repr(obj), last=root)
+ elif obj:
+ children = []
+ node = Node(
+ open_brace=open_brace,
+ close_brace=close_brace,
+ children=children,
+ last=root,
+ )
+ append = children.append
+ num_items = len(obj)
+ last_item_index = num_items - 1
+
+ if isinstance(obj, _MAPPING_CONTAINERS):
+ iter_items = iter(obj.items())
+ if max_length is not None:
+ iter_items = islice(iter_items, max_length)
+ for index, (key, child) in enumerate(iter_items):
+ child_node = _traverse(child)
+ child_node.key_repr = to_repr(key)
+ child_node.last = index == last_item_index
+ append(child_node)
+ else:
+ iter_values = iter(obj)
+ if max_length is not None:
+ iter_values = islice(iter_values, max_length)
+ for index, child in enumerate(iter_values):
+ child_node = _traverse(child)
+ child_node.last = index == last_item_index
+ append(child_node)
+ if max_length is not None and num_items > max_length:
+ append(Node(value_repr=f"... +{num_items-max_length}", last=True))
+ else:
+ node = Node(empty=empty, children=[], last=root)
+
+ pop_visited(obj_id)
+ else:
+ node = Node(value_repr=to_repr(obj), last=root)
+ node.is_tuple = isinstance(obj, tuple)
+ return node
+
+ node = _traverse(_object, root=True)
+ return node
+
+
+def pretty_repr(
+ _object: Any,
+ *,
+ max_width: int = 80,
+ indent_size: int = 4,
+ max_length: Optional[int] = None,
+ max_string: Optional[int] = None,
+ expand_all: bool = False,
+) -> str:
+ """Prettify repr string by expanding on to new lines to fit within a given width.
+
+ Args:
+ _object (Any): Object to repr.
+ max_width (int, optional): Desired maximum width of repr string. Defaults to 80.
+ indent_size (int, optional): Number of spaces to indent. Defaults to 4.
+ max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+ Defaults to None.
+ max_string (int, optional): Maximum length of string before truncating, or None to disable truncating.
+ Defaults to None.
+ expand_all (bool, optional): Expand all containers regardless of available width. Defaults to False.
+
+ Returns:
+ str: A possibly multi-line representation of the object.
+ """
+
+ if isinstance(_object, Node):
+ node = _object
+ else:
+ node = traverse(_object, max_length=max_length, max_string=max_string)
+ repr_str = node.render(
+ max_width=max_width, indent_size=indent_size, expand_all=expand_all
+ )
+ return repr_str
+
+
+def pprint(
+ _object: Any,
+ *,
+ console: Optional["Console"] = None,
+ indent_guides: bool = True,
+ max_length: Optional[int] = None,
+ max_string: Optional[int] = None,
+ expand_all: bool = False,
+) -> None:
+ """A convenience function for pretty printing.
+
+ Args:
+ _object (Any): Object to pretty print.
+ console (Console, optional): Console instance, or None to use default. Defaults to None.
+ max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+ Defaults to None.
+ max_string (int, optional): Maximum length of strings before truncating, or None to disable. Defaults to None.
+ indent_guides (bool, optional): Enable indentation guides. Defaults to True.
+ expand_all (bool, optional): Expand all containers. Defaults to False.
+ """
+ _console = get_console() if console is None else console
+ _console.print(
+ Pretty(
+ _object,
+ max_length=max_length,
+ max_string=max_string,
+ indent_guides=indent_guides,
+ expand_all=expand_all,
+ overflow="ignore",
+ ),
+ soft_wrap=True,
+ )
+
+
+if __name__ == "__main__": # pragma: no cover
+
+ class BrokenRepr:
+ def __repr__(self) -> str:
+ 1 / 0
+ return "this will fail"
+
+ d = defaultdict(int)
+ d["foo"] = 5
+ data = {
+ "foo": [
+ 1,
+ "Hello World!",
+ 100.123,
+ 323.232,
+ 432324.0,
+ {5, 6, 7, (1, 2, 3, 4), 8},
+ ],
+ "bar": frozenset({1, 2, 3}),
+ "defaultdict": defaultdict(
+ list, {"crumble": ["apple", "rhubarb", "butter", "sugar", "flour"]}
+ ),
+ "counter": Counter(
+ [
+ "apple",
+ "orange",
+ "pear",
+ "kumquat",
+ "kumquat",
+ "durian" * 100,
+ ]
+ ),
+ "atomic": (False, True, None),
+ "Broken": BrokenRepr(),
+ }
+ data["foo"].append(data) # type: ignore
+
+ from pip._vendor.rich import print
+
+ print(Pretty(data, indent_guides=True, max_string=20))
diff --git a/src/pip/_vendor/rich/progress.py b/src/pip/_vendor/rich/progress.py
new file mode 100644
index 000000000..8f2ec0266
--- /dev/null
+++ b/src/pip/_vendor/rich/progress.py
@@ -0,0 +1,1034 @@
+from abc import ABC, abstractmethod
+from collections import deque
+from collections.abc import Sized
+from dataclasses import dataclass, field
+from datetime import timedelta
+from math import ceil
+from threading import Event, RLock, Thread
+from types import TracebackType
+from typing import (
+ Any,
+ Callable,
+ Deque,
+ Dict,
+ Iterable,
+ List,
+ NamedTuple,
+ NewType,
+ Optional,
+ Sequence,
+ Tuple,
+ Type,
+ TypeVar,
+ Union,
+)
+
+from . import filesize, get_console
+from .console import Console, JustifyMethod, RenderableType, Group
+from .highlighter import Highlighter
+from .jupyter import JupyterMixin
+from .live import Live
+from .progress_bar import ProgressBar
+from .spinner import Spinner
+from .style import StyleType
+from .table import Column, Table
+from .text import Text, TextType
+
+TaskID = NewType("TaskID", int)
+
+ProgressType = TypeVar("ProgressType")
+
+GetTimeCallable = Callable[[], float]
+
+
+class _TrackThread(Thread):
+ """A thread to periodically update progress."""
+
+ def __init__(self, progress: "Progress", task_id: "TaskID", update_period: float):
+ self.progress = progress
+ self.task_id = task_id
+ self.update_period = update_period
+ self.done = Event()
+
+ self.completed = 0
+ super().__init__()
+
+ def run(self) -> None:
+ task_id = self.task_id
+ advance = self.progress.advance
+ update_period = self.update_period
+ last_completed = 0
+ wait = self.done.wait
+ while not wait(update_period):
+ completed = self.completed
+ if last_completed != completed:
+ advance(task_id, completed - last_completed)
+ last_completed = completed
+
+ self.progress.update(self.task_id, completed=self.completed, refresh=True)
+
+ def __enter__(self) -> "_TrackThread":
+ self.start()
+ return self
+
+ def __exit__(
+ self,
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[TracebackType],
+ ) -> None:
+ self.done.set()
+ self.join()
+
+
+def track(
+ sequence: Union[Sequence[ProgressType], Iterable[ProgressType]],
+ description: str = "Working...",
+ total: Optional[float] = None,
+ auto_refresh: bool = True,
+ console: Optional[Console] = None,
+ transient: bool = False,
+ get_time: Optional[Callable[[], float]] = None,
+ refresh_per_second: float = 10,
+ style: StyleType = "bar.back",
+ complete_style: StyleType = "bar.complete",
+ finished_style: StyleType = "bar.finished",
+ pulse_style: StyleType = "bar.pulse",
+ update_period: float = 0.1,
+ disable: bool = False,
+) -> Iterable[ProgressType]:
+ """Track progress by iterating over a sequence.
+
+ Args:
+ sequence (Iterable[ProgressType]): A sequence (must support "len") you wish to iterate over.
+ description (str, optional): Description of task show next to progress bar. Defaults to "Working".
+ total: (float, optional): Total number of steps. Default is len(sequence).
+ auto_refresh (bool, optional): Automatic refresh, disable to force a refresh after each iteration. Default is True.
+ transient: (bool, optional): Clear the progress on exit. Defaults to False.
+ console (Console, optional): Console to write to. Default creates internal Console instance.
+ refresh_per_second (float): Number of times per second to refresh the progress information. Defaults to 10.
+ style (StyleType, optional): Style for the bar background. Defaults to "bar.back".
+ complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete".
+ finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.done".
+ pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse".
+ update_period (float, optional): Minimum time (in seconds) between calls to update(). Defaults to 0.1.
+ disable (bool, optional): Disable display of progress.
+ Returns:
+ Iterable[ProgressType]: An iterable of the values in the sequence.
+
+ """
+
+ columns: List["ProgressColumn"] = (
+ [TextColumn("[progress.description]{task.description}")] if description else []
+ )
+ columns.extend(
+ (
+ BarColumn(
+ style=style,
+ complete_style=complete_style,
+ finished_style=finished_style,
+ pulse_style=pulse_style,
+ ),
+ TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
+ TimeRemainingColumn(),
+ )
+ )
+ progress = Progress(
+ *columns,
+ auto_refresh=auto_refresh,
+ console=console,
+ transient=transient,
+ get_time=get_time,
+ refresh_per_second=refresh_per_second or 10,
+ disable=disable,
+ )
+
+ with progress:
+ yield from progress.track(
+ sequence, total=total, description=description, update_period=update_period
+ )
+
+
+class ProgressColumn(ABC):
+ """Base class for a widget to use in progress display."""
+
+ max_refresh: Optional[float] = None
+
+ def __init__(self, table_column: Optional[Column] = None) -> None:
+ self._table_column = table_column
+ self._renderable_cache: Dict[TaskID, Tuple[float, RenderableType]] = {}
+ self._update_time: Optional[float] = None
+
+ def get_table_column(self) -> Column:
+ """Get a table column, used to build tasks table."""
+ return self._table_column or Column()
+
+ def __call__(self, task: "Task") -> RenderableType:
+ """Called by the Progress object to return a renderable for the given task.
+
+ Args:
+ task (Task): An object containing information regarding the task.
+
+ Returns:
+ RenderableType: Anything renderable (including str).
+ """
+ current_time = task.get_time()
+ if self.max_refresh is not None and not task.completed:
+ try:
+ timestamp, renderable = self._renderable_cache[task.id]
+ except KeyError:
+ pass
+ else:
+ if timestamp + self.max_refresh > current_time:
+ return renderable
+
+ renderable = self.render(task)
+ self._renderable_cache[task.id] = (current_time, renderable)
+ return renderable
+
+ @abstractmethod
+ def render(self, task: "Task") -> RenderableType:
+ """Should return a renderable object."""
+
+
+class RenderableColumn(ProgressColumn):
+ """A column to insert an arbitrary column.
+
+ Args:
+ renderable (RenderableType, optional): Any renderable. Defaults to empty string.
+ """
+
+ def __init__(
+ self, renderable: RenderableType = "", *, table_column: Optional[Column] = None
+ ):
+ self.renderable = renderable
+ super().__init__(table_column=table_column)
+
+ def render(self, task: "Task") -> RenderableType:
+ return self.renderable
+
+
+class SpinnerColumn(ProgressColumn):
+ """A column with a 'spinner' animation.
+
+ Args:
+ spinner_name (str, optional): Name of spinner animation. Defaults to "dots".
+ style (StyleType, optional): Style of spinner. Defaults to "progress.spinner".
+ speed (float, optional): Speed factor of spinner. Defaults to 1.0.
+ finished_text (TextType, optional): Text used when task is finished. Defaults to " ".
+ """
+
+ def __init__(
+ self,
+ spinner_name: str = "dots",
+ style: Optional[StyleType] = "progress.spinner",
+ speed: float = 1.0,
+ finished_text: TextType = " ",
+ table_column: Optional[Column] = None,
+ ):
+ self.spinner = Spinner(spinner_name, style=style, speed=speed)
+ self.finished_text = (
+ Text.from_markup(finished_text)
+ if isinstance(finished_text, str)
+ else finished_text
+ )
+ super().__init__(table_column=table_column)
+
+ def set_spinner(
+ self,
+ spinner_name: str,
+ spinner_style: Optional[StyleType] = "progress.spinner",
+ speed: float = 1.0,
+ ) -> None:
+ """Set a new spinner.
+
+ Args:
+ spinner_name (str): Spinner name, see python -m rich.spinner.
+ spinner_style (Optional[StyleType], optional): Spinner style. Defaults to "progress.spinner".
+ speed (float, optional): Speed factor of spinner. Defaults to 1.0.
+ """
+ self.spinner = Spinner(spinner_name, style=spinner_style, speed=speed)
+
+ def render(self, task: "Task") -> RenderableType:
+ text = (
+ self.finished_text
+ if task.finished
+ else self.spinner.render(task.get_time())
+ )
+ return text
+
+
+class TextColumn(ProgressColumn):
+ """A column containing text."""
+
+ def __init__(
+ self,
+ text_format: str,
+ style: StyleType = "none",
+ justify: JustifyMethod = "left",
+ markup: bool = True,
+ highlighter: Optional[Highlighter] = None,
+ table_column: Optional[Column] = None,
+ ) -> None:
+ self.text_format = text_format
+ self.justify: JustifyMethod = justify
+ self.style = style
+ self.markup = markup
+ self.highlighter = highlighter
+ super().__init__(table_column=table_column or Column(no_wrap=True))
+
+ def render(self, task: "Task") -> Text:
+ _text = self.text_format.format(task=task)
+ if self.markup:
+ text = Text.from_markup(_text, style=self.style, justify=self.justify)
+ else:
+ text = Text(_text, style=self.style, justify=self.justify)
+ if self.highlighter:
+ self.highlighter.highlight(text)
+ return text
+
+
+class BarColumn(ProgressColumn):
+ """Renders a visual progress bar.
+
+ Args:
+ bar_width (Optional[int], optional): Width of bar or None for full width. Defaults to 40.
+ style (StyleType, optional): Style for the bar background. Defaults to "bar.back".
+ complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete".
+ finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.done".
+ pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse".
+ """
+
+ def __init__(
+ self,
+ bar_width: Optional[int] = 40,
+ style: StyleType = "bar.back",
+ complete_style: StyleType = "bar.complete",
+ finished_style: StyleType = "bar.finished",
+ pulse_style: StyleType = "bar.pulse",
+ table_column: Optional[Column] = None,
+ ) -> None:
+ self.bar_width = bar_width
+ self.style = style
+ self.complete_style = complete_style
+ self.finished_style = finished_style
+ self.pulse_style = pulse_style
+ super().__init__(table_column=table_column)
+
+ def render(self, task: "Task") -> ProgressBar:
+ """Gets a progress bar widget for a task."""
+ return ProgressBar(
+ total=max(0, task.total),
+ completed=max(0, task.completed),
+ width=None if self.bar_width is None else max(1, self.bar_width),
+ pulse=not task.started,
+ animation_time=task.get_time(),
+ style=self.style,
+ complete_style=self.complete_style,
+ finished_style=self.finished_style,
+ pulse_style=self.pulse_style,
+ )
+
+
+class TimeElapsedColumn(ProgressColumn):
+ """Renders time elapsed."""
+
+ def render(self, task: "Task") -> Text:
+ """Show time remaining."""
+ elapsed = task.finished_time if task.finished else task.elapsed
+ if elapsed is None:
+ return Text("-:--:--", style="progress.elapsed")
+ delta = timedelta(seconds=int(elapsed))
+ return Text(str(delta), style="progress.elapsed")
+
+
+class TimeRemainingColumn(ProgressColumn):
+ """Renders estimated time remaining."""
+
+ # Only refresh twice a second to prevent jitter
+ max_refresh = 0.5
+
+ def render(self, task: "Task") -> Text:
+ """Show time remaining."""
+ remaining = task.time_remaining
+ if remaining is None:
+ return Text("-:--:--", style="progress.remaining")
+ remaining_delta = timedelta(seconds=int(remaining))
+ return Text(str(remaining_delta), style="progress.remaining")
+
+
+class FileSizeColumn(ProgressColumn):
+ """Renders completed filesize."""
+
+ def render(self, task: "Task") -> Text:
+ """Show data completed."""
+ data_size = filesize.decimal(int(task.completed))
+ return Text(data_size, style="progress.filesize")
+
+
+class TotalFileSizeColumn(ProgressColumn):
+ """Renders total filesize."""
+
+ def render(self, task: "Task") -> Text:
+ """Show data completed."""
+ data_size = filesize.decimal(int(task.total))
+ return Text(data_size, style="progress.filesize.total")
+
+
+class DownloadColumn(ProgressColumn):
+ """Renders file size downloaded and total, e.g. '0.5/2.3 GB'.
+
+ Args:
+ binary_units (bool, optional): Use binary units, KiB, MiB etc. Defaults to False.
+ """
+
+ def __init__(
+ self, binary_units: bool = False, table_column: Optional[Column] = None
+ ) -> None:
+ self.binary_units = binary_units
+ super().__init__(table_column=table_column)
+
+ def render(self, task: "Task") -> Text:
+ """Calculate common unit for completed and total."""
+ completed = int(task.completed)
+ total = int(task.total)
+ if self.binary_units:
+ unit, suffix = filesize.pick_unit_and_suffix(
+ total,
+ ["bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"],
+ 1024,
+ )
+ else:
+ unit, suffix = filesize.pick_unit_and_suffix(
+ total, ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], 1000
+ )
+ completed_ratio = completed / unit
+ total_ratio = total / unit
+ precision = 0 if unit == 1 else 1
+ completed_str = f"{completed_ratio:,.{precision}f}"
+ total_str = f"{total_ratio:,.{precision}f}"
+ download_status = f"{completed_str}/{total_str} {suffix}"
+ download_text = Text(download_status, style="progress.download")
+ return download_text
+
+
+class TransferSpeedColumn(ProgressColumn):
+ """Renders human readable transfer speed."""
+
+ def render(self, task: "Task") -> Text:
+ """Show data transfer speed."""
+ speed = task.finished_speed or task.speed
+ if speed is None:
+ return Text("?", style="progress.data.speed")
+ data_speed = filesize.decimal(int(speed))
+ return Text(f"{data_speed}/s", style="progress.data.speed")
+
+
+class ProgressSample(NamedTuple):
+ """Sample of progress for a given time."""
+
+ timestamp: float
+ """Timestamp of sample."""
+ completed: float
+ """Number of steps completed."""
+
+
+@dataclass
+class Task:
+ """Information regarding a progress task.
+
+ This object should be considered read-only outside of the :class:`~Progress` class.
+
+ """
+
+ id: TaskID
+ """Task ID associated with this task (used in Progress methods)."""
+
+ description: str
+ """str: Description of the task."""
+
+ total: float
+ """str: Total number of steps in this task."""
+
+ completed: float
+ """float: Number of steps completed"""
+
+ _get_time: GetTimeCallable
+ """Callable to get the current time."""
+
+ finished_time: Optional[float] = None
+ """float: Time task was finished."""
+
+ visible: bool = True
+ """bool: Indicates if this task is visible in the progress display."""
+
+ fields: Dict[str, Any] = field(default_factory=dict)
+ """dict: Arbitrary fields passed in via Progress.update."""
+
+ start_time: Optional[float] = field(default=None, init=False, repr=False)
+ """Optional[float]: Time this task was started, or None if not started."""
+
+ stop_time: Optional[float] = field(default=None, init=False, repr=False)
+ """Optional[float]: Time this task was stopped, or None if not stopped."""
+
+ finished_speed: Optional[float] = None
+ """Optional[float]: The last speed for a finished task."""
+
+ _progress: Deque[ProgressSample] = field(
+ default_factory=deque, init=False, repr=False
+ )
+
+ _lock: RLock = field(repr=False, default_factory=RLock)
+ """Thread lock."""
+
+ def get_time(self) -> float:
+ """float: Get the current time, in seconds."""
+ return self._get_time()
+
+ @property
+ def started(self) -> bool:
+ """bool: Check if the task as started."""
+ return self.start_time is not None
+
+ @property
+ def remaining(self) -> float:
+ """float: Get the number of steps remaining."""
+ return self.total - self.completed
+
+ @property
+ def elapsed(self) -> Optional[float]:
+ """Optional[float]: Time elapsed since task was started, or ``None`` if the task hasn't started."""
+ if self.start_time is None:
+ return None
+ if self.stop_time is not None:
+ return self.stop_time - self.start_time
+ return self.get_time() - self.start_time
+
+ @property
+ def finished(self) -> bool:
+ """Check if the task has finished."""
+ return self.finished_time is not None
+
+ @property
+ def percentage(self) -> float:
+ """float: Get progress of task as a percentage."""
+ if not self.total:
+ return 0.0
+ completed = (self.completed / self.total) * 100.0
+ completed = min(100.0, max(0.0, completed))
+ return completed
+
+ @property
+ def speed(self) -> Optional[float]:
+ """Optional[float]: Get the estimated speed in steps per second."""
+ if self.start_time is None:
+ return None
+ with self._lock:
+ progress = self._progress
+ if not progress:
+ return None
+ total_time = progress[-1].timestamp - progress[0].timestamp
+ if total_time == 0:
+ return None
+ iter_progress = iter(progress)
+ next(iter_progress)
+ total_completed = sum(sample.completed for sample in iter_progress)
+ speed = total_completed / total_time
+ return speed
+
+ @property
+ def time_remaining(self) -> Optional[float]:
+ """Optional[float]: Get estimated time to completion, or ``None`` if no data."""
+ if self.finished:
+ return 0.0
+ speed = self.speed
+ if not speed:
+ return None
+ estimate = ceil(self.remaining / speed)
+ return estimate
+
+ def _reset(self) -> None:
+ """Reset progress."""
+ self._progress.clear()
+ self.finished_time = None
+ self.finished_speed = None
+
+
+class Progress(JupyterMixin):
+ """Renders an auto-updating progress bar(s).
+
+ Args:
+ console (Console, optional): Optional Console instance. Default will an internal Console instance writing to stdout.
+ auto_refresh (bool, optional): Enable auto refresh. If disabled, you will need to call `refresh()`.
+ refresh_per_second (Optional[float], optional): Number of times per second to refresh the progress information or None to use default (10). Defaults to None.
+ speed_estimate_period: (float, optional): Period (in seconds) used to calculate the speed estimate. Defaults to 30.
+ transient: (bool, optional): Clear the progress on exit. Defaults to False.
+ redirect_stdout: (bool, optional): Enable redirection of stdout, so ``print`` may be used. Defaults to True.
+ redirect_stderr: (bool, optional): Enable redirection of stderr. Defaults to True.
+ get_time: (Callable, optional): A callable that gets the current time, or None to use Console.get_time. Defaults to None.
+ disable (bool, optional): Disable progress display. Defaults to False
+ expand (bool, optional): Expand tasks table to fit width. Defaults to False.
+ """
+
+ def __init__(
+ self,
+ *columns: Union[str, ProgressColumn],
+ console: Optional[Console] = None,
+ auto_refresh: bool = True,
+ refresh_per_second: float = 10,
+ speed_estimate_period: float = 30.0,
+ transient: bool = False,
+ redirect_stdout: bool = True,
+ redirect_stderr: bool = True,
+ get_time: Optional[GetTimeCallable] = None,
+ disable: bool = False,
+ expand: bool = False,
+ ) -> None:
+ assert (
+ refresh_per_second is None or refresh_per_second > 0
+ ), "refresh_per_second must be > 0"
+ self._lock = RLock()
+ self.columns = columns or (
+ TextColumn("[progress.description]{task.description}"),
+ BarColumn(),
+ TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
+ TimeRemainingColumn(),
+ )
+ self.speed_estimate_period = speed_estimate_period
+
+ self.disable = disable
+ self.expand = expand
+ self._tasks: Dict[TaskID, Task] = {}
+ self._task_index: TaskID = TaskID(0)
+ self.live = Live(
+ console=console or get_console(),
+ auto_refresh=auto_refresh,
+ refresh_per_second=refresh_per_second,
+ transient=transient,
+ redirect_stdout=redirect_stdout,
+ redirect_stderr=redirect_stderr,
+ get_renderable=self.get_renderable,
+ )
+ self.get_time = get_time or self.console.get_time
+ self.print = self.console.print
+ self.log = self.console.log
+
+ @property
+ def console(self) -> Console:
+ return self.live.console
+
+ @property
+ def tasks(self) -> List[Task]:
+ """Get a list of Task instances."""
+ with self._lock:
+ return list(self._tasks.values())
+
+ @property
+ def task_ids(self) -> List[TaskID]:
+ """A list of task IDs."""
+ with self._lock:
+ return list(self._tasks.keys())
+
+ @property
+ def finished(self) -> bool:
+ """Check if all tasks have been completed."""
+ with self._lock:
+ if not self._tasks:
+ return True
+ return all(task.finished for task in self._tasks.values())
+
+ def start(self) -> None:
+ """Start the progress display."""
+ if not self.disable:
+ self.live.start(refresh=True)
+
+ def stop(self) -> None:
+ """Stop the progress display."""
+ self.live.stop()
+
+ def __enter__(self) -> "Progress":
+ self.start()
+ return self
+
+ def __exit__(
+ self,
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[TracebackType],
+ ) -> None:
+ self.stop()
+
+ def track(
+ self,
+ sequence: Union[Iterable[ProgressType], Sequence[ProgressType]],
+ total: Optional[float] = None,
+ task_id: Optional[TaskID] = None,
+ description: str = "Working...",
+ update_period: float = 0.1,
+ ) -> Iterable[ProgressType]:
+ """Track progress by iterating over a sequence.
+
+ Args:
+ sequence (Sequence[ProgressType]): A sequence of values you want to iterate over and track progress.
+ total: (float, optional): Total number of steps. Default is len(sequence).
+ task_id: (TaskID): Task to track. Default is new task.
+ description: (str, optional): Description of task, if new task is created.
+ update_period (float, optional): Minimum time (in seconds) between calls to update(). Defaults to 0.1.
+
+ Returns:
+ Iterable[ProgressType]: An iterable of values taken from the provided sequence.
+ """
+
+ if total is None:
+ if isinstance(sequence, Sized):
+ task_total = float(len(sequence))
+ else:
+ raise ValueError(
+ f"unable to get size of {sequence!r}, please specify 'total'"
+ )
+ else:
+ task_total = total
+
+ if task_id is None:
+ task_id = self.add_task(description, total=task_total)
+ else:
+ self.update(task_id, total=task_total)
+
+ if self.live.auto_refresh:
+ with _TrackThread(self, task_id, update_period) as track_thread:
+ for value in sequence:
+ yield value
+ track_thread.completed += 1
+ else:
+ advance = self.advance
+ refresh = self.refresh
+ for value in sequence:
+ yield value
+ advance(task_id, 1)
+ refresh()
+
+ def start_task(self, task_id: TaskID) -> None:
+ """Start a task.
+
+ Starts a task (used when calculating elapsed time). You may need to call this manually,
+ if you called ``add_task`` with ``start=False``.
+
+ Args:
+ task_id (TaskID): ID of task.
+ """
+ with self._lock:
+ task = self._tasks[task_id]
+ if task.start_time is None:
+ task.start_time = self.get_time()
+
+ def stop_task(self, task_id: TaskID) -> None:
+ """Stop a task.
+
+ This will freeze the elapsed time on the task.
+
+ Args:
+ task_id (TaskID): ID of task.
+ """
+ with self._lock:
+ task = self._tasks[task_id]
+ current_time = self.get_time()
+ if task.start_time is None:
+ task.start_time = current_time
+ task.stop_time = current_time
+
+ def update(
+ self,
+ task_id: TaskID,
+ *,
+ total: Optional[float] = None,
+ completed: Optional[float] = None,
+ advance: Optional[float] = None,
+ description: Optional[str] = None,
+ visible: Optional[bool] = None,
+ refresh: bool = False,
+ **fields: Any,
+ ) -> None:
+ """Update information associated with a task.
+
+ Args:
+ task_id (TaskID): Task id (returned by add_task).
+ total (float, optional): Updates task.total if not None.
+ completed (float, optional): Updates task.completed if not None.
+ advance (float, optional): Add a value to task.completed if not None.
+ description (str, optional): Change task description if not None.
+ visible (bool, optional): Set visible flag if not None.
+ refresh (bool): Force a refresh of progress information. Default is False.
+ **fields (Any): Additional data fields required for rendering.
+ """
+ with self._lock:
+ task = self._tasks[task_id]
+ completed_start = task.completed
+
+ if total is not None:
+ task.total = total
+ task._reset()
+ if advance is not None:
+ task.completed += advance
+ if completed is not None:
+ task.completed = completed
+ if description is not None:
+ task.description = description
+ if visible is not None:
+ task.visible = visible
+ task.fields.update(fields)
+ update_completed = task.completed - completed_start
+
+ current_time = self.get_time()
+ old_sample_time = current_time - self.speed_estimate_period
+ _progress = task._progress
+
+ popleft = _progress.popleft
+ while _progress and _progress[0].timestamp < old_sample_time:
+ popleft()
+ while len(_progress) > 1000:
+ popleft()
+ if update_completed > 0:
+ _progress.append(ProgressSample(current_time, update_completed))
+ if task.completed >= task.total and task.finished_time is None:
+ task.finished_time = task.elapsed
+
+ if refresh:
+ self.refresh()
+
+ def reset(
+ self,
+ task_id: TaskID,
+ *,
+ start: bool = True,
+ total: Optional[float] = None,
+ completed: int = 0,
+ visible: Optional[bool] = None,
+ description: Optional[str] = None,
+ **fields: Any,
+ ) -> None:
+ """Reset a task so completed is 0 and the clock is reset.
+
+ Args:
+ task_id (TaskID): ID of task.
+ start (bool, optional): Start the task after reset. Defaults to True.
+ total (float, optional): New total steps in task, or None to use current total. Defaults to None.
+ completed (int, optional): Number of steps completed. Defaults to 0.
+ **fields (str): Additional data fields required for rendering.
+ """
+ current_time = self.get_time()
+ with self._lock:
+ task = self._tasks[task_id]
+ task._reset()
+ task.start_time = current_time if start else None
+ if total is not None:
+ task.total = total
+ task.completed = completed
+ if visible is not None:
+ task.visible = visible
+ if fields:
+ task.fields = fields
+ if description is not None:
+ task.description = description
+ task.finished_time = None
+ self.refresh()
+
+ def advance(self, task_id: TaskID, advance: float = 1) -> None:
+ """Advance task by a number of steps.
+
+ Args:
+ task_id (TaskID): ID of task.
+ advance (float): Number of steps to advance. Default is 1.
+ """
+ current_time = self.get_time()
+ with self._lock:
+ task = self._tasks[task_id]
+ completed_start = task.completed
+ task.completed += advance
+ update_completed = task.completed - completed_start
+ old_sample_time = current_time - self.speed_estimate_period
+ _progress = task._progress
+
+ popleft = _progress.popleft
+ while _progress and _progress[0].timestamp < old_sample_time:
+ popleft()
+ while len(_progress) > 1000:
+ popleft()
+ _progress.append(ProgressSample(current_time, update_completed))
+ if task.completed >= task.total and task.finished_time is None:
+ task.finished_time = task.elapsed
+ task.finished_speed = task.speed
+
+ def refresh(self) -> None:
+ """Refresh (render) the progress information."""
+ if not self.disable and self.live.is_started:
+ self.live.refresh()
+
+ def get_renderable(self) -> RenderableType:
+ """Get a renderable for the progress display."""
+ renderable = Group(*self.get_renderables())
+ return renderable
+
+ def get_renderables(self) -> Iterable[RenderableType]:
+ """Get a number of renderables for the progress display."""
+ table = self.make_tasks_table(self.tasks)
+ yield table
+
+ def make_tasks_table(self, tasks: Iterable[Task]) -> Table:
+ """Get a table to render the Progress display.
+
+ Args:
+ tasks (Iterable[Task]): An iterable of Task instances, one per row of the table.
+
+ Returns:
+ Table: A table instance.
+ """
+ table_columns = (
+ (
+ Column(no_wrap=True)
+ if isinstance(_column, str)
+ else _column.get_table_column().copy()
+ )
+ for _column in self.columns
+ )
+ table = Table.grid(*table_columns, padding=(0, 1), expand=self.expand)
+
+ for task in tasks:
+ if task.visible:
+ table.add_row(
+ *(
+ (
+ column.format(task=task)
+ if isinstance(column, str)
+ else column(task)
+ )
+ for column in self.columns
+ )
+ )
+ return table
+
+ def __rich__(self) -> RenderableType:
+ """Makes the Progress class itself renderable."""
+ with self._lock:
+ return self.get_renderable()
+
+ def add_task(
+ self,
+ description: str,
+ start: bool = True,
+ total: float = 100.0,
+ completed: int = 0,
+ visible: bool = True,
+ **fields: Any,
+ ) -> TaskID:
+ """Add a new 'task' to the Progress display.
+
+ Args:
+ description (str): A description of the task.
+ start (bool, optional): Start the task immediately (to calculate elapsed time). If set to False,
+ you will need to call `start` manually. Defaults to True.
+ total (float, optional): Number of total steps in the progress if know. Defaults to 100.
+ completed (int, optional): Number of steps completed so far.. Defaults to 0.
+ visible (bool, optional): Enable display of the task. Defaults to True.
+ **fields (str): Additional data fields required for rendering.
+
+ Returns:
+ TaskID: An ID you can use when calling `update`.
+ """
+ with self._lock:
+ task = Task(
+ self._task_index,
+ description,
+ total,
+ completed,
+ visible=visible,
+ fields=fields,
+ _get_time=self.get_time,
+ _lock=self._lock,
+ )
+ self._tasks[self._task_index] = task
+ if start:
+ self.start_task(self._task_index)
+ new_task_index = self._task_index
+ self._task_index = TaskID(int(self._task_index) + 1)
+ self.refresh()
+ return new_task_index
+
+ def remove_task(self, task_id: TaskID) -> None:
+ """Delete a task if it exists.
+
+ Args:
+ task_id (TaskID): A task ID.
+
+ """
+ with self._lock:
+ del self._tasks[task_id]
+
+
+if __name__ == "__main__": # pragma: no coverage
+
+ import random
+ import time
+
+ from .panel import Panel
+ from .rule import Rule
+ from .syntax import Syntax
+ from .table import Table
+
+ syntax = Syntax(
+ '''def loop_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]:
+ """Iterate and generate a tuple with a flag for last value."""
+ iter_values = iter(values)
+ try:
+ previous_value = next(iter_values)
+ except StopIteration:
+ return
+ for value in iter_values:
+ yield False, previous_value
+ previous_value = value
+ yield True, previous_value''',
+ "python",
+ line_numbers=True,
+ )
+
+ table = Table("foo", "bar", "baz")
+ table.add_row("1", "2", "3")
+
+ progress_renderables = [
+ "Text may be printed while the progress bars are rendering.",
+ Panel("In fact, [i]any[/i] renderable will work"),
+ "Such as [magenta]tables[/]...",
+ table,
+ "Pretty printed structures...",
+ {"type": "example", "text": "Pretty printed"},
+ "Syntax...",
+ syntax,
+ Rule("Give it a try!"),
+ ]
+
+ from itertools import cycle
+
+ examples = cycle(progress_renderables)
+
+ console = Console(record=True)
+
+ with Progress(
+ SpinnerColumn(),
+ TextColumn("[progress.description]{task.description}"),
+ BarColumn(),
+ TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
+ TimeRemainingColumn(),
+ TimeElapsedColumn(),
+ console=console,
+ transient=True,
+ ) as progress:
+
+ task1 = progress.add_task("[red]Downloading", total=1000)
+ task2 = progress.add_task("[green]Processing", total=1000)
+ task3 = progress.add_task("[yellow]Thinking", total=1000, start=False)
+
+ while not progress.finished:
+ progress.update(task1, advance=0.5)
+ progress.update(task2, advance=0.3)
+ time.sleep(0.01)
+ if random.randint(0, 100) < 1:
+ progress.log(next(examples))
diff --git a/src/pip/_vendor/rich/progress_bar.py b/src/pip/_vendor/rich/progress_bar.py
new file mode 100644
index 000000000..1797b5f78
--- /dev/null
+++ b/src/pip/_vendor/rich/progress_bar.py
@@ -0,0 +1,216 @@
+import math
+from functools import lru_cache
+from time import monotonic
+from typing import Iterable, List, Optional
+
+from .color import Color, blend_rgb
+from .color_triplet import ColorTriplet
+from .console import Console, ConsoleOptions, RenderResult
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .segment import Segment
+from .style import Style, StyleType
+
+# Number of characters before 'pulse' animation repeats
+PULSE_SIZE = 20
+
+
+class ProgressBar(JupyterMixin):
+ """Renders a (progress) bar. Used by rich.progress.
+
+ Args:
+ total (float, optional): Number of steps in the bar. Defaults to 100.
+ completed (float, optional): Number of steps completed. Defaults to 0.
+ width (int, optional): Width of the bar, or ``None`` for maximum width. Defaults to None.
+ pulse (bool, optional): Enable pulse effect. Defaults to False.
+ style (StyleType, optional): Style for the bar background. Defaults to "bar.back".
+ complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete".
+ finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.done".
+ pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse".
+ animation_time (Optional[float], optional): Time in seconds to use for animation, or None to use system time.
+ """
+
+ def __init__(
+ self,
+ total: float = 100.0,
+ completed: float = 0,
+ width: Optional[int] = None,
+ pulse: bool = False,
+ style: StyleType = "bar.back",
+ complete_style: StyleType = "bar.complete",
+ finished_style: StyleType = "bar.finished",
+ pulse_style: StyleType = "bar.pulse",
+ animation_time: Optional[float] = None,
+ ):
+ self.total = total
+ self.completed = completed
+ self.width = width
+ self.pulse = pulse
+ self.style = style
+ self.complete_style = complete_style
+ self.finished_style = finished_style
+ self.pulse_style = pulse_style
+ self.animation_time = animation_time
+
+ self._pulse_segments: Optional[List[Segment]] = None
+
+ def __repr__(self) -> str:
+ return f"<Bar {self.completed!r} of {self.total!r}>"
+
+ @property
+ def percentage_completed(self) -> float:
+ """Calculate percentage complete."""
+ completed = (self.completed / self.total) * 100.0
+ completed = min(100, max(0.0, completed))
+ return completed
+
+ @lru_cache(maxsize=16)
+ def _get_pulse_segments(
+ self,
+ fore_style: Style,
+ back_style: Style,
+ color_system: str,
+ no_color: bool,
+ ascii: bool = False,
+ ) -> List[Segment]:
+ """Get a list of segments to render a pulse animation.
+
+ Returns:
+ List[Segment]: A list of segments, one segment per character.
+ """
+ bar = "-" if ascii else "━"
+ segments: List[Segment] = []
+ if color_system not in ("standard", "eight_bit", "truecolor") or no_color:
+ segments += [Segment(bar, fore_style)] * (PULSE_SIZE // 2)
+ segments += [Segment(" " if no_color else bar, back_style)] * (
+ PULSE_SIZE - (PULSE_SIZE // 2)
+ )
+ return segments
+
+ append = segments.append
+ fore_color = (
+ fore_style.color.get_truecolor()
+ if fore_style.color
+ else ColorTriplet(255, 0, 255)
+ )
+ back_color = (
+ back_style.color.get_truecolor()
+ if back_style.color
+ else ColorTriplet(0, 0, 0)
+ )
+ cos = math.cos
+ pi = math.pi
+ _Segment = Segment
+ _Style = Style
+ from_triplet = Color.from_triplet
+
+ for index in range(PULSE_SIZE):
+ position = index / PULSE_SIZE
+ fade = 0.5 + cos((position * pi * 2)) / 2.0
+ color = blend_rgb(fore_color, back_color, cross_fade=fade)
+ append(_Segment(bar, _Style(color=from_triplet(color))))
+ return segments
+
+ def update(self, completed: float, total: Optional[float] = None) -> None:
+ """Update progress with new values.
+
+ Args:
+ completed (float): Number of steps completed.
+ total (float, optional): Total number of steps, or ``None`` to not change. Defaults to None.
+ """
+ self.completed = completed
+ self.total = total if total is not None else self.total
+
+ def _render_pulse(
+ self, console: Console, width: int, ascii: bool = False
+ ) -> Iterable[Segment]:
+ """Renders the pulse animation.
+
+ Args:
+ console (Console): Console instance.
+ width (int): Width in characters of pulse animation.
+
+ Returns:
+ RenderResult: [description]
+
+ Yields:
+ Iterator[Segment]: Segments to render pulse
+ """
+ fore_style = console.get_style(self.pulse_style, default="white")
+ back_style = console.get_style(self.style, default="black")
+
+ pulse_segments = self._get_pulse_segments(
+ fore_style, back_style, console.color_system, console.no_color, ascii=ascii
+ )
+ segment_count = len(pulse_segments)
+ current_time = (
+ monotonic() if self.animation_time is None else self.animation_time
+ )
+ segments = pulse_segments * (int(width / segment_count) + 2)
+ offset = int(-current_time * 15) % segment_count
+ segments = segments[offset : offset + width]
+ yield from segments
+
+ def __rich_console__(
+ self, console: Console, options: ConsoleOptions
+ ) -> RenderResult:
+
+ width = min(self.width or options.max_width, options.max_width)
+ ascii = options.legacy_windows or options.ascii_only
+ if self.pulse:
+ yield from self._render_pulse(console, width, ascii=ascii)
+ return
+
+ completed = min(self.total, max(0, self.completed))
+
+ bar = "-" if ascii else "━"
+ half_bar_right = " " if ascii else "╸"
+ half_bar_left = " " if ascii else "╺"
+ complete_halves = (
+ int(width * 2 * completed / self.total) if self.total else width * 2
+ )
+ bar_count = complete_halves // 2
+ half_bar_count = complete_halves % 2
+ style = console.get_style(self.style)
+ complete_style = console.get_style(
+ self.complete_style if self.completed < self.total else self.finished_style
+ )
+ _Segment = Segment
+ if bar_count:
+ yield _Segment(bar * bar_count, complete_style)
+ if half_bar_count:
+ yield _Segment(half_bar_right * half_bar_count, complete_style)
+
+ if not console.no_color:
+ remaining_bars = width - bar_count - half_bar_count
+ if remaining_bars and console.color_system is not None:
+ if not half_bar_count and bar_count:
+ yield _Segment(half_bar_left, style)
+ remaining_bars -= 1
+ if remaining_bars:
+ yield _Segment(bar * remaining_bars, style)
+
+ def __rich_measure__(
+ self, console: Console, options: ConsoleOptions
+ ) -> Measurement:
+ return (
+ Measurement(self.width, self.width)
+ if self.width is not None
+ else Measurement(4, options.max_width)
+ )
+
+
+if __name__ == "__main__": # pragma: no cover
+ console = Console()
+ bar = ProgressBar(width=50, total=100)
+
+ import time
+
+ console.show_cursor(False)
+ for n in range(0, 101, 1):
+ bar.update(n)
+ console.print(bar)
+ console.file.write("\r")
+ time.sleep(0.05)
+ console.show_cursor(True)
+ console.print()
diff --git a/src/pip/_vendor/rich/prompt.py b/src/pip/_vendor/rich/prompt.py
new file mode 100644
index 000000000..b2cea2b52
--- /dev/null
+++ b/src/pip/_vendor/rich/prompt.py
@@ -0,0 +1,376 @@
+from typing import Any, Generic, List, Optional, TextIO, TypeVar, Union, overload
+
+from . import get_console
+from .console import Console
+from .text import Text, TextType
+
+PromptType = TypeVar("PromptType")
+DefaultType = TypeVar("DefaultType")
+
+
+class PromptError(Exception):
+ """Exception base class for prompt related errors."""
+
+
+class InvalidResponse(PromptError):
+ """Exception to indicate a response was invalid. Raise this within process_response() to indicate an error
+ and provide an error message.
+
+ Args:
+ message (Union[str, Text]): Error message.
+ """
+
+ def __init__(self, message: TextType) -> None:
+ self.message = message
+
+ def __rich__(self) -> TextType:
+ return self.message
+
+
+class PromptBase(Generic[PromptType]):
+ """Ask the user for input until a valid response is received. This is the base class, see one of
+ the concrete classes for examples.
+
+ Args:
+ prompt (TextType, optional): Prompt text. Defaults to "".
+ console (Console, optional): A Console instance or None to use global console. Defaults to None.
+ password (bool, optional): Enable password input. Defaults to False.
+ choices (List[str], optional): A list of valid choices. Defaults to None.
+ show_default (bool, optional): Show default in prompt. Defaults to True.
+ show_choices (bool, optional): Show choices in prompt. Defaults to True.
+ """
+
+ response_type: type = str
+
+ validate_error_message = "[prompt.invalid]Please enter a valid value"
+ illegal_choice_message = (
+ "[prompt.invalid.choice]Please select one of the available options"
+ )
+ prompt_suffix = ": "
+
+ choices: Optional[List[str]] = None
+
+ def __init__(
+ self,
+ prompt: TextType = "",
+ *,
+ console: Optional[Console] = None,
+ password: bool = False,
+ choices: Optional[List[str]] = None,
+ show_default: bool = True,
+ show_choices: bool = True,
+ ) -> None:
+ self.console = console or get_console()
+ self.prompt = (
+ Text.from_markup(prompt, style="prompt")
+ if isinstance(prompt, str)
+ else prompt
+ )
+ self.password = password
+ if choices is not None:
+ self.choices = choices
+ self.show_default = show_default
+ self.show_choices = show_choices
+
+ @classmethod
+ @overload
+ def ask(
+ cls,
+ prompt: TextType = "",
+ *,
+ console: Optional[Console] = None,
+ password: bool = False,
+ choices: Optional[List[str]] = None,
+ show_default: bool = True,
+ show_choices: bool = True,
+ default: DefaultType,
+ stream: Optional[TextIO] = None,
+ ) -> Union[DefaultType, PromptType]:
+ ...
+
+ @classmethod
+ @overload
+ def ask(
+ cls,
+ prompt: TextType = "",
+ *,
+ console: Optional[Console] = None,
+ password: bool = False,
+ choices: Optional[List[str]] = None,
+ show_default: bool = True,
+ show_choices: bool = True,
+ stream: Optional[TextIO] = None,
+ ) -> PromptType:
+ ...
+
+ @classmethod
+ def ask(
+ cls,
+ prompt: TextType = "",
+ *,
+ console: Optional[Console] = None,
+ password: bool = False,
+ choices: Optional[List[str]] = None,
+ show_default: bool = True,
+ show_choices: bool = True,
+ default: Any = ...,
+ stream: Optional[TextIO] = None,
+ ) -> Any:
+ """Shortcut to construct and run a prompt loop and return the result.
+
+ Example:
+ >>> filename = Prompt.ask("Enter a filename")
+
+ Args:
+ prompt (TextType, optional): Prompt text. Defaults to "".
+ console (Console, optional): A Console instance or None to use global console. Defaults to None.
+ password (bool, optional): Enable password input. Defaults to False.
+ choices (List[str], optional): A list of valid choices. Defaults to None.
+ show_default (bool, optional): Show default in prompt. Defaults to True.
+ show_choices (bool, optional): Show choices in prompt. Defaults to True.
+ stream (TextIO, optional): Optional text file open for reading to get input. Defaults to None.
+ """
+ _prompt = cls(
+ prompt,
+ console=console,
+ password=password,
+ choices=choices,
+ show_default=show_default,
+ show_choices=show_choices,
+ )
+ return _prompt(default=default, stream=stream)
+
+ def render_default(self, default: DefaultType) -> Text:
+ """Turn the supplied default in to a Text instance.
+
+ Args:
+ default (DefaultType): Default value.
+
+ Returns:
+ Text: Text containing rendering of default value.
+ """
+ return Text(f"({default})", "prompt.default")
+
+ def make_prompt(self, default: DefaultType) -> Text:
+ """Make prompt text.
+
+ Args:
+ default (DefaultType): Default value.
+
+ Returns:
+ Text: Text to display in prompt.
+ """
+ prompt = self.prompt.copy()
+ prompt.end = ""
+
+ if self.show_choices and self.choices:
+ _choices = "/".join(self.choices)
+ choices = f"[{_choices}]"
+ prompt.append(" ")
+ prompt.append(choices, "prompt.choices")
+
+ if (
+ default != ...
+ and self.show_default
+ and isinstance(default, (str, self.response_type))
+ ):
+ prompt.append(" ")
+ _default = self.render_default(default)
+ prompt.append(_default)
+
+ prompt.append(self.prompt_suffix)
+
+ return prompt
+
+ @classmethod
+ def get_input(
+ cls,
+ console: Console,
+ prompt: TextType,
+ password: bool,
+ stream: Optional[TextIO] = None,
+ ) -> str:
+ """Get input from user.
+
+ Args:
+ console (Console): Console instance.
+ prompt (TextType): Prompt text.
+ password (bool): Enable password entry.
+
+ Returns:
+ str: String from user.
+ """
+ return console.input(prompt, password=password, stream=stream)
+
+ def check_choice(self, value: str) -> bool:
+ """Check value is in the list of valid choices.
+
+ Args:
+ value (str): Value entered by user.
+
+ Returns:
+ bool: True if choice was valid, otherwise False.
+ """
+ assert self.choices is not None
+ return value.strip() in self.choices
+
+ def process_response(self, value: str) -> PromptType:
+ """Process response from user, convert to prompt type.
+
+ Args:
+ value (str): String typed by user.
+
+ Raises:
+ InvalidResponse: If ``value`` is invalid.
+
+ Returns:
+ PromptType: The value to be returned from ask method.
+ """
+ value = value.strip()
+ try:
+ return_value = self.response_type(value)
+ except ValueError:
+ raise InvalidResponse(self.validate_error_message)
+
+ if self.choices is not None and not self.check_choice(value):
+ raise InvalidResponse(self.illegal_choice_message)
+
+ return return_value # type: ignore
+
+ def on_validate_error(self, value: str, error: InvalidResponse) -> None:
+ """Called to handle validation error.
+
+ Args:
+ value (str): String entered by user.
+ error (InvalidResponse): Exception instance the initiated the error.
+ """
+ self.console.print(error)
+
+ def pre_prompt(self) -> None:
+ """Hook to display something before the prompt."""
+
+ @overload
+ def __call__(self, *, stream: Optional[TextIO] = None) -> PromptType:
+ ...
+
+ @overload
+ def __call__(
+ self, *, default: DefaultType, stream: Optional[TextIO] = None
+ ) -> Union[PromptType, DefaultType]:
+ ...
+
+ def __call__(self, *, default: Any = ..., stream: Optional[TextIO] = None) -> Any:
+ """Run the prompt loop.
+
+ Args:
+ default (Any, optional): Optional default value.
+
+ Returns:
+ PromptType: Processed value.
+ """
+ while True:
+ self.pre_prompt()
+ prompt = self.make_prompt(default)
+ value = self.get_input(self.console, prompt, self.password, stream=stream)
+ if value == "" and default != ...:
+ return default
+ try:
+ return_value = self.process_response(value)
+ except InvalidResponse as error:
+ self.on_validate_error(value, error)
+ continue
+ else:
+ return return_value
+
+
+class Prompt(PromptBase[str]):
+ """A prompt that returns a str.
+
+ Example:
+ >>> name = Prompt.ask("Enter your name")
+
+
+ """
+
+ response_type = str
+
+
+class IntPrompt(PromptBase[int]):
+ """A prompt that returns an integer.
+
+ Example:
+ >>> burrito_count = IntPrompt.ask("How many burritos do you want to order")
+
+ """
+
+ response_type = int
+ validate_error_message = "[prompt.invalid]Please enter a valid integer number"
+
+
+class FloatPrompt(PromptBase[int]):
+ """A prompt that returns a float.
+
+ Example:
+ >>> temperature = FloatPrompt.ask("Enter desired temperature")
+
+ """
+
+ response_type = float
+ validate_error_message = "[prompt.invalid]Please enter a number"
+
+
+class Confirm(PromptBase[bool]):
+ """A yes / no confirmation prompt.
+
+ Example:
+ >>> if Confirm.ask("Continue"):
+ run_job()
+
+ """
+
+ response_type = bool
+ validate_error_message = "[prompt.invalid]Please enter Y or N"
+ choices: List[str] = ["y", "n"]
+
+ def render_default(self, default: DefaultType) -> Text:
+ """Render the default as (y) or (n) rather than True/False."""
+ yes, no = self.choices
+ return Text(f"({yes})" if default else f"({no})", style="prompt.default")
+
+ def process_response(self, value: str) -> bool:
+ """Convert choices to a bool."""
+ value = value.strip().lower()
+ if value not in self.choices:
+ raise InvalidResponse(self.validate_error_message)
+ return value == self.choices[0]
+
+
+if __name__ == "__main__": # pragma: no cover
+
+ from pip._vendor.rich import print
+
+ if Confirm.ask("Run [i]prompt[/i] tests?", default=True):
+ while True:
+ result = IntPrompt.ask(
+ ":rocket: Enter a number between [b]1[/b] and [b]10[/b]", default=5
+ )
+ if result >= 1 and result <= 10:
+ break
+ print(":pile_of_poo: [prompt.invalid]Number must be between 1 and 10")
+ print(f"number={result}")
+
+ while True:
+ password = Prompt.ask(
+ "Please enter a password [cyan](must be at least 5 characters)",
+ password=True,
+ )
+ if len(password) >= 5:
+ break
+ print("[prompt.invalid]password too short")
+ print(f"password={password!r}")
+
+ fruit = Prompt.ask("Enter a fruit", choices=["apple", "orange", "pear"])
+ print(f"fruit={fruit!r}")
+
+ else:
+ print("[b]OK :loudly_crying_face:")
diff --git a/src/pip/_vendor/rich/protocol.py b/src/pip/_vendor/rich/protocol.py
new file mode 100644
index 000000000..711b31b6a
--- /dev/null
+++ b/src/pip/_vendor/rich/protocol.py
@@ -0,0 +1,10 @@
+from typing import Any
+
+
+def is_renderable(check_object: Any) -> bool:
+ """Check if an object may be rendered by Rich."""
+ return (
+ isinstance(check_object, str)
+ or hasattr(check_object, "__rich__")
+ or hasattr(check_object, "__rich_console__")
+ )
diff --git a/src/pip/_vendor/rich/py.typed b/src/pip/_vendor/rich/py.typed
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/pip/_vendor/rich/py.typed
diff --git a/src/pip/_vendor/rich/region.py b/src/pip/_vendor/rich/region.py
new file mode 100644
index 000000000..75b3631c3
--- /dev/null
+++ b/src/pip/_vendor/rich/region.py
@@ -0,0 +1,10 @@
+from typing import NamedTuple
+
+
+class Region(NamedTuple):
+ """Defines a rectangular region of the screen."""
+
+ x: int
+ y: int
+ width: int
+ height: int
diff --git a/src/pip/_vendor/rich/repr.py b/src/pip/_vendor/rich/repr.py
new file mode 100644
index 000000000..17147fd4b
--- /dev/null
+++ b/src/pip/_vendor/rich/repr.py
@@ -0,0 +1,151 @@
+from functools import partial
+import inspect
+
+from typing import (
+ Any,
+ Callable,
+ Iterable,
+ List,
+ Optional,
+ overload,
+ Union,
+ Tuple,
+ Type,
+ TypeVar,
+)
+
+
+T = TypeVar("T")
+
+
+Result = Iterable[Union[Any, Tuple[Any], Tuple[str, Any], Tuple[str, Any, Any]]]
+RichReprResult = Result
+
+
+class ReprError(Exception):
+ """An error occurred when attempting to build a repr."""
+
+
+@overload
+def auto(cls: Optional[T]) -> T:
+ ...
+
+
+@overload
+def auto(*, angular: bool = False) -> Callable[[T], T]:
+ ...
+
+
+def auto(
+ cls: Optional[T] = None, *, angular: Optional[bool] = None
+) -> Union[T, Callable[[T], T]]:
+ """Class decorator to create __repr__ from __rich_repr__"""
+
+ def do_replace(cls: Type[T], angular: Optional[bool] = None) -> Type[T]:
+ def auto_repr(self: Type[T]) -> str:
+ """Create repr string from __rich_repr__"""
+ repr_str: List[str] = []
+ append = repr_str.append
+
+ angular = getattr(self.__rich_repr__, "angular", False) # type: ignore
+ for arg in self.__rich_repr__(): # type: ignore
+ if isinstance(arg, tuple):
+ if len(arg) == 1:
+ append(repr(arg[0]))
+ else:
+ key, value, *default = arg
+ if key is None:
+ append(repr(value))
+ else:
+ if len(default) and default[0] == value:
+ continue
+ append(f"{key}={value!r}")
+ else:
+ append(repr(arg))
+ if angular:
+ return f"<{self.__class__.__name__} {' '.join(repr_str)}>"
+ else:
+ return f"{self.__class__.__name__}({', '.join(repr_str)})"
+
+ def auto_rich_repr(self: Type[T]) -> Result:
+ """Auto generate __rich_rep__ from signature of __init__"""
+ try:
+ signature = inspect.signature(self.__init__) ## type: ignore
+ for name, param in signature.parameters.items():
+ if param.kind == param.POSITIONAL_ONLY:
+ yield getattr(self, name)
+ elif param.kind in (
+ param.POSITIONAL_OR_KEYWORD,
+ param.KEYWORD_ONLY,
+ ):
+ if param.default == param.empty:
+ yield getattr(self, param.name)
+ else:
+ yield param.name, getattr(self, param.name), param.default
+ except Exception as error:
+ raise ReprError(
+ f"Failed to auto generate __rich_repr__; {error}"
+ ) from None
+
+ if not hasattr(cls, "__rich_repr__"):
+ auto_rich_repr.__doc__ = "Build a rich repr"
+ cls.__rich_repr__ = auto_rich_repr # type: ignore
+
+ auto_repr.__doc__ = "Return repr(self)"
+ cls.__repr__ = auto_repr # type: ignore
+ if angular is not None:
+ cls.__rich_repr__.angular = angular # type: ignore
+ return cls
+
+ if cls is None:
+ return partial(do_replace, angular=angular) # type: ignore
+ else:
+ return do_replace(cls, angular=angular) # type: ignore
+
+
+@overload
+def rich_repr(cls: Optional[T]) -> T:
+ ...
+
+
+@overload
+def rich_repr(*, angular: bool = False) -> Callable[[T], T]:
+ ...
+
+
+def rich_repr(
+ cls: Optional[T] = None, *, angular: bool = False
+) -> Union[T, Callable[[T], T]]:
+ if cls is None:
+ return auto(angular=angular)
+ else:
+ return auto(cls)
+
+
+if __name__ == "__main__":
+
+ @auto
+ class Foo:
+ def __rich_repr__(self) -> Result:
+ yield "foo"
+ yield "bar", {"shopping": ["eggs", "ham", "pineapple"]}
+ yield "buy", "hand sanitizer"
+
+ foo = Foo()
+ from pip._vendor.rich.console import Console
+
+ console = Console()
+
+ console.rule("Standard repr")
+ console.print(foo)
+
+ console.print(foo, width=60)
+ console.print(foo, width=30)
+
+ console.rule("Angular repr")
+ Foo.__rich_repr__.angular = True # type: ignore
+
+ console.print(foo)
+
+ console.print(foo, width=60)
+ console.print(foo, width=30)
diff --git a/src/pip/_vendor/rich/rule.py b/src/pip/_vendor/rich/rule.py
new file mode 100644
index 000000000..ce4754f6a
--- /dev/null
+++ b/src/pip/_vendor/rich/rule.py
@@ -0,0 +1,115 @@
+from typing import Union
+
+from .align import AlignMethod
+from .cells import cell_len, set_cell_size
+from .console import Console, ConsoleOptions, RenderResult
+from .jupyter import JupyterMixin
+from .style import Style
+from .text import Text
+
+
+class Rule(JupyterMixin):
+ """A console renderable to draw a horizontal rule (line).
+
+ Args:
+ title (Union[str, Text], optional): Text to render in the rule. Defaults to "".
+ characters (str, optional): Character(s) used to draw the line. Defaults to "─".
+ style (StyleType, optional): Style of Rule. Defaults to "rule.line".
+ end (str, optional): Character at end of Rule. defaults to "\\\\n"
+ align (str, optional): How to align the title, one of "left", "center", or "right". Defaults to "center".
+ """
+
+ def __init__(
+ self,
+ title: Union[str, Text] = "",
+ *,
+ characters: str = "─",
+ style: Union[str, Style] = "rule.line",
+ end: str = "\n",
+ align: AlignMethod = "center",
+ ) -> None:
+ if cell_len(characters) < 1:
+ raise ValueError(
+ "'characters' argument must have a cell width of at least 1"
+ )
+ if align not in ("left", "center", "right"):
+ raise ValueError(
+ f'invalid value for align, expected "left", "center", "right" (not {align!r})'
+ )
+ self.title = title
+ self.characters = characters
+ self.style = style
+ self.end = end
+ self.align = align
+
+ def __repr__(self) -> str:
+ return f"Rule({self.title!r}, {self.characters!r})"
+
+ def __rich_console__(
+ self, console: Console, options: ConsoleOptions
+ ) -> 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))
+ else self.characters
+ )
+
+ chars_len = cell_len(characters)
+ if not self.title:
+ rule_text = Text(characters * ((width // chars_len) + 1), self.style)
+ rule_text.truncate(width)
+ rule_text.plain = set_cell_size(rule_text.plain, width)
+ yield rule_text
+ return
+
+ if isinstance(self.title, Text):
+ title_text = self.title
+ else:
+ title_text = console.render_str(self.title, style="rule.text")
+
+ title_text.plain = title_text.plain.replace("\n", " ")
+ title_text.expand_tabs()
+ rule_text = Text(end=self.end)
+
+ if self.align == "center":
+ title_text.truncate(width - 4, overflow="ellipsis")
+ side_width = (width - cell_len(title_text.plain)) // 2
+ left = Text(characters * (side_width // chars_len + 1))
+ left.truncate(side_width - 1)
+ right_length = width - cell_len(left.plain) - cell_len(title_text.plain)
+ right = Text(characters * (side_width // chars_len + 1))
+ right.truncate(right_length)
+ rule_text.append(left.plain + " ", self.style)
+ rule_text.append(title_text)
+ rule_text.append(" " + right.plain, self.style)
+ elif self.align == "left":
+ title_text.truncate(width - 2, overflow="ellipsis")
+ rule_text.append(title_text)
+ rule_text.append(" ")
+ rule_text.append(characters * (width - rule_text.cell_len), self.style)
+ elif self.align == "right":
+ title_text.truncate(width - 2, overflow="ellipsis")
+ rule_text.append(characters * (width - title_text.cell_len - 1), self.style)
+ rule_text.append(" ")
+ rule_text.append(title_text)
+
+ rule_text.plain = set_cell_size(rule_text.plain, width)
+ yield rule_text
+
+
+if __name__ == "__main__": # pragma: no cover
+ from pip._vendor.rich.console import Console
+ import sys
+
+ try:
+ text = sys.argv[1]
+ except IndexError:
+ text = "Hello, World"
+ console = Console()
+ console.print(Rule(title=text))
diff --git a/src/pip/_vendor/rich/scope.py b/src/pip/_vendor/rich/scope.py
new file mode 100644
index 000000000..6822b8ca5
--- /dev/null
+++ b/src/pip/_vendor/rich/scope.py
@@ -0,0 +1,86 @@
+from collections.abc import Mapping
+from typing import TYPE_CHECKING, Any, Optional, Tuple
+
+from .highlighter import ReprHighlighter
+from .panel import Panel
+from .pretty import Pretty
+from .table import Table
+from .text import Text, TextType
+
+if TYPE_CHECKING:
+ from .console import ConsoleRenderable
+
+
+def render_scope(
+ scope: "Mapping[str, Any]",
+ *,
+ title: Optional[TextType] = None,
+ sort_keys: bool = True,
+ indent_guides: bool = False,
+ max_length: Optional[int] = None,
+ max_string: Optional[int] = None,
+) -> "ConsoleRenderable":
+ """Render python variables in a given scope.
+
+ Args:
+ scope (Mapping): A mapping containing variable names and values.
+ title (str, optional): Optional title. Defaults to None.
+ sort_keys (bool, optional): Enable sorting of items. Defaults to True.
+ indent_guides (bool, optional): Enable indentaton guides. Defaults to False.
+ max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+ Defaults to None.
+ max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to None.
+
+ Returns:
+ ConsoleRenderable: A renderable object.
+ """
+ highlighter = ReprHighlighter()
+ items_table = Table.grid(padding=(0, 1), expand=False)
+ items_table.add_column(justify="right")
+
+ def sort_items(item: Tuple[str, Any]) -> Tuple[bool, str]:
+ """Sort special variables first, then alphabetically."""
+ key, _ = item
+ return (not key.startswith("__"), key.lower())
+
+ items = sorted(scope.items(), key=sort_items) if sort_keys else scope.items()
+ for key, value in items:
+ key_text = Text.assemble(
+ (key, "scope.key.special" if key.startswith("__") else "scope.key"),
+ (" =", "scope.equals"),
+ )
+ items_table.add_row(
+ key_text,
+ Pretty(
+ value,
+ highlighter=highlighter,
+ indent_guides=indent_guides,
+ max_length=max_length,
+ max_string=max_string,
+ ),
+ )
+ return Panel.fit(
+ items_table,
+ title=title,
+ border_style="scope.border",
+ padding=(0, 1),
+ )
+
+
+if __name__ == "__main__": # pragma: no cover
+ from pip._vendor.rich import print
+
+ print()
+
+ def test(foo: float, bar: float) -> None:
+ list_of_things = [1, 2, 3, None, 4, True, False, "Hello World"]
+ dict_of_things = {
+ "version": "1.1",
+ "method": "confirmFruitPurchase",
+ "params": [["apple", "orange", "mangoes", "pomelo"], 1.123],
+ "id": "194521489",
+ }
+ print(render_scope(locals(), title="[i]locals", sort_keys=False))
+
+ test(20.3423, 3.1427)
+ print()
diff --git a/src/pip/_vendor/rich/screen.py b/src/pip/_vendor/rich/screen.py
new file mode 100644
index 000000000..7f416e1e7
--- /dev/null
+++ b/src/pip/_vendor/rich/screen.py
@@ -0,0 +1,54 @@
+from typing import Optional, TYPE_CHECKING
+
+from .segment import Segment
+from .style import StyleType
+from ._loop import loop_last
+
+
+if TYPE_CHECKING:
+ from .console import (
+ Console,
+ ConsoleOptions,
+ RenderResult,
+ RenderableType,
+ Group,
+ )
+
+
+class Screen:
+ """A renderable that fills the terminal screen and crops excess.
+
+ Args:
+ renderable (RenderableType): Child renderable.
+ style (StyleType, optional): Optional background style. Defaults to None.
+ """
+
+ renderable: "RenderableType"
+
+ def __init__(
+ self,
+ *renderables: "RenderableType",
+ style: Optional[StyleType] = None,
+ application_mode: bool = False,
+ ) -> None:
+ from pip._vendor.rich.console import Group
+
+ self.renderable = Group(*renderables)
+ self.style = style
+ self.application_mode = application_mode
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+ width, height = options.size
+ style = console.get_style(self.style) if self.style else None
+ render_options = options.update(width=width, height=height)
+ lines = console.render_lines(
+ self.renderable or "", render_options, style=style, pad=True
+ )
+ lines = Segment.set_shape(lines, width, height, style=style)
+ new_line = Segment("\n\r") if self.application_mode else Segment.line()
+ for last, line in loop_last(lines):
+ yield from line
+ if not last:
+ yield new_line
diff --git a/src/pip/_vendor/rich/segment.py b/src/pip/_vendor/rich/segment.py
new file mode 100644
index 000000000..869b61010
--- /dev/null
+++ b/src/pip/_vendor/rich/segment.py
@@ -0,0 +1,623 @@
+from enum import IntEnum
+from functools import lru_cache
+from itertools import filterfalse
+from logging import getLogger
+from operator import attrgetter
+from typing import (
+ TYPE_CHECKING,
+ Dict,
+ Iterable,
+ List,
+ NamedTuple,
+ Optional,
+ Sequence,
+ Tuple,
+ Union,
+)
+
+from .cells import cell_len, get_character_cell_size, set_cell_size
+from .repr import Result, rich_repr
+from .style import Style
+
+if TYPE_CHECKING:
+ from .console import Console, ConsoleOptions, RenderResult
+
+log = getLogger("rich")
+
+
+class ControlType(IntEnum):
+ """Non-printable control codes which typically translate to ANSI codes."""
+
+ BELL = 1
+ CARRIAGE_RETURN = 2
+ HOME = 3
+ CLEAR = 4
+ SHOW_CURSOR = 5
+ HIDE_CURSOR = 6
+ ENABLE_ALT_SCREEN = 7
+ DISABLE_ALT_SCREEN = 8
+ CURSOR_UP = 9
+ CURSOR_DOWN = 10
+ CURSOR_FORWARD = 11
+ CURSOR_BACKWARD = 12
+ CURSOR_MOVE_TO_COLUMN = 13
+ CURSOR_MOVE_TO = 14
+ ERASE_IN_LINE = 15
+
+
+ControlCode = Union[
+ Tuple[ControlType], Tuple[ControlType, int], Tuple[ControlType, int, int]
+]
+
+
+@rich_repr()
+class Segment(NamedTuple):
+ """A piece of text with associated style. Segments are produced by the Console render process and
+ are ultimately converted in to strings to be written to the terminal.
+
+ Args:
+ text (str): A piece of text.
+ style (:class:`~rich.style.Style`, optional): An optional style to apply to the text.
+ control (Tuple[ControlCode..], optional): Optional sequence of control codes.
+ """
+
+ text: str = ""
+ """Raw text."""
+ style: Optional[Style] = None
+ """An optional style."""
+ control: Optional[Sequence[ControlCode]] = None
+ """Optional sequence of control codes."""
+
+ def __rich_repr__(self) -> Result:
+ yield self.text
+ if self.control is None:
+ if self.style is not None:
+ yield self.style
+ else:
+ yield self.style
+ yield self.control
+
+ def __bool__(self) -> bool:
+ """Check if the segment contains text."""
+ return bool(self.text)
+
+ @property
+ def cell_length(self) -> int:
+ """Get cell length of segment."""
+ return 0 if self.control else cell_len(self.text)
+
+ @property
+ def is_control(self) -> bool:
+ """Check if the segment contains control codes."""
+ return self.control is not None
+
+ @classmethod
+ @lru_cache(1024 * 16)
+ def _split_cells(cls, segment: "Segment", cut: int) -> Tuple["Segment", "Segment"]: # type: ignore
+
+ text, style, control = segment
+ _Segment = Segment
+ if cut >= segment.cell_length:
+ return segment, _Segment("", style, control)
+
+ if len(text) == segment.cell_length:
+ # Fast path with all 1 cell characters
+ return (
+ _Segment(text[:cut], style, control),
+ _Segment(text[cut:], style, control),
+ )
+
+ cell_size = get_character_cell_size
+
+ pos = int((cut / segment.cell_length) * len(text))
+
+ before = text[:pos]
+ cell_pos = cell_len(before)
+ if cell_pos == cut:
+ return (
+ _Segment(before, style, control),
+ _Segment(text[pos:], style, control),
+ )
+ while pos < len(text):
+ char = text[pos]
+ pos += 1
+ cell_pos += cell_size(char)
+ before = text[:pos]
+ if cell_pos == cut:
+ return (
+ _Segment(before, style, control),
+ _Segment(text[pos:], style, control),
+ )
+ if cell_pos > cut:
+ return (
+ _Segment(before[: pos - 1] + " ", style, control),
+ _Segment(" " + text[pos:], style, control),
+ )
+
+ def split_cells(self, cut: int) -> Tuple["Segment", "Segment"]:
+ """Split segment in to two segments at the specified column.
+
+ If the cut point falls in the middle of a 2-cell wide character then it is replaced
+ by two spaces, to preserve the display width of the parent segment.
+
+ Returns:
+ Tuple[Segment, Segment]: Two segments.
+ """
+ return self._split_cells(self, cut)
+
+ @classmethod
+ def line(cls) -> "Segment":
+ """Make a new line segment."""
+ return cls("\n")
+
+ @classmethod
+ def apply_style(
+ cls,
+ segments: Iterable["Segment"],
+ style: Optional[Style] = None,
+ post_style: Optional[Style] = None,
+ ) -> Iterable["Segment"]:
+ """Apply style(s) to an iterable of segments.
+
+ Returns an iterable of segments where the style is replaced by ``style + segment.style + post_style``.
+
+ Args:
+ segments (Iterable[Segment]): Segments to process.
+ style (Style, optional): Base style. Defaults to None.
+ post_style (Style, optional): Style to apply on top of segment style. Defaults to None.
+
+ Returns:
+ Iterable[Segments]: A new iterable of segments (possibly the same iterable).
+ """
+ result_segments = segments
+ if style:
+ apply = style.__add__
+ result_segments = (
+ cls(text, None if control else apply(_style), control)
+ for text, _style, control in result_segments
+ )
+ if post_style:
+ result_segments = (
+ cls(
+ text,
+ (
+ None
+ if control
+ else (_style + post_style if _style else post_style)
+ ),
+ control,
+ )
+ for text, _style, control in result_segments
+ )
+ return result_segments
+
+ @classmethod
+ def filter_control(
+ cls, segments: Iterable["Segment"], is_control: bool = False
+ ) -> Iterable["Segment"]:
+ """Filter segments by ``is_control`` attribute.
+
+ Args:
+ segments (Iterable[Segment]): An iterable of Segment instances.
+ is_control (bool, optional): is_control flag to match in search.
+
+ Returns:
+ Iterable[Segment]: And iterable of Segment instances.
+
+ """
+ if is_control:
+ return filter(attrgetter("control"), segments)
+ else:
+ return filterfalse(attrgetter("control"), segments)
+
+ @classmethod
+ def split_lines(cls, segments: Iterable["Segment"]) -> Iterable[List["Segment"]]:
+ """Split a sequence of segments in to a list of lines.
+
+ Args:
+ segments (Iterable[Segment]): Segments potentially containing line feeds.
+
+ Yields:
+ Iterable[List[Segment]]: Iterable of segment lists, one per line.
+ """
+ line: List[Segment] = []
+ append = line.append
+
+ for segment in segments:
+ if "\n" in segment.text and not segment.control:
+ text, style, _ = segment
+ while text:
+ _text, new_line, text = text.partition("\n")
+ if _text:
+ append(cls(_text, style))
+ if new_line:
+ yield line
+ line = []
+ append = line.append
+ else:
+ append(segment)
+ if line:
+ yield line
+
+ @classmethod
+ def split_and_crop_lines(
+ cls,
+ segments: Iterable["Segment"],
+ length: int,
+ style: Optional[Style] = None,
+ pad: bool = True,
+ include_new_lines: bool = True,
+ ) -> Iterable[List["Segment"]]:
+ """Split segments in to lines, and crop lines greater than a given length.
+
+ Args:
+ segments (Iterable[Segment]): An iterable of segments, probably
+ generated from console.render.
+ length (int): Desired line length.
+ style (Style, optional): Style to use for any padding.
+ pad (bool): Enable padding of lines that are less than `length`.
+
+ Returns:
+ Iterable[List[Segment]]: An iterable of lines of segments.
+ """
+ line: List[Segment] = []
+ append = line.append
+
+ adjust_line_length = cls.adjust_line_length
+ new_line_segment = cls("\n")
+
+ for segment in segments:
+ if "\n" in segment.text and not segment.control:
+ text, style, _ = segment
+ while text:
+ _text, new_line, text = text.partition("\n")
+ if _text:
+ append(cls(_text, style))
+ if new_line:
+ cropped_line = adjust_line_length(
+ line, length, style=style, pad=pad
+ )
+ if include_new_lines:
+ cropped_line.append(new_line_segment)
+ yield cropped_line
+ del line[:]
+ else:
+ append(segment)
+ if line:
+ yield adjust_line_length(line, length, style=style, pad=pad)
+
+ @classmethod
+ def adjust_line_length(
+ cls,
+ line: List["Segment"],
+ length: int,
+ style: Optional[Style] = None,
+ pad: bool = True,
+ ) -> List["Segment"]:
+ """Adjust a line to a given width (cropping or padding as required).
+
+ Args:
+ segments (Iterable[Segment]): A list of segments in a single line.
+ length (int): The desired width of the line.
+ style (Style, optional): The style of padding if used (space on the end). Defaults to None.
+ pad (bool, optional): Pad lines with spaces if they are shorter than `length`. Defaults to True.
+
+ Returns:
+ List[Segment]: A line of segments with the desired length.
+ """
+ line_length = sum(segment.cell_length for segment in line)
+ new_line: List[Segment]
+
+ if line_length < length:
+ if pad:
+ new_line = line + [cls(" " * (length - line_length), style)]
+ else:
+ new_line = line[:]
+ elif line_length > length:
+ new_line = []
+ append = new_line.append
+ line_length = 0
+ for segment in line:
+ segment_length = segment.cell_length
+ if line_length + segment_length < length or segment.control:
+ append(segment)
+ line_length += segment_length
+ else:
+ text, segment_style, _ = segment
+ text = set_cell_size(text, length - line_length)
+ append(cls(text, segment_style))
+ break
+ else:
+ new_line = line[:]
+ return new_line
+
+ @classmethod
+ def get_line_length(cls, line: List["Segment"]) -> int:
+ """Get the length of list of segments.
+
+ Args:
+ line (List[Segment]): A line encoded as a list of Segments (assumes no '\\\\n' characters),
+
+ Returns:
+ int: The length of the line.
+ """
+ return sum(segment.cell_length for segment in line)
+
+ @classmethod
+ def get_shape(cls, lines: List[List["Segment"]]) -> Tuple[int, int]:
+ """Get the shape (enclosing rectangle) of a list of lines.
+
+ Args:
+ lines (List[List[Segment]]): A list of lines (no '\\\\n' characters).
+
+ Returns:
+ Tuple[int, int]: Width and height in characters.
+ """
+ get_line_length = cls.get_line_length
+ max_width = max(get_line_length(line) for line in lines) if lines else 0
+ return (max_width, len(lines))
+
+ @classmethod
+ def set_shape(
+ cls,
+ lines: List[List["Segment"]],
+ width: int,
+ height: Optional[int] = None,
+ style: Optional[Style] = None,
+ new_lines: bool = False,
+ ) -> List[List["Segment"]]:
+ """Set the shape of a list of lines (enclosing rectangle).
+
+ Args:
+ lines (List[List[Segment]]): A list of lines.
+ width (int): Desired width.
+ height (int, optional): Desired height or None for no change.
+ style (Style, optional): Style of any padding added. Defaults to None.
+ new_lines (bool, optional): Padded lines should include "\n". Defaults to False.
+
+ Returns:
+ List[List[Segment]]: New list of lines that fits width x height.
+ """
+ if height is None:
+ height = len(lines)
+ shaped_lines: List[List[Segment]] = []
+ pad_line = (
+ [Segment(" " * width, style), Segment("\n")]
+ if new_lines
+ else [Segment(" " * width, style)]
+ )
+
+ append = shaped_lines.append
+ adjust_line_length = cls.adjust_line_length
+ line: Optional[List[Segment]]
+ iter_lines = iter(lines)
+ for _ in range(height):
+ line = next(iter_lines, None)
+ if line is None:
+ append(pad_line)
+ else:
+ append(adjust_line_length(line, width, style=style))
+ return shaped_lines
+
+ @classmethod
+ def simplify(cls, segments: Iterable["Segment"]) -> Iterable["Segment"]:
+ """Simplify an iterable of segments by combining contiguous segments with the same style.
+
+ Args:
+ segments (Iterable[Segment]): An iterable of segments.
+
+ Returns:
+ Iterable[Segment]: A possibly smaller iterable of segments that will render the same way.
+ """
+ iter_segments = iter(segments)
+ try:
+ last_segment = next(iter_segments)
+ except StopIteration:
+ return
+
+ _Segment = Segment
+ for segment in iter_segments:
+ if last_segment.style == segment.style and not segment.control:
+ last_segment = _Segment(
+ last_segment.text + segment.text, last_segment.style
+ )
+ else:
+ yield last_segment
+ last_segment = segment
+ yield last_segment
+
+ @classmethod
+ def strip_links(cls, segments: Iterable["Segment"]) -> Iterable["Segment"]:
+ """Remove all links from an iterable of styles.
+
+ Args:
+ segments (Iterable[Segment]): An iterable segments.
+
+ Yields:
+ Segment: Segments with link removed.
+ """
+ for segment in segments:
+ if segment.control or segment.style is None:
+ yield segment
+ else:
+ text, style, _control = segment
+ yield cls(text, style.update_link(None) if style else None)
+
+ @classmethod
+ def strip_styles(cls, segments: Iterable["Segment"]) -> Iterable["Segment"]:
+ """Remove all styles from an iterable of segments.
+
+ Args:
+ segments (Iterable[Segment]): An iterable segments.
+
+ Yields:
+ Segment: Segments with styles replace with None
+ """
+ for text, _style, control in segments:
+ yield cls(text, None, control)
+
+ @classmethod
+ def remove_color(cls, segments: Iterable["Segment"]) -> Iterable["Segment"]:
+ """Remove all color from an iterable of segments.
+
+ Args:
+ segments (Iterable[Segment]): An iterable segments.
+
+ Yields:
+ Segment: Segments with colorless style.
+ """
+
+ cache: Dict[Style, Style] = {}
+ for text, style, control in segments:
+ if style:
+ colorless_style = cache.get(style)
+ if colorless_style is None:
+ colorless_style = style.without_color
+ cache[style] = colorless_style
+ yield cls(text, colorless_style, control)
+ else:
+ yield cls(text, None, control)
+
+ @classmethod
+ def divide(
+ cls, segments: Iterable["Segment"], cuts: Iterable[int]
+ ) -> Iterable[List["Segment"]]:
+ """Divides an iterable of segments in to portions.
+
+ Args:
+ cuts (Iterable[int]): Cell positions where to divide.
+
+ Yields:
+ [Iterable[List[Segment]]]: An iterable of Segments in List.
+ """
+ split_segments: List["Segment"] = []
+ add_segment = split_segments.append
+ iter_cuts = iter(cuts)
+
+ while True:
+ try:
+ cut = next(iter_cuts)
+ except StopIteration:
+ return []
+ if cut != 0:
+ break
+ yield []
+ pos = 0
+
+ for segment in segments:
+ while segment.text:
+ end_pos = pos + segment.cell_length
+ if end_pos < cut:
+ add_segment(segment)
+ pos = end_pos
+ break
+
+ try:
+ if end_pos == cut:
+ add_segment(segment)
+ yield split_segments[:]
+ del split_segments[:]
+ pos = end_pos
+ break
+ else:
+ before, segment = segment.split_cells(cut - pos)
+ add_segment(before)
+ yield split_segments[:]
+ del split_segments[:]
+ pos = cut
+ finally:
+ try:
+ cut = next(iter_cuts)
+ except StopIteration:
+ if split_segments:
+ yield split_segments[:]
+ return
+ yield split_segments[:]
+
+
+class Segments:
+ """A simple renderable to render an iterable of segments. This class may be useful if
+ you want to print segments outside of a __rich_console__ method.
+
+ Args:
+ segments (Iterable[Segment]): An iterable of segments.
+ new_lines (bool, optional): Add new lines between segments. Defaults to False.
+ """
+
+ def __init__(self, segments: Iterable[Segment], new_lines: bool = False) -> None:
+ self.segments = list(segments)
+ self.new_lines = new_lines
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+ if self.new_lines:
+ line = Segment.line()
+ for segment in self.segments:
+ yield segment
+ yield line
+ else:
+ yield from self.segments
+
+
+class SegmentLines:
+ def __init__(self, lines: Iterable[List[Segment]], new_lines: bool = False) -> None:
+ """A simple renderable containing a number of lines of segments. May be used as an intermediate
+ in rendering process.
+
+ Args:
+ lines (Iterable[List[Segment]]): Lists of segments forming lines.
+ new_lines (bool, optional): Insert new lines after each line. Defaults to False.
+ """
+ self.lines = list(lines)
+ self.new_lines = new_lines
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+ if self.new_lines:
+ new_line = Segment.line()
+ for line in self.lines:
+ yield from line
+ yield new_line
+ else:
+ for line in self.lines:
+ yield from line
+
+
+if __name__ == "__main__":
+
+ if __name__ == "__main__": # pragma: no cover
+ from pip._vendor.rich.console import Console
+ from pip._vendor.rich.syntax import Syntax
+ from pip._vendor.rich.text import Text
+
+ code = """from rich.console import Console
+ console = Console()
+ text = Text.from_markup("Hello, [bold magenta]World[/]!")
+ console.print(text)"""
+
+ text = Text.from_markup("Hello, [bold magenta]World[/]!")
+
+ console = Console()
+
+ console.rule("rich.Segment")
+ console.print(
+ "A Segment is the last step in the Rich render process before generating text with ANSI codes."
+ )
+ console.print("\nConsider the following code:\n")
+ 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"
+ )
+ fragments = list(console.render(text))
+ console.print(fragments)
+ console.print()
+ console.print(
+ "The Segments are then processed to produce the following output:\n"
+ )
+ console.print(text)
+ console.print(
+ "\nYou will only need to know this if you are implementing your own Rich renderables."
+ )
diff --git a/src/pip/_vendor/rich/spinner.py b/src/pip/_vendor/rich/spinner.py
new file mode 100644
index 000000000..5b13b1e9b
--- /dev/null
+++ b/src/pip/_vendor/rich/spinner.py
@@ -0,0 +1,134 @@
+from typing import cast, List, Optional, TYPE_CHECKING
+
+from ._spinners import SPINNERS
+from .measure import Measurement
+from .table import Table
+from .text import Text
+
+if TYPE_CHECKING:
+ from .console import Console, ConsoleOptions, RenderResult, RenderableType
+ from .style import StyleType
+
+
+class Spinner:
+ def __init__(
+ self,
+ name: str,
+ text: "RenderableType" = "",
+ *,
+ 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:
+ raise KeyError(f"no spinner called {name!r}")
+ self.text = Text.from_markup(text) if isinstance(text, str) else text
+ self.frames = cast(List[str], spinner["frames"])[:]
+ self.interval = cast(float, spinner["interval"])
+ self.start_time: Optional[float] = None
+ self.style = style
+ self.speed = speed
+ self.frame_no_offset: float = 0.0
+ self._update_speed = 0.0
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+ yield self.render(console.get_time())
+
+ def __rich_measure__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> Measurement:
+ text = self.render(0)
+ return Measurement.get(console, options, text)
+
+ def render(self, time: float) -> "RenderableType":
+ """Render the spinner for a given time.
+
+ Args:
+ time (float): Time in seconds.
+
+ Returns:
+ RenderableType: A renderable containing animation frame.
+ """
+ if self.start_time is None:
+ self.start_time = time
+
+ frame_no = ((time - self.start_time) * self.speed) / (
+ self.interval / 1000.0
+ ) + self.frame_no_offset
+ frame = Text(
+ self.frames[int(frame_no) % len(self.frames)], style=self.style or ""
+ )
+
+ if self._update_speed:
+ self.frame_no_offset = frame_no
+ self.start_time = time
+ self.speed = self._update_speed
+ self._update_speed = 0.0
+
+ if not self.text:
+ return frame
+ elif isinstance(self.text, (str, Text)):
+ return Text.assemble(frame, " ", self.text)
+ else:
+ table = Table.grid(padding=1)
+ table.add_row(frame, self.text)
+ return table
+
+ def update(
+ self,
+ *,
+ text: "RenderableType" = "",
+ style: Optional["StyleType"] = None,
+ speed: Optional[float] = None,
+ ) -> None:
+ """Updates attributes of a spinner after it has been started.
+
+ Args:
+ 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 None.
+ """
+ if text:
+ self.text = Text.from_markup(text) if isinstance(text, str) else text
+ if style:
+ self.style = style
+ if speed:
+ self._update_speed = speed
+
+
+if __name__ == "__main__": # pragma: no cover
+ from time import sleep
+
+ from .columns import Columns
+ from .panel import Panel
+ from .live import Live
+
+ all_spinners = Columns(
+ [
+ Spinner(spinner_name, text=Text(repr(spinner_name), style="green"))
+ for spinner_name in sorted(SPINNERS.keys())
+ ],
+ column_first=True,
+ expand=True,
+ )
+
+ with Live(
+ Panel(all_spinners, title="Spinners", border_style="blue"),
+ refresh_per_second=20,
+ ) as live:
+ while True:
+ sleep(0.1)
diff --git a/src/pip/_vendor/rich/status.py b/src/pip/_vendor/rich/status.py
new file mode 100644
index 000000000..09eff405e
--- /dev/null
+++ b/src/pip/_vendor/rich/status.py
@@ -0,0 +1,132 @@
+from types import TracebackType
+from typing import Optional, Type
+
+from .console import Console, RenderableType
+from .jupyter import JupyterMixin
+from .live import Live
+from .spinner import Spinner
+from .style import StyleType
+
+
+class Status(JupyterMixin):
+ """Displays a status indicator with a 'spinner' animation.
+
+ Args:
+ status (RenderableType): A status renderable (str or Text typically).
+ console (Console, optional): Console instance to use, or None for global console. Defaults to None.
+ spinner (str, optional): Name of spinner animation (see python -m rich.spinner). Defaults to "dots".
+ spinner_style (StyleType, optional): Style of spinner. Defaults to "status.spinner".
+ speed (float, optional): Speed factor for spinner animation. Defaults to 1.0.
+ refresh_per_second (float, optional): Number of refreshes per second. Defaults to 12.5.
+ """
+
+ def __init__(
+ self,
+ status: RenderableType,
+ *,
+ console: Optional[Console] = None,
+ spinner: str = "dots",
+ spinner_style: StyleType = "status.spinner",
+ speed: float = 1.0,
+ refresh_per_second: float = 12.5,
+ ):
+ self.status = status
+ self.spinner_style = spinner_style
+ self.speed = speed
+ self._spinner = Spinner(spinner, text=status, style=spinner_style, speed=speed)
+ self._live = Live(
+ self.renderable,
+ console=console,
+ refresh_per_second=refresh_per_second,
+ transient=True,
+ )
+
+ @property
+ def renderable(self) -> Spinner:
+ return self._spinner
+
+ @property
+ def console(self) -> "Console":
+ """Get the Console used by the Status objects."""
+ return self._live.console
+
+ def update(
+ self,
+ status: Optional[RenderableType] = None,
+ *,
+ spinner: Optional[str] = None,
+ spinner_style: Optional[StyleType] = None,
+ speed: Optional[float] = None,
+ ) -> None:
+ """Update status.
+
+ Args:
+ status (Optional[RenderableType], optional): New status renderable or None for no change. Defaults to None.
+ spinner (Optional[str], optional): New spinner or None for no change. Defaults to None.
+ spinner_style (Optional[StyleType], optional): New spinner style or None for no change. Defaults to None.
+ speed (Optional[float], optional): Speed factor for spinner animation or None for no change. Defaults to None.
+ """
+ if status is not None:
+ self.status = status
+ if spinner_style is not None:
+ self.spinner_style = spinner_style
+ if speed is not None:
+ self.speed = speed
+ if spinner is not None:
+ self._spinner = Spinner(
+ spinner, text=self.status, style=self.spinner_style, speed=self.speed
+ )
+ self._live.update(self.renderable, refresh=True)
+ else:
+ self._spinner.update(
+ text=self.status, style=self.spinner_style, speed=self.speed
+ )
+
+ def start(self) -> None:
+ """Start the status animation."""
+ self._live.start()
+
+ def stop(self) -> None:
+ """Stop the spinner animation."""
+ self._live.stop()
+
+ def __rich__(self) -> RenderableType:
+ return self.renderable
+
+ def __enter__(self) -> "Status":
+ self.start()
+ return self
+
+ def __exit__(
+ self,
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[TracebackType],
+ ) -> None:
+ self.stop()
+
+
+if __name__ == "__main__": # pragma: no cover
+
+ from time import sleep
+
+ from .console import Console
+
+ console = Console()
+ with console.status("[magenta]Covid detector booting up") as status:
+ sleep(3)
+ console.log("Importing advanced AI")
+ sleep(3)
+ console.log("Advanced Covid AI Ready")
+ sleep(3)
+ status.update(status="[bold blue] Scanning for Covid", spinner="earth")
+ sleep(3)
+ console.log("Found 10,000,000,000 copies of Covid32.exe")
+ sleep(3)
+ status.update(
+ status="[bold red]Moving Covid32.exe to Trash",
+ spinner="bouncingBall",
+ spinner_style="yellow",
+ )
+ sleep(5)
+ console.print("[bold green]Covid deleted successfully")
diff --git a/src/pip/_vendor/rich/style.py b/src/pip/_vendor/rich/style.py
new file mode 100644
index 000000000..0787c3314
--- /dev/null
+++ b/src/pip/_vendor/rich/style.py
@@ -0,0 +1,785 @@
+import sys
+from functools import lru_cache
+from marshal import loads, dumps
+from random import randint
+from typing import Any, cast, Dict, Iterable, List, Optional, Type, Union
+
+from . import errors
+from .color import Color, ColorParseError, ColorSystem, blend_rgb
+from .repr import rich_repr, Result
+from .terminal_theme import DEFAULT_TERMINAL_THEME, TerminalTheme
+
+
+# Style instances and style definitions are often interchangeable
+StyleType = Union[str, "Style"]
+
+
+class _Bit:
+ """A descriptor to get/set a style attribute bit."""
+
+ __slots__ = ["bit"]
+
+ def __init__(self, bit_no: int) -> None:
+ self.bit = 1 << bit_no
+
+ def __get__(self, obj: "Style", objtype: Type["Style"]) -> Optional[bool]:
+ if obj._set_attributes & self.bit:
+ return obj._attributes & self.bit != 0
+ return None
+
+
+@rich_repr
+class Style:
+ """A terminal style.
+
+ A terminal style consists of a color (`color`), a background color (`bgcolor`), and a number of attributes, such
+ as bold, italic etc. The attributes have 3 states: they can either be on
+ (``True``), off (``False``), or not set (``None``).
+
+ Args:
+ color (Union[Color, str], optional): Color of terminal text. Defaults to None.
+ bgcolor (Union[Color, str], optional): Color of terminal background. Defaults to None.
+ bold (bool, optional): Enable bold text. Defaults to None.
+ dim (bool, optional): Enable dim text. Defaults to None.
+ italic (bool, optional): Enable italic text. Defaults to None.
+ underline (bool, optional): Enable underlined text. Defaults to None.
+ blink (bool, optional): Enabled blinking text. Defaults to None.
+ blink2 (bool, optional): Enable fast blinking text. Defaults to None.
+ reverse (bool, optional): Enabled reverse text. Defaults to None.
+ conceal (bool, optional): Enable concealed text. Defaults to None.
+ strike (bool, optional): Enable strikethrough text. Defaults to None.
+ underline2 (bool, optional): Enable doubly underlined text. Defaults to None.
+ frame (bool, optional): Enable framed text. Defaults to None.
+ encircle (bool, optional): Enable encircled text. Defaults to None.
+ overline (bool, optional): Enable overlined text. Defaults to None.
+ link (str, link): Link URL. Defaults to None.
+
+ """
+
+ _color: Optional[Color]
+ _bgcolor: Optional[Color]
+ _attributes: int
+ _set_attributes: int
+ _hash: int
+ _null: bool
+ _meta: Optional[bytes]
+
+ __slots__ = [
+ "_color",
+ "_bgcolor",
+ "_attributes",
+ "_set_attributes",
+ "_link",
+ "_link_id",
+ "_ansi",
+ "_style_definition",
+ "_hash",
+ "_null",
+ "_meta",
+ ]
+
+ # maps bits on to SGR parameter
+ _style_map = {
+ 0: "1",
+ 1: "2",
+ 2: "3",
+ 3: "4",
+ 4: "5",
+ 5: "6",
+ 6: "7",
+ 7: "8",
+ 8: "9",
+ 9: "21",
+ 10: "51",
+ 11: "52",
+ 12: "53",
+ }
+
+ STYLE_ATTRIBUTES = {
+ "dim": "dim",
+ "d": "dim",
+ "bold": "bold",
+ "b": "bold",
+ "italic": "italic",
+ "i": "italic",
+ "underline": "underline",
+ "u": "underline",
+ "blink": "blink",
+ "blink2": "blink2",
+ "reverse": "reverse",
+ "r": "reverse",
+ "conceal": "conceal",
+ "c": "conceal",
+ "strike": "strike",
+ "s": "strike",
+ "underline2": "underline2",
+ "uu": "underline2",
+ "frame": "frame",
+ "encircle": "encircle",
+ "overline": "overline",
+ "o": "overline",
+ }
+
+ def __init__(
+ self,
+ *,
+ color: Optional[Union[Color, str]] = None,
+ bgcolor: Optional[Union[Color, str]] = None,
+ bold: Optional[bool] = None,
+ dim: Optional[bool] = None,
+ italic: Optional[bool] = None,
+ underline: Optional[bool] = None,
+ blink: Optional[bool] = None,
+ blink2: Optional[bool] = None,
+ reverse: Optional[bool] = None,
+ conceal: Optional[bool] = None,
+ strike: Optional[bool] = None,
+ underline2: Optional[bool] = None,
+ frame: Optional[bool] = None,
+ encircle: Optional[bool] = None,
+ overline: Optional[bool] = None,
+ link: Optional[str] = None,
+ meta: Optional[Dict[str, Any]] = None,
+ ):
+ self._ansi: Optional[str] = None
+ self._style_definition: Optional[str] = None
+
+ def _make_color(color: Union[Color, str]) -> Color:
+ return color if isinstance(color, Color) else Color.parse(color)
+
+ self._color = None if color is None else _make_color(color)
+ self._bgcolor = None if bgcolor is None else _make_color(bgcolor)
+ self._set_attributes = sum(
+ (
+ bold is not None,
+ dim is not None and 2,
+ italic is not None and 4,
+ underline is not None and 8,
+ blink is not None and 16,
+ blink2 is not None and 32,
+ reverse is not None and 64,
+ conceal is not None and 128,
+ strike is not None and 256,
+ underline2 is not None and 512,
+ frame is not None and 1024,
+ encircle is not None and 2048,
+ overline is not None and 4096,
+ )
+ )
+ self._attributes = (
+ sum(
+ (
+ bold and 1 or 0,
+ dim and 2 or 0,
+ italic and 4 or 0,
+ underline and 8 or 0,
+ blink and 16 or 0,
+ blink2 and 32 or 0,
+ reverse and 64 or 0,
+ conceal and 128 or 0,
+ strike and 256 or 0,
+ underline2 and 512 or 0,
+ frame and 1024 or 0,
+ encircle and 2048 or 0,
+ overline and 4096 or 0,
+ )
+ )
+ if self._set_attributes
+ else 0
+ )
+
+ self._link = link
+ self._link_id = f"{randint(0, 999999)}" if link else ""
+ self._meta = None if meta is None else dumps(meta)
+ self._hash = hash(
+ (
+ self._color,
+ self._bgcolor,
+ self._attributes,
+ self._set_attributes,
+ link,
+ self._meta,
+ )
+ )
+ self._null = not (self._set_attributes or color or bgcolor or link or meta)
+
+ @classmethod
+ def null(cls) -> "Style":
+ """Create an 'null' style, equivalent to Style(), but more performant."""
+ return NULL_STYLE
+
+ @classmethod
+ def from_color(
+ cls, color: Optional[Color] = None, bgcolor: Optional[Color] = None
+ ) -> "Style":
+ """Create a new style with colors and no attributes.
+
+ Returns:
+ color (Optional[Color]): A (foreground) color, or None for no color. Defaults to None.
+ bgcolor (Optional[Color]): A (background) color, or None for no color. Defaults to None.
+ """
+ style: Style = cls.__new__(Style)
+ style._ansi = None
+ style._style_definition = None
+ style._color = color
+ style._bgcolor = bgcolor
+ style._set_attributes = 0
+ style._attributes = 0
+ style._link = None
+ style._link_id = ""
+ style._meta = None
+ style._hash = hash(
+ (
+ color,
+ bgcolor,
+ None,
+ None,
+ None,
+ None,
+ )
+ )
+ style._null = not (color or bgcolor)
+ return style
+
+ @classmethod
+ def from_meta(cls, meta: Optional[Dict[str, Any]]) -> "Style":
+ """Create a new style with meta data.
+
+ Returns:
+ meta (Optional[Dict[str, Any]]): A dictionary of meta data. Defaults to None.
+ """
+ style: Style = cls.__new__(Style)
+ style._ansi = None
+ style._style_definition = None
+ style._color = None
+ style._bgcolor = None
+ style._set_attributes = 0
+ style._attributes = 0
+ style._link = None
+ style._link_id = ""
+ style._meta = dumps(meta)
+ style._hash = hash(
+ (
+ None,
+ None,
+ None,
+ None,
+ None,
+ style._meta,
+ )
+ )
+ style._null = not (meta)
+ return style
+
+ @classmethod
+ def on(cls, meta: Optional[Dict[str, Any]] = None, **handlers: Any) -> "Style":
+ """Create a blank style with meta information.
+
+ Example:
+ style = Style.on(click=self.on_click)
+
+ Args:
+ meta (Optiona[Dict[str, Any]], optional): An optional dict of meta information.
+ **handlers (Any): Keyword arguments are translated in to handlers.
+
+ Returns:
+ Style: A Style with meta information attached.
+ """
+ meta = {} if meta is None else meta
+ meta.update({f"@{key}": value for key, value in handlers.items()})
+ return cls.from_meta(meta)
+
+ bold = _Bit(0)
+ dim = _Bit(1)
+ italic = _Bit(2)
+ underline = _Bit(3)
+ blink = _Bit(4)
+ blink2 = _Bit(5)
+ reverse = _Bit(6)
+ conceal = _Bit(7)
+ strike = _Bit(8)
+ underline2 = _Bit(9)
+ frame = _Bit(10)
+ encircle = _Bit(11)
+ overline = _Bit(12)
+
+ @property
+ def link_id(self) -> str:
+ """Get a link id, used in ansi code for links."""
+ return self._link_id
+
+ def __str__(self) -> str:
+ """Re-generate style definition from attributes."""
+ if self._style_definition is None:
+ attributes: List[str] = []
+ append = attributes.append
+ bits = self._set_attributes
+ if bits & 0b0000000001111:
+ if bits & 1:
+ append("bold" if self.bold else "not bold")
+ if bits & (1 << 1):
+ append("dim" if self.dim else "not dim")
+ if bits & (1 << 2):
+ append("italic" if self.italic else "not italic")
+ if bits & (1 << 3):
+ append("underline" if self.underline else "not underline")
+ if bits & 0b0000111110000:
+ if bits & (1 << 4):
+ append("blink" if self.blink else "not blink")
+ if bits & (1 << 5):
+ append("blink2" if self.blink2 else "not blink2")
+ if bits & (1 << 6):
+ append("reverse" if self.reverse else "not reverse")
+ if bits & (1 << 7):
+ append("conceal" if self.conceal else "not conceal")
+ if bits & (1 << 8):
+ append("strike" if self.strike else "not strike")
+ if bits & 0b1111000000000:
+ if bits & (1 << 9):
+ append("underline2" if self.underline2 else "not underline2")
+ if bits & (1 << 10):
+ append("frame" if self.frame else "not frame")
+ if bits & (1 << 11):
+ append("encircle" if self.encircle else "not encircle")
+ if bits & (1 << 12):
+ append("overline" if self.overline else "not overline")
+ if self._color is not None:
+ append(self._color.name)
+ if self._bgcolor is not None:
+ append("on")
+ append(self._bgcolor.name)
+ if self._link:
+ append("link")
+ append(self._link)
+ self._style_definition = " ".join(attributes) or "none"
+ return self._style_definition
+
+ def __bool__(self) -> bool:
+ """A Style is false if it has no attributes, colors, or links."""
+ return not self._null
+
+ def _make_ansi_codes(self, color_system: ColorSystem) -> str:
+ """Generate ANSI codes for this style.
+
+ Args:
+ color_system (ColorSystem): Color system.
+
+ Returns:
+ str: String containing codes.
+ """
+ if self._ansi is None:
+ sgr: List[str] = []
+ append = sgr.append
+ _style_map = self._style_map
+ attributes = self._attributes & self._set_attributes
+ if attributes:
+ if attributes & 1:
+ append(_style_map[0])
+ if attributes & 2:
+ append(_style_map[1])
+ if attributes & 4:
+ append(_style_map[2])
+ if attributes & 8:
+ append(_style_map[3])
+ if attributes & 0b0000111110000:
+ for bit in range(4, 9):
+ if attributes & (1 << bit):
+ append(_style_map[bit])
+ if attributes & 0b1111000000000:
+ for bit in range(9, 13):
+ if attributes & (1 << bit):
+ append(_style_map[bit])
+ if self._color is not None:
+ sgr.extend(self._color.downgrade(color_system).get_ansi_codes())
+ if self._bgcolor is not None:
+ sgr.extend(
+ self._bgcolor.downgrade(color_system).get_ansi_codes(
+ foreground=False
+ )
+ )
+ self._ansi = ";".join(sgr)
+ return self._ansi
+
+ @classmethod
+ @lru_cache(maxsize=1024)
+ def normalize(cls, style: str) -> str:
+ """Normalize a style definition so that styles with the same effect have the same string
+ representation.
+
+ Args:
+ style (str): A style definition.
+
+ Returns:
+ str: Normal form of style definition.
+ """
+ try:
+ return str(cls.parse(style))
+ except errors.StyleSyntaxError:
+ return style.strip().lower()
+
+ @classmethod
+ def pick_first(cls, *values: Optional[StyleType]) -> StyleType:
+ """Pick first non-None style."""
+ for value in values:
+ if value is not None:
+ return value
+ raise ValueError("expected at least one non-None style")
+
+ def __rich_repr__(self) -> Result:
+ yield "color", self.color, None
+ yield "bgcolor", self.bgcolor, None
+ yield "bold", self.bold, None,
+ yield "dim", self.dim, None,
+ yield "italic", self.italic, None
+ yield "underline", self.underline, None,
+ yield "blink", self.blink, None
+ yield "blink2", self.blink2, None
+ yield "reverse", self.reverse, None
+ yield "conceal", self.conceal, None
+ yield "strike", self.strike, None
+ yield "underline2", self.underline2, None
+ yield "frame", self.frame, None
+ yield "encircle", self.encircle, None
+ yield "link", self.link, None
+ if self._meta:
+ yield "meta", self.meta
+
+ def __eq__(self, other: Any) -> bool:
+ if not isinstance(other, Style):
+ return NotImplemented
+ return (
+ self._color == other._color
+ and self._bgcolor == other._bgcolor
+ and self._set_attributes == other._set_attributes
+ and self._attributes == other._attributes
+ and self._link == other._link
+ and self._meta == other._meta
+ )
+
+ def __hash__(self) -> int:
+ return self._hash
+
+ @property
+ def color(self) -> Optional[Color]:
+ """The foreground color or None if it is not set."""
+ return self._color
+
+ @property
+ def bgcolor(self) -> Optional[Color]:
+ """The background color or None if it is not set."""
+ return self._bgcolor
+
+ @property
+ def link(self) -> Optional[str]:
+ """Link text, if set."""
+ return self._link
+
+ @property
+ def transparent_background(self) -> bool:
+ """Check if the style specified a transparent background."""
+ return self.bgcolor is None or self.bgcolor.is_default
+
+ @property
+ def background_style(self) -> "Style":
+ """A Style with background only."""
+ return Style(bgcolor=self.bgcolor)
+
+ @property
+ def meta(self) -> Dict[str, Any]:
+ """Get meta information (can not be changed after construction)."""
+ return {} if self._meta is None else cast(Dict[str, Any], loads(self._meta))
+
+ @property
+ def without_color(self) -> "Style":
+ """Get a copy of the style with color removed."""
+ if self._null:
+ return NULL_STYLE
+ style: Style = self.__new__(Style)
+ style._ansi = None
+ style._style_definition = None
+ style._color = None
+ style._bgcolor = None
+ style._attributes = self._attributes
+ style._set_attributes = self._set_attributes
+ style._link = self._link
+ style._link_id = f"{randint(0, 999999)}" if self._link else ""
+ style._hash = self._hash
+ style._null = False
+ style._meta = None
+ return style
+
+ @classmethod
+ @lru_cache(maxsize=4096)
+ def parse(cls, style_definition: str) -> "Style":
+ """Parse a style definition.
+
+ Args:
+ style_definition (str): A string containing a style.
+
+ Raises:
+ errors.StyleSyntaxError: If the style definition syntax is invalid.
+
+ Returns:
+ `Style`: A Style instance.
+ """
+ if style_definition.strip() == "none" or not style_definition:
+ return cls.null()
+
+ STYLE_ATTRIBUTES = cls.STYLE_ATTRIBUTES
+ color: Optional[str] = None
+ bgcolor: Optional[str] = None
+ attributes: Dict[str, Optional[Any]] = {}
+ link: Optional[str] = None
+
+ words = iter(style_definition.split())
+ for original_word in words:
+ word = original_word.lower()
+ if word == "on":
+ word = next(words, "")
+ if not word:
+ raise errors.StyleSyntaxError("color expected after 'on'")
+ try:
+ Color.parse(word) is None
+ except ColorParseError as error:
+ raise errors.StyleSyntaxError(
+ f"unable to parse {word!r} as background color; {error}"
+ ) from None
+ bgcolor = word
+
+ elif word == "not":
+ word = next(words, "")
+ attribute = STYLE_ATTRIBUTES.get(word)
+ if attribute is None:
+ raise errors.StyleSyntaxError(
+ f"expected style attribute after 'not', found {word!r}"
+ )
+ attributes[attribute] = False
+
+ elif word == "link":
+ word = next(words, "")
+ if not word:
+ raise errors.StyleSyntaxError("URL expected after 'link'")
+ link = word
+
+ elif word in STYLE_ATTRIBUTES:
+ attributes[STYLE_ATTRIBUTES[word]] = True
+
+ else:
+ try:
+ Color.parse(word)
+ except ColorParseError as error:
+ raise errors.StyleSyntaxError(
+ f"unable to parse {word!r} as color; {error}"
+ ) from None
+ color = word
+ style = Style(color=color, bgcolor=bgcolor, link=link, **attributes)
+ return style
+
+ @lru_cache(maxsize=1024)
+ def get_html_style(self, theme: Optional[TerminalTheme] = None) -> str:
+ """Get a CSS style rule."""
+ theme = theme or DEFAULT_TERMINAL_THEME
+ css: List[str] = []
+ append = css.append
+
+ color = self.color
+ bgcolor = self.bgcolor
+ if self.reverse:
+ color, bgcolor = bgcolor, color
+ if self.dim:
+ foreground_color = (
+ theme.foreground_color if color is None else color.get_truecolor(theme)
+ )
+ color = Color.from_triplet(
+ blend_rgb(foreground_color, theme.background_color, 0.5)
+ )
+ if color is not None:
+ theme_color = color.get_truecolor(theme)
+ append(f"color: {theme_color.hex}")
+ append(f"text-decoration-color: {theme_color.hex}")
+ if bgcolor is not None:
+ theme_color = bgcolor.get_truecolor(theme, foreground=False)
+ append(f"background-color: {theme_color.hex}")
+ if self.bold:
+ append("font-weight: bold")
+ if self.italic:
+ append("font-style: italic")
+ if self.underline:
+ append("text-decoration: underline")
+ if self.strike:
+ append("text-decoration: line-through")
+ if self.overline:
+ append("text-decoration: overline")
+ return "; ".join(css)
+
+ @classmethod
+ def combine(cls, styles: Iterable["Style"]) -> "Style":
+ """Combine styles and get result.
+
+ Args:
+ styles (Iterable[Style]): Styles to combine.
+
+ Returns:
+ Style: A new style instance.
+ """
+ iter_styles = iter(styles)
+ return sum(iter_styles, next(iter_styles))
+
+ @classmethod
+ def chain(cls, *styles: "Style") -> "Style":
+ """Combine styles from positional argument in to a single style.
+
+ Args:
+ *styles (Iterable[Style]): Styles to combine.
+
+ Returns:
+ Style: A new style instance.
+ """
+ iter_styles = iter(styles)
+ return sum(iter_styles, next(iter_styles))
+
+ def copy(self) -> "Style":
+ """Get a copy of this style.
+
+ Returns:
+ Style: A new Style instance with identical attributes.
+ """
+ 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 = self._link
+ style._link_id = f"{randint(0, 999999)}" if self._link else ""
+ style._hash = self._hash
+ style._null = False
+ style._meta = self._meta
+ return style
+
+ def update_link(self, link: Optional[str] = None) -> "Style":
+ """Get a copy with a different value for link.
+
+ Args:
+ link (str, optional): New value for link. Defaults to None.
+
+ Returns:
+ Style: A new Style instance.
+ """
+ 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 = link
+ style._link_id = f"{randint(0, 999999)}" if link else ""
+ style._hash = self._hash
+ style._null = False
+ style._meta = self._meta
+ return style
+
+ def render(
+ self,
+ text: str = "",
+ *,
+ color_system: Optional[ColorSystem] = ColorSystem.TRUECOLOR,
+ legacy_windows: bool = False,
+ ) -> str:
+ """Render the ANSI codes for the style.
+
+ Args:
+ text (str, optional): A string to style. Defaults to "".
+ color_system (Optional[ColorSystem], optional): Color system to render to. Defaults to ColorSystem.TRUECOLOR.
+
+ Returns:
+ str: A string containing ANSI style codes.
+ """
+ if not text or color_system is None:
+ return text
+ attrs = self._make_ansi_codes(color_system)
+ rendered = f"\x1b[{attrs}m{text}\x1b[0m" if attrs else text
+ if self._link and not legacy_windows:
+ rendered = (
+ f"\x1b]8;id={self._link_id};{self._link}\x1b\\{rendered}\x1b]8;;\x1b\\"
+ )
+ return rendered
+
+ def test(self, text: Optional[str] = None) -> None:
+ """Write text with style directly to terminal.
+
+ This method is for testing purposes only.
+
+ Args:
+ text (Optional[str], optional): Text to style or None for style name.
+
+ """
+ text = text or str(self)
+ sys.stdout.write(f"{self.render(text)}\n")
+
+ def __add__(self, style: Optional["Style"]) -> "Style":
+ if not (isinstance(style, Style) or style is None):
+ return NotImplemented
+ if style is None or style._null:
+ return self
+ if self._null:
+ return style
+ new_style: Style = self.__new__(Style)
+ new_style._ansi = None
+ new_style._style_definition = None
+ new_style._color = style._color or self._color
+ new_style._bgcolor = style._bgcolor or self._bgcolor
+ new_style._attributes = (self._attributes & ~style._set_attributes) | (
+ style._attributes & style._set_attributes
+ )
+ new_style._set_attributes = self._set_attributes | style._set_attributes
+ new_style._link = style._link or self._link
+ new_style._link_id = style._link_id or self._link_id
+ new_style._hash = style._hash
+ new_style._null = self._null or style._null
+ if self._meta and style._meta:
+ new_style._meta = dumps({**self.meta, **style.meta})
+ else:
+ new_style._meta = self._meta or style._meta
+ return new_style
+
+
+NULL_STYLE = Style()
+
+
+class StyleStack:
+ """A stack of styles."""
+
+ __slots__ = ["_stack"]
+
+ def __init__(self, default_style: "Style") -> None:
+ self._stack: List[Style] = [default_style]
+
+ def __repr__(self) -> str:
+ return f"<stylestack {self._stack!r}>"
+
+ @property
+ def current(self) -> Style:
+ """Get the Style at the top of the stack."""
+ return self._stack[-1]
+
+ def push(self, style: Style) -> None:
+ """Push a new style on to the stack.
+
+ Args:
+ style (Style): New style to combine with current style.
+ """
+ self._stack.append(self._stack[-1] + style)
+
+ def pop(self) -> Style:
+ """Pop last style and discard.
+
+ Returns:
+ Style: New current style (also available as stack.current)
+ """
+ self._stack.pop()
+ return self._stack[-1]
diff --git a/src/pip/_vendor/rich/styled.py b/src/pip/_vendor/rich/styled.py
new file mode 100644
index 000000000..91cd0db31
--- /dev/null
+++ b/src/pip/_vendor/rich/styled.py
@@ -0,0 +1,42 @@
+from typing import TYPE_CHECKING
+
+from .measure import Measurement
+from .segment import Segment
+from .style import StyleType
+
+if TYPE_CHECKING:
+ from .console import Console, ConsoleOptions, RenderResult, RenderableType
+
+
+class Styled:
+ """Apply a style to a renderable.
+
+ Args:
+ renderable (RenderableType): Any renderable.
+ style (StyleType): A style to apply across the entire renderable.
+ """
+
+ def __init__(self, renderable: "RenderableType", style: "StyleType") -> None:
+ self.renderable = renderable
+ self.style = style
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+ style = console.get_style(self.style)
+ rendered_segments = console.render(self.renderable, options)
+ segments = Segment.apply_style(rendered_segments, style)
+ return segments
+
+ def __rich_measure__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> Measurement:
+ return Measurement.get(console, options, self.renderable)
+
+
+if __name__ == "__main__": # pragma: no cover
+ from pip._vendor.rich import print
+ from pip._vendor.rich.panel import Panel
+
+ panel = Styled(Panel("hello"), "on blue")
+ print(panel)
diff --git a/src/pip/_vendor/rich/syntax.py b/src/pip/_vendor/rich/syntax.py
new file mode 100644
index 000000000..254963e91
--- /dev/null
+++ b/src/pip/_vendor/rich/syntax.py
@@ -0,0 +1,717 @@
+import os.path
+import platform
+from pip._vendor.rich.containers import Lines
+import textwrap
+from abc import ABC, abstractmethod
+from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type, Union
+
+from pip._vendor.pygments.lexers import get_lexer_by_name, guess_lexer_for_filename
+from pip._vendor.pygments.style import Style as PygmentsStyle
+from pip._vendor.pygments.styles import get_style_by_name
+from pip._vendor.pygments.token import (
+ Comment,
+ Error,
+ Generic,
+ Keyword,
+ Name,
+ Number,
+ Operator,
+ String,
+ Token,
+ Whitespace,
+)
+from pip._vendor.pygments.util import ClassNotFound
+
+from ._loop import loop_first
+from .color import Color, blend_rgb
+from .console import Console, ConsoleOptions, JustifyMethod, RenderResult
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .segment import Segment
+from .style import Style
+from .text import Text
+
+TokenType = Tuple[str, ...]
+
+WINDOWS = platform.system() == "Windows"
+DEFAULT_THEME = "monokai"
+
+# The following styles are based on https://github.com/pygments/pygments/blob/master/pygments/formatters/terminal.py
+# A few modifications were made
+
+ANSI_LIGHT: Dict[TokenType, Style] = {
+ Token: Style(),
+ Whitespace: Style(color="white"),
+ Comment: Style(dim=True),
+ Comment.Preproc: Style(color="cyan"),
+ Keyword: Style(color="blue"),
+ Keyword.Type: Style(color="cyan"),
+ Operator.Word: Style(color="magenta"),
+ Name.Builtin: Style(color="cyan"),
+ Name.Function: Style(color="green"),
+ Name.Namespace: Style(color="cyan", underline=True),
+ Name.Class: Style(color="green", underline=True),
+ Name.Exception: Style(color="cyan"),
+ Name.Decorator: Style(color="magenta", bold=True),
+ Name.Variable: Style(color="red"),
+ Name.Constant: Style(color="red"),
+ Name.Attribute: Style(color="cyan"),
+ Name.Tag: Style(color="bright_blue"),
+ String: Style(color="yellow"),
+ Number: Style(color="blue"),
+ Generic.Deleted: Style(color="bright_red"),
+ Generic.Inserted: Style(color="green"),
+ Generic.Heading: Style(bold=True),
+ Generic.Subheading: Style(color="magenta", bold=True),
+ Generic.Prompt: Style(bold=True),
+ Generic.Error: Style(color="bright_red"),
+ Error: Style(color="red", underline=True),
+}
+
+ANSI_DARK: Dict[TokenType, Style] = {
+ Token: Style(),
+ Whitespace: Style(color="bright_black"),
+ Comment: Style(dim=True),
+ Comment.Preproc: Style(color="bright_cyan"),
+ Keyword: Style(color="bright_blue"),
+ Keyword.Type: Style(color="bright_cyan"),
+ Operator.Word: Style(color="bright_magenta"),
+ Name.Builtin: Style(color="bright_cyan"),
+ Name.Function: Style(color="bright_green"),
+ Name.Namespace: Style(color="bright_cyan", underline=True),
+ Name.Class: Style(color="bright_green", underline=True),
+ Name.Exception: Style(color="bright_cyan"),
+ Name.Decorator: Style(color="bright_magenta", bold=True),
+ Name.Variable: Style(color="bright_red"),
+ Name.Constant: Style(color="bright_red"),
+ Name.Attribute: Style(color="bright_cyan"),
+ Name.Tag: Style(color="bright_blue"),
+ String: Style(color="yellow"),
+ Number: Style(color="bright_blue"),
+ Generic.Deleted: Style(color="bright_red"),
+ Generic.Inserted: Style(color="bright_green"),
+ Generic.Heading: Style(bold=True),
+ Generic.Subheading: Style(color="bright_magenta", bold=True),
+ Generic.Prompt: Style(bold=True),
+ Generic.Error: Style(color="bright_red"),
+ Error: Style(color="red", underline=True),
+}
+
+RICH_SYNTAX_THEMES = {"ansi_light": ANSI_LIGHT, "ansi_dark": ANSI_DARK}
+
+
+class SyntaxTheme(ABC):
+ """Base class for a syntax theme."""
+
+ @abstractmethod
+ def get_style_for_token(self, token_type: TokenType) -> Style:
+ """Get a style for a given Pygments token."""
+ raise NotImplementedError # pragma: no cover
+
+ @abstractmethod
+ def get_background_style(self) -> Style:
+ """Get the background color."""
+ raise NotImplementedError # pragma: no cover
+
+
+class PygmentsSyntaxTheme(SyntaxTheme):
+ """Syntax theme that delegates to Pygments theme."""
+
+ def __init__(self, theme: Union[str, Type[PygmentsStyle]]) -> None:
+ self._style_cache: Dict[TokenType, Style] = {}
+ if isinstance(theme, str):
+ try:
+ self._pygments_style_class = get_style_by_name(theme)
+ except ClassNotFound:
+ self._pygments_style_class = get_style_by_name("default")
+ else:
+ self._pygments_style_class = theme
+
+ self._background_color = self._pygments_style_class.background_color
+ self._background_style = Style(bgcolor=self._background_color)
+
+ def get_style_for_token(self, token_type: TokenType) -> Style:
+ """Get a style from a Pygments class."""
+ try:
+ return self._style_cache[token_type]
+ except KeyError:
+ try:
+ pygments_style = self._pygments_style_class.style_for_token(token_type)
+ except KeyError:
+ style = Style.null()
+ else:
+ color = pygments_style["color"]
+ bgcolor = pygments_style["bgcolor"]
+ style = Style(
+ color="#" + color if color else "#000000",
+ bgcolor="#" + bgcolor if bgcolor else self._background_color,
+ bold=pygments_style["bold"],
+ italic=pygments_style["italic"],
+ underline=pygments_style["underline"],
+ )
+ self._style_cache[token_type] = style
+ return style
+
+ def get_background_style(self) -> Style:
+ return self._background_style
+
+
+class ANSISyntaxTheme(SyntaxTheme):
+ """Syntax theme to use standard colors."""
+
+ def __init__(self, style_map: Dict[TokenType, Style]) -> None:
+ self.style_map = style_map
+ self._missing_style = Style.null()
+ self._background_style = Style.null()
+ self._style_cache: Dict[TokenType, Style] = {}
+
+ def get_style_for_token(self, token_type: TokenType) -> Style:
+ """Look up style in the style map."""
+ try:
+ return self._style_cache[token_type]
+ except KeyError:
+ # Styles form a hierarchy
+ # We need to go from most to least specific
+ # e.g. ("foo", "bar", "baz") to ("foo", "bar") to ("foo",)
+ get_style = self.style_map.get
+ token = tuple(token_type)
+ style = self._missing_style
+ while token:
+ _style = get_style(token)
+ if _style is not None:
+ style = _style
+ break
+ token = token[:-1]
+ self._style_cache[token_type] = style
+ return style
+
+ def get_background_style(self) -> Style:
+ return self._background_style
+
+
+class Syntax(JupyterMixin):
+ """Construct a Syntax object to render syntax highlighted code.
+
+ Args:
+ code (str): Code to highlight.
+ lexer_name (str): Lexer to use (see https://pygments.org/docs/lexers/)
+ theme (str, optional): Color theme, aka Pygments style (see https://pygments.org/docs/styles/#getting-a-list-of-available-styles). Defaults to "monokai".
+ dedent (bool, optional): Enable stripping of initial whitespace. Defaults to False.
+ line_numbers (bool, optional): Enable rendering of line numbers. Defaults to False.
+ start_line (int, optional): Starting number for line numbers. Defaults to 1.
+ line_range (Tuple[int, int], optional): If given should be a tuple of the start and end line to render.
+ highlight_lines (Set[int]): A set of line numbers to highlight.
+ code_width: Width of code to render (not including line numbers), or ``None`` to use all available width.
+ tab_size (int, optional): Size of tabs. Defaults to 4.
+ word_wrap (bool, optional): Enable word wrapping.
+ background_color (str, optional): Optional background color, or None to use theme color. Defaults to None.
+ indent_guides (bool, optional): Show indent guides. Defaults to False.
+ """
+
+ _pygments_style_class: Type[PygmentsStyle]
+ _theme: SyntaxTheme
+
+ @classmethod
+ def get_theme(cls, name: Union[str, SyntaxTheme]) -> SyntaxTheme:
+ """Get a syntax theme instance."""
+ if isinstance(name, SyntaxTheme):
+ return name
+ theme: SyntaxTheme
+ if name in RICH_SYNTAX_THEMES:
+ theme = ANSISyntaxTheme(RICH_SYNTAX_THEMES[name])
+ else:
+ theme = PygmentsSyntaxTheme(name)
+ return theme
+
+ def __init__(
+ self,
+ code: str,
+ lexer_name: str,
+ *,
+ theme: Union[str, SyntaxTheme] = DEFAULT_THEME,
+ dedent: bool = False,
+ line_numbers: bool = False,
+ start_line: int = 1,
+ line_range: Optional[Tuple[int, int]] = None,
+ highlight_lines: Optional[Set[int]] = None,
+ code_width: Optional[int] = None,
+ tab_size: int = 4,
+ word_wrap: bool = False,
+ background_color: Optional[str] = None,
+ indent_guides: bool = False,
+ ) -> None:
+ self.code = code
+ self.lexer_name = lexer_name
+ self.dedent = dedent
+ self.line_numbers = line_numbers
+ self.start_line = start_line
+ self.line_range = line_range
+ self.highlight_lines = highlight_lines or set()
+ self.code_width = code_width
+ self.tab_size = tab_size
+ self.word_wrap = word_wrap
+ self.background_color = background_color
+ self.background_style = (
+ Style(bgcolor=background_color) if background_color else Style()
+ )
+ self.indent_guides = indent_guides
+
+ self._theme = self.get_theme(theme)
+
+ @classmethod
+ def from_path(
+ cls,
+ path: str,
+ encoding: str = "utf-8",
+ theme: Union[str, SyntaxTheme] = DEFAULT_THEME,
+ dedent: bool = False,
+ line_numbers: bool = False,
+ line_range: Optional[Tuple[int, int]] = None,
+ start_line: int = 1,
+ highlight_lines: Optional[Set[int]] = None,
+ code_width: Optional[int] = None,
+ tab_size: int = 4,
+ word_wrap: bool = False,
+ background_color: Optional[str] = None,
+ indent_guides: bool = False,
+ ) -> "Syntax":
+ """Construct a Syntax object from a file.
+
+ Args:
+ path (str): Path to file to highlight.
+ encoding (str): Encoding of file.
+ theme (str, optional): Color theme, aka Pygments style (see https://pygments.org/docs/styles/#getting-a-list-of-available-styles). Defaults to "emacs".
+ dedent (bool, optional): Enable stripping of initial whitespace. Defaults to True.
+ line_numbers (bool, optional): Enable rendering of line numbers. Defaults to False.
+ start_line (int, optional): Starting number for line numbers. Defaults to 1.
+ line_range (Tuple[int, int], optional): If given should be a tuple of the start and end line to render.
+ highlight_lines (Set[int]): A set of line numbers to highlight.
+ code_width: Width of code to render (not including line numbers), or ``None`` to use all available width.
+ tab_size (int, optional): Size of tabs. Defaults to 4.
+ word_wrap (bool, optional): Enable word wrapping of code.
+ background_color (str, optional): Optional background color, or None to use theme color. Defaults to None.
+ indent_guides (bool, optional): Show indent guides. Defaults to False.
+
+ 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()
+
+ lexer = None
+ lexer_name = "default"
+ try:
+ _, ext = os.path.splitext(path)
+ if ext:
+ extension = ext.lstrip(".").lower()
+ lexer = get_lexer_by_name(extension)
+ lexer_name = lexer.name
+ except ClassNotFound:
+ pass
+
+ if lexer is None:
+ try:
+ lexer_name = guess_lexer_for_filename(path, code).name
+ except ClassNotFound:
+ pass
+
+ return cls(
+ code,
+ lexer_name,
+ theme=theme,
+ dedent=dedent,
+ line_numbers=line_numbers,
+ line_range=line_range,
+ start_line=start_line,
+ highlight_lines=highlight_lines,
+ code_width=code_width,
+ tab_size=tab_size,
+ word_wrap=word_wrap,
+ background_color=background_color,
+ indent_guides=indent_guides,
+ )
+
+ def _get_base_style(self) -> Style:
+ """Get the base style."""
+ default_style = self._theme.get_background_style() + self.background_style
+ return default_style
+
+ def _get_token_color(self, token_type: TokenType) -> Optional[Color]:
+ """Get a color (if any) for the given token.
+
+ Args:
+ token_type (TokenType): A token type tuple from Pygments.
+
+ Returns:
+ Optional[Color]: Color from theme, or None for no color.
+ """
+ style = self._theme.get_style_for_token(token_type)
+ return style.color
+
+ def highlight(
+ self, code: str, line_range: Optional[Tuple[int, int]] = None
+ ) -> Text:
+ """Highlight code and return a Text instance.
+
+ Args:
+ code (str): Code to highlight.
+ line_range(Tuple[int, int], optional): Optional line range to highlight.
+
+ Returns:
+ Text: A text instance containing highlighted syntax.
+ """
+
+ base_style = self._get_base_style()
+ justify: JustifyMethod = (
+ "default" if base_style.transparent_background else "left"
+ )
+
+ text = Text(
+ justify=justify,
+ style=base_style,
+ tab_size=self.tab_size,
+ no_wrap=not self.word_wrap,
+ )
+ _get_theme_style = self._theme.get_style_for_token
+ try:
+ lexer = get_lexer_by_name(
+ self.lexer_name,
+ stripnl=False,
+ ensurenl=True,
+ tabsize=self.tab_size,
+ )
+ except ClassNotFound:
+ text.append(code)
+ else:
+ if line_range:
+ # More complicated path to only stylize a portion of the code
+ # This speeds up further operations as there are less spans to process
+ line_start, line_end = line_range
+
+ def line_tokenize() -> Iterable[Tuple[Any, str]]:
+ """Split tokens to one per line."""
+ for token_type, token in lexer.get_tokens(code):
+ while token:
+ line_token, new_line, token = token.partition("\n")
+ yield token_type, line_token + new_line
+
+ def tokens_to_spans() -> Iterable[Tuple[str, Optional[Style]]]:
+ """Convert tokens to spans."""
+ tokens = iter(line_tokenize())
+ line_no = 0
+ _line_start = line_start - 1
+
+ # Skip over tokens until line start
+ while line_no < _line_start:
+ _token_type, token = next(tokens)
+ yield (token, None)
+ if token.endswith("\n"):
+ line_no += 1
+ # Generate spans until line end
+ for token_type, token in tokens:
+ yield (token, _get_theme_style(token_type))
+ if token.endswith("\n"):
+ line_no += 1
+ if line_no >= line_end:
+ break
+
+ text.append_tokens(tokens_to_spans())
+
+ else:
+ text.append_tokens(
+ (token, _get_theme_style(token_type))
+ for token_type, token in lexer.get_tokens(code)
+ )
+ if self.background_color is not None:
+ text.stylize(f"on {self.background_color}")
+ return text
+
+ def _get_line_numbers_color(self, blend: float = 0.3) -> Color:
+ background_style = self._theme.get_background_style() + self.background_style
+ background_color = background_style.bgcolor
+ if background_color is None or background_color.is_system_defined:
+ return Color.default()
+ foreground_color = self._get_token_color(Token.Text)
+ if foreground_color is None or foreground_color.is_system_defined:
+ return foreground_color or Color.default()
+ new_color = blend_rgb(
+ background_color.get_truecolor(),
+ foreground_color.get_truecolor(),
+ cross_fade=blend,
+ )
+ return Color.from_triplet(new_color)
+
+ @property
+ def _numbers_column_width(self) -> int:
+ """Get the number of characters used to render the numbers column."""
+ column_width = 0
+ if self.line_numbers:
+ column_width = len(str(self.start_line + self.code.count("\n"))) + 2
+ return column_width
+
+ def _get_number_styles(self, console: Console) -> Tuple[Style, Style, Style]:
+ """Get background, number, and highlight styles for line numbers."""
+ background_style = self._get_base_style()
+ if background_style.transparent_background:
+ return Style.null(), Style(dim=True), Style.null()
+ if console.color_system in ("256", "truecolor"):
+ number_style = Style.chain(
+ background_style,
+ self._theme.get_style_for_token(Token.Text),
+ Style(color=self._get_line_numbers_color()),
+ self.background_style,
+ )
+ highlight_number_style = Style.chain(
+ background_style,
+ self._theme.get_style_for_token(Token.Text),
+ Style(bold=True, color=self._get_line_numbers_color(0.9)),
+ self.background_style,
+ )
+ else:
+ number_style = background_style + Style(dim=True)
+ highlight_number_style = background_style + Style(dim=False)
+ return background_style, number_style, highlight_number_style
+
+ def __rich_measure__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "Measurement":
+ if self.code_width is not None:
+ width = self.code_width + self._numbers_column_width
+ return Measurement(self._numbers_column_width, width)
+ return Measurement(self._numbers_column_width, options.max_width)
+
+ def __rich_console__(
+ self, console: Console, options: ConsoleOptions
+ ) -> RenderResult:
+
+ transparent_background = self._get_base_style().transparent_background
+ code_width = (
+ (
+ (options.max_width - self._numbers_column_width - 1)
+ if self.line_numbers
+ else options.max_width
+ )
+ if self.code_width is None
+ else self.code_width
+ )
+
+ line_offset = 0
+ if self.line_range:
+ start_line, end_line = self.line_range
+ line_offset = max(0, start_line - 1)
+
+ ends_on_nl = self.code.endswith("\n")
+ code = self.code if ends_on_nl else self.code + "\n"
+ code = textwrap.dedent(code) if self.dedent else code
+ code = code.expandtabs(self.tab_size)
+ text = self.highlight(code, self.line_range)
+
+ (
+ background_style,
+ number_style,
+ highlight_number_style,
+ ) = self._get_number_styles(console)
+
+ if not self.line_numbers and not self.word_wrap and not self.line_range:
+ if not ends_on_nl:
+ text.remove_suffix("\n")
+ # Simple case of just rendering text
+ style = (
+ self._get_base_style()
+ + self._theme.get_style_for_token(Comment)
+ + Style(dim=True)
+ + self.background_style
+ )
+ if self.indent_guides and not options.ascii_only:
+ text = text.with_indent_guides(self.tab_size, style=style)
+ text.overflow = "crop"
+ if style.transparent_background:
+ yield from console.render(
+ text, options=options.update(width=code_width)
+ )
+ else:
+ syntax_lines = console.render_lines(
+ text,
+ options.update(width=code_width, height=None),
+ style=self.background_style,
+ pad=True,
+ new_lines=True,
+ )
+ for syntax_line in syntax_lines:
+ yield from syntax_line
+ return
+
+ lines: Union[List[Text], Lines] = text.split("\n", allow_blank=ends_on_nl)
+ if self.line_range:
+ lines = lines[line_offset:end_line]
+
+ if self.indent_guides and not options.ascii_only:
+ style = (
+ self._get_base_style()
+ + self._theme.get_style_for_token(Comment)
+ + Style(dim=True)
+ + self.background_style
+ )
+ lines = (
+ Text("\n")
+ .join(lines)
+ .with_indent_guides(self.tab_size, style=style)
+ .split("\n", allow_blank=True)
+ )
+
+ numbers_column_width = self._numbers_column_width
+ render_options = options.update(width=code_width)
+
+ highlight_line = self.highlight_lines.__contains__
+ _Segment = Segment
+ padding = _Segment(" " * numbers_column_width + " ", background_style)
+ new_line = _Segment("\n")
+
+ line_pointer = "> " if options.legacy_windows else "❱ "
+
+ for line_no, line in enumerate(lines, self.start_line + line_offset):
+ if self.word_wrap:
+ wrapped_lines = console.render_lines(
+ line,
+ render_options.update(height=None),
+ style=background_style,
+ pad=not transparent_background,
+ )
+
+ else:
+ segments = list(line.render(console, end=""))
+ if options.no_wrap:
+ wrapped_lines = [segments]
+ else:
+ wrapped_lines = [
+ _Segment.adjust_line_length(
+ segments,
+ render_options.max_width,
+ style=background_style,
+ pad=not transparent_background,
+ )
+ ]
+ if self.line_numbers:
+ for first, wrapped_line in loop_first(wrapped_lines):
+ if first:
+ line_column = str(line_no).rjust(numbers_column_width - 2) + " "
+ if highlight_line(line_no):
+ yield _Segment(line_pointer, Style(color="red"))
+ yield _Segment(line_column, highlight_number_style)
+ else:
+ yield _Segment(" ", highlight_number_style)
+ yield _Segment(line_column, number_style)
+ else:
+ yield padding
+ yield from wrapped_line
+ yield new_line
+ else:
+ for wrapped_line in wrapped_lines:
+ yield from wrapped_line
+ yield new_line
+
+
+if __name__ == "__main__": # pragma: no cover
+
+ import argparse
+ import sys
+
+ parser = argparse.ArgumentParser(
+ description="Render syntax to the console with Rich"
+ )
+ parser.add_argument(
+ "path",
+ metavar="PATH",
+ help="path to file, or - for stdin",
+ )
+ parser.add_argument(
+ "-c",
+ "--force-color",
+ dest="force_color",
+ action="store_true",
+ default=None,
+ help="force color for non-terminals",
+ )
+ parser.add_argument(
+ "-i",
+ "--indent-guides",
+ dest="indent_guides",
+ action="store_true",
+ default=False,
+ help="display indent guides",
+ )
+ parser.add_argument(
+ "-l",
+ "--line-numbers",
+ dest="line_numbers",
+ action="store_true",
+ help="render line numbers",
+ )
+ parser.add_argument(
+ "-w",
+ "--width",
+ type=int,
+ dest="width",
+ default=None,
+ help="width of output (default will auto-detect)",
+ )
+ parser.add_argument(
+ "-r",
+ "--wrap",
+ dest="word_wrap",
+ action="store_true",
+ default=False,
+ help="word wrap long lines",
+ )
+ parser.add_argument(
+ "-s",
+ "--soft-wrap",
+ action="store_true",
+ dest="soft_wrap",
+ default=False,
+ help="enable soft wrapping mode",
+ )
+ parser.add_argument(
+ "-t", "--theme", dest="theme", default="monokai", help="pygments theme"
+ )
+ parser.add_argument(
+ "-b",
+ "--background-color",
+ dest="background_color",
+ default=None,
+ help="Override background color",
+ )
+ parser.add_argument(
+ "-x",
+ "--lexer",
+ default="default",
+ dest="lexer_name",
+ help="Lexer name",
+ )
+ args = parser.parse_args()
+
+ from pip._vendor.rich.console import Console
+
+ console = Console(force_terminal=args.force_color, width=args.width)
+
+ if args.path == "-":
+ code = sys.stdin.read()
+ syntax = Syntax(
+ code=code,
+ lexer_name=args.lexer_name,
+ line_numbers=args.line_numbers,
+ word_wrap=args.word_wrap,
+ theme=args.theme,
+ background_color=args.background_color,
+ indent_guides=args.indent_guides,
+ )
+ else:
+ syntax = Syntax.from_path(
+ args.path,
+ line_numbers=args.line_numbers,
+ word_wrap=args.word_wrap,
+ theme=args.theme,
+ background_color=args.background_color,
+ indent_guides=args.indent_guides,
+ )
+ console.print(syntax, soft_wrap=args.soft_wrap)
diff --git a/src/pip/_vendor/rich/table.py b/src/pip/_vendor/rich/table.py
new file mode 100644
index 000000000..554ff4013
--- /dev/null
+++ b/src/pip/_vendor/rich/table.py
@@ -0,0 +1,919 @@
+from dataclasses import dataclass, field, replace
+from typing import (
+ Dict,
+ TYPE_CHECKING,
+ Iterable,
+ List,
+ NamedTuple,
+ Optional,
+ Sequence,
+ Tuple,
+ Union,
+)
+
+from . import box, errors
+from ._loop import loop_first_last, loop_last
+from ._pick import pick_bool
+from ._ratio import ratio_distribute, ratio_reduce
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .padding import Padding, PaddingDimensions
+from .protocol import is_renderable
+from .segment import Segment
+from .style import Style, StyleType
+from .text import Text, TextType
+
+if TYPE_CHECKING:
+ from .console import (
+ Console,
+ ConsoleOptions,
+ JustifyMethod,
+ OverflowMethod,
+ RenderableType,
+ RenderResult,
+ )
+
+
+@dataclass
+class Column:
+ """Defines a column in a table."""
+
+ header: "RenderableType" = ""
+ """RenderableType: Renderable for the header (typically a string)"""
+
+ footer: "RenderableType" = ""
+ """RenderableType: Renderable for the footer (typically a string)"""
+
+ header_style: StyleType = ""
+ """StyleType: The style of the header."""
+
+ footer_style: StyleType = ""
+ """StyleType: The style of the footer."""
+
+ style: StyleType = ""
+ """StyleType: The style of the column."""
+
+ justify: "JustifyMethod" = "left"
+ """str: How to justify text within the column ("left", "center", "right", or "full")"""
+
+ overflow: "OverflowMethod" = "ellipsis"
+ """str: Overflow method."""
+
+ width: Optional[int] = None
+ """Optional[int]: Width of the column, or ``None`` (default) to auto calculate width."""
+
+ min_width: Optional[int] = None
+ """Optional[int]: Minimum width of column, or ``None`` for no minimum. Defaults to None."""
+
+ max_width: Optional[int] = None
+ """Optional[int]: Maximum width of column, or ``None`` for no maximum. Defaults to None."""
+
+ ratio: Optional[int] = None
+ """Optional[int]: Ratio to use when calculating column width, or ``None`` (default) to adapt to column contents."""
+
+ no_wrap: bool = False
+ """bool: Prevent wrapping of text within the column. Defaults to ``False``."""
+
+ _index: int = 0
+ """Index of column."""
+
+ _cells: List["RenderableType"] = field(default_factory=list)
+
+ def copy(self) -> "Column":
+ """Return a copy of this Column."""
+ return replace(self, _cells=[])
+
+ @property
+ def cells(self) -> Iterable["RenderableType"]:
+ """Get all cells in the column, not including header."""
+ yield from self._cells
+
+ @property
+ def flexible(self) -> bool:
+ """Check if this column is flexible."""
+ return self.ratio is not None
+
+
+@dataclass
+class Row:
+ """Information regarding a row."""
+
+ style: Optional[StyleType] = None
+ """Style to apply to row."""
+
+ end_section: bool = False
+ """Indicated end of section, which will force a line beneath the row."""
+
+
+class _Cell(NamedTuple):
+ """A single cell in a table."""
+
+ style: StyleType
+ """Style to apply to cell."""
+ renderable: "RenderableType"
+ """Cell renderable."""
+
+
+class Table(JupyterMixin):
+ """A console renderable to draw a table.
+
+ Args:
+ *headers (Union[Column, str]): Column headers, either as a string, or :class:`~rich.table.Column` instance.
+ title (Union[str, Text], optional): The title of the table rendered at the top. Defaults to None.
+ caption (Union[str, Text], optional): The table caption rendered below. Defaults to None.
+ width (int, optional): The width in characters of the table, or ``None`` to automatically fit. Defaults to None.
+ min_width (Optional[int], optional): The minimum width of the table, or ``None`` for no minimum. Defaults to None.
+ box (box.Box, optional): One of the constants in box.py used to draw the edges (see :ref:`appendix_box`), or ``None`` for no box lines. Defaults to box.HEAVY_HEAD.
+ safe_box (Optional[bool], optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True.
+ padding (PaddingDimensions, optional): Padding for cells (top, right, bottom, left). Defaults to (0, 1).
+ collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to False.
+ pad_edge (bool, optional): Enable padding of edge cells. Defaults to True.
+ expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.
+ show_header (bool, optional): Show a header row. Defaults to True.
+ show_footer (bool, optional): Show a footer row. Defaults to False.
+ show_edge (bool, optional): Draw a box around the outside of the table. Defaults to True.
+ show_lines (bool, optional): Draw lines between every row. Defaults to False.
+ leading (bool, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0.
+ style (Union[str, Style], optional): Default style for the table. Defaults to "none".
+ row_styles (List[Union, str], optional): Optional list of row styles, if more than one style is given then the styles will alternate. Defaults to None.
+ header_style (Union[str, Style], optional): Style of the header. Defaults to "table.header".
+ footer_style (Union[str, Style], optional): Style of the footer. Defaults to "table.footer".
+ border_style (Union[str, Style], optional): Style of the border. Defaults to None.
+ title_style (Union[str, Style], optional): Style of the title. Defaults to None.
+ caption_style (Union[str, Style], optional): Style of the caption. Defaults to None.
+ title_justify (str, optional): Justify method for title. Defaults to "center".
+ caption_justify (str, optional): Justify method for caption. Defaults to "center".
+ highlight (bool, optional): Highlight cell contents (if str). Defaults to False.
+ """
+
+ columns: List[Column]
+ rows: List[Row]
+
+ def __init__(
+ self,
+ *headers: Union[Column, str],
+ title: Optional[TextType] = None,
+ caption: Optional[TextType] = None,
+ width: Optional[int] = None,
+ min_width: Optional[int] = None,
+ box: Optional[box.Box] = box.HEAVY_HEAD,
+ safe_box: Optional[bool] = None,
+ padding: PaddingDimensions = (0, 1),
+ collapse_padding: bool = False,
+ pad_edge: bool = True,
+ expand: bool = False,
+ show_header: bool = True,
+ show_footer: bool = False,
+ show_edge: bool = True,
+ show_lines: bool = False,
+ leading: int = 0,
+ style: StyleType = "none",
+ row_styles: Optional[Iterable[StyleType]] = None,
+ header_style: Optional[StyleType] = "table.header",
+ footer_style: Optional[StyleType] = "table.footer",
+ border_style: Optional[StyleType] = None,
+ title_style: Optional[StyleType] = None,
+ caption_style: Optional[StyleType] = None,
+ title_justify: "JustifyMethod" = "center",
+ caption_justify: "JustifyMethod" = "center",
+ highlight: bool = False,
+ ) -> None:
+
+ self.columns: List[Column] = []
+ self.rows: List[Row] = []
+ self.title = title
+ self.caption = caption
+ self.width = width
+ self.min_width = min_width
+ self.box = box
+ self.safe_box = safe_box
+ self._padding = Padding.unpack(padding)
+ self.pad_edge = pad_edge
+ self._expand = expand
+ self.show_header = show_header
+ self.show_footer = show_footer
+ self.show_edge = show_edge
+ self.show_lines = show_lines
+ self.leading = leading
+ self.collapse_padding = collapse_padding
+ self.style = style
+ self.header_style = header_style or ""
+ self.footer_style = footer_style or ""
+ self.border_style = border_style
+ self.title_style = title_style
+ self.caption_style = caption_style
+ self.title_justify: "JustifyMethod" = title_justify
+ self.caption_justify: "JustifyMethod" = caption_justify
+ self.highlight = highlight
+ self.row_styles: Sequence[StyleType] = list(row_styles or [])
+ append_column = self.columns.append
+ for header in headers:
+ if isinstance(header, str):
+ self.add_column(header=header)
+ else:
+ header._index = len(self.columns)
+ append_column(header)
+
+ @classmethod
+ def grid(
+ cls,
+ *headers: Union[Column, str],
+ padding: PaddingDimensions = 0,
+ collapse_padding: bool = True,
+ pad_edge: bool = False,
+ expand: bool = False,
+ ) -> "Table":
+ """Get a table with no lines, headers, or footer.
+
+ Args:
+ *headers (Union[Column, str]): Column headers, either as a string, or :class:`~rich.table.Column` instance.
+ padding (PaddingDimensions, optional): Get padding around cells. Defaults to 0.
+ collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to True.
+ pad_edge (bool, optional): Enable padding around edges of table. Defaults to False.
+ expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.
+
+ Returns:
+ Table: A table instance.
+ """
+ return cls(
+ *headers,
+ box=None,
+ padding=padding,
+ collapse_padding=collapse_padding,
+ show_header=False,
+ show_footer=False,
+ show_edge=False,
+ pad_edge=pad_edge,
+ expand=expand,
+ )
+
+ @property
+ def expand(self) -> bool:
+ """Setting a non-None self.width implies expand."""
+ return self._expand or self.width is not None
+
+ @expand.setter
+ def expand(self, expand: bool) -> None:
+ """Set expand."""
+ self._expand = expand
+
+ @property
+ def _extra_width(self) -> int:
+ """Get extra width to add to cell content."""
+ width = 0
+ if self.box and self.show_edge:
+ width += 2
+ if self.box:
+ width += len(self.columns) - 1
+ return width
+
+ @property
+ def row_count(self) -> int:
+ """Get the current number of rows."""
+ return len(self.rows)
+
+ def get_row_style(self, console: "Console", index: int) -> StyleType:
+ """Get the current row style."""
+ style = Style.null()
+ if self.row_styles:
+ style += console.get_style(self.row_styles[index % len(self.row_styles)])
+ row_style = self.rows[index].style
+ if row_style is not None:
+ style += console.get_style(row_style)
+ return style
+
+ def __rich_measure__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> Measurement:
+ max_width = options.max_width
+ if self.width is not None:
+ max_width = self.width
+ if max_width < 0:
+ return Measurement(0, 0)
+
+ extra_width = self._extra_width
+ max_width = sum(
+ self._calculate_column_widths(
+ console, options.update_width(max_width - extra_width)
+ )
+ )
+ _measure_column = self._measure_column
+
+ measurements = [
+ _measure_column(console, options.update_width(max_width), column)
+ for column in self.columns
+ ]
+ minimum_width = (
+ sum(measurement.minimum for measurement in measurements) + extra_width
+ )
+ maximum_width = (
+ sum(measurement.maximum for measurement in measurements) + extra_width
+ if (self.width is None)
+ else self.width
+ )
+ measurement = Measurement(minimum_width, maximum_width)
+ measurement = measurement.clamp(self.min_width)
+ return measurement
+
+ @property
+ def padding(self) -> Tuple[int, int, int, int]:
+ """Get cell padding."""
+ return self._padding
+
+ @padding.setter
+ def padding(self, padding: PaddingDimensions) -> "Table":
+ """Set cell padding."""
+ self._padding = Padding.unpack(padding)
+ return self
+
+ def add_column(
+ self,
+ header: "RenderableType" = "",
+ footer: "RenderableType" = "",
+ *,
+ header_style: Optional[StyleType] = None,
+ footer_style: Optional[StyleType] = None,
+ style: Optional[StyleType] = None,
+ justify: "JustifyMethod" = "left",
+ overflow: "OverflowMethod" = "ellipsis",
+ width: Optional[int] = None,
+ min_width: Optional[int] = None,
+ max_width: Optional[int] = None,
+ ratio: Optional[int] = None,
+ no_wrap: bool = False,
+ ) -> None:
+ """Add a column to the table.
+
+ Args:
+ header (RenderableType, optional): Text or renderable for the header.
+ Defaults to "".
+ footer (RenderableType, optional): Text or renderable for the footer.
+ Defaults to "".
+ header_style (Union[str, Style], optional): Style for the header, or None for default. Defaults to None.
+ footer_style (Union[str, Style], optional): Style for the footer, or None for default. Defaults to None.
+ style (Union[str, Style], optional): Style for the column cells, or None for default. Defaults to None.
+ justify (JustifyMethod, optional): Alignment for cells. Defaults to "left".
+ overflow (OverflowMethod): Overflow method: "crop", "fold", "ellipsis". Defaults to "ellipsis".
+ width (int, optional): Desired width of column in characters, or None to fit to contents. Defaults to None.
+ min_width (Optional[int], optional): Minimum width of column, or ``None`` for no minimum. Defaults to None.
+ max_width (Optional[int], optional): Maximum width of column, or ``None`` for no maximum. Defaults to None.
+ ratio (int, optional): Flexible ratio for the column (requires ``Table.expand`` or ``Table.width``). Defaults to None.
+ no_wrap (bool, optional): Set to ``True`` to disable wrapping of this column.
+ """
+
+ column = Column(
+ _index=len(self.columns),
+ header=header,
+ footer=footer,
+ header_style=header_style or "",
+ footer_style=footer_style or "",
+ style=style or "",
+ justify=justify,
+ overflow=overflow,
+ width=width,
+ min_width=min_width,
+ max_width=max_width,
+ ratio=ratio,
+ no_wrap=no_wrap,
+ )
+ self.columns.append(column)
+
+ def add_row(
+ self,
+ *renderables: Optional["RenderableType"],
+ style: Optional[StyleType] = None,
+ end_section: bool = False,
+ ) -> None:
+ """Add a row of renderables.
+
+ Args:
+ *renderables (None or renderable): Each cell in a row must be a renderable object (including str),
+ or ``None`` for a blank cell.
+ style (StyleType, optional): An optional style to apply to the entire row. Defaults to None.
+ end_section (bool, optional): End a section and draw a line. Defaults to False.
+
+ Raises:
+ errors.NotRenderableError: If you add something that can't be rendered.
+ """
+
+ def add_cell(column: Column, renderable: "RenderableType") -> None:
+ column._cells.append(renderable)
+
+ cell_renderables: List[Optional["RenderableType"]] = list(renderables)
+
+ columns = self.columns
+ if len(cell_renderables) < len(columns):
+ cell_renderables = [
+ *cell_renderables,
+ *[None] * (len(columns) - len(cell_renderables)),
+ ]
+ for index, renderable in enumerate(cell_renderables):
+ if index == len(columns):
+ column = Column(_index=index)
+ for _ in self.rows:
+ add_cell(column, Text(""))
+ self.columns.append(column)
+ else:
+ column = columns[index]
+ if renderable is None:
+ add_cell(column, "")
+ elif is_renderable(renderable):
+ add_cell(column, renderable)
+ else:
+ raise errors.NotRenderableError(
+ f"unable to render {type(renderable).__name__}; a string or other renderable object is required"
+ )
+ self.rows.append(Row(style=style, end_section=end_section))
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+
+ if not self.columns:
+ yield Segment("\n")
+ return
+
+ max_width = options.max_width
+ if self.width is not None:
+ max_width = self.width
+
+ extra_width = self._extra_width
+ widths = self._calculate_column_widths(
+ console, options.update_width(max_width - extra_width)
+ )
+ table_width = sum(widths) + extra_width
+
+ render_options = options.update(
+ width=table_width, highlight=self.highlight, height=None
+ )
+
+ def render_annotation(
+ text: TextType, style: StyleType, justify: "JustifyMethod" = "center"
+ ) -> "RenderResult":
+ render_text = (
+ console.render_str(text, style=style, highlight=False)
+ if isinstance(text, str)
+ else text
+ )
+ return console.render(
+ render_text, options=render_options.update(justify=justify)
+ )
+
+ if self.title:
+ yield from render_annotation(
+ self.title,
+ style=Style.pick_first(self.title_style, "table.title"),
+ justify=self.title_justify,
+ )
+ yield from self._render(console, render_options, widths)
+ if self.caption:
+ yield from render_annotation(
+ self.caption,
+ style=Style.pick_first(self.caption_style, "table.caption"),
+ justify=self.caption_justify,
+ )
+
+ def _calculate_column_widths(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> List[int]:
+ """Calculate the widths of each column, including padding, not including borders."""
+ max_width = options.max_width
+ columns = self.columns
+ width_ranges = [
+ self._measure_column(console, options, column) for column in columns
+ ]
+ widths = [_range.maximum or 1 for _range in width_ranges]
+ get_padding_width = self._get_padding_width
+ extra_width = self._extra_width
+ if self.expand:
+ ratios = [col.ratio or 0 for col in columns if col.flexible]
+ if any(ratios):
+ fixed_widths = [
+ 0 if column.flexible else _range.maximum
+ for _range, column in zip(width_ranges, columns)
+ ]
+ flex_minimum = [
+ (column.width or 1) + get_padding_width(column._index)
+ for column in columns
+ if column.flexible
+ ]
+ flexible_width = max_width - sum(fixed_widths)
+ flex_widths = ratio_distribute(flexible_width, ratios, flex_minimum)
+ iter_flex_widths = iter(flex_widths)
+ for index, column in enumerate(columns):
+ if column.flexible:
+ widths[index] = fixed_widths[index] + next(iter_flex_widths)
+ table_width = sum(widths)
+
+ if table_width > max_width:
+ widths = self._collapse_widths(
+ widths,
+ [(column.width is None and not column.no_wrap) for column in columns],
+ max_width,
+ )
+ table_width = sum(widths)
+ # last resort, reduce columns evenly
+ if table_width > max_width:
+ excess_width = table_width - max_width
+ widths = ratio_reduce(excess_width, [1] * len(widths), widths, widths)
+ table_width = sum(widths)
+
+ width_ranges = [
+ self._measure_column(console, options.update_width(width), column)
+ for width, column in zip(widths, columns)
+ ]
+ widths = [_range.maximum or 0 for _range in width_ranges]
+
+ if (table_width < max_width and self.expand) or (
+ self.min_width is not None and table_width < (self.min_width - extra_width)
+ ):
+ _max_width = (
+ max_width
+ if self.min_width is None
+ else min(self.min_width - extra_width, max_width)
+ )
+ pad_widths = ratio_distribute(_max_width - table_width, widths)
+ widths = [_width + pad for _width, pad in zip(widths, pad_widths)]
+
+ return widths
+
+ @classmethod
+ def _collapse_widths(
+ cls, widths: List[int], wrapable: List[bool], max_width: int
+ ) -> List[int]:
+ """Reduce widths so that the total is under max_width.
+
+ Args:
+ widths (List[int]): List of widths.
+ wrapable (List[bool]): List of booleans that indicate if a column may shrink.
+ max_width (int): Maximum width to reduce to.
+
+ Returns:
+ List[int]: A new list of widths.
+ """
+ total_width = sum(widths)
+ excess_width = total_width - max_width
+ if any(wrapable):
+ while total_width and excess_width > 0:
+ max_column = max(
+ width for width, allow_wrap in zip(widths, wrapable) if allow_wrap
+ )
+ second_max_column = max(
+ width if allow_wrap and width != max_column else 0
+ for width, allow_wrap in zip(widths, wrapable)
+ )
+ column_difference = max_column - second_max_column
+ ratios = [
+ (1 if (width == max_column and allow_wrap) else 0)
+ for width, allow_wrap in zip(widths, wrapable)
+ ]
+ if not any(ratios) or not column_difference:
+ break
+ max_reduce = [min(excess_width, column_difference)] * len(widths)
+ widths = ratio_reduce(excess_width, ratios, max_reduce, widths)
+
+ total_width = sum(widths)
+ excess_width = total_width - max_width
+ return widths
+
+ def _get_cells(
+ self, console: "Console", column_index: int, column: Column
+ ) -> Iterable[_Cell]:
+ """Get all the cells with padding and optional header."""
+
+ collapse_padding = self.collapse_padding
+ pad_edge = self.pad_edge
+ padding = self.padding
+ any_padding = any(padding)
+
+ first_column = column_index == 0
+ last_column = column_index == len(self.columns) - 1
+
+ _padding_cache: Dict[Tuple[bool, bool], Tuple[int, int, int, int]] = {}
+
+ def get_padding(first_row: bool, last_row: bool) -> Tuple[int, int, int, int]:
+ cached = _padding_cache.get((first_row, last_row))
+ if cached:
+ return cached
+ top, right, bottom, left = padding
+
+ if collapse_padding:
+ if not first_column:
+ left = max(0, left - right)
+ if not last_row:
+ bottom = max(0, top - bottom)
+
+ if not pad_edge:
+ if first_column:
+ left = 0
+ if last_column:
+ right = 0
+ if first_row:
+ top = 0
+ if last_row:
+ bottom = 0
+ _padding = (top, right, bottom, left)
+ _padding_cache[(first_row, last_row)] = _padding
+ return _padding
+
+ raw_cells: List[Tuple[StyleType, "RenderableType"]] = []
+ _append = raw_cells.append
+ get_style = console.get_style
+ if self.show_header:
+ header_style = get_style(self.header_style or "") + get_style(
+ column.header_style
+ )
+ _append((header_style, column.header))
+ cell_style = get_style(column.style or "")
+ for cell in column.cells:
+ _append((cell_style, cell))
+ if self.show_footer:
+ footer_style = get_style(self.footer_style or "") + get_style(
+ column.footer_style
+ )
+ _append((footer_style, column.footer))
+
+ if any_padding:
+ _Padding = Padding
+ for first, last, (style, renderable) in loop_first_last(raw_cells):
+ yield _Cell(style, _Padding(renderable, get_padding(first, last)))
+ else:
+ for (style, renderable) in raw_cells:
+ yield _Cell(style, renderable)
+
+ def _get_padding_width(self, column_index: int) -> int:
+ """Get extra width from padding."""
+ _, pad_right, _, pad_left = self.padding
+ if self.collapse_padding:
+ if column_index > 0:
+ pad_left = max(0, pad_left - pad_right)
+ return pad_left + pad_right
+
+ def _measure_column(
+ self,
+ console: "Console",
+ options: "ConsoleOptions",
+ column: Column,
+ ) -> Measurement:
+ """Get the minimum and maximum width of the column."""
+
+ max_width = options.max_width
+ if max_width < 1:
+ return Measurement(0, 0)
+
+ padding_width = self._get_padding_width(column._index)
+
+ if column.width is not None:
+ # Fixed width column
+ return Measurement(
+ column.width + padding_width, column.width + padding_width
+ ).with_maximum(max_width)
+ # Flexible column, we need to measure contents
+ min_widths: List[int] = []
+ max_widths: List[int] = []
+ append_min = min_widths.append
+ append_max = max_widths.append
+ get_render_width = Measurement.get
+ for cell in self._get_cells(console, column._index, column):
+ _min, _max = get_render_width(console, options, cell.renderable)
+ append_min(_min)
+ append_max(_max)
+
+ measurement = Measurement(
+ max(min_widths) if min_widths else 1,
+ max(max_widths) if max_widths else max_width,
+ ).with_maximum(max_width)
+ measurement = measurement.clamp(
+ None if column.min_width is None else column.min_width + padding_width,
+ None if column.max_width is None else column.max_width + padding_width,
+ )
+ return measurement
+
+ def _render(
+ self, console: "Console", options: "ConsoleOptions", widths: List[int]
+ ) -> "RenderResult":
+ table_style = console.get_style(self.style or "")
+
+ border_style = table_style + console.get_style(self.border_style or "")
+ _column_cells = (
+ self._get_cells(console, column_index, column)
+ for column_index, column in enumerate(self.columns)
+ )
+ row_cells: List[Tuple[_Cell, ...]] = list(zip(*_column_cells))
+ _box = (
+ self.box.substitute(
+ options, safe=pick_bool(self.safe_box, console.safe_box)
+ )
+ if self.box
+ else None
+ )
+
+ # _box = self.box
+ new_line = Segment.line()
+
+ columns = self.columns
+ show_header = self.show_header
+ show_footer = self.show_footer
+ show_edge = self.show_edge
+ show_lines = self.show_lines
+ leading = self.leading
+
+ _Segment = Segment
+ if _box:
+ box_segments = [
+ (
+ _Segment(_box.head_left, border_style),
+ _Segment(_box.head_right, border_style),
+ _Segment(_box.head_vertical, border_style),
+ ),
+ (
+ _Segment(_box.foot_left, border_style),
+ _Segment(_box.foot_right, border_style),
+ _Segment(_box.foot_vertical, border_style),
+ ),
+ (
+ _Segment(_box.mid_left, border_style),
+ _Segment(_box.mid_right, border_style),
+ _Segment(_box.mid_vertical, border_style),
+ ),
+ ]
+ if show_edge:
+ yield _Segment(_box.get_top(widths), border_style)
+ yield new_line
+ else:
+ box_segments = []
+
+ get_row_style = self.get_row_style
+ get_style = console.get_style
+
+ for index, (first, last, row_cell) in enumerate(loop_first_last(row_cells)):
+ header_row = first and show_header
+ footer_row = last and show_footer
+ row = (
+ self.rows[index - show_header]
+ if (not header_row and not footer_row)
+ else None
+ )
+ max_height = 1
+ cells: List[List[List[Segment]]] = []
+ if header_row or footer_row:
+ row_style = Style.null()
+ else:
+ row_style = get_style(
+ get_row_style(console, index - 1 if show_header else index)
+ )
+ for width, cell, column in zip(widths, row_cell, columns):
+ render_options = options.update(
+ width=width,
+ justify=column.justify,
+ no_wrap=column.no_wrap,
+ overflow=column.overflow,
+ height=None,
+ )
+ cell_style = table_style + row_style + get_style(cell.style)
+ lines = console.render_lines(
+ cell.renderable, render_options, style=cell_style
+ )
+ max_height = max(max_height, len(lines))
+ cells.append(lines)
+
+ cells[:] = [
+ _Segment.set_shape(
+ _cell, width, max_height, style=table_style + row_style
+ )
+ for width, _cell in zip(widths, cells)
+ ]
+
+ if _box:
+ if last and show_footer:
+ yield _Segment(
+ _box.get_row(widths, "foot", edge=show_edge), border_style
+ )
+ yield new_line
+ left, right, _divider = box_segments[0 if first else (2 if last else 1)]
+
+ # If the column divider is whitespace also style it with the row background
+ divider = (
+ _divider
+ if _divider.text.strip()
+ else _Segment(
+ _divider.text, row_style.background_style + _divider.style
+ )
+ )
+ for line_no in range(max_height):
+ if show_edge:
+ yield left
+ for last_cell, rendered_cell in loop_last(cells):
+ yield from rendered_cell[line_no]
+ if not last_cell:
+ yield divider
+ if show_edge:
+ yield right
+ yield new_line
+ else:
+ for line_no in range(max_height):
+ for rendered_cell in cells:
+ yield from rendered_cell[line_no]
+ yield new_line
+ if _box and first and show_header:
+ yield _Segment(
+ _box.get_row(widths, "head", edge=show_edge), border_style
+ )
+ yield new_line
+ end_section = row and row.end_section
+ if _box and (show_lines or leading or end_section):
+ if (
+ not last
+ and not (show_footer and index >= len(row_cells) - 2)
+ and not (show_header and header_row)
+ ):
+ if leading:
+ yield _Segment(
+ _box.get_row(widths, "mid", edge=show_edge) * leading,
+ border_style,
+ )
+ else:
+ yield _Segment(
+ _box.get_row(widths, "row", edge=show_edge), border_style
+ )
+ yield new_line
+
+ if _box and show_edge:
+ yield _Segment(_box.get_bottom(widths), border_style)
+ yield new_line
+
+
+if __name__ == "__main__": # pragma: no cover
+ from pip._vendor.rich.console import Console
+ from pip._vendor.rich.highlighter import ReprHighlighter
+ from pip._vendor.rich.table import Table as Table
+
+ table = Table(
+ title="Star Wars Movies",
+ caption="Rich example table",
+ caption_justify="right",
+ )
+
+ table.add_column("Released", header_style="bright_cyan", style="cyan", no_wrap=True)
+ table.add_column("Title", style="magenta")
+ table.add_column("Box Office", justify="right", style="green")
+
+ table.add_row(
+ "Dec 20, 2019",
+ "Star Wars: The Rise of Skywalker",
+ "$952,110,690",
+ )
+ table.add_row("May 25, 2018", "Solo: A Star Wars Story", "$393,151,347")
+ table.add_row(
+ "Dec 15, 2017",
+ "Star Wars Ep. V111: The Last Jedi",
+ "$1,332,539,889",
+ style="on black",
+ end_section=True,
+ )
+ table.add_row(
+ "Dec 16, 2016",
+ "Rogue One: A Star Wars Story",
+ "$1,332,439,889",
+ )
+
+ def header(text: str) -> None:
+ console.print()
+ console.rule(highlight(text))
+ console.print()
+
+ console = Console()
+ highlight = ReprHighlighter()
+ header("Example Table")
+ console.print(table, justify="center")
+
+ table.expand = True
+ header("expand=True")
+ console.print(table)
+
+ table.width = 50
+ header("width=50")
+
+ console.print(table, justify="center")
+
+ table.width = None
+ table.expand = False
+ table.row_styles = ["dim", "none"]
+ header("row_styles=['dim', 'none']")
+
+ console.print(table, justify="center")
+
+ table.width = None
+ table.expand = False
+ table.row_styles = ["dim", "none"]
+ table.leading = 1
+ header("leading=1, row_styles=['dim', 'none']")
+ console.print(table, justify="center")
+
+ table.width = None
+ table.expand = False
+ table.row_styles = ["dim", "none"]
+ table.show_lines = True
+ table.leading = 0
+ header("show_lines=True, row_styles=['dim', 'none']")
+ console.print(table, justify="center")
diff --git a/src/pip/_vendor/rich/tabulate.py b/src/pip/_vendor/rich/tabulate.py
new file mode 100644
index 000000000..6889f2d33
--- /dev/null
+++ b/src/pip/_vendor/rich/tabulate.py
@@ -0,0 +1,51 @@
+from collections.abc import Mapping
+from typing import Any, Optional
+import warnings
+
+from pip._vendor.rich.console import JustifyMethod
+
+from . import box
+from .highlighter import ReprHighlighter
+from .pretty import Pretty
+from .table import Table
+
+
+def tabulate_mapping(
+ mapping: "Mapping[Any, Any]",
+ title: Optional[str] = None,
+ caption: Optional[str] = None,
+ title_justify: Optional[JustifyMethod] = None,
+ caption_justify: Optional[JustifyMethod] = None,
+) -> Table:
+ """Generate a simple table from a mapping.
+
+ Args:
+ mapping (Mapping): A mapping object (e.g. a dict);
+ title (str, optional): Optional title to be displayed over the table.
+ caption (str, optional): Optional caption to be displayed below the table.
+ title_justify (str, optional): Justify method for title. Defaults to None.
+ caption_justify (str, optional): Justify method for caption. Defaults to None.
+
+ Returns:
+ Table: A table instance which may be rendered by the Console.
+ """
+ warnings.warn("tabulate_mapping will be deprecated in Rich v11", DeprecationWarning)
+ table = Table(
+ show_header=False,
+ title=title,
+ caption=caption,
+ box=box.ROUNDED,
+ border_style="blue",
+ )
+ table.title = title
+ table.caption = caption
+ if title_justify is not None:
+ table.title_justify = title_justify
+ if caption_justify is not None:
+ table.caption_justify = caption_justify
+ highlighter = ReprHighlighter()
+ for key, value in mapping.items():
+ table.add_row(
+ Pretty(key, highlighter=highlighter), Pretty(value, highlighter=highlighter)
+ )
+ return table
diff --git a/src/pip/_vendor/rich/terminal_theme.py b/src/pip/_vendor/rich/terminal_theme.py
new file mode 100644
index 000000000..801ac0b7b
--- /dev/null
+++ b/src/pip/_vendor/rich/terminal_theme.py
@@ -0,0 +1,55 @@
+from typing import List, Optional, Tuple
+
+from .color_triplet import ColorTriplet
+from .palette import Palette
+
+_ColorTuple = Tuple[int, int, int]
+
+
+class TerminalTheme:
+ """A color theme used when exporting console content.
+
+ Args:
+ background (Tuple[int, int, int]): The background color.
+ foreground (Tuple[int, int, int]): The foreground (text) color.
+ normal (List[Tuple[int, int, int]]): A list of 8 normal intensity colors.
+ bright (List[Tuple[int, int, int]], optional): A list of 8 bright colors, or None
+ to repeat normal intensity. Defaults to None.
+ """
+
+ def __init__(
+ self,
+ background: _ColorTuple,
+ foreground: _ColorTuple,
+ normal: List[_ColorTuple],
+ bright: Optional[List[_ColorTuple]] = None,
+ ) -> None:
+ self.background_color = ColorTriplet(*background)
+ self.foreground_color = ColorTriplet(*foreground)
+ self.ansi_colors = Palette(normal + (bright or normal))
+
+
+DEFAULT_TERMINAL_THEME = TerminalTheme(
+ (255, 255, 255),
+ (0, 0, 0),
+ [
+ (0, 0, 0),
+ (128, 0, 0),
+ (0, 128, 0),
+ (128, 128, 0),
+ (0, 0, 128),
+ (128, 0, 128),
+ (0, 128, 128),
+ (192, 192, 192),
+ ],
+ [
+ (128, 128, 128),
+ (255, 0, 0),
+ (0, 255, 0),
+ (255, 255, 0),
+ (0, 0, 255),
+ (255, 0, 255),
+ (0, 255, 255),
+ (255, 255, 255),
+ ],
+)
diff --git a/src/pip/_vendor/rich/text.py b/src/pip/_vendor/rich/text.py
new file mode 100644
index 000000000..0ea6da5fe
--- /dev/null
+++ b/src/pip/_vendor/rich/text.py
@@ -0,0 +1,1196 @@
+import re
+from functools import partial, reduce
+from math import gcd
+from operator import attrgetter, itemgetter
+from pip._vendor.rich.emoji import EmojiVariant
+from typing import (
+ TYPE_CHECKING,
+ Any,
+ Callable,
+ Dict,
+ Iterable,
+ List,
+ NamedTuple,
+ Optional,
+ Tuple,
+ Union,
+)
+
+from ._loop import loop_last
+from ._pick import pick_bool
+from ._wrap import divide_line
+from .align import AlignMethod
+from .cells import cell_len, set_cell_size
+from .containers import Lines
+from .control import strip_control_codes
+from .emoji import EmojiVariant
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .segment import Segment
+from .style import Style, StyleType
+
+if TYPE_CHECKING: # pragma: no cover
+ from .console import Console, ConsoleOptions, JustifyMethod, OverflowMethod
+
+DEFAULT_JUSTIFY: "JustifyMethod" = "default"
+DEFAULT_OVERFLOW: "OverflowMethod" = "fold"
+
+
+_re_whitespace = re.compile(r"\s+$")
+
+TextType = Union[str, "Text"]
+
+GetStyleCallable = Callable[[str], Optional[StyleType]]
+
+
+class Span(NamedTuple):
+ """A marked up region in some text."""
+
+ start: int
+ """Span start index."""
+ end: int
+ """Span end index."""
+ style: Union[str, Style]
+ """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}, {str(self.style)!r})"
+ )
+
+ def __bool__(self) -> bool:
+ return self.end > self.start
+
+ def split(self, offset: int) -> Tuple["Span", Optional["Span"]]:
+ """Split a span in to 2 from a given offset."""
+
+ if offset < self.start:
+ return self, None
+ if offset >= self.end:
+ return self, None
+
+ start, end, style = self
+ span1 = Span(start, min(end, offset), style)
+ span2 = Span(span1.end, end, style)
+ return span1, span2
+
+ def move(self, offset: int) -> "Span":
+ """Move start and end by a given offset.
+
+ Args:
+ offset (int): Number of characters to add to start and end.
+
+ Returns:
+ TextSpan: A new TextSpan with adjusted position.
+ """
+ start, end, style = self
+ return Span(start + offset, end + offset, style)
+
+ def right_crop(self, offset: int) -> "Span":
+ """Crop the span at the given offset.
+
+ Args:
+ offset (int): A value between start and end.
+
+ Returns:
+ Span: A new (possibly smaller) span.
+ """
+ start, end, style = self
+ if offset >= end:
+ return self
+ return Span(start, min(offset, end), style)
+
+
+class Text(JupyterMixin):
+ """Text with color / style.
+
+ Args:
+ text (str, optional): Default unstyled text. Defaults to "".
+ style (Union[str, Style], optional): Base style for text. Defaults to "".
+ justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None.
+ overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None.
+ no_wrap (bool, optional): Disable text wrapping, or None for default. Defaults to None.
+ end (str, optional): Character to end text with. Defaults to "\\\\n".
+ tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to 8.
+ spans (List[Span], optional). A list of predefined style spans. Defaults to None.
+ """
+
+ __slots__ = [
+ "_text",
+ "style",
+ "justify",
+ "overflow",
+ "no_wrap",
+ "end",
+ "tab_size",
+ "_spans",
+ "_length",
+ ]
+
+ def __init__(
+ self,
+ text: str = "",
+ style: Union[str, Style] = "",
+ *,
+ justify: Optional["JustifyMethod"] = None,
+ overflow: Optional["OverflowMethod"] = None,
+ no_wrap: Optional[bool] = None,
+ end: str = "\n",
+ tab_size: Optional[int] = 8,
+ spans: Optional[List[Span]] = None,
+ ) -> None:
+ self._text = [strip_control_codes(text)]
+ self.style = style
+ self.justify: Optional["JustifyMethod"] = justify
+ self.overflow: Optional["OverflowMethod"] = overflow
+ self.no_wrap = no_wrap
+ self.end = end
+ self.tab_size = tab_size
+ self._spans: List[Span] = spans or []
+ self._length: int = len(text)
+
+ def __len__(self) -> int:
+ return self._length
+
+ def __bool__(self) -> bool:
+ return bool(self._length)
+
+ def __str__(self) -> str:
+ return self.plain
+
+ def __repr__(self) -> str:
+ return f"<text {self.plain!r} {self._spans!r}>"
+
+ def __add__(self, other: Any) -> "Text":
+ if isinstance(other, (str, Text)):
+ result = self.copy()
+ result.append(other)
+ return result
+ return NotImplemented
+
+ def __eq__(self, other: object) -> bool:
+ if not isinstance(other, Text):
+ return NotImplemented
+ return self.plain == other.plain and self._spans == other._spans
+
+ def __contains__(self, other: object) -> bool:
+ if isinstance(other, str):
+ return other in self.plain
+ elif isinstance(other, Text):
+ return other.plain in self.plain
+ return False
+
+ def __getitem__(self, slice: Union[int, slice]) -> "Text":
+ def get_text_at(offset: int) -> "Text":
+ _Span = Span
+ text = Text(
+ self.plain[offset],
+ spans=[
+ _Span(0, 1, style)
+ for start, end, style in self._spans
+ if end > offset >= start
+ ],
+ end="",
+ )
+ return text
+
+ if isinstance(slice, int):
+ return get_text_at(slice)
+ else:
+ start, stop, step = slice.indices(len(self.plain))
+ if step == 1:
+ lines = self.divide([start, stop])
+ return lines[1]
+ else:
+ # This would be a bit of work to implement efficiently
+ # For now, its not required
+ raise TypeError("slices with step!=1 are not supported")
+
+ @property
+ def cell_len(self) -> int:
+ """Get the number of cells required to render this text."""
+ return cell_len(self.plain)
+
+ @classmethod
+ def from_markup(
+ cls,
+ text: str,
+ *,
+ style: Union[str, Style] = "",
+ emoji: bool = True,
+ emoji_variant: Optional[EmojiVariant] = None,
+ justify: Optional["JustifyMethod"] = None,
+ overflow: Optional["OverflowMethod"] = None,
+ ) -> "Text":
+ """Create Text instance from markup.
+
+ Args:
+ text (str): A string containing console markup.
+ emoji (bool, optional): Also render emoji code. Defaults to True.
+ justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None.
+ overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None.
+
+ Returns:
+ Text: A Text instance with markup rendered.
+ """
+ from .markup import render
+
+ rendered_text = render(text, style, emoji=emoji, emoji_variant=emoji_variant)
+ rendered_text.justify = justify
+ rendered_text.overflow = overflow
+ return rendered_text
+
+ @classmethod
+ def styled(
+ cls,
+ text: str,
+ style: StyleType = "",
+ *,
+ justify: Optional["JustifyMethod"] = None,
+ overflow: Optional["OverflowMethod"] = None,
+ ) -> "Text":
+ """Construct a Text instance with a pre-applied styled. A style applied in this way won't be used
+ to pad the text when it is justified.
+
+ Args:
+ text (str): A string containing console markup.
+ style (Union[str, Style]): Style to apply to the text. Defaults to "".
+ justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None.
+ overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None.
+
+ Returns:
+ Text: A text instance with a style applied to the entire string.
+ """
+ styled_text = cls(text, justify=justify, overflow=overflow)
+ styled_text.stylize(style)
+ return styled_text
+
+ @classmethod
+ def assemble(
+ cls,
+ *parts: Union[str, "Text", Tuple[str, StyleType]],
+ style: Union[str, Style] = "",
+ justify: Optional["JustifyMethod"] = None,
+ overflow: Optional["OverflowMethod"] = None,
+ no_wrap: Optional[bool] = None,
+ end: str = "\n",
+ tab_size: int = 8,
+ meta: Optional[Dict[str, Any]] = None,
+ ) -> "Text":
+ """Construct a text instance by combining a sequence of strings with optional styles.
+ The positional arguments should be either strings, or a tuple of string + style.
+
+ Args:
+ style (Union[str, Style], optional): Base style for text. Defaults to "".
+ justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None.
+ overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None.
+ end (str, optional): Character to end text with. Defaults to "\\\\n".
+ tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to 8.
+ meta (Dict[str, Any], optional). Meta data to apply to text, or None for no meta data. Default to None
+
+ Returns:
+ Text: A new text instance.
+ """
+ text = cls(
+ style=style,
+ justify=justify,
+ overflow=overflow,
+ no_wrap=no_wrap,
+ end=end,
+ tab_size=tab_size,
+ )
+ append = text.append
+ _Text = Text
+ for part in parts:
+ if isinstance(part, (_Text, str)):
+ append(part)
+ else:
+ append(*part)
+ if meta:
+ text.apply_meta(meta)
+ return text
+
+ @property
+ def plain(self) -> str:
+ """Get the text as a single string."""
+ if len(self._text) != 1:
+ self._text[:] = ["".join(self._text)]
+ return self._text[0]
+
+ @plain.setter
+ def plain(self, new_text: str) -> None:
+ """Set the text to a new value."""
+ if new_text != self.plain:
+ self._text[:] = [new_text]
+ old_length = self._length
+ self._length = len(new_text)
+ if old_length > self._length:
+ self._trim_spans()
+
+ @property
+ def spans(self) -> List[Span]:
+ """Get a reference to the internal list of spans."""
+ return self._spans
+
+ @spans.setter
+ def spans(self, spans: List[Span]) -> None:
+ """Set spans."""
+ self._spans = spans[:]
+
+ def blank_copy(self, plain: str = "") -> "Text":
+ """Return a new Text instance with copied meta data (but not the string or spans)."""
+ copy_self = Text(
+ plain,
+ style=self.style,
+ justify=self.justify,
+ overflow=self.overflow,
+ no_wrap=self.no_wrap,
+ end=self.end,
+ tab_size=self.tab_size,
+ )
+ return copy_self
+
+ def copy(self) -> "Text":
+ """Return a copy of this instance."""
+ copy_self = Text(
+ self.plain,
+ style=self.style,
+ justify=self.justify,
+ overflow=self.overflow,
+ no_wrap=self.no_wrap,
+ end=self.end,
+ tab_size=self.tab_size,
+ )
+ copy_self._spans[:] = self._spans
+ return copy_self
+
+ def stylize(
+ self,
+ style: Union[str, Style],
+ start: int = 0,
+ end: Optional[int] = None,
+ ) -> None:
+ """Apply a style to the text, or a portion of the text.
+
+ Args:
+ style (Union[str, Style]): Style instance or style definition to apply.
+ start (int): Start offset (negative indexing is supported). Defaults to 0.
+ end (Optional[int], optional): End offset (negative indexing is supported), or None for end of text. Defaults to None.
+
+ """
+ if style:
+ length = len(self)
+ if start < 0:
+ start = length + start
+ if end is None:
+ end = length
+ if end < 0:
+ end = length + end
+ if start >= length or end <= start:
+ # Span not in text or not valid
+ return
+ self._spans.append(Span(start, min(length, end), style))
+
+ def apply_meta(
+ self, meta: Dict[str, Any], start: int = 0, end: Optional[int] = None
+ ) -> None:
+ """Apply meta data to the text, or a portion of the text.
+
+ Args:
+ meta (Dict[str, Any]): A dict of meta information.
+ start (int): Start offset (negative indexing is supported). Defaults to 0.
+ end (Optional[int], optional): End offset (negative indexing is supported), or None for end of text. Defaults to None.
+
+ """
+ style = Style.from_meta(meta)
+ self.stylize(style, start=start, end=end)
+
+ def on(self, meta: Optional[Dict[str, Any]] = None, **handlers: Any) -> "Text":
+ """Apply event handlers (used by Textual project).
+
+ Example:
+ >>> from rich.text import Text
+ >>> text = Text("hello world")
+ >>> text.on(click="view.toggle('world')")
+
+ Args:
+ meta (Dict[str, Any]): Mapping of meta information.
+ **handlers: Keyword args are prefixed with "@" to defined handlers.
+
+ Returns:
+ Text: Self is returned to method may be chained.
+ """
+ meta = {} if meta is None else meta
+ meta.update({f"@{key}": value for key, value in handlers.items()})
+ self.stylize(Style.from_meta(meta))
+ return self
+
+ def remove_suffix(self, suffix: str) -> None:
+ """Remove a suffix if it exists.
+
+ Args:
+ suffix (str): Suffix to remove.
+ """
+ if self.plain.endswith(suffix):
+ self.right_crop(len(suffix))
+
+ def get_style_at_offset(self, console: "Console", offset: int) -> Style:
+ """Get the style of a character at give offset.
+
+ Args:
+ console (~Console): Console where text will be rendered.
+ offset (int): Offset in to text (negative indexing supported)
+
+ Returns:
+ Style: A Style instance.
+ """
+ # TODO: This is a little inefficient, it is only used by full justify
+ if offset < 0:
+ offset = len(self) + offset
+ get_style = console.get_style
+ style = get_style(self.style).copy()
+ for start, end, span_style in self._spans:
+ if end > offset >= start:
+ style += get_style(span_style, default="")
+ return style
+
+ def highlight_regex(
+ self,
+ re_highlight: str,
+ style: Optional[Union[GetStyleCallable, StyleType]] = None,
+ *,
+ style_prefix: str = "",
+ ) -> int:
+ """Highlight text with a regular expression, where group names are
+ translated to styles.
+
+ Args:
+ re_highlight (str): A regular expression.
+ style (Union[GetStyleCallable, StyleType]): Optional style to apply to whole match, or a callable
+ which accepts the matched text and returns a style. Defaults to None.
+ style_prefix (str, optional): Optional prefix to add to style group names.
+
+ Returns:
+ int: Number of regex matches
+ """
+ count = 0
+ append_span = self._spans.append
+ _Span = Span
+ plain = self.plain
+ for match in re.finditer(re_highlight, plain):
+ get_span = match.span
+ if style:
+ start, end = get_span()
+ match_style = style(plain[start:end]) if callable(style) else style
+ if match_style is not None and end > start:
+ append_span(_Span(start, end, match_style))
+
+ count += 1
+ for name in match.groupdict().keys():
+ start, end = get_span(name)
+ if start != -1 and end > start:
+ append_span(_Span(start, end, f"{style_prefix}{name}"))
+ return count
+
+ def highlight_words(
+ self,
+ words: Iterable[str],
+ style: Union[str, Style],
+ *,
+ case_sensitive: bool = True,
+ ) -> int:
+ """Highlight words with a style.
+
+ Args:
+ words (Iterable[str]): Worlds to highlight.
+ style (Union[str, Style]): Style to apply.
+ case_sensitive (bool, optional): Enable case sensitive matchings. Defaults to True.
+
+ Returns:
+ int: Number of words highlighted.
+ """
+ re_words = "|".join(re.escape(word) for word in words)
+ add_span = self._spans.append
+ count = 0
+ _Span = Span
+ for match in re.finditer(
+ re_words, self.plain, flags=0 if case_sensitive else re.IGNORECASE
+ ):
+ start, end = match.span(0)
+ add_span(_Span(start, end, style))
+ count += 1
+ return count
+
+ def rstrip(self) -> None:
+ """Strip whitespace from end of text."""
+ self.plain = self.plain.rstrip()
+
+ def rstrip_end(self, size: int) -> None:
+ """Remove whitespace beyond a certain width at the end of the text.
+
+ Args:
+ size (int): The desired size of the text.
+ """
+ text_length = len(self)
+ if text_length > size:
+ excess = text_length - size
+ whitespace_match = _re_whitespace.search(self.plain)
+ if whitespace_match is not None:
+ whitespace_count = len(whitespace_match.group(0))
+ self.right_crop(min(whitespace_count, excess))
+
+ def set_length(self, new_length: int) -> None:
+ """Set new length of the text, clipping or padding is required."""
+ length = len(self)
+ if length != new_length:
+ if length < new_length:
+ self.pad_right(new_length - length)
+ else:
+ self.right_crop(length - new_length)
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> Iterable[Segment]:
+ tab_size: int = console.tab_size or self.tab_size or 8
+ justify = self.justify or options.justify or DEFAULT_JUSTIFY
+
+ overflow = self.overflow or options.overflow or DEFAULT_OVERFLOW
+
+ lines = self.wrap(
+ console,
+ options.max_width,
+ justify=justify,
+ overflow=overflow,
+ tab_size=tab_size or 8,
+ no_wrap=pick_bool(self.no_wrap, options.no_wrap, False),
+ )
+ all_lines = Text("\n").join(lines)
+ yield from all_lines.render(console, end=self.end)
+
+ def __rich_measure__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> Measurement:
+ text = self.plain
+ lines = text.splitlines()
+ max_text_width = max(cell_len(line) for line in lines) if lines else 0
+ words = text.split()
+ min_text_width = (
+ max(cell_len(word) for word in words) if words else max_text_width
+ )
+ return Measurement(min_text_width, max_text_width)
+
+ def render(self, console: "Console", end: str = "") -> Iterable["Segment"]:
+ """Render the text as Segments.
+
+ Args:
+ console (Console): Console instance.
+ end (Optional[str], optional): Optional end character.
+
+ Returns:
+ Iterable[Segment]: Result of render that may be written to the console.
+ """
+ _Segment = Segment
+ text = self.plain
+ if not self._spans:
+ yield Segment(text)
+ if end:
+ yield _Segment(end)
+ return
+ get_style = partial(console.get_style, default=Style.null())
+
+ enumerated_spans = list(enumerate(self._spans, 1))
+ style_map = {index: get_style(span.style) for index, span in enumerated_spans}
+ style_map[0] = get_style(self.style)
+
+ spans = [
+ (0, False, 0),
+ *((span.start, False, index) for index, span in enumerated_spans),
+ *((span.end, True, index) for index, span in enumerated_spans),
+ (len(text), True, 0),
+ ]
+ spans.sort(key=itemgetter(0, 1))
+
+ stack: List[int] = []
+ stack_append = stack.append
+ stack_pop = stack.remove
+
+ style_cache: Dict[Tuple[Style, ...], Style] = {}
+ style_cache_get = style_cache.get
+ combine = Style.combine
+
+ def get_current_style() -> Style:
+ """Construct current style from stack."""
+ styles = tuple(style_map[_style_id] for _style_id in sorted(stack))
+ cached_style = style_cache_get(styles)
+ if cached_style is not None:
+ return cached_style
+ current_style = combine(styles)
+ style_cache[styles] = current_style
+ return current_style
+
+ for (offset, leaving, style_id), (next_offset, _, _) in zip(spans, spans[1:]):
+ if leaving:
+ stack_pop(style_id)
+ else:
+ stack_append(style_id)
+ if next_offset > offset:
+ yield _Segment(text[offset:next_offset], get_current_style())
+ if end:
+ yield _Segment(end)
+
+ def join(self, lines: Iterable["Text"]) -> "Text":
+ """Join text together with this instance as the separator.
+
+ Args:
+ lines (Iterable[Text]): An iterable of Text instances to join.
+
+ Returns:
+ Text: A new text instance containing join text.
+ """
+
+ new_text = self.blank_copy()
+
+ def iter_text() -> Iterable["Text"]:
+ if self.plain:
+ for last, line in loop_last(lines):
+ yield line
+ if not last:
+ yield self
+ else:
+ yield from lines
+
+ extend_text = new_text._text.extend
+ append_span = new_text._spans.append
+ extend_spans = new_text._spans.extend
+ offset = 0
+ _Span = Span
+
+ for text in iter_text():
+ extend_text(text._text)
+ if text.style:
+ append_span(_Span(offset, offset + len(text), text.style))
+ extend_spans(
+ _Span(offset + start, offset + end, style)
+ for start, end, style in text._spans
+ )
+ offset += len(text)
+ new_text._length = offset
+ return new_text
+
+ def expand_tabs(self, tab_size: Optional[int] = None) -> None:
+ """Converts tabs to spaces.
+
+ Args:
+ tab_size (int, optional): Size of tabs. Defaults to 8.
+
+ """
+ if "\t" not in self.plain:
+ return
+ pos = 0
+ if tab_size is None:
+ tab_size = self.tab_size
+ assert tab_size is not None
+ result = self.blank_copy()
+ append = result.append
+
+ _style = self.style
+ for line in self.split("\n", include_separator=True):
+ parts = line.split("\t", include_separator=True)
+ for part in parts:
+ if part.plain.endswith("\t"):
+ part._text = [part.plain[:-1] + " "]
+ append(part)
+ pos += len(part)
+ spaces = tab_size - ((pos - 1) % tab_size) - 1
+ if spaces:
+ append(" " * spaces, _style)
+ pos += spaces
+ else:
+ append(part)
+ self._text = [result.plain]
+ self._length = len(self.plain)
+ self._spans[:] = result._spans
+
+ def truncate(
+ self,
+ max_width: int,
+ *,
+ overflow: Optional["OverflowMethod"] = None,
+ pad: bool = False,
+ ) -> None:
+ """Truncate text if it is longer that a given width.
+
+ Args:
+ max_width (int): Maximum number of characters in text.
+ overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to None, to use self.overflow.
+ pad (bool, optional): Pad with spaces if the length is less than max_width. Defaults to False.
+ """
+ _overflow = overflow or self.overflow or DEFAULT_OVERFLOW
+ if _overflow != "ignore":
+ length = cell_len(self.plain)
+ if length > max_width:
+ if _overflow == "ellipsis":
+ self.plain = set_cell_size(self.plain, max_width - 1) + "…"
+ else:
+ self.plain = set_cell_size(self.plain, max_width)
+ if pad and length < max_width:
+ spaces = max_width - length
+ self._text = [f"{self.plain}{' ' * spaces}"]
+ self._length = len(self.plain)
+
+ def _trim_spans(self) -> None:
+ """Remove or modify any spans that are over the end of the text."""
+ max_offset = len(self.plain)
+ _Span = Span
+ self._spans[:] = [
+ (
+ span
+ if span.end < max_offset
+ else _Span(span.start, min(max_offset, span.end), span.style)
+ )
+ for span in self._spans
+ if span.start < max_offset
+ ]
+
+ def pad(self, count: int, character: str = " ") -> None:
+ """Pad left and right with a given number of characters.
+
+ Args:
+ count (int): Width of padding.
+ """
+ assert len(character) == 1, "Character must be a string of length 1"
+ if count:
+ pad_characters = character * count
+ self.plain = f"{pad_characters}{self.plain}{pad_characters}"
+ _Span = Span
+ self._spans[:] = [
+ _Span(start + count, end + count, style)
+ for start, end, style in self._spans
+ ]
+
+ def pad_left(self, count: int, character: str = " ") -> None:
+ """Pad the left with a given character.
+
+ Args:
+ count (int): Number of characters to pad.
+ character (str, optional): Character to pad with. Defaults to " ".
+ """
+ assert len(character) == 1, "Character must be a string of length 1"
+ if count:
+ self.plain = f"{character * count}{self.plain}"
+ _Span = Span
+ self._spans[:] = [
+ _Span(start + count, end + count, style)
+ for start, end, style in self._spans
+ ]
+
+ def pad_right(self, count: int, character: str = " ") -> None:
+ """Pad the right with a given character.
+
+ Args:
+ count (int): Number of characters to pad.
+ character (str, optional): Character to pad with. Defaults to " ".
+ """
+ assert len(character) == 1, "Character must be a string of length 1"
+ if count:
+ self.plain = f"{self.plain}{character * count}"
+
+ def align(self, align: AlignMethod, width: int, character: str = " ") -> None:
+ """Align text to a given width.
+
+ Args:
+ align (AlignMethod): One of "left", "center", or "right".
+ width (int): Desired width.
+ character (str, optional): Character to pad with. Defaults to " ".
+ """
+ self.truncate(width)
+ excess_space = width - cell_len(self.plain)
+ if excess_space:
+ if align == "left":
+ self.pad_right(excess_space, character)
+ elif align == "center":
+ left = excess_space // 2
+ self.pad_left(left, character)
+ self.pad_right(excess_space - left, character)
+ else:
+ self.pad_left(excess_space, character)
+
+ def append(
+ self, text: Union["Text", str], style: Optional[Union[str, "Style"]] = None
+ ) -> "Text":
+ """Add text with an optional style.
+
+ Args:
+ text (Union[Text, str]): A str or Text to append.
+ style (str, optional): A style name. Defaults to None.
+
+ Returns:
+ Text: Returns self for chaining.
+ """
+
+ if not isinstance(text, (str, Text)):
+ raise TypeError("Only str or Text can be appended to Text")
+
+ if len(text):
+ if isinstance(text, str):
+ text = strip_control_codes(text)
+ self._text.append(text)
+ offset = len(self)
+ text_length = len(text)
+ if style is not None:
+ self._spans.append(Span(offset, offset + text_length, style))
+ self._length += text_length
+ elif isinstance(text, Text):
+ _Span = Span
+ if style is not None:
+ raise ValueError(
+ "style must not be set when appending Text instance"
+ )
+ text_length = self._length
+ if text.style is not None:
+ self._spans.append(
+ _Span(text_length, text_length + len(text), text.style)
+ )
+ self._text.append(text.plain)
+ self._spans.extend(
+ _Span(start + text_length, end + text_length, style)
+ for start, end, style in text._spans
+ )
+ self._length += len(text)
+ return self
+
+ def append_text(self, text: "Text") -> "Text":
+ """Append another Text instance. This method is more performant that Text.append, but
+ only works for Text.
+
+ Returns:
+ Text: Returns self for chaining.
+ """
+ _Span = Span
+ text_length = self._length
+ if text.style is not None:
+ self._spans.append(_Span(text_length, text_length + len(text), text.style))
+ self._text.append(text.plain)
+ self._spans.extend(
+ _Span(start + text_length, end + text_length, style)
+ for start, end, style in text._spans
+ )
+ self._length += len(text)
+ return self
+
+ def append_tokens(
+ self, tokens: Iterable[Tuple[str, Optional[StyleType]]]
+ ) -> "Text":
+ """Append iterable of str and style. Style may be a Style instance or a str style definition.
+
+ Args:
+ pairs (Iterable[Tuple[str, Optional[StyleType]]]): An iterable of tuples containing str content and style.
+
+ Returns:
+ Text: Returns self for chaining.
+ """
+ append_text = self._text.append
+ append_span = self._spans.append
+ _Span = Span
+ offset = len(self)
+ for content, style in tokens:
+ append_text(content)
+ if style is not None:
+ append_span(_Span(offset, offset + len(content), style))
+ offset += len(content)
+ self._length = offset
+ return self
+
+ def copy_styles(self, text: "Text") -> None:
+ """Copy styles from another Text instance.
+
+ Args:
+ text (Text): A Text instance to copy styles from, must be the same length.
+ """
+ self._spans.extend(text._spans)
+
+ def split(
+ self,
+ separator: str = "\n",
+ *,
+ include_separator: bool = False,
+ allow_blank: bool = False,
+ ) -> Lines:
+ """Split rich text in to lines, preserving styles.
+
+ Args:
+ separator (str, optional): String to split on. Defaults to "\\\\n".
+ include_separator (bool, optional): Include the separator in the lines. Defaults to False.
+ allow_blank (bool, optional): Return a blank line if the text ends with a separator. Defaults to False.
+
+ Returns:
+ List[RichText]: A list of rich text, one per line of the original.
+ """
+ assert separator, "separator must not be empty"
+
+ text = self.plain
+ if separator not in text:
+ return Lines([self.copy()])
+
+ if include_separator:
+ lines = self.divide(
+ match.end() for match in re.finditer(re.escape(separator), text)
+ )
+ else:
+
+ def flatten_spans() -> Iterable[int]:
+ for match in re.finditer(re.escape(separator), text):
+ start, end = match.span()
+ yield start
+ yield end
+
+ lines = Lines(
+ line for line in self.divide(flatten_spans()) if line.plain != separator
+ )
+
+ if not allow_blank and text.endswith(separator):
+ lines.pop()
+
+ return lines
+
+ def divide(self, offsets: Iterable[int]) -> Lines:
+ """Divide text in to a number of lines at given offsets.
+
+ Args:
+ offsets (Iterable[int]): Offsets used to divide text.
+
+ Returns:
+ Lines: New RichText instances between offsets.
+ """
+ _offsets = list(offsets)
+ if not _offsets:
+ return Lines([self.copy()])
+
+ text = self.plain
+ text_length = len(text)
+ divide_offsets = [0, *_offsets, text_length]
+ line_ranges = list(zip(divide_offsets, divide_offsets[1:]))
+
+ style = self.style
+ justify = self.justify
+ overflow = self.overflow
+ _Text = Text
+ new_lines = Lines(
+ _Text(
+ text[start:end],
+ style=style,
+ justify=justify,
+ overflow=overflow,
+ )
+ for start, end in line_ranges
+ )
+ if not self._spans:
+ return new_lines
+ order = {span: span_index for span_index, span in enumerate(self._spans)}
+ span_stack = sorted(self._spans, key=attrgetter("start"), reverse=True)
+
+ pop = span_stack.pop
+ push = span_stack.append
+ _Span = Span
+ get_order = order.__getitem__
+
+ for line, (start, end) in zip(new_lines, line_ranges):
+ if not span_stack:
+ break
+ append_span = line._spans.append
+ position = len(span_stack) - 1
+ while span_stack[position].start < end:
+ span = pop(position)
+ add_span, remaining_span = span.split(end)
+ if remaining_span:
+ push(remaining_span)
+ order[remaining_span] = order[span]
+ span_start, span_end, span_style = add_span
+ line_span = _Span(span_start - start, span_end - start, span_style)
+ order[line_span] = order[span]
+ append_span(line_span)
+ position -= 1
+ if position < 0 or not span_stack:
+ break # pragma: no cover
+ line._spans.sort(key=get_order)
+
+ return new_lines
+
+ def right_crop(self, amount: int = 1) -> None:
+ """Remove a number of characters from the end of the text."""
+ max_offset = len(self.plain) - amount
+ _Span = Span
+ self._spans[:] = [
+ (
+ span
+ if span.end < max_offset
+ else _Span(span.start, min(max_offset, span.end), span.style)
+ )
+ for span in self._spans
+ if span.start < max_offset
+ ]
+ self._text = [self.plain[:-amount]]
+ self._length -= amount
+
+ def wrap(
+ self,
+ console: "Console",
+ width: int,
+ *,
+ justify: Optional["JustifyMethod"] = None,
+ overflow: Optional["OverflowMethod"] = None,
+ tab_size: int = 8,
+ no_wrap: Optional[bool] = None,
+ ) -> Lines:
+ """Word wrap the text.
+
+ Args:
+ console (Console): Console instance.
+ width (int): Number of characters per line.
+ emoji (bool, optional): Also render emoji code. Defaults to True.
+ justify (str, optional): Justify method: "default", "left", "center", "full", "right". Defaults to "default".
+ overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to None.
+ tab_size (int, optional): Default tab size. Defaults to 8.
+ no_wrap (bool, optional): Disable wrapping, Defaults to False.
+
+ Returns:
+ Lines: Number of lines.
+ """
+ wrap_justify = justify or self.justify or DEFAULT_JUSTIFY
+ wrap_overflow = overflow or self.overflow or DEFAULT_OVERFLOW
+
+ no_wrap = pick_bool(no_wrap, self.no_wrap, False) or overflow == "ignore"
+
+ lines = Lines()
+ for line in self.split(allow_blank=True):
+ if "\t" in line:
+ line.expand_tabs(tab_size)
+ if no_wrap:
+ new_lines = Lines([line])
+ else:
+ offsets = divide_line(str(line), width, fold=wrap_overflow == "fold")
+ new_lines = line.divide(offsets)
+ for line in new_lines:
+ line.rstrip_end(width)
+ if wrap_justify:
+ new_lines.justify(
+ console, width, justify=wrap_justify, overflow=wrap_overflow
+ )
+ for line in new_lines:
+ line.truncate(width, overflow=wrap_overflow)
+ lines.extend(new_lines)
+ return lines
+
+ def fit(self, width: int) -> Lines:
+ """Fit the text in to given width by chopping in to lines.
+
+ Args:
+ width (int): Maximum characters in a line.
+
+ Returns:
+ Lines: List of lines.
+ """
+ lines: Lines = Lines()
+ append = lines.append
+ for line in self.split():
+ line.set_length(width)
+ append(line)
+ return lines
+
+ def detect_indentation(self) -> int:
+ """Auto-detect indentation of code.
+
+ Returns:
+ int: Number of spaces used to indent code.
+ """
+
+ _indentations = {
+ len(match.group(1))
+ for match in re.finditer(r"^( *)(.*)$", self.plain, flags=re.MULTILINE)
+ }
+
+ try:
+ indentation = (
+ reduce(gcd, [indent for indent in _indentations if not indent % 2]) or 1
+ )
+ except TypeError:
+ indentation = 1
+
+ return indentation
+
+ def with_indent_guides(
+ self,
+ indent_size: Optional[int] = None,
+ *,
+ character: str = "│",
+ style: StyleType = "dim green",
+ ) -> "Text":
+ """Adds indent guide lines to text.
+
+ Args:
+ indent_size (Optional[int]): Size of indentation, or None to auto detect. Defaults to None.
+ character (str, optional): Character to use for indentation. Defaults to "│".
+ style (Union[Style, str], optional): Style of indent guides.
+
+ Returns:
+ Text: New text with indentation guides.
+ """
+
+ _indent_size = self.detect_indentation() if indent_size is None else indent_size
+
+ text = self.copy()
+ text.expand_tabs()
+ indent_line = f"{character}{' ' * (_indent_size - 1)}"
+
+ re_indent = re.compile(r"^( *)(.*)$")
+ new_lines: List[Text] = []
+ add_line = new_lines.append
+ blank_lines = 0
+ for line in text.split(allow_blank=True):
+ match = re_indent.match(line.plain)
+ if not match or not match.group(2):
+ blank_lines += 1
+ continue
+ indent = match.group(1)
+ full_indents, remaining_space = divmod(len(indent), _indent_size)
+ new_indent = f"{indent_line * full_indents}{' ' * remaining_space}"
+ line.plain = new_indent + line.plain[len(new_indent) :]
+ line.stylize(style, 0, len(new_indent))
+ if blank_lines:
+ new_lines.extend([Text(new_indent, style=style)] * blank_lines)
+ blank_lines = 0
+ add_line(line)
+ if blank_lines:
+ new_lines.extend([Text("", style=style)] * blank_lines)
+
+ new_text = text.blank_copy("\n").join(new_lines)
+ return new_text
+
+
+if __name__ == "__main__": # pragma: no cover
+ from pip._vendor.rich.console import Console
+
+ text = Text(
+ """\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n"""
+ )
+ text.highlight_words(["Lorem"], "bold")
+ text.highlight_words(["ipsum"], "italic")
+
+ console = Console()
+ console.rule("justify='left'")
+ console.print(text, style="red")
+ console.print()
+
+ console.rule("justify='center'")
+ console.print(text, style="green", justify="center")
+ console.print()
+
+ console.rule("justify='right'")
+ console.print(text, style="blue", justify="right")
+ console.print()
+
+ console.rule("justify='full'")
+ console.print(text, style="magenta", justify="full")
+ console.print()
diff --git a/src/pip/_vendor/rich/theme.py b/src/pip/_vendor/rich/theme.py
new file mode 100644
index 000000000..bfb3c7f82
--- /dev/null
+++ b/src/pip/_vendor/rich/theme.py
@@ -0,0 +1,112 @@
+import configparser
+from typing import Dict, List, IO, Mapping, Optional
+
+from .default_styles import DEFAULT_STYLES
+from .style import Style, StyleType
+
+
+class Theme:
+ """A container for style information, used by :class:`~rich.console.Console`.
+
+ Args:
+ styles (Dict[str, Style], optional): A mapping of style names on to styles. Defaults to None for a theme with no styles.
+ inherit (bool, optional): Inherit default styles. Defaults to True.
+ """
+
+ styles: Dict[str, Style]
+
+ def __init__(
+ self, styles: Optional[Mapping[str, StyleType]] = None, inherit: bool = True
+ ):
+ self.styles = DEFAULT_STYLES.copy() if inherit else {}
+ if styles is not None:
+ self.styles.update(
+ {
+ name: style if isinstance(style, Style) else Style.parse(style)
+ for name, style in styles.items()
+ }
+ )
+
+ @property
+ def config(self) -> str:
+ """Get contents of a config file for this theme."""
+ config = "[styles]\n" + "\n".join(
+ f"{name} = {style}" for name, style in sorted(self.styles.items())
+ )
+ return config
+
+ @classmethod
+ def from_file(
+ cls, config_file: IO[str], source: Optional[str] = None, inherit: bool = True
+ ) -> "Theme":
+ """Load a theme from a text mode file.
+
+ Args:
+ config_file (IO[str]): An open conf file.
+ source (str, optional): The filename of the open file. Defaults to None.
+ inherit (bool, optional): Inherit default styles. Defaults to True.
+
+ Returns:
+ Theme: A New theme instance.
+ """
+ config = configparser.ConfigParser()
+ config.read_file(config_file, source=source)
+ styles = {name: Style.parse(value) for name, value in config.items("styles")}
+ theme = Theme(styles, inherit=inherit)
+ return theme
+
+ @classmethod
+ def read(cls, path: str, inherit: bool = True) -> "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.
+
+ Returns:
+ Theme: A new theme instance.
+ """
+ with open(path, "rt") as config_file:
+ return cls.from_file(config_file, source=path, inherit=inherit)
+
+
+class ThemeStackError(Exception):
+ """Base exception for errors related to the theme stack."""
+
+
+class ThemeStack:
+ """A stack of themes.
+
+ Args:
+ theme (Theme): A theme instance
+ """
+
+ def __init__(self, theme: Theme) -> None:
+ self._entries: List[Dict[str, Style]] = [theme.styles]
+ self.get = self._entries[-1].get
+
+ def push_theme(self, theme: Theme, inherit: bool = True) -> None:
+ """Push a theme on the top of the stack.
+
+ Args:
+ theme (Theme): A Theme instance.
+ inherit (boolean, optional): Inherit styles from current top of stack.
+ """
+ styles: Dict[str, Style]
+ styles = (
+ {**self._entries[-1], **theme.styles} if inherit else theme.styles.copy()
+ )
+ self._entries.append(styles)
+ self.get = self._entries[-1].get
+
+ def pop_theme(self) -> None:
+ """Pop (and discard) the top-most theme."""
+ if len(self._entries) == 1:
+ raise ThemeStackError("Unable to pop base theme")
+ self._entries.pop()
+ self.get = self._entries[-1].get
+
+
+if __name__ == "__main__": # pragma: no cover
+ theme = Theme()
+ print(theme.config)
diff --git a/src/pip/_vendor/rich/themes.py b/src/pip/_vendor/rich/themes.py
new file mode 100644
index 000000000..bf6db104a
--- /dev/null
+++ b/src/pip/_vendor/rich/themes.py
@@ -0,0 +1,5 @@
+from .default_styles import DEFAULT_STYLES
+from .theme import Theme
+
+
+DEFAULT = Theme(DEFAULT_STYLES)
diff --git a/src/pip/_vendor/rich/traceback.py b/src/pip/_vendor/rich/traceback.py
new file mode 100644
index 000000000..dbb9f1f70
--- /dev/null
+++ b/src/pip/_vendor/rich/traceback.py
@@ -0,0 +1,680 @@
+from __future__ import absolute_import
+
+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 pip._vendor.pygments.lexers import guess_lexer_for_filename
+from pip._vendor.pygments.token import Comment, Keyword, Name, Number, Operator, String
+from pip._vendor.pygments.token import Text as TextToken
+from pip._vendor.pygments.token import Token
+
+from . import pretty
+from ._loop import loop_first, loop_last
+from .columns import Columns
+from .console import (
+ Console,
+ ConsoleOptions,
+ ConsoleRenderable,
+ RenderResult,
+ group,
+)
+from .constrain import Constrain
+from .highlighter import RegexHighlighter, ReprHighlighter
+from .panel import Panel
+from .scope import render_scope
+from .style import Style
+from .syntax import Syntax
+from .text import Text
+from .theme import Theme
+
+WINDOWS = platform.system() == "Windows"
+
+LOCALS_MAX_LENGTH = 10
+LOCALS_MAX_STRING = 80
+
+
+def install(
+ *,
+ console: Optional[Console] = 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,
+ suppress: Iterable[Union[str, ModuleType]] = (),
+ max_frames: int = 100,
+) -> Callable[[Type[BaseException], BaseException, Optional[TracebackType]], Any]:
+ """Install a rich traceback handler.
+
+ Once installed, any tracebacks will be printed with syntax highlighting and rich formatting.
+
+
+ Args:
+ console (Optional[Console], optional): Console to write exception to. Default uses internal Console instance.
+ width (Optional[int], optional): Width (in characters) of traceback. Defaults to 100.
+ extra_lines (int, optional): Extra lines of code. Defaults to 3.
+ theme (Optional[str], optional): Pygments theme to use in traceback. Defaults to ``None`` which will pick
+ 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.
+ 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.
+
+ Returns:
+ Callable: The previous exception handler that was replaced.
+
+ """
+ traceback_console = Console(file=sys.stderr) if console is None else console
+
+ def excepthook(
+ type_: Type[BaseException],
+ value: BaseException,
+ traceback: Optional[TracebackType],
+ ) -> None:
+ traceback_console.print(
+ Traceback.from_exception(
+ type_,
+ value,
+ traceback,
+ width=width,
+ extra_lines=extra_lines,
+ theme=theme,
+ word_wrap=word_wrap,
+ show_locals=show_locals,
+ indent_guides=indent_guides,
+ suppress=suppress,
+ max_frames=max_frames,
+ )
+ )
+
+ def ipy_excepthook_closure(ip: Any) -> None: # pragma: no cover
+ tb_data = {} # store information about showtraceback call
+ default_showtraceback = ip.showtraceback # keep reference of default traceback
+
+ def ipy_show_traceback(*args: Any, **kwargs: Any) -> None:
+ """wrap the default ip.showtraceback to store info for ip._showtraceback"""
+ nonlocal tb_data
+ tb_data = kwargs
+ default_showtraceback(*args, **kwargs)
+
+ def ipy_display_traceback(
+ *args: Any, is_syntax: bool = False, **kwargs: Any
+ ) -> None:
+ """Internally called traceback from ip._showtraceback"""
+ nonlocal tb_data
+ exc_tuple = ip._get_exc_info()
+
+ # do not display trace on syntax error
+ tb: Optional[TracebackType] = None if is_syntax else exc_tuple[2]
+
+ # determine correct tb_offset
+ compiled = tb_data.get("running_compiled_code", False)
+ tb_offset = tb_data.get("tb_offset", 1 if compiled else 0)
+ # remove ipython internal frames from trace with tb_offset
+ for _ in range(tb_offset):
+ if tb is None:
+ break
+ tb = tb.tb_next
+
+ excepthook(exc_tuple[0], exc_tuple[1], tb)
+ tb_data = {} # clear data upon usage
+
+ # replace _showtraceback instead of showtraceback to allow ipython features such as debugging to work
+ # this is also what the ipython docs recommends to modify when subclassing InteractiveShell
+ ip._showtraceback = ipy_display_traceback
+ # add wrapper to capture tb_data
+ ip.showtraceback = ipy_show_traceback
+ ip.showsyntaxerror = lambda *args, **kwargs: ipy_display_traceback(
+ *args, is_syntax=True, **kwargs
+ )
+
+ try: # pragma: no cover
+ # if wihin ipython, use customized traceback
+ ip = get_ipython() # type: ignore
+ ipy_excepthook_closure(ip)
+ return sys.excepthook # type: ignore # more strict signature that mypy can't interpret
+ except Exception:
+ # otherwise use default system hook
+ old_excepthook = sys.excepthook
+ sys.excepthook = excepthook
+ return old_excepthook # type: ignore # more strict signature that mypy can't interpret
+
+
+@dataclass
+class Frame:
+ filename: str
+ lineno: int
+ name: str
+ line: str = ""
+ locals: Optional[Dict[str, pretty.Node]] = None
+
+
+@dataclass
+class _SyntaxError:
+ offset: int
+ filename: str
+ line: str
+ lineno: int
+ msg: str
+
+
+@dataclass
+class Stack:
+ exc_type: str
+ exc_value: str
+ syntax_error: Optional[_SyntaxError] = None
+ is_cause: bool = False
+ frames: List[Frame] = field(default_factory=list)
+
+
+@dataclass
+class Trace:
+ stacks: List[Stack]
+
+
+class PathHighlighter(RegexHighlighter):
+ highlights = [r"(?P<dim>.*/)(?P<bold>.+)"]
+
+
+class Traceback:
+ """A Console renderable that renders a traceback.
+
+ Args:
+ trace (Trace, optional): A `Trace` object produced from `extract`. Defaults to None, which uses
+ the last exception.
+ width (Optional[int], optional): Number of characters used to traceback. Defaults to 100.
+ extra_lines (int, optional): Additional lines of code to render. Defaults to 3.
+ theme (str, optional): Override pygments theme used in traceback.
+ 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.
+ indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
+ 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.
+ 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.
+
+ """
+
+ LEXERS = {
+ "": "text",
+ ".py": "python",
+ ".pxd": "cython",
+ ".pyx": "cython",
+ ".pxi": "pyrex",
+ }
+
+ 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,
+ suppress: Iterable[Union[str, ModuleType]] = (),
+ max_frames: int = 100,
+ ):
+ if trace is None:
+ exc_type, exc_value, traceback = sys.exc_info()
+ if exc_type is None or exc_value is None or traceback is None:
+ raise ValueError(
+ "Value for 'trace' required if not called in except: block"
+ )
+ trace = self.extract(
+ exc_type, exc_value, traceback, show_locals=show_locals
+ )
+ self.trace = trace
+ self.width = width
+ self.extra_lines = extra_lines
+ self.theme = Syntax.get_theme(theme or "ansi_dark")
+ self.word_wrap = word_wrap
+ self.show_locals = show_locals
+ self.indent_guides = indent_guides
+ self.locals_max_length = locals_max_length
+ self.locals_max_string = locals_max_string
+
+ self.suppress: Sequence[str] = []
+ for suppress_entity in suppress:
+ if not isinstance(suppress_entity, str):
+ path = os.path.dirname(suppress_entity.__file__)
+ else:
+ path = suppress_entity
+ path = os.path.normpath(os.path.abspath(path))
+ self.suppress.append(path)
+ self.max_frames = max(4, max_frames) if max_frames > 0 else 0
+
+ @classmethod
+ def from_exception(
+ cls,
+ 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,
+ suppress: Iterable[Union[str, ModuleType]] = (),
+ max_frames: int = 100,
+ ) -> "Traceback":
+ """Create a traceback from exception info
+
+ Args:
+ exc_type (Type[BaseException]): Exception type.
+ exc_value (BaseException): Exception value.
+ traceback (TracebackType): Python Traceback object.
+ width (Optional[int], optional): Number of characters used to traceback. Defaults to 100.
+ extra_lines (int, optional): Additional lines of code to render. Defaults to 3.
+ theme (str, optional): Override pygments theme used in traceback.
+ 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.
+ indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
+ 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.
+ 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.
+
+ Returns:
+ Traceback: A Traceback instance that may be printed.
+ """
+ rich_traceback = cls.extract(
+ exc_type, exc_value, traceback, show_locals=show_locals
+ )
+ return cls(
+ rich_traceback,
+ width=width,
+ extra_lines=extra_lines,
+ theme=theme,
+ word_wrap=word_wrap,
+ show_locals=show_locals,
+ indent_guides=indent_guides,
+ locals_max_length=locals_max_length,
+ locals_max_string=locals_max_string,
+ suppress=suppress,
+ max_frames=max_frames,
+ )
+
+ @classmethod
+ def extract(
+ cls,
+ 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,
+ ) -> Trace:
+ """Extract traceback information.
+
+ Args:
+ exc_type (Type[BaseException]): Exception type.
+ exc_value (BaseException): Exception value.
+ traceback (TracebackType): Python Traceback object.
+ 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.
+
+ Returns:
+ Trace: A Trace instance which you can use to construct a `Traceback`.
+ """
+
+ stacks: List[Stack] = []
+ is_cause = False
+
+ from pip._vendor.rich import _IMPORT_CWD
+
+ def safe_str(_object: Any) -> str:
+ """Don't allow exceptions from __str__ to propegate."""
+ try:
+ return str(_object)
+ except Exception:
+ return "<exception str() failed>"
+
+ while True:
+ stack = Stack(
+ exc_type=safe_str(exc_type.__name__),
+ exc_value=safe_str(exc_value),
+ is_cause=is_cause,
+ )
+
+ if isinstance(exc_value, SyntaxError):
+ stack.syntax_error = _SyntaxError(
+ offset=exc_value.offset or 0,
+ filename=exc_value.filename or "?",
+ lineno=exc_value.lineno or 0,
+ line=exc_value.text or "",
+ msg=exc_value.msg,
+ )
+
+ stacks.append(stack)
+ append = stack.frames.append
+
+ for frame_summary, line_no in walk_tb(traceback):
+ filename = frame_summary.f_code.co_filename
+ if filename and not filename.startswith("<"):
+ if not os.path.isabs(filename):
+ filename = os.path.join(_IMPORT_CWD, filename)
+ frame = Frame(
+ filename=filename or "?",
+ lineno=line_no,
+ name=frame_summary.f_code.co_name,
+ locals={
+ key: pretty.traverse(
+ value,
+ max_length=locals_max_length,
+ max_string=locals_max_string,
+ )
+ for key, value in frame_summary.f_locals.items()
+ }
+ if show_locals
+ else None,
+ )
+ append(frame)
+ if "_rich_traceback_guard" in frame_summary.f_locals:
+ del stack.frames[:]
+
+ cause = getattr(exc_value, "__cause__", None)
+ if cause and cause.__traceback__:
+ exc_type = cause.__class__
+ exc_value = cause
+ traceback = cause.__traceback__
+ if traceback:
+ is_cause = True
+ continue
+
+ cause = exc_value.__context__
+ if (
+ cause
+ and cause.__traceback__
+ and not getattr(exc_value, "__suppress_context__", False)
+ ):
+ exc_type = cause.__class__
+ exc_value = cause
+ traceback = cause.__traceback__
+ if traceback:
+ is_cause = False
+ continue
+ # No cover, code is reached but coverage doesn't recognize it.
+ break # pragma: no cover
+
+ trace = Trace(stacks=stacks)
+ return trace
+
+ def __rich_console__(
+ self, console: Console, options: ConsoleOptions
+ ) -> RenderResult:
+ theme = self.theme
+ background_style = theme.get_background_style()
+ token_style = theme.get_style_for_token
+
+ traceback_theme = Theme(
+ {
+ "pretty": token_style(TextToken),
+ "pygments.text": token_style(Token),
+ "pygments.string": token_style(String),
+ "pygments.function": token_style(Name.Function),
+ "pygments.number": token_style(Number),
+ "repr.indent": token_style(Comment) + Style(dim=True),
+ "repr.str": token_style(String),
+ "repr.brace": token_style(TextToken) + Style(bold=True),
+ "repr.number": token_style(Number),
+ "repr.bool_true": token_style(Keyword.Constant),
+ "repr.bool_false": token_style(Keyword.Constant),
+ "repr.none": token_style(Keyword.Constant),
+ "scope.border": token_style(String.Delimiter),
+ "scope.equals": token_style(Operator),
+ "scope.key": token_style(Name),
+ "scope.key.special": token_style(Name.Constant) + Style(dim=True),
+ }
+ )
+
+ highlighter = ReprHighlighter()
+ for last, stack in loop_last(reversed(self.trace.stacks)):
+ if stack.frames:
+ stack_renderable: ConsoleRenderable = Panel(
+ self._render_stack(stack),
+ title="[traceback.title]Traceback [dim](most recent call last)",
+ style=background_style,
+ border_style="traceback.border.syntax_error",
+ expand=True,
+ padding=(0, 1),
+ )
+ stack_renderable = Constrain(stack_renderable, self.width)
+ with console.use_theme(traceback_theme):
+ yield stack_renderable
+ if stack.syntax_error is not None:
+ with console.use_theme(traceback_theme):
+ yield Constrain(
+ Panel(
+ self._render_syntax_error(stack.syntax_error),
+ style=background_style,
+ border_style="traceback.border",
+ expand=True,
+ padding=(0, 1),
+ width=self.width,
+ ),
+ self.width,
+ )
+ yield Text.assemble(
+ (f"{stack.exc_type}: ", "traceback.exc_type"),
+ highlighter(stack.syntax_error.msg),
+ )
+ elif stack.exc_value:
+ yield Text.assemble(
+ (f"{stack.exc_type}: ", "traceback.exc_type"),
+ highlighter(stack.exc_value),
+ )
+ else:
+ yield Text.assemble((f"{stack.exc_type}", "traceback.exc_type"))
+
+ if not last:
+ if stack.is_cause:
+ yield Text.from_markup(
+ "\n[i]The above exception was the direct cause of the following exception:\n",
+ )
+ else:
+ yield Text.from_markup(
+ "\n[i]During handling of the above exception, another exception occurred:\n",
+ )
+
+ @group()
+ def _render_syntax_error(self, syntax_error: _SyntaxError) -> RenderResult:
+ 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)
+ syntax_error_text = highlighter(syntax_error.line.rstrip())
+ syntax_error_text.no_wrap = True
+ offset = min(syntax_error.offset - 1, len(syntax_error_text))
+ syntax_error_text.stylize("bold underline", offset, offset)
+ syntax_error_text += Text.from_markup(
+ "\n" + " " * offset + "[traceback.offset]▲[/]",
+ style="pygments.text",
+ )
+ yield syntax_error_text
+
+ @classmethod
+ def _guess_lexer(cls, filename: str, code: str) -> str:
+ ext = os.path.splitext(filename)[-1]
+ if not ext:
+ # No extension, look at first line to see if it is a hashbang
+ # Note, this is an educated guess and not a guarantee
+ # If it fails, the only downside is that the code is highlighted strangely
+ new_line_index = code.index("\n")
+ first_line = code[:new_line_index] if new_line_index != -1 else code
+ if first_line.startswith("#!") and "python" in first_line.lower():
+ return "python"
+ lexer_name = (
+ cls.LEXERS.get(ext) or guess_lexer_for_filename(filename, code).name
+ )
+ return lexer_name
+
+ @group()
+ 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.
+
+ Args:
+ filename (str): Filename to read
+
+ 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
+
+ def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]:
+ if frame.locals:
+ yield render_scope(
+ frame.locals,
+ title="locals",
+ indent_guides=self.indent_guides,
+ max_length=self.locals_max_length,
+ max_string=self.locals_max_string,
+ )
+
+ exclude_frames: Optional[range] = None
+ if self.max_frames != 0:
+ exclude_frames = range(
+ self.max_frames // 2,
+ len(stack.frames) - self.max_frames // 2,
+ )
+
+ excluded = False
+ for frame_index, frame in enumerate(stack.frames):
+
+ if exclude_frames and frame_index in exclude_frames:
+ excluded = True
+ continue
+
+ if excluded:
+ assert exclude_frames is not None
+ yield Text(
+ f"\n... {len(exclude_frames)} frames hidden ...",
+ justify="center",
+ style="traceback.error",
+ )
+ excluded = False
+
+ first = frame_index == 1
+ 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 not frame.filename.startswith("<") and not first:
+ yield ""
+ yield text
+ if frame.filename.startswith("<"):
+ yield from render_locals(frame)
+ continue
+ if not suppressed:
+ try:
+ code = read_code(frame.filename)
+ lexer_name = self._guess_lexer(frame.filename, code)
+ syntax = Syntax(
+ code,
+ lexer_name,
+ theme=theme,
+ line_numbers=True,
+ line_range=(
+ frame.lineno - self.extra_lines,
+ frame.lineno + self.extra_lines,
+ ),
+ highlight_lines={frame.lineno},
+ word_wrap=self.word_wrap,
+ code_width=88,
+ indent_guides=self.indent_guides,
+ dedent=False,
+ )
+ yield ""
+ except Exception as error:
+ yield Text.assemble(
+ (f"\n{error}", "traceback.error"),
+ )
+ else:
+ yield (
+ Columns(
+ [
+ syntax,
+ *render_locals(frame),
+ ],
+ padding=1,
+ )
+ if frame.locals
+ else syntax
+ )
+
+
+if __name__ == "__main__": # pragma: no cover
+
+ from .console import Console
+
+ console = Console()
+ import sys
+
+ def bar(a: Any) -> None: # 这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑
+ one = 1
+ print(one / a)
+
+ def foo(a: Any) -> None:
+ _rich_traceback_guard = True
+ zed = {
+ "characters": {
+ "Paul Atreides",
+ "Vladimir Harkonnen",
+ "Thufir Hawat",
+ "Duncan Idaho",
+ },
+ "atomic_types": (None, False, True),
+ }
+ bar(a)
+
+ def error() -> None:
+
+ try:
+ try:
+ foo(0)
+ except:
+ slfkjsldkfj # type: ignore
+ except:
+ console.print_exception(show_locals=True)
+
+ error()
diff --git a/src/pip/_vendor/rich/tree.py b/src/pip/_vendor/rich/tree.py
new file mode 100644
index 000000000..c5ec27da9
--- /dev/null
+++ b/src/pip/_vendor/rich/tree.py
@@ -0,0 +1,249 @@
+from typing import Iterator, List, Optional, Tuple
+
+from ._loop import loop_first, loop_last
+from .console import Console, ConsoleOptions, RenderableType, RenderResult
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .segment import Segment
+from .style import Style, StyleStack, StyleType
+from .styled import Styled
+
+
+class Tree(JupyterMixin):
+ """A renderable for a tree structure.
+
+ Args:
+ label (RenderableType): The renderable or str for the tree label.
+ style (StyleType, optional): Style of this tree. Defaults to "tree".
+ guide_style (StyleType, optional): Style of the guide lines. Defaults to "tree.line".
+ expanded (bool, optional): Also display children. Defaults to True.
+ highlight (bool, optional): Highlight renderable (if str). Defaults to False.
+ """
+
+ def __init__(
+ self,
+ label: RenderableType,
+ *,
+ style: StyleType = "tree",
+ guide_style: StyleType = "tree.line",
+ expanded: bool = True,
+ highlight: bool = False,
+ hide_root: bool = False,
+ ) -> None:
+ self.label = label
+ self.style = style
+ self.guide_style = guide_style
+ self.children: List[Tree] = []
+ self.expanded = expanded
+ self.highlight = highlight
+ self.hide_root = hide_root
+
+ def add(
+ self,
+ label: RenderableType,
+ *,
+ style: Optional[StyleType] = None,
+ guide_style: Optional[StyleType] = None,
+ expanded: bool = True,
+ highlight: bool = False,
+ ) -> "Tree":
+ """Add a child tree.
+
+ Args:
+ label (RenderableType): The renderable or str for the tree label.
+ style (StyleType, optional): Style of this tree. Defaults to "tree".
+ guide_style (StyleType, optional): Style of the guide lines. Defaults to "tree.line".
+ expanded (bool, optional): Also display children. Defaults to True.
+ highlight (Optional[bool], optional): Highlight renderable (if str). Defaults to False.
+
+ Returns:
+ Tree: A new child Tree, which may be further modified.
+ """
+ node = Tree(
+ label,
+ style=self.style if style is None else style,
+ guide_style=self.guide_style if guide_style is None else guide_style,
+ expanded=expanded,
+ highlight=self.highlight if highlight is None else highlight,
+ )
+ self.children.append(node)
+ return node
+
+ def __rich_console__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "RenderResult":
+
+ stack: List[Iterator[Tuple[bool, Tree]]] = []
+ pop = stack.pop
+ push = stack.append
+ new_line = Segment.line()
+
+ get_style = console.get_style
+ null_style = Style.null()
+ guide_style = get_style(self.guide_style, default="") or null_style
+ SPACE, CONTINUE, FORK, END = range(4)
+
+ ASCII_GUIDES = (" ", "| ", "+-- ", "`-- ")
+ TREE_GUIDES = [
+ (" ", "│ ", "├── ", "└── "),
+ (" ", "┃ ", "┣━━ ", "┗━━ "),
+ (" ", "║ ", "╠══ ", "╚══ "),
+ ]
+ _Segment = Segment
+
+ def make_guide(index: int, style: Style) -> Segment:
+ """Make a Segment for a level of the guide lines."""
+ if options.ascii_only:
+ line = ASCII_GUIDES[index]
+ else:
+ guide = 1 if style.bold else (2 if style.underline2 else 0)
+ line = TREE_GUIDES[0 if options.legacy_windows else guide][index]
+ return _Segment(line, style)
+
+ levels: List[Segment] = [make_guide(CONTINUE, guide_style)]
+ push(iter(loop_last([self])))
+
+ guide_style_stack = StyleStack(get_style(self.guide_style))
+ style_stack = StyleStack(get_style(self.style))
+ remove_guide_styles = Style(bold=False, underline2=False)
+
+ depth = 0
+
+ while stack:
+ stack_node = pop()
+ try:
+ last, node = next(stack_node)
+ except StopIteration:
+ levels.pop()
+ if levels:
+ guide_style = levels[-1].style or null_style
+ levels[-1] = make_guide(FORK, guide_style)
+ guide_style_stack.pop()
+ style_stack.pop()
+ continue
+ push(stack_node)
+ if last:
+ levels[-1] = make_guide(END, levels[-1].style or null_style)
+
+ guide_style = guide_style_stack.current + get_style(node.guide_style)
+ style = style_stack.current + get_style(node.style)
+ prefix = levels[(2 if self.hide_root else 1) :]
+ renderable_lines = console.render_lines(
+ Styled(node.label, style),
+ options.update(
+ width=options.max_width
+ - sum(level.cell_length for level in prefix),
+ highlight=self.highlight,
+ height=None,
+ ),
+ )
+
+ if not (depth == 0 and self.hide_root):
+ for first, line in loop_first(renderable_lines):
+ if prefix:
+ yield from _Segment.apply_style(
+ prefix,
+ style.background_style,
+ post_style=remove_guide_styles,
+ )
+ yield from line
+ yield new_line
+ if first and prefix:
+ prefix[-1] = make_guide(
+ SPACE if last else CONTINUE, prefix[-1].style or null_style
+ )
+
+ if node.expanded and node.children:
+ levels[-1] = make_guide(
+ SPACE if last else CONTINUE, levels[-1].style or null_style
+ )
+ levels.append(
+ make_guide(END if len(node.children) == 1 else FORK, guide_style)
+ )
+ style_stack.push(get_style(node.style))
+ guide_style_stack.push(get_style(node.guide_style))
+ push(iter(loop_last(node.children)))
+ depth += 1
+
+ def __rich_measure__(
+ self, console: "Console", options: "ConsoleOptions"
+ ) -> "Measurement":
+ stack: List[Iterator[Tree]] = [iter([self])]
+ pop = stack.pop
+ push = stack.append
+ minimum = 0
+ maximum = 0
+ measure = Measurement.get
+ level = 0
+ while stack:
+ iter_tree = pop()
+ try:
+ tree = next(iter_tree)
+ except StopIteration:
+ level -= 1
+ continue
+ push(iter_tree)
+ min_measure, max_measure = measure(console, options, tree.label)
+ indent = level * 4
+ minimum = max(min_measure + indent, minimum)
+ maximum = max(max_measure + indent, maximum)
+ if tree.expanded and tree.children:
+ push(iter(tree.children))
+ level += 1
+ return Measurement(minimum, maximum)
+
+
+if __name__ == "__main__": # pragma: no cover
+
+ from pip._vendor.rich.console import Group
+ from pip._vendor.rich.markdown import Markdown
+ from pip._vendor.rich.panel import Panel
+ from pip._vendor.rich.syntax import Syntax
+ from pip._vendor.rich.table import Table
+
+ table = Table(row_styles=["", "dim"])
+
+ table.add_column("Released", style="cyan", no_wrap=True)
+ table.add_column("Title", style="magenta")
+ table.add_column("Box Office", justify="right", style="green")
+
+ table.add_row("Dec 20, 2019", "Star Wars: The Rise of Skywalker", "$952,110,690")
+ table.add_row("May 25, 2018", "Solo: A Star Wars Story", "$393,151,347")
+ table.add_row("Dec 15, 2017", "Star Wars Ep. V111: The Last Jedi", "$1,332,539,889")
+ table.add_row("Dec 16, 2016", "Rogue One: A Star Wars Story", "$1,332,439,889")
+
+ code = """\
+class Segment(NamedTuple):
+ text: str = ""
+ style: Optional[Style] = None
+ is_control: bool = False
+"""
+ syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
+
+ markdown = Markdown(
+ """\
+### example.md
+> Hello, World!
+>
+> Markdown _all_ the things
+"""
+ )
+
+ root = Tree("🌲 [b green]Rich Tree", highlight=True, hide_root=True)
+
+ node = root.add(":file_folder: Renderables", guide_style="red")
+ simple_node = node.add(":file_folder: [bold yellow]Atomic", guide_style="uu green")
+ simple_node.add(Group("📄 Syntax", syntax))
+ simple_node.add(Group("📄 Markdown", Panel(markdown, border_style="green")))
+
+ containers_node = node.add(
+ ":file_folder: [bold magenta]Containers", guide_style="bold magenta"
+ )
+ containers_node.expanded = True
+ panel = Panel.fit("Just a panel", border_style="red")
+ containers_node.add(Group("📄 Panels", panel))
+
+ containers_node.add(Group("📄 [b magenta]Table", table))
+
+ console = Console()
+ console.print(root)