Cleaned up the directories
This commit is contained in:
parent
f708506d68
commit
a683fcffea
1340 changed files with 554582 additions and 6840 deletions
|
@ -0,0 +1,177 @@
|
|||
"""Rich text and beautiful formatting in the terminal."""
|
||||
|
||||
import os
|
||||
from typing import IO, TYPE_CHECKING, Any, Callable, Optional, Union
|
||||
|
||||
from ._extension import load_ipython_extension # noqa: F401
|
||||
|
||||
__all__ = ["get_console", "reconfigure", "print", "inspect", "print_json"]
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .console import Console
|
||||
|
||||
# Global console used by alternative print
|
||||
_console: Optional["Console"] = None
|
||||
|
||||
try:
|
||||
_IMPORT_CWD = os.path.abspath(os.getcwd())
|
||||
except FileNotFoundError:
|
||||
# Can happen if the cwd has been deleted
|
||||
_IMPORT_CWD = ""
|
||||
|
||||
|
||||
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:
|
||||
*args (Any): Positional arguments for the replacement :class:`~rich.console.Console`.
|
||||
**kwargs (Any): Keyword arguments for the replacement :class:`~rich.console.Console`.
|
||||
"""
|
||||
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: Union[None, int, str] = 2,
|
||||
highlight: bool = True,
|
||||
skip_keys: bool = False,
|
||||
ensure_ascii: bool = False,
|
||||
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**")
|
|
@ -0,0 +1,274 @@
|
|||
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, RenderableType, RenderResult
|
||||
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 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
|
||||
start = process_time()
|
||||
console.print(test_card)
|
||||
pre_cache_taken = round((process_time() - start) * 1000.0, 1)
|
||||
|
||||
console.file = io.StringIO()
|
||||
|
||||
start = process_time()
|
||||
console.print(test_card)
|
||||
taken = round((process_time() - start) * 1000.0, 1)
|
||||
|
||||
c = Console(record=True)
|
||||
c.print(test_card)
|
||||
|
||||
print(f"rendered in {pre_cache_taken}ms (cold cache)")
|
||||
print(f"rendered in {taken}ms (warm cache)")
|
||||
|
||||
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(
|
||||
"Textualize",
|
||||
"[u blue link=https://github.com/textualize]https://github.com/textualize",
|
||||
)
|
||||
sponsor_message.add_row(
|
||||
"Twitter",
|
||||
"[u blue link=https://twitter.com/willmcgugan]https://twitter.com/willmcgugan",
|
||||
)
|
||||
|
||||
intro_message = Text.from_markup(
|
||||
"""\
|
||||
We hope you enjoy using Rich!
|
||||
|
||||
Rich is maintained with [red]:heart:[/] by [link=https://www.textualize.io]Textualize.io[/]
|
||||
|
||||
- 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",
|
||||
)
|
|
@ -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),
|
||||
]
|
File diff suppressed because it is too large
Load diff
|
@ -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)
|
|
@ -0,0 +1,76 @@
|
|||
CONSOLE_HTML_FORMAT = """\
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
{stylesheet}
|
||||
body {{
|
||||
color: {foreground};
|
||||
background-color: {background};
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<html>
|
||||
<body>
|
||||
<pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><code>{code}</code></pre>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
CONSOLE_SVG_FORMAT = """\
|
||||
<svg class="rich-terminal" viewBox="0 0 {width} {height}" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {{
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}}
|
||||
@font-face {{
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}}
|
||||
|
||||
.{unique_id}-matrix {{
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: {char_height}px;
|
||||
line-height: {line_height}px;
|
||||
font-variant-east-asian: full-width;
|
||||
}}
|
||||
|
||||
.{unique_id}-title {{
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}}
|
||||
|
||||
{styles}
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="{unique_id}-clip-terminal">
|
||||
<rect x="0" y="0" width="{terminal_width}" height="{terminal_height}" />
|
||||
</clipPath>
|
||||
{lines}
|
||||
</defs>
|
||||
|
||||
{chrome}
|
||||
<g transform="translate({terminal_x}, {terminal_y})" clip-path="url(#{unique_id}-clip-terminal)">
|
||||
{backgrounds}
|
||||
<g class="{unique_id}-matrix">
|
||||
{matrix}
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
_SVG_FONT_FAMILY = "Rich Fira Code"
|
||||
_SVG_CLASSES_PREFIX = "rich-svg"
|
|
@ -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()
|
|
@ -0,0 +1,24 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import IO, Callable
|
||||
|
||||
|
||||
def get_fileno(file_like: IO[str]) -> int | None:
|
||||
"""Get fileno() from a file, accounting for poorly implemented file-like objects.
|
||||
|
||||
Args:
|
||||
file_like (IO): A file-like object.
|
||||
|
||||
Returns:
|
||||
int | None: The result of fileno if available, or None if operation failed.
|
||||
"""
|
||||
fileno: Callable[[], int] | None = getattr(file_like, "fileno", None)
|
||||
if fileno is not None:
|
||||
try:
|
||||
return fileno()
|
||||
except Exception:
|
||||
# `fileno` is documented as potentially raising a OSError
|
||||
# Alas, from the issues, there are so many poorly implemented file-like objects,
|
||||
# that `fileno()` can raise just about anything.
|
||||
return None
|
||||
return None
|
|
@ -0,0 +1,270 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import inspect
|
||||
from inspect import cleandoc, getdoc, getfile, isclass, ismodule, signature
|
||||
from typing import Any, Collection, Iterable, Optional, Tuple, Type, Union
|
||||
|
||||
from .console import Group, RenderableType
|
||||
from .control import escape_control_codes
|
||||
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
|
||||
|
||||
|
||||
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 (OSError, TypeError):
|
||||
# OSError is raised if obj has no source file, e.g. when defined in REPL.
|
||||
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)
|
||||
|
||||
# If obj is a module, there may be classes (which are callable) to display
|
||||
if inspect.isclass(obj):
|
||||
prefix = "class"
|
||||
elif inspect.iscoroutinefunction(obj):
|
||||
prefix = "async def"
|
||||
else:
|
||||
prefix = "def"
|
||||
|
||||
qual_signature = Text.assemble(
|
||||
(f"{prefix} ", f"inspect.{prefix.replace(' ', '_')}"),
|
||||
(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 = self._get_formatted_doc(obj)
|
||||
if _doc is not None:
|
||||
doc_text = Text(_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 = self._get_formatted_doc(value)
|
||||
if docs is not None:
|
||||
_signature_text.append("\n" if "\n" in docs else " ")
|
||||
doc = highlighter(docs)
|
||||
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
|
||||
elif not_shown_count:
|
||||
yield Text.from_markup(
|
||||
f"[b cyan]{not_shown_count}[/][i] attribute(s) not shown.[/i] "
|
||||
f"Run [b][magenta]inspect[/]([not b]inspect[/])[/b] for options."
|
||||
)
|
||||
|
||||
def _get_formatted_doc(self, object_: Any) -> Optional[str]:
|
||||
"""
|
||||
Extract the docstring of an object, process it and returns it.
|
||||
The processing consists in cleaning up the doctring's indentation,
|
||||
taking only its 1st paragraph if `self.help` is not True,
|
||||
and escape its control codes.
|
||||
|
||||
Args:
|
||||
object_ (Any): the object to get the docstring from.
|
||||
|
||||
Returns:
|
||||
Optional[str]: the processed docstring, or None if no docstring was found.
|
||||
"""
|
||||
docs = getdoc(object_)
|
||||
if docs is None:
|
||||
return None
|
||||
docs = cleandoc(docs).strip()
|
||||
if not self.help:
|
||||
docs = _first_paragraph(docs)
|
||||
return escape_control_codes(docs)
|
||||
|
||||
|
||||
def get_object_types_mro(obj: Union[object, Type[Any]]) -> Tuple[type, ...]:
|
||||
"""Returns the MRO of an object's class, or of the object itself if it's a class."""
|
||||
if not hasattr(obj, "__mro__"):
|
||||
# N.B. we cannot use `if type(obj) is type` here because it doesn't work with
|
||||
# some types of classes, such as the ones that use abc.ABCMeta.
|
||||
obj = type(obj)
|
||||
return getattr(obj, "__mro__", ())
|
||||
|
||||
|
||||
def get_object_types_mro_as_strings(obj: object) -> Collection[str]:
|
||||
"""
|
||||
Returns the MRO of an object's class as full qualified names, or of the object itself if it's a class.
|
||||
|
||||
Examples:
|
||||
`object_types_mro_as_strings(JSONDecoder)` will return `['json.decoder.JSONDecoder', 'builtins.object']`
|
||||
"""
|
||||
return [
|
||||
f'{getattr(type_, "__module__", "")}.{getattr(type_, "__qualname__", "")}'
|
||||
for type_ in get_object_types_mro(obj)
|
||||
]
|
||||
|
||||
|
||||
def is_object_one_of_types(
|
||||
obj: object, fully_qualified_types_names: Collection[str]
|
||||
) -> bool:
|
||||
"""
|
||||
Returns `True` if the given object's class (or the object itself, if it's a class) has one of the
|
||||
fully qualified names in its MRO.
|
||||
"""
|
||||
for type_name in get_object_types_mro_as_strings(obj):
|
||||
if type_name in fully_qualified_types_names:
|
||||
return True
|
||||
return False
|
|
@ -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")
|
|
@ -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
|
|
@ -0,0 +1,69 @@
|
|||
from types import TracebackType
|
||||
from typing import IO, Iterable, Iterator, List, Optional, Type
|
||||
|
||||
|
||||
class NullFile(IO[str]):
|
||||
def close(self) -> None:
|
||||
pass
|
||||
|
||||
def isatty(self) -> bool:
|
||||
return False
|
||||
|
||||
def read(self, __n: int = 1) -> str:
|
||||
return ""
|
||||
|
||||
def readable(self) -> bool:
|
||||
return False
|
||||
|
||||
def readline(self, __limit: int = 1) -> str:
|
||||
return ""
|
||||
|
||||
def readlines(self, __hint: int = 1) -> List[str]:
|
||||
return []
|
||||
|
||||
def seek(self, __offset: int, __whence: int = 1) -> int:
|
||||
return 0
|
||||
|
||||
def seekable(self) -> bool:
|
||||
return False
|
||||
|
||||
def tell(self) -> int:
|
||||
return 0
|
||||
|
||||
def truncate(self, __size: Optional[int] = 1) -> int:
|
||||
return 0
|
||||
|
||||
def writable(self) -> bool:
|
||||
return False
|
||||
|
||||
def writelines(self, __lines: Iterable[str]) -> None:
|
||||
pass
|
||||
|
||||
def __next__(self) -> str:
|
||||
return ""
|
||||
|
||||
def __iter__(self) -> Iterator[str]:
|
||||
return iter([""])
|
||||
|
||||
def __enter__(self) -> IO[str]:
|
||||
pass
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
__t: Optional[Type[BaseException]],
|
||||
__value: Optional[BaseException],
|
||||
__traceback: Optional[TracebackType],
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
def write(self, text: str) -> int:
|
||||
return 0
|
||||
|
||||
def flush(self) -> None:
|
||||
pass
|
||||
|
||||
def fileno(self) -> int:
|
||||
return -1
|
||||
|
||||
|
||||
NULL_FILE = NullFile()
|
|
@ -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),
|
||||
]
|
||||
)
|
|
@ -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)
|
160
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/_ratio.py
Normal file
160
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/_ratio.py
Normal file
|
@ -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))
|
|
@ -0,0 +1,482 @@
|
|||
"""
|
||||
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": " .oO@* "},
|
||||
"balloon2": {"interval": 120, "frames": ".oO°Oo."},
|
||||
"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": "dqpb"},
|
||||
"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": [
|
||||
"▰▱▱▱▱▱▱",
|
||||
"▰▰▱▱▱▱▱",
|
||||
"▰▰▰▱▱▱▱",
|
||||
"▰▰▰▰▱▱▱",
|
||||
"▰▰▰▰▰▱▱",
|
||||
"▰▰▰▰▰▰▱",
|
||||
"▰▰▰▰▰▰▰",
|
||||
"▰▱▱▱▱▱▱",
|
||||
],
|
||||
},
|
||||
}
|
|
@ -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)
|
|
@ -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")
|
|
@ -0,0 +1,662 @@
|
|||
"""Light wrapper around the Win32 Console API - this module should only be imported on Windows
|
||||
|
||||
The API that this module wraps is documented at https://docs.microsoft.com/en-us/windows/console/console-functions
|
||||
"""
|
||||
import ctypes
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
windll: Any = None
|
||||
if sys.platform == "win32":
|
||||
windll = ctypes.LibraryLoader(ctypes.WinDLL)
|
||||
else:
|
||||
raise ImportError(f"{__name__} can only be imported on Windows")
|
||||
|
||||
import time
|
||||
from ctypes import Structure, byref, wintypes
|
||||
from typing import IO, NamedTuple, Type, cast
|
||||
|
||||
from pip._vendor.rich.color import ColorSystem
|
||||
from pip._vendor.rich.style import Style
|
||||
|
||||
STDOUT = -11
|
||||
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
||||
|
||||
COORD = wintypes._COORD
|
||||
|
||||
|
||||
class LegacyWindowsError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class WindowsCoordinates(NamedTuple):
|
||||
"""Coordinates in the Windows Console API are (y, x), not (x, y).
|
||||
This class is intended to prevent that confusion.
|
||||
Rows and columns are indexed from 0.
|
||||
This class can be used in place of wintypes._COORD in arguments and argtypes.
|
||||
"""
|
||||
|
||||
row: int
|
||||
col: int
|
||||
|
||||
@classmethod
|
||||
def from_param(cls, value: "WindowsCoordinates") -> COORD:
|
||||
"""Converts a WindowsCoordinates into a wintypes _COORD structure.
|
||||
This classmethod is internally called by ctypes to perform the conversion.
|
||||
|
||||
Args:
|
||||
value (WindowsCoordinates): The input coordinates to convert.
|
||||
|
||||
Returns:
|
||||
wintypes._COORD: The converted coordinates struct.
|
||||
"""
|
||||
return COORD(value.col, value.row)
|
||||
|
||||
|
||||
class CONSOLE_SCREEN_BUFFER_INFO(Structure):
|
||||
_fields_ = [
|
||||
("dwSize", COORD),
|
||||
("dwCursorPosition", COORD),
|
||||
("wAttributes", wintypes.WORD),
|
||||
("srWindow", wintypes.SMALL_RECT),
|
||||
("dwMaximumWindowSize", COORD),
|
||||
]
|
||||
|
||||
|
||||
class CONSOLE_CURSOR_INFO(ctypes.Structure):
|
||||
_fields_ = [("dwSize", wintypes.DWORD), ("bVisible", wintypes.BOOL)]
|
||||
|
||||
|
||||
_GetStdHandle = windll.kernel32.GetStdHandle
|
||||
_GetStdHandle.argtypes = [
|
||||
wintypes.DWORD,
|
||||
]
|
||||
_GetStdHandle.restype = wintypes.HANDLE
|
||||
|
||||
|
||||
def GetStdHandle(handle: int = STDOUT) -> wintypes.HANDLE:
|
||||
"""Retrieves a handle to the specified standard device (standard input, standard output, or standard error).
|
||||
|
||||
Args:
|
||||
handle (int): Integer identifier for the handle. Defaults to -11 (stdout).
|
||||
|
||||
Returns:
|
||||
wintypes.HANDLE: The handle
|
||||
"""
|
||||
return cast(wintypes.HANDLE, _GetStdHandle(handle))
|
||||
|
||||
|
||||
_GetConsoleMode = windll.kernel32.GetConsoleMode
|
||||
_GetConsoleMode.argtypes = [wintypes.HANDLE, wintypes.LPDWORD]
|
||||
_GetConsoleMode.restype = wintypes.BOOL
|
||||
|
||||
|
||||
def GetConsoleMode(std_handle: wintypes.HANDLE) -> int:
|
||||
"""Retrieves the current input mode of a console's input buffer
|
||||
or the current output mode of a console screen buffer.
|
||||
|
||||
Args:
|
||||
std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
|
||||
|
||||
Raises:
|
||||
LegacyWindowsError: If any error occurs while calling the Windows console API.
|
||||
|
||||
Returns:
|
||||
int: Value representing the current console mode as documented at
|
||||
https://docs.microsoft.com/en-us/windows/console/getconsolemode#parameters
|
||||
"""
|
||||
|
||||
console_mode = wintypes.DWORD()
|
||||
success = bool(_GetConsoleMode(std_handle, console_mode))
|
||||
if not success:
|
||||
raise LegacyWindowsError("Unable to get legacy Windows Console Mode")
|
||||
return console_mode.value
|
||||
|
||||
|
||||
_FillConsoleOutputCharacterW = windll.kernel32.FillConsoleOutputCharacterW
|
||||
_FillConsoleOutputCharacterW.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
ctypes.c_char,
|
||||
wintypes.DWORD,
|
||||
cast(Type[COORD], WindowsCoordinates),
|
||||
ctypes.POINTER(wintypes.DWORD),
|
||||
]
|
||||
_FillConsoleOutputCharacterW.restype = wintypes.BOOL
|
||||
|
||||
|
||||
def FillConsoleOutputCharacter(
|
||||
std_handle: wintypes.HANDLE,
|
||||
char: str,
|
||||
length: int,
|
||||
start: WindowsCoordinates,
|
||||
) -> int:
|
||||
"""Writes a character to the console screen buffer a specified number of times, beginning at the specified coordinates.
|
||||
|
||||
Args:
|
||||
std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
|
||||
char (str): The character to write. Must be a string of length 1.
|
||||
length (int): The number of times to write the character.
|
||||
start (WindowsCoordinates): The coordinates to start writing at.
|
||||
|
||||
Returns:
|
||||
int: The number of characters written.
|
||||
"""
|
||||
character = ctypes.c_char(char.encode())
|
||||
num_characters = wintypes.DWORD(length)
|
||||
num_written = wintypes.DWORD(0)
|
||||
_FillConsoleOutputCharacterW(
|
||||
std_handle,
|
||||
character,
|
||||
num_characters,
|
||||
start,
|
||||
byref(num_written),
|
||||
)
|
||||
return num_written.value
|
||||
|
||||
|
||||
_FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute
|
||||
_FillConsoleOutputAttribute.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
wintypes.WORD,
|
||||
wintypes.DWORD,
|
||||
cast(Type[COORD], WindowsCoordinates),
|
||||
ctypes.POINTER(wintypes.DWORD),
|
||||
]
|
||||
_FillConsoleOutputAttribute.restype = wintypes.BOOL
|
||||
|
||||
|
||||
def FillConsoleOutputAttribute(
|
||||
std_handle: wintypes.HANDLE,
|
||||
attributes: int,
|
||||
length: int,
|
||||
start: WindowsCoordinates,
|
||||
) -> int:
|
||||
"""Sets the character attributes for a specified number of character cells,
|
||||
beginning at the specified coordinates in a screen buffer.
|
||||
|
||||
Args:
|
||||
std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
|
||||
attributes (int): Integer value representing the foreground and background colours of the cells.
|
||||
length (int): The number of cells to set the output attribute of.
|
||||
start (WindowsCoordinates): The coordinates of the first cell whose attributes are to be set.
|
||||
|
||||
Returns:
|
||||
int: The number of cells whose attributes were actually set.
|
||||
"""
|
||||
num_cells = wintypes.DWORD(length)
|
||||
style_attrs = wintypes.WORD(attributes)
|
||||
num_written = wintypes.DWORD(0)
|
||||
_FillConsoleOutputAttribute(
|
||||
std_handle, style_attrs, num_cells, start, byref(num_written)
|
||||
)
|
||||
return num_written.value
|
||||
|
||||
|
||||
_SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute
|
||||
_SetConsoleTextAttribute.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
wintypes.WORD,
|
||||
]
|
||||
_SetConsoleTextAttribute.restype = wintypes.BOOL
|
||||
|
||||
|
||||
def SetConsoleTextAttribute(
|
||||
std_handle: wintypes.HANDLE, attributes: wintypes.WORD
|
||||
) -> bool:
|
||||
"""Set the colour attributes for all text written after this function is called.
|
||||
|
||||
Args:
|
||||
std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
|
||||
attributes (int): Integer value representing the foreground and background colours.
|
||||
|
||||
|
||||
Returns:
|
||||
bool: True if the attribute was set successfully, otherwise False.
|
||||
"""
|
||||
return bool(_SetConsoleTextAttribute(std_handle, attributes))
|
||||
|
||||
|
||||
_GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo
|
||||
_GetConsoleScreenBufferInfo.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
ctypes.POINTER(CONSOLE_SCREEN_BUFFER_INFO),
|
||||
]
|
||||
_GetConsoleScreenBufferInfo.restype = wintypes.BOOL
|
||||
|
||||
|
||||
def GetConsoleScreenBufferInfo(
|
||||
std_handle: wintypes.HANDLE,
|
||||
) -> CONSOLE_SCREEN_BUFFER_INFO:
|
||||
"""Retrieves information about the specified console screen buffer.
|
||||
|
||||
Args:
|
||||
std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
|
||||
|
||||
Returns:
|
||||
CONSOLE_SCREEN_BUFFER_INFO: A CONSOLE_SCREEN_BUFFER_INFO ctype struct contain information about
|
||||
screen size, cursor position, colour attributes, and more."""
|
||||
console_screen_buffer_info = CONSOLE_SCREEN_BUFFER_INFO()
|
||||
_GetConsoleScreenBufferInfo(std_handle, byref(console_screen_buffer_info))
|
||||
return console_screen_buffer_info
|
||||
|
||||
|
||||
_SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition
|
||||
_SetConsoleCursorPosition.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
cast(Type[COORD], WindowsCoordinates),
|
||||
]
|
||||
_SetConsoleCursorPosition.restype = wintypes.BOOL
|
||||
|
||||
|
||||
def SetConsoleCursorPosition(
|
||||
std_handle: wintypes.HANDLE, coords: WindowsCoordinates
|
||||
) -> bool:
|
||||
"""Set the position of the cursor in the console screen
|
||||
|
||||
Args:
|
||||
std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
|
||||
coords (WindowsCoordinates): The coordinates to move the cursor to.
|
||||
|
||||
Returns:
|
||||
bool: True if the function succeeds, otherwise False.
|
||||
"""
|
||||
return bool(_SetConsoleCursorPosition(std_handle, coords))
|
||||
|
||||
|
||||
_GetConsoleCursorInfo = windll.kernel32.GetConsoleCursorInfo
|
||||
_GetConsoleCursorInfo.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
ctypes.POINTER(CONSOLE_CURSOR_INFO),
|
||||
]
|
||||
_GetConsoleCursorInfo.restype = wintypes.BOOL
|
||||
|
||||
|
||||
def GetConsoleCursorInfo(
|
||||
std_handle: wintypes.HANDLE, cursor_info: CONSOLE_CURSOR_INFO
|
||||
) -> bool:
|
||||
"""Get the cursor info - used to get cursor visibility and width
|
||||
|
||||
Args:
|
||||
std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
|
||||
cursor_info (CONSOLE_CURSOR_INFO): CONSOLE_CURSOR_INFO ctype struct that receives information
|
||||
about the console's cursor.
|
||||
|
||||
Returns:
|
||||
bool: True if the function succeeds, otherwise False.
|
||||
"""
|
||||
return bool(_GetConsoleCursorInfo(std_handle, byref(cursor_info)))
|
||||
|
||||
|
||||
_SetConsoleCursorInfo = windll.kernel32.SetConsoleCursorInfo
|
||||
_SetConsoleCursorInfo.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
ctypes.POINTER(CONSOLE_CURSOR_INFO),
|
||||
]
|
||||
_SetConsoleCursorInfo.restype = wintypes.BOOL
|
||||
|
||||
|
||||
def SetConsoleCursorInfo(
|
||||
std_handle: wintypes.HANDLE, cursor_info: CONSOLE_CURSOR_INFO
|
||||
) -> bool:
|
||||
"""Set the cursor info - used for adjusting cursor visibility and width
|
||||
|
||||
Args:
|
||||
std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
|
||||
cursor_info (CONSOLE_CURSOR_INFO): CONSOLE_CURSOR_INFO ctype struct containing the new cursor info.
|
||||
|
||||
Returns:
|
||||
bool: True if the function succeeds, otherwise False.
|
||||
"""
|
||||
return bool(_SetConsoleCursorInfo(std_handle, byref(cursor_info)))
|
||||
|
||||
|
||||
_SetConsoleTitle = windll.kernel32.SetConsoleTitleW
|
||||
_SetConsoleTitle.argtypes = [wintypes.LPCWSTR]
|
||||
_SetConsoleTitle.restype = wintypes.BOOL
|
||||
|
||||
|
||||
def SetConsoleTitle(title: str) -> bool:
|
||||
"""Sets the title of the current console window
|
||||
|
||||
Args:
|
||||
title (str): The new title of the console window.
|
||||
|
||||
Returns:
|
||||
bool: True if the function succeeds, otherwise False.
|
||||
"""
|
||||
return bool(_SetConsoleTitle(title))
|
||||
|
||||
|
||||
class LegacyWindowsTerm:
|
||||
"""This class allows interaction with the legacy Windows Console API. It should only be used in the context
|
||||
of environments where virtual terminal processing is not available. However, if it is used in a Windows environment,
|
||||
the entire API should work.
|
||||
|
||||
Args:
|
||||
file (IO[str]): The file which the Windows Console API HANDLE is retrieved from, defaults to sys.stdout.
|
||||
"""
|
||||
|
||||
BRIGHT_BIT = 8
|
||||
|
||||
# Indices are ANSI color numbers, values are the corresponding Windows Console API color numbers
|
||||
ANSI_TO_WINDOWS = [
|
||||
0, # black The Windows colours are defined in wincon.h as follows:
|
||||
4, # red define FOREGROUND_BLUE 0x0001 -- 0000 0001
|
||||
2, # green define FOREGROUND_GREEN 0x0002 -- 0000 0010
|
||||
6, # yellow define FOREGROUND_RED 0x0004 -- 0000 0100
|
||||
1, # blue define FOREGROUND_INTENSITY 0x0008 -- 0000 1000
|
||||
5, # magenta define BACKGROUND_BLUE 0x0010 -- 0001 0000
|
||||
3, # cyan define BACKGROUND_GREEN 0x0020 -- 0010 0000
|
||||
7, # white define BACKGROUND_RED 0x0040 -- 0100 0000
|
||||
8, # bright black (grey) define BACKGROUND_INTENSITY 0x0080 -- 1000 0000
|
||||
12, # bright red
|
||||
10, # bright green
|
||||
14, # bright yellow
|
||||
9, # bright blue
|
||||
13, # bright magenta
|
||||
11, # bright cyan
|
||||
15, # bright white
|
||||
]
|
||||
|
||||
def __init__(self, file: "IO[str]") -> None:
|
||||
handle = GetStdHandle(STDOUT)
|
||||
self._handle = handle
|
||||
default_text = GetConsoleScreenBufferInfo(handle).wAttributes
|
||||
self._default_text = default_text
|
||||
|
||||
self._default_fore = default_text & 7
|
||||
self._default_back = (default_text >> 4) & 7
|
||||
self._default_attrs = self._default_fore | (self._default_back << 4)
|
||||
|
||||
self._file = file
|
||||
self.write = file.write
|
||||
self.flush = file.flush
|
||||
|
||||
@property
|
||||
def cursor_position(self) -> WindowsCoordinates:
|
||||
"""Returns the current position of the cursor (0-based)
|
||||
|
||||
Returns:
|
||||
WindowsCoordinates: The current cursor position.
|
||||
"""
|
||||
coord: COORD = GetConsoleScreenBufferInfo(self._handle).dwCursorPosition
|
||||
return WindowsCoordinates(row=cast(int, coord.Y), col=cast(int, coord.X))
|
||||
|
||||
@property
|
||||
def screen_size(self) -> WindowsCoordinates:
|
||||
"""Returns the current size of the console screen buffer, in character columns and rows
|
||||
|
||||
Returns:
|
||||
WindowsCoordinates: The width and height of the screen as WindowsCoordinates.
|
||||
"""
|
||||
screen_size: COORD = GetConsoleScreenBufferInfo(self._handle).dwSize
|
||||
return WindowsCoordinates(
|
||||
row=cast(int, screen_size.Y), col=cast(int, screen_size.X)
|
||||
)
|
||||
|
||||
def write_text(self, text: str) -> None:
|
||||
"""Write text directly to the terminal without any modification of styles
|
||||
|
||||
Args:
|
||||
text (str): The text to write to the console
|
||||
"""
|
||||
self.write(text)
|
||||
self.flush()
|
||||
|
||||
def write_styled(self, text: str, style: Style) -> None:
|
||||
"""Write styled text to the terminal.
|
||||
|
||||
Args:
|
||||
text (str): The text to write
|
||||
style (Style): The style of the text
|
||||
"""
|
||||
color = style.color
|
||||
bgcolor = style.bgcolor
|
||||
if style.reverse:
|
||||
color, bgcolor = bgcolor, color
|
||||
|
||||
if color:
|
||||
fore = color.downgrade(ColorSystem.WINDOWS).number
|
||||
fore = fore if fore is not None else 7 # Default to ANSI 7: White
|
||||
if style.bold:
|
||||
fore = fore | self.BRIGHT_BIT
|
||||
if style.dim:
|
||||
fore = fore & ~self.BRIGHT_BIT
|
||||
fore = self.ANSI_TO_WINDOWS[fore]
|
||||
else:
|
||||
fore = self._default_fore
|
||||
|
||||
if bgcolor:
|
||||
back = bgcolor.downgrade(ColorSystem.WINDOWS).number
|
||||
back = back if back is not None else 0 # Default to ANSI 0: Black
|
||||
back = self.ANSI_TO_WINDOWS[back]
|
||||
else:
|
||||
back = self._default_back
|
||||
|
||||
assert fore is not None
|
||||
assert back is not None
|
||||
|
||||
SetConsoleTextAttribute(
|
||||
self._handle, attributes=ctypes.c_ushort(fore | (back << 4))
|
||||
)
|
||||
self.write_text(text)
|
||||
SetConsoleTextAttribute(self._handle, attributes=self._default_text)
|
||||
|
||||
def move_cursor_to(self, new_position: WindowsCoordinates) -> None:
|
||||
"""Set the position of the cursor
|
||||
|
||||
Args:
|
||||
new_position (WindowsCoordinates): The WindowsCoordinates representing the new position of the cursor.
|
||||
"""
|
||||
if new_position.col < 0 or new_position.row < 0:
|
||||
return
|
||||
SetConsoleCursorPosition(self._handle, coords=new_position)
|
||||
|
||||
def erase_line(self) -> None:
|
||||
"""Erase all content on the line the cursor is currently located at"""
|
||||
screen_size = self.screen_size
|
||||
cursor_position = self.cursor_position
|
||||
cells_to_erase = screen_size.col
|
||||
start_coordinates = WindowsCoordinates(row=cursor_position.row, col=0)
|
||||
FillConsoleOutputCharacter(
|
||||
self._handle, " ", length=cells_to_erase, start=start_coordinates
|
||||
)
|
||||
FillConsoleOutputAttribute(
|
||||
self._handle,
|
||||
self._default_attrs,
|
||||
length=cells_to_erase,
|
||||
start=start_coordinates,
|
||||
)
|
||||
|
||||
def erase_end_of_line(self) -> None:
|
||||
"""Erase all content from the cursor position to the end of that line"""
|
||||
cursor_position = self.cursor_position
|
||||
cells_to_erase = self.screen_size.col - cursor_position.col
|
||||
FillConsoleOutputCharacter(
|
||||
self._handle, " ", length=cells_to_erase, start=cursor_position
|
||||
)
|
||||
FillConsoleOutputAttribute(
|
||||
self._handle,
|
||||
self._default_attrs,
|
||||
length=cells_to_erase,
|
||||
start=cursor_position,
|
||||
)
|
||||
|
||||
def erase_start_of_line(self) -> None:
|
||||
"""Erase all content from the cursor position to the start of that line"""
|
||||
row, col = self.cursor_position
|
||||
start = WindowsCoordinates(row, 0)
|
||||
FillConsoleOutputCharacter(self._handle, " ", length=col, start=start)
|
||||
FillConsoleOutputAttribute(
|
||||
self._handle, self._default_attrs, length=col, start=start
|
||||
)
|
||||
|
||||
def move_cursor_up(self) -> None:
|
||||
"""Move the cursor up a single cell"""
|
||||
cursor_position = self.cursor_position
|
||||
SetConsoleCursorPosition(
|
||||
self._handle,
|
||||
coords=WindowsCoordinates(
|
||||
row=cursor_position.row - 1, col=cursor_position.col
|
||||
),
|
||||
)
|
||||
|
||||
def move_cursor_down(self) -> None:
|
||||
"""Move the cursor down a single cell"""
|
||||
cursor_position = self.cursor_position
|
||||
SetConsoleCursorPosition(
|
||||
self._handle,
|
||||
coords=WindowsCoordinates(
|
||||
row=cursor_position.row + 1,
|
||||
col=cursor_position.col,
|
||||
),
|
||||
)
|
||||
|
||||
def move_cursor_forward(self) -> None:
|
||||
"""Move the cursor forward a single cell. Wrap to the next line if required."""
|
||||
row, col = self.cursor_position
|
||||
if col == self.screen_size.col - 1:
|
||||
row += 1
|
||||
col = 0
|
||||
else:
|
||||
col += 1
|
||||
SetConsoleCursorPosition(
|
||||
self._handle, coords=WindowsCoordinates(row=row, col=col)
|
||||
)
|
||||
|
||||
def move_cursor_to_column(self, column: int) -> None:
|
||||
"""Move cursor to the column specified by the zero-based column index, staying on the same row
|
||||
|
||||
Args:
|
||||
column (int): The zero-based column index to move the cursor to.
|
||||
"""
|
||||
row, _ = self.cursor_position
|
||||
SetConsoleCursorPosition(self._handle, coords=WindowsCoordinates(row, column))
|
||||
|
||||
def move_cursor_backward(self) -> None:
|
||||
"""Move the cursor backward a single cell. Wrap to the previous line if required."""
|
||||
row, col = self.cursor_position
|
||||
if col == 0:
|
||||
row -= 1
|
||||
col = self.screen_size.col - 1
|
||||
else:
|
||||
col -= 1
|
||||
SetConsoleCursorPosition(
|
||||
self._handle, coords=WindowsCoordinates(row=row, col=col)
|
||||
)
|
||||
|
||||
def hide_cursor(self) -> None:
|
||||
"""Hide the cursor"""
|
||||
current_cursor_size = self._get_cursor_size()
|
||||
invisible_cursor = CONSOLE_CURSOR_INFO(dwSize=current_cursor_size, bVisible=0)
|
||||
SetConsoleCursorInfo(self._handle, cursor_info=invisible_cursor)
|
||||
|
||||
def show_cursor(self) -> None:
|
||||
"""Show the cursor"""
|
||||
current_cursor_size = self._get_cursor_size()
|
||||
visible_cursor = CONSOLE_CURSOR_INFO(dwSize=current_cursor_size, bVisible=1)
|
||||
SetConsoleCursorInfo(self._handle, cursor_info=visible_cursor)
|
||||
|
||||
def set_title(self, title: str) -> None:
|
||||
"""Set the title of the terminal window
|
||||
|
||||
Args:
|
||||
title (str): The new title of the console window
|
||||
"""
|
||||
assert len(title) < 255, "Console title must be less than 255 characters"
|
||||
SetConsoleTitle(title)
|
||||
|
||||
def _get_cursor_size(self) -> int:
|
||||
"""Get the percentage of the character cell that is filled by the cursor"""
|
||||
cursor_info = CONSOLE_CURSOR_INFO()
|
||||
GetConsoleCursorInfo(self._handle, cursor_info=cursor_info)
|
||||
return int(cursor_info.dwSize)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
handle = GetStdHandle()
|
||||
|
||||
from pip._vendor.rich.console import Console
|
||||
|
||||
console = Console()
|
||||
|
||||
term = LegacyWindowsTerm(sys.stdout)
|
||||
term.set_title("Win32 Console Examples")
|
||||
|
||||
style = Style(color="black", bgcolor="red")
|
||||
|
||||
heading = Style.parse("black on green")
|
||||
|
||||
# Check colour output
|
||||
console.rule("Checking colour output")
|
||||
console.print("[on red]on red!")
|
||||
console.print("[blue]blue!")
|
||||
console.print("[yellow]yellow!")
|
||||
console.print("[bold yellow]bold yellow!")
|
||||
console.print("[bright_yellow]bright_yellow!")
|
||||
console.print("[dim bright_yellow]dim bright_yellow!")
|
||||
console.print("[italic cyan]italic cyan!")
|
||||
console.print("[bold white on blue]bold white on blue!")
|
||||
console.print("[reverse bold white on blue]reverse bold white on blue!")
|
||||
console.print("[bold black on cyan]bold black on cyan!")
|
||||
console.print("[black on green]black on green!")
|
||||
console.print("[blue on green]blue on green!")
|
||||
console.print("[white on black]white on black!")
|
||||
console.print("[black on white]black on white!")
|
||||
console.print("[#1BB152 on #DA812D]#1BB152 on #DA812D!")
|
||||
|
||||
# Check cursor movement
|
||||
console.rule("Checking cursor movement")
|
||||
console.print()
|
||||
term.move_cursor_backward()
|
||||
term.move_cursor_backward()
|
||||
term.write_text("went back and wrapped to prev line")
|
||||
time.sleep(1)
|
||||
term.move_cursor_up()
|
||||
term.write_text("we go up")
|
||||
time.sleep(1)
|
||||
term.move_cursor_down()
|
||||
term.write_text("and down")
|
||||
time.sleep(1)
|
||||
term.move_cursor_up()
|
||||
term.move_cursor_backward()
|
||||
term.move_cursor_backward()
|
||||
term.write_text("we went up and back 2")
|
||||
time.sleep(1)
|
||||
term.move_cursor_down()
|
||||
term.move_cursor_backward()
|
||||
term.move_cursor_backward()
|
||||
term.write_text("we went down and back 2")
|
||||
time.sleep(1)
|
||||
|
||||
# Check erasing of lines
|
||||
term.hide_cursor()
|
||||
console.print()
|
||||
console.rule("Checking line erasing")
|
||||
console.print("\n...Deleting to the start of the line...")
|
||||
term.write_text("The red arrow shows the cursor location, and direction of erase")
|
||||
time.sleep(1)
|
||||
term.move_cursor_to_column(16)
|
||||
term.write_styled("<", Style.parse("black on red"))
|
||||
term.move_cursor_backward()
|
||||
time.sleep(1)
|
||||
term.erase_start_of_line()
|
||||
time.sleep(1)
|
||||
|
||||
console.print("\n\n...And to the end of the line...")
|
||||
term.write_text("The red arrow shows the cursor location, and direction of erase")
|
||||
time.sleep(1)
|
||||
|
||||
term.move_cursor_to_column(16)
|
||||
term.write_styled(">", Style.parse("black on red"))
|
||||
time.sleep(1)
|
||||
term.erase_end_of_line()
|
||||
time.sleep(1)
|
||||
|
||||
console.print("\n\n...Now the whole line will be erased...")
|
||||
term.write_styled("I'm going to disappear!", style=Style.parse("black on cyan"))
|
||||
time.sleep(1)
|
||||
term.erase_line()
|
||||
|
||||
term.show_cursor()
|
||||
print("\n")
|
|
@ -0,0 +1,72 @@
|
|||
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 LibraryLoader
|
||||
|
||||
if sys.platform == "win32":
|
||||
windll = LibraryLoader(ctypes.WinDLL)
|
||||
else:
|
||||
windll = None
|
||||
raise ImportError("Not windows")
|
||||
|
||||
from pip._vendor.rich._win32_console import (
|
||||
ENABLE_VIRTUAL_TERMINAL_PROCESSING,
|
||||
GetConsoleMode,
|
||||
GetStdHandle,
|
||||
LegacyWindowsError,
|
||||
)
|
||||
|
||||
except (AttributeError, ImportError, ValueError):
|
||||
|
||||
# Fallback if we can't load the Windows DLL
|
||||
def get_windows_console_features() -> WindowsConsoleFeatures:
|
||||
features = WindowsConsoleFeatures()
|
||||
return features
|
||||
|
||||
else:
|
||||
|
||||
def get_windows_console_features() -> WindowsConsoleFeatures:
|
||||
"""Get windows console features.
|
||||
|
||||
Returns:
|
||||
WindowsConsoleFeatures: An instance of WindowsConsoleFeatures.
|
||||
"""
|
||||
handle = GetStdHandle()
|
||||
try:
|
||||
console_mode = GetConsoleMode(handle)
|
||||
success = True
|
||||
except LegacyWindowsError:
|
||||
console_mode = 0
|
||||
success = False
|
||||
vt = bool(success and console_mode & 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))
|
|
@ -0,0 +1,56 @@
|
|||
from typing import Iterable, Sequence, Tuple, cast
|
||||
|
||||
from pip._vendor.rich._win32_console import LegacyWindowsTerm, WindowsCoordinates
|
||||
from pip._vendor.rich.segment import ControlCode, ControlType, Segment
|
||||
|
||||
|
||||
def legacy_windows_render(buffer: Iterable[Segment], term: LegacyWindowsTerm) -> None:
|
||||
"""Makes appropriate Windows Console API calls based on the segments in the buffer.
|
||||
|
||||
Args:
|
||||
buffer (Iterable[Segment]): Iterable of Segments to convert to Win32 API calls.
|
||||
term (LegacyWindowsTerm): Used to call the Windows Console API.
|
||||
"""
|
||||
for text, style, control in buffer:
|
||||
if not control:
|
||||
if style:
|
||||
term.write_styled(text, style)
|
||||
else:
|
||||
term.write_text(text)
|
||||
else:
|
||||
control_codes: Sequence[ControlCode] = control
|
||||
for control_code in control_codes:
|
||||
control_type = control_code[0]
|
||||
if control_type == ControlType.CURSOR_MOVE_TO:
|
||||
_, x, y = cast(Tuple[ControlType, int, int], control_code)
|
||||
term.move_cursor_to(WindowsCoordinates(row=y - 1, col=x - 1))
|
||||
elif control_type == ControlType.CARRIAGE_RETURN:
|
||||
term.write_text("\r")
|
||||
elif control_type == ControlType.HOME:
|
||||
term.move_cursor_to(WindowsCoordinates(0, 0))
|
||||
elif control_type == ControlType.CURSOR_UP:
|
||||
term.move_cursor_up()
|
||||
elif control_type == ControlType.CURSOR_DOWN:
|
||||
term.move_cursor_down()
|
||||
elif control_type == ControlType.CURSOR_FORWARD:
|
||||
term.move_cursor_forward()
|
||||
elif control_type == ControlType.CURSOR_BACKWARD:
|
||||
term.move_cursor_backward()
|
||||
elif control_type == ControlType.CURSOR_MOVE_TO_COLUMN:
|
||||
_, column = cast(Tuple[ControlType, int], control_code)
|
||||
term.move_cursor_to_column(column - 1)
|
||||
elif control_type == ControlType.HIDE_CURSOR:
|
||||
term.hide_cursor()
|
||||
elif control_type == ControlType.SHOW_CURSOR:
|
||||
term.show_cursor()
|
||||
elif control_type == ControlType.ERASE_IN_LINE:
|
||||
_, mode = cast(Tuple[ControlType, int], control_code)
|
||||
if mode == 0:
|
||||
term.erase_end_of_line()
|
||||
elif mode == 1:
|
||||
term.erase_start_of_line()
|
||||
elif mode == 2:
|
||||
term.erase_line()
|
||||
elif control_type == ControlType.SET_WINDOW_TITLE:
|
||||
_, title = cast(Tuple[ControlType, str], control_code)
|
||||
term.set_title(title)
|
|
@ -0,0 +1,56 @@
|
|||
import re
|
||||
from typing import Iterable, List, Tuple
|
||||
|
||||
from ._loop import loop_last
|
||||
from .cells import cell_len, chop_cells
|
||||
|
||||
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:
|
||||
chopped_words = chop_cells(word, max_size=width, position=0)
|
||||
for last, line in loop_last(chopped_words):
|
||||
if start:
|
||||
append(start)
|
||||
|
||||
if last:
|
||||
line_position = _cell_len(line)
|
||||
else:
|
||||
start += len(line)
|
||||
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))
|
|
@ -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))
|
311
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/align.py
Normal file
311
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/align.py
Normal file
|
@ -0,0 +1,311 @@
|
|||
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"]
|
||||
|
||||
|
||||
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="Align",
|
||||
)
|
||||
|
||||
console.print(
|
||||
Align.center(panel, vertical="middle", style="on red", height=console.height)
|
||||
)
|
240
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/ansi.py
Normal file
240
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/ansi.py
Normal file
|
@ -0,0 +1,240 @@
|
|||
import re
|
||||
import sys
|
||||
from contextlib import suppress
|
||||
from typing import Iterable, NamedTuple, Optional
|
||||
|
||||
from .color import Color
|
||||
from .style import Style
|
||||
from .text import Text
|
||||
|
||||
re_ansi = re.compile(
|
||||
r"""
|
||||
(?:\x1b\](.*?)\x1b\\)|
|
||||
(?:\x1b([(@-Z\\-_]|\[[0-?]*[ -/]*[@-~]))
|
||||
""",
|
||||
re.VERBOSE,
|
||||
)
|
||||
|
||||
|
||||
class _AnsiToken(NamedTuple):
|
||||
"""Result of ansi tokenized string."""
|
||||
|
||||
plain: str = ""
|
||||
sgr: Optional[str] = ""
|
||||
osc: Optional[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)
|
||||
"""
|
||||
|
||||
position = 0
|
||||
sgr: Optional[str]
|
||||
osc: Optional[str]
|
||||
for match in re_ansi.finditer(ansi_text):
|
||||
start, end = match.span(0)
|
||||
osc, sgr = match.groups()
|
||||
if start > position:
|
||||
yield _AnsiToken(ansi_text[position:start])
|
||||
if sgr:
|
||||
if sgr == "(":
|
||||
position = end + 1
|
||||
continue
|
||||
if sgr.endswith("m"):
|
||||
yield _AnsiToken("", sgr[1:-1], osc)
|
||||
else:
|
||||
yield _AnsiToken("", sgr, osc)
|
||||
position = end
|
||||
if position < len(ansi_text):
|
||||
yield _AnsiToken(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 iterable 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 plain_text, sgr, osc in _ansi_tokenize(line):
|
||||
if plain_text:
|
||||
append(plain_text, self.style or None)
|
||||
elif osc is not None:
|
||||
if osc.startswith("8;"):
|
||||
_params, semicolon, link = osc[2:].partition(";")
|
||||
if semicolon:
|
||||
self.style = self.style.update_link(link or None)
|
||||
elif sgr is not None:
|
||||
# Translate in to semi-colon separated codes
|
||||
# Ignore invalid codes, because we want to be lenient
|
||||
codes = [
|
||||
min(255, int(_code) if _code else 0)
|
||||
for _code in sgr.split(";")
|
||||
if _code.isdigit() or _code == ""
|
||||
]
|
||||
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 sys.platform != "win32" and __name__ == "__main__": # pragma: no cover
|
||||
import io
|
||||
import os
|
||||
import pty
|
||||
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")
|
|
@ -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)
|
||||
)
|
517
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/box.py
Normal file
517
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/box.py
Normal file
|
@ -0,0 +1,517 @@
|
|||
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_plain_headed_box(self) -> "Box":
|
||||
"""If this box uses special characters for the borders of the header, then
|
||||
return the equivalent box that does not.
|
||||
|
||||
Returns:
|
||||
Box: The most similar Box that doesn't use header-specific box characters.
|
||||
If the current Box already satisfies this criterion, then it's returned.
|
||||
"""
|
||||
return PLAIN_HEADED_SUBSTITUTIONS.get(self, self)
|
||||
|
||||
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(
|
||||
"""\
|
||||
╔═╤╗
|
||||
║ │║
|
||||
╟─┼╢
|
||||
║ │║
|
||||
╟─┼╢
|
||||
╟─┼╢
|
||||
║ │║
|
||||
╚═╧╝
|
||||
"""
|
||||
)
|
||||
|
||||
MARKDOWN: Box = Box(
|
||||
"""\
|
||||
|
||||
| ||
|
||||
|-||
|
||||
| ||
|
||||
|-||
|
||||
|-||
|
||||
| ||
|
||||
|
||||
""",
|
||||
ascii=True,
|
||||
)
|
||||
|
||||
# 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,
|
||||
}
|
||||
|
||||
# Map headed boxes to their headerless equivalents
|
||||
PLAIN_HEADED_SUBSTITUTIONS = {
|
||||
HEAVY_HEAD: SQUARE,
|
||||
SQUARE_DOUBLE_HEAD: SQUARE,
|
||||
MINIMAL_DOUBLE_HEAD: MINIMAL,
|
||||
MINIMAL_HEAVY_HEAD: MINIMAL,
|
||||
ASCII_DOUBLE_HEAD: ASCII2,
|
||||
}
|
||||
|
||||
|
||||
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",
|
||||
"MARKDOWN",
|
||||
]
|
||||
|
||||
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_svg("box.svg")
|
154
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/cells.py
Normal file
154
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/cells.py
Normal file
|
@ -0,0 +1,154 @@
|
|||
import re
|
||||
from functools import lru_cache
|
||||
from typing import Callable, List
|
||||
|
||||
from ._cell_widths import CELL_WIDTHS
|
||||
|
||||
# Regex to match sequence of the most common character ranges
|
||||
_is_single_cell_widths = re.compile("^[\u0020-\u006f\u00a0\u02ff\u0370-\u0482]*$").match
|
||||
|
||||
|
||||
@lru_cache(4096)
|
||||
def cached_cell_len(text: str) -> int:
|
||||
"""Get the number of cells required to display text.
|
||||
|
||||
This method always caches, which may use up a lot of memory. It is recommended to use
|
||||
`cell_len` over this method.
|
||||
|
||||
Args:
|
||||
text (str): Text to display.
|
||||
|
||||
Returns:
|
||||
int: Get the number of cells required to display text.
|
||||
"""
|
||||
_get_size = get_character_cell_size
|
||||
total_size = sum(_get_size(character) for character in text)
|
||||
return total_size
|
||||
|
||||
|
||||
def cell_len(text: str, _cell_len: Callable[[str], int] = cached_cell_len) -> int:
|
||||
"""Get the number of cells required to display text.
|
||||
|
||||
Args:
|
||||
text (str): Text to display.
|
||||
|
||||
Returns:
|
||||
int: Get the number of cells required to display text.
|
||||
"""
|
||||
if len(text) < 512:
|
||||
return _cell_len(text)
|
||||
_get_size = get_character_cell_size
|
||||
total_size = sum(_get_size(character) for character in text)
|
||||
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.
|
||||
"""
|
||||
return _get_codepoint_cell_size(ord(character))
|
||||
|
||||
|
||||
@lru_cache(maxsize=4096)
|
||||
def _get_codepoint_cell_size(codepoint: int) -> int:
|
||||
"""Get the cell size of a character.
|
||||
|
||||
Args:
|
||||
codepoint (int): Codepoint of a 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."""
|
||||
|
||||
if _is_single_cell_widths(text):
|
||||
size = len(text)
|
||||
if size < total:
|
||||
return text + " " * (total - size)
|
||||
return text[:total]
|
||||
|
||||
if total <= 0:
|
||||
return ""
|
||||
cell_size = cell_len(text)
|
||||
if cell_size == total:
|
||||
return text
|
||||
if cell_size < total:
|
||||
return text + " " * (total - cell_size)
|
||||
|
||||
start = 0
|
||||
end = len(text)
|
||||
|
||||
# Binary search until we find the right size
|
||||
while True:
|
||||
pos = (start + end) // 2
|
||||
before = text[: pos + 1]
|
||||
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, returning the characters in reverse
|
||||
order"""
|
||||
_get_character_cell_size = get_character_cell_size
|
||||
characters = [
|
||||
(character, _get_character_cell_size(character)) for character in text
|
||||
]
|
||||
total_size = position
|
||||
lines: List[List[str]] = [[]]
|
||||
append = lines[-1].append
|
||||
|
||||
for character, size in reversed(characters):
|
||||
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)
|
622
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/color.py
Normal file
622
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/color.py
Normal file
|
@ -0,0 +1,622 @@
|
|||
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 Result, rich_repr
|
||||
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}"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return repr(self)
|
||||
|
||||
|
||||
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,
|
||||
"gray0": 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,
|
||||
"gray37": 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,
|
||||
"gray53": 102,
|
||||
"light_slate_grey": 103,
|
||||
"light_slate_gray": 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,
|
||||
"gray63": 139,
|
||||
"medium_purple1": 141,
|
||||
"gold3": 178,
|
||||
"dark_khaki": 143,
|
||||
"navajo_white3": 144,
|
||||
"grey69": 145,
|
||||
"gray69": 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,
|
||||
"gray84": 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,
|
||||
"gray100": 231,
|
||||
"grey3": 232,
|
||||
"gray3": 232,
|
||||
"grey7": 233,
|
||||
"gray7": 233,
|
||||
"grey11": 234,
|
||||
"gray11": 234,
|
||||
"grey15": 235,
|
||||
"gray15": 235,
|
||||
"grey19": 236,
|
||||
"gray19": 236,
|
||||
"grey23": 237,
|
||||
"gray23": 237,
|
||||
"grey27": 238,
|
||||
"gray27": 238,
|
||||
"grey30": 239,
|
||||
"gray30": 239,
|
||||
"grey35": 240,
|
||||
"gray35": 240,
|
||||
"grey39": 241,
|
||||
"gray39": 241,
|
||||
"grey42": 242,
|
||||
"gray42": 242,
|
||||
"grey46": 243,
|
||||
"gray46": 243,
|
||||
"grey50": 244,
|
||||
"gray50": 244,
|
||||
"grey54": 245,
|
||||
"gray54": 245,
|
||||
"grey58": 246,
|
||||
"gray58": 246,
|
||||
"grey62": 247,
|
||||
"gray62": 247,
|
||||
"grey66": 248,
|
||||
"gray66": 248,
|
||||
"grey70": 249,
|
||||
"gray70": 249,
|
||||
"grey74": 250,
|
||||
"gray74": 250,
|
||||
"grey78": 251,
|
||||
"gray78": 251,
|
||||
"grey82": 252,
|
||||
"gray82": 252,
|
||||
"grey85": 253,
|
||||
"gray85": 253,
|
||||
"grey89": 254,
|
||||
"gray89": 254,
|
||||
"grey93": 255,
|
||||
"gray93": 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":
|
||||
"""Displays the actual color if Rich printed."""
|
||||
from .style import Style
|
||||
from .text import Text
|
||||
|
||||
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
|
||||
_h, l, s = rgb_to_hls(*self.triplet.normalized)
|
||||
# If saturation is under 15% assume it is grayscale
|
||||
if s < 0.15:
|
||||
gray = round(l * 25.0)
|
||||
if gray == 0:
|
||||
color_number = 16
|
||||
elif gray == 25:
|
||||
color_number = 231
|
||||
else:
|
||||
color_number = 231 + gray
|
||||
return Color(self.name, ColorType.EIGHT_BIT, number=color_number)
|
||||
|
||||
red, green, blue = self.triplet
|
||||
six_red = red / 95 if red < 95 else 1 + (red - 95) / 40
|
||||
six_green = green / 95 if green < 95 else 1 + (green - 95) / 40
|
||||
six_blue = blue / 95 if blue < 95 else 1 + (blue - 95) / 40
|
||||
|
||||
color_number = (
|
||||
16 + 36 * round(six_red) + 6 * round(six_green) + round(six_blue)
|
||||
)
|
||||
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:
|
||||
if "grey" in name:
|
||||
continue
|
||||
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[has-type]
|
||||
table.add_row(
|
||||
color_cell, str(color_number), Text(f'"{name}"'), color.hex, color.rgb
|
||||
)
|
||||
|
||||
console.print(table)
|
|
@ -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
|
|
@ -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)
|
2633
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/console.py
Normal file
2633
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/console.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
|
|
@ -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)
|
|
@ -0,0 +1,225 @@
|
|||
import sys
|
||||
import time
|
||||
from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Union
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Final
|
||||
else:
|
||||
from pip._vendor.typing_extensions import Final # pragma: no cover
|
||||
|
||||
from .segment import ControlCode, ControlType, Segment
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .console import Console, ConsoleOptions, RenderResult
|
||||
|
||||
STRIP_CONTROL_CODES: Final = [
|
||||
7, # Bell
|
||||
8, # Backspace
|
||||
11, # Vertical tab
|
||||
12, # Form feed
|
||||
13, # Carriage return
|
||||
]
|
||||
_CONTROL_STRIP_TRANSLATE: Final = {
|
||||
_codepoint: None for _codepoint in STRIP_CONTROL_CODES
|
||||
}
|
||||
|
||||
CONTROL_ESCAPE: Final = {
|
||||
7: "\\a",
|
||||
8: "\\b",
|
||||
11: "\\v",
|
||||
12: "\\f",
|
||||
13: "\\r",
|
||||
}
|
||||
|
||||
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",
|
||||
ControlType.SET_WINDOW_TITLE: lambda title: f"\x1b]0;{title}\x07",
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
|
||||
@classmethod
|
||||
def title(cls, title: str) -> "Control":
|
||||
"""Set the terminal window title
|
||||
|
||||
Args:
|
||||
title (str): The new terminal window title
|
||||
"""
|
||||
return cls((ControlType.SET_WINDOW_TITLE, title))
|
||||
|
||||
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_STRIP_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)
|
||||
|
||||
|
||||
def escape_control_codes(
|
||||
text: str,
|
||||
_translate_table: Dict[int, str] = CONTROL_ESCAPE,
|
||||
) -> str:
|
||||
"""Replace control codes with their "escaped" equivalent in the given text.
|
||||
(e.g. "\b" becomes "\\b")
|
||||
|
||||
Args:
|
||||
text (str): A string possibly containing control codes.
|
||||
|
||||
Returns:
|
||||
str: String with control codes replaced with their escaped version.
|
||||
"""
|
||||
return text.translate(_translate_table)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
from pip._vendor.rich.console import Console
|
||||
|
||||
console = Console()
|
||||
console.print("Look at the title of your terminal window ^")
|
||||
# console.print(Control((ControlType.SET_WINDOW_TITLE, "Hello, world!")))
|
||||
for i in range(10):
|
||||
console.set_window_title("🚀 Loading" + "." * i)
|
||||
time.sleep(0.5)
|
|
@ -0,0 +1,190 @@
|
|||
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.async_def": Style(italic=True, color="bright_cyan"),
|
||||
"inspect.def": Style(italic=True, color="bright_cyan"),
|
||||
"inspect.class": 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.number_complex": Style(color="cyan", bold=True, italic=False), # same
|
||||
"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.em": Style(italic=True),
|
||||
"markdown.emph": Style(italic=True), # For commonmark backwards compatibility
|
||||
"markdown.strong": Style(bold=True),
|
||||
"markdown.code": Style(bold=True, color="cyan", bgcolor="black"),
|
||||
"markdown.code_block": Style(color="cyan", bgcolor="black"),
|
||||
"markdown.block_quote": Style(color="magenta"),
|
||||
"markdown.list": Style(color="cyan"),
|
||||
"markdown.item": Style(),
|
||||
"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", underline=True),
|
||||
"markdown.s": Style(strike=True),
|
||||
"iso8601.date": Style(color="blue"),
|
||||
"iso8601.time": Style(color="magenta"),
|
||||
"iso8601.timezone": Style(color="yellow"),
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
import argparse
|
||||
import io
|
||||
|
||||
from pip._vendor.rich.console import Console
|
||||
from pip._vendor.rich.table import Table
|
||||
from pip._vendor.rich.text import Text
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--html", action="store_true", help="Export as HTML table")
|
||||
args = parser.parse_args()
|
||||
html: bool = args.html
|
||||
console = Console(record=True, width=70, file=io.StringIO()) if html else Console()
|
||||
|
||||
table = Table("Name", "Styling")
|
||||
|
||||
for style_name, style in DEFAULT_STYLES.items():
|
||||
table.add_row(Text(style_name, style=style), str(style))
|
||||
|
||||
console.print(table)
|
||||
if html:
|
||||
print(console.export_html(inline_styles=True))
|
|
@ -0,0 +1,37 @@
|
|||
import os
|
||||
import platform
|
||||
|
||||
from pip._vendor.rich import inspect
|
||||
from pip._vendor.rich.console import Console, get_windows_console_features
|
||||
from pip._vendor.rich.panel import Panel
|
||||
from pip._vendor.rich.pretty import Pretty
|
||||
|
||||
|
||||
def report() -> None: # pragma: no cover
|
||||
"""Print a report to the terminal with debugging information"""
|
||||
console = Console()
|
||||
inspect(console)
|
||||
features = get_windows_console_features()
|
||||
inspect(features)
|
||||
|
||||
env_names = (
|
||||
"TERM",
|
||||
"COLORTERM",
|
||||
"CLICOLOR",
|
||||
"NO_COLOR",
|
||||
"TERM_PROGRAM",
|
||||
"COLUMNS",
|
||||
"LINES",
|
||||
"JUPYTER_COLUMNS",
|
||||
"JUPYTER_LINES",
|
||||
"JPY_PARENT_PID",
|
||||
"VSCODE_VERBOSE_LOGGING",
|
||||
)
|
||||
env = {name: os.getenv(name) for name in env_names}
|
||||
console.print(Panel.fit((Pretty(env)), title="[b]Environment Variables"))
|
||||
|
||||
console.print(f'platform="{platform.system()}"')
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
report()
|
|
@ -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])
|
|
@ -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."""
|
|
@ -0,0 +1,57 @@
|
|||
import io
|
||||
from typing import IO, TYPE_CHECKING, Any, List
|
||||
|
||||
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)
|
||||
buffer.clear()
|
||||
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)
|
||||
return len(text)
|
||||
|
||||
def flush(self) -> None:
|
||||
output = "".join(self.__buffer)
|
||||
if output:
|
||||
self.__console.print(output)
|
||||
del self.__buffer[:]
|
||||
|
||||
def fileno(self) -> int:
|
||||
return self.__file.fileno()
|
|
@ -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
|
||||
use cases 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, Optional, Tuple
|
||||
|
||||
|
||||
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,
|
||||
)
|
|
@ -0,0 +1,232 @@
|
|||
import re
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List, Union
|
||||
|
||||
from .text import Span, 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<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<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_complex>(?<!\w)(?:\-?[0-9]+\.?[0-9]*(?:e[-+]?\d+?)?)(?:[-+](?:[0-9]+\.?[0-9]*(?:e[-+]?\d+)?))?j)",
|
||||
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<url>(file|https|http|ws|wss)://[-0-9a-zA-Z$_+!`(),.?/;:&=%#]*)",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class JSONHighlighter(RegexHighlighter):
|
||||
"""Highlights JSON"""
|
||||
|
||||
# Captures the start and end of JSON strings, handling escaped quotes
|
||||
JSON_STR = r"(?<![\\\w])(?P<str>b?\".*?(?<!\\)\")"
|
||||
JSON_WHITESPACE = {" ", "\n", "\r", "\t"}
|
||||
|
||||
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]*)",
|
||||
JSON_STR,
|
||||
),
|
||||
]
|
||||
|
||||
def highlight(self, text: Text) -> None:
|
||||
super().highlight(text)
|
||||
|
||||
# Additional work to handle highlighting JSON keys
|
||||
plain = text.plain
|
||||
append = text.spans.append
|
||||
whitespace = self.JSON_WHITESPACE
|
||||
for match in re.finditer(self.JSON_STR, plain):
|
||||
start, end = match.span()
|
||||
cursor = end
|
||||
while cursor < len(plain):
|
||||
char = plain[cursor]
|
||||
cursor += 1
|
||||
if char == ":":
|
||||
append(Span(start, end, "json.key"))
|
||||
elif char in whitespace:
|
||||
continue
|
||||
break
|
||||
|
||||
|
||||
class ISO8601Highlighter(RegexHighlighter):
|
||||
"""Highlights the ISO8601 date time strings.
|
||||
Regex reference: https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch04s07.html
|
||||
"""
|
||||
|
||||
base_style = "iso8601."
|
||||
highlights = [
|
||||
#
|
||||
# Dates
|
||||
#
|
||||
# Calendar month (e.g. 2008-08). The hyphen is required
|
||||
r"^(?P<year>[0-9]{4})-(?P<month>1[0-2]|0[1-9])$",
|
||||
# Calendar date w/o hyphens (e.g. 20080830)
|
||||
r"^(?P<date>(?P<year>[0-9]{4})(?P<month>1[0-2]|0[1-9])(?P<day>3[01]|0[1-9]|[12][0-9]))$",
|
||||
# Ordinal date (e.g. 2008-243). The hyphen is optional
|
||||
r"^(?P<date>(?P<year>[0-9]{4})-?(?P<day>36[0-6]|3[0-5][0-9]|[12][0-9]{2}|0[1-9][0-9]|00[1-9]))$",
|
||||
#
|
||||
# Weeks
|
||||
#
|
||||
# Week of the year (e.g., 2008-W35). The hyphen is optional
|
||||
r"^(?P<date>(?P<year>[0-9]{4})-?W(?P<week>5[0-3]|[1-4][0-9]|0[1-9]))$",
|
||||
# Week date (e.g., 2008-W35-6). The hyphens are optional
|
||||
r"^(?P<date>(?P<year>[0-9]{4})-?W(?P<week>5[0-3]|[1-4][0-9]|0[1-9])-?(?P<day>[1-7]))$",
|
||||
#
|
||||
# Times
|
||||
#
|
||||
# Hours and minutes (e.g., 17:21). The colon is optional
|
||||
r"^(?P<time>(?P<hour>2[0-3]|[01][0-9]):?(?P<minute>[0-5][0-9]))$",
|
||||
# Hours, minutes, and seconds w/o colons (e.g., 172159)
|
||||
r"^(?P<time>(?P<hour>2[0-3]|[01][0-9])(?P<minute>[0-5][0-9])(?P<second>[0-5][0-9]))$",
|
||||
# Time zone designator (e.g., Z, +07 or +07:00). The colons and the minutes are optional
|
||||
r"^(?P<timezone>(Z|[+-](?:2[0-3]|[01][0-9])(?::?(?:[0-5][0-9]))?))$",
|
||||
# Hours, minutes, and seconds with time zone designator (e.g., 17:21:59+07:00).
|
||||
# All the colons are optional. The minutes in the time zone designator are also optional
|
||||
r"^(?P<time>(?P<hour>2[0-3]|[01][0-9])(?P<minute>[0-5][0-9])(?P<second>[0-5][0-9]))(?P<timezone>Z|[+-](?:2[0-3]|[01][0-9])(?::?(?:[0-5][0-9]))?)$",
|
||||
#
|
||||
# Date and Time
|
||||
#
|
||||
# Calendar date with hours, minutes, and seconds (e.g., 2008-08-30 17:21:59 or 20080830 172159).
|
||||
# A space is required between the date and the time. The hyphens and colons are optional.
|
||||
# This regex matches dates and times that specify some hyphens or colons but omit others.
|
||||
# This does not follow ISO 8601
|
||||
r"^(?P<date>(?P<year>[0-9]{4})(?P<hyphen>-)?(?P<month>1[0-2]|0[1-9])(?(hyphen)-)(?P<day>3[01]|0[1-9]|[12][0-9])) (?P<time>(?P<hour>2[0-3]|[01][0-9])(?(hyphen):)(?P<minute>[0-5][0-9])(?(hyphen):)(?P<second>[0-5][0-9]))$",
|
||||
#
|
||||
# XML Schema dates and times
|
||||
#
|
||||
# Date, with optional time zone (e.g., 2008-08-30 or 2008-08-30+07:00).
|
||||
# Hyphens are required. This is the XML Schema 'date' type
|
||||
r"^(?P<date>(?P<year>-?(?:[1-9][0-9]*)?[0-9]{4})-(?P<month>1[0-2]|0[1-9])-(?P<day>3[01]|0[1-9]|[12][0-9]))(?P<timezone>Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$",
|
||||
# Time, with optional fractional seconds and time zone (e.g., 01:45:36 or 01:45:36.123+07:00).
|
||||
# There is no limit on the number of digits for the fractional seconds. This is the XML Schema 'time' type
|
||||
r"^(?P<time>(?P<hour>2[0-3]|[01][0-9]):(?P<minute>[0-5][0-9]):(?P<second>[0-5][0-9])(?P<frac>\.[0-9]+)?)(?P<timezone>Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$",
|
||||
# Date and time, with optional fractional seconds and time zone (e.g., 2008-08-30T01:45:36 or 2008-08-30T01:45:36.123Z).
|
||||
# This is the XML Schema 'dateTime' type
|
||||
r"^(?P<date>(?P<year>-?(?:[1-9][0-9]*)?[0-9]{4})-(?P<month>1[0-2]|0[1-9])-(?P<day>3[01]|0[1-9]|[12][0-9]))T(?P<time>(?P<hour>2[0-3]|[01][0-9]):(?P<minute>[0-5][0-9]):(?P<second>[0-5][0-9])(?P<ms>\.[0-9]+)?)(?P<timezone>Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$",
|
||||
]
|
||||
|
||||
|
||||
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"
|
||||
)
|
||||
import json
|
||||
|
||||
console.print_json(json.dumps(obj={"name": "apple", "count": 1}), indent=None)
|
140
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/json.py
Normal file
140
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/json.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
from pathlib import Path
|
||||
from json import loads, dumps
|
||||
from typing import Any, Callable, Optional, Union
|
||||
|
||||
from .text import Text
|
||||
from .highlighter import JSONHighlighter, NullHighlighter
|
||||
|
||||
|
||||
class JSON:
|
||||
"""A renderable which pretty prints JSON.
|
||||
|
||||
Args:
|
||||
json (str): JSON encoded data.
|
||||
indent (Union[None, int, str], 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: Union[None, int, str] = 2,
|
||||
highlight: bool = True,
|
||||
skip_keys: bool = False,
|
||||
ensure_ascii: bool = False,
|
||||
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: Union[None, int, str] = 2,
|
||||
highlight: bool = True,
|
||||
skip_keys: bool = False,
|
||||
ensure_ascii: bool = False,
|
||||
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 (Union[None, int, str], 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:
|
||||
json_data = Path(args.path).read_text()
|
||||
except Exception as error:
|
||||
error_console.print(f"Unable to read {args.path!r}; {error}")
|
||||
sys.exit(-1)
|
||||
|
||||
console.print(JSON(json_data, indent=args.indent), soft_wrap=True)
|
|
@ -0,0 +1,101 @@
|
|||
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Sequence
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pip._vendor.rich.console import ConsoleRenderable
|
||||
|
||||
from . import get_console
|
||||
from .segment import Segment
|
||||
from .terminal_theme import DEFAULT_TERMINAL_THEME
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pip._vendor.rich.console import ConsoleRenderable
|
||||
|
||||
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: Sequence[str], exclude: Sequence[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: "ConsoleRenderable",
|
||||
include: Sequence[str],
|
||||
exclude: Sequence[str],
|
||||
**kwargs: Any,
|
||||
) -> Dict[str, str]:
|
||||
console = get_console()
|
||||
segments = list(console.render(self, console.options))
|
||||
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("&", "&").replace("<", "<").replace(">", ">")
|
||||
|
||||
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}" target="_blank">{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."""
|
||||
html = _render_segments(segments)
|
||||
jupyter_renderable = JupyterRenderable(html, text)
|
||||
try:
|
||||
from IPython.display import display as ipython_display
|
||||
|
||||
ipython_display(jupyter_renderable)
|
||||
except ModuleNotFoundError:
|
||||
# Handle the case where the Console has force_jupyter=True,
|
||||
# but IPython is not installed.
|
||||
pass
|
||||
|
||||
|
||||
def print(*args: Any, **kwargs: Any) -> None:
|
||||
"""Proxy for Console print."""
|
||||
console = get_console()
|
||||
return console.print(*args, **kwargs)
|
443
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/layout.py
Normal file
443
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/layout.py
Normal file
|
@ -0,0 +1,443 @@
|
|||
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 .region import Region
|
||||
from .repr import Result, rich_repr
|
||||
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",
|
||||
height=height,
|
||||
)
|
||||
|
||||
|
||||
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,
|
||||
) -> 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.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 to 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)
|
375
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/live.py
Normal file
375
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/live.py
Normal file
|
@ -0,0 +1,375 @@
|
|||
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:
|
||||
try:
|
||||
self.refresh()
|
||||
except Exception:
|
||||
# If refresh fails, we want to stop the redirection of sys.stderr,
|
||||
# so the error stacktrace is properly displayed in the terminal.
|
||||
# (or, if the code that calls Rich captures the exception and wants to display something,
|
||||
# let this be displayed in the terminal).
|
||||
self.stop()
|
||||
raise
|
||||
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
|
||||
|
||||
if self.auto_refresh and self._refresh_thread is not None:
|
||||
self._refresh_thread.stop()
|
||||
self._refresh_thread = None
|
||||
# allow it to fully render on the last even if overflow
|
||||
self.vertical_overflow = "visible"
|
||||
with self.console:
|
||||
try:
|
||||
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.transient and not self._alt_screen:
|
||||
self.console.control(self._live_render.restore_cursor())
|
||||
if self.ipy_widget is not None and self.transient:
|
||||
self.ipy_widget.close() # pragma: no cover
|
||||
|
||||
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 or self.console.is_jupyter:
|
||||
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.
|
||||
"""
|
||||
if isinstance(renderable, str):
|
||||
renderable = self.console.render_str(renderable)
|
||||
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))
|
|
@ -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
|
||||
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
|
||||
|
||||
new_line = Segment.line()
|
||||
for last, line in loop_last(lines):
|
||||
yield from line
|
||||
if not last:
|
||||
yield new_line
|
|
@ -0,0 +1,289 @@
|
|||
import logging
|
||||
from datetime import datetime
|
||||
from logging import Handler, LogRecord
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
from typing import ClassVar, Iterable, List, Optional, Type, Union
|
||||
|
||||
from pip._vendor.rich._null_file import NullFile
|
||||
|
||||
from . import get_console
|
||||
from ._log_render import FormatTimeCallable, LogRender
|
||||
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.
|
||||
tracebacks_suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
|
||||
locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
|
||||
Defaults to 10.
|
||||
locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
|
||||
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 (List[str], optional): List of words to highlight instead of ``RichHandler.KEYWORDS``.
|
||||
"""
|
||||
|
||||
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,
|
||||
tracebacks_suppress: Iterable[Union[str, ModuleType]] = (),
|
||||
locals_max_length: int = 10,
|
||||
locals_max_string: int = 80,
|
||||
log_time_format: Union[str, FormatTimeCallable] = "[%x %X]",
|
||||
keywords: Optional[List[str]] = None,
|
||||
) -> 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.tracebacks_suppress = tracebacks_suppress
|
||||
self.locals_max_length = locals_max_length
|
||||
self.locals_max_string = locals_max_string
|
||||
self.keywords = keywords
|
||||
|
||||
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,
|
||||
suppress=self.tracebacks_suppress,
|
||||
)
|
||||
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
|
||||
)
|
||||
if isinstance(self.console.file, NullFile):
|
||||
# Handles pythonw, where stdout/stderr are null, and we return NullFile
|
||||
# instance from Console.file. In this case, we still want to make a log record
|
||||
# even though we won't be writing anything to a file.
|
||||
self.handleError(record)
|
||||
else:
|
||||
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.
|
||||
|
||||
Args:
|
||||
record (LogRecord): logging Record.
|
||||
message (str): String containing log message.
|
||||
|
||||
Returns:
|
||||
ConsoleRenderable: Renderable to display log message.
|
||||
"""
|
||||
use_markup = getattr(record, "markup", self.markup)
|
||||
message_text = Text.from_markup(message) if use_markup else Text(message)
|
||||
|
||||
highlighter = getattr(record, "highlighter", self.highlighter)
|
||||
if highlighter:
|
||||
message_text = highlighter(message_text)
|
||||
|
||||
if self.keywords is None:
|
||||
self.keywords = self.KEYWORDS
|
||||
|
||||
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))
|
246
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/markup.py
Normal file
246
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/markup.py
Normal file
|
@ -0,0 +1,246 @@
|
|||
import re
|
||||
from ast import literal_eval
|
||||
from operator import attrgetter
|
||||
from typing import Callable, Iterable, List, Match, NamedTuple, Optional, Tuple, Union
|
||||
|
||||
from ._emoji_replace import _emoji_replace
|
||||
from .emoji import EmojiVariant
|
||||
from .errors import MarkupError
|
||||
from .style import Style
|
||||
from .text import Span, Text
|
||||
|
||||
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:
|
||||
# Handle open brace escapes, where the brace is not part of a tag.
|
||||
plain_text = plain_text.replace("\\[", "[")
|
||||
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 import print
|
||||
from pip._vendor.rich.table import Table
|
||||
|
||||
grid = Table("Markup", "Result", padding=(0, 1))
|
||||
|
||||
for markup in MARKUP:
|
||||
grid.add_row(Text(markup), markup)
|
||||
|
||||
print(grid)
|
|
@ -0,0 +1,151 @@
|
|||
from operator import itemgetter
|
||||
from typing import TYPE_CHECKING, Callable, NamedTuple, Optional, Sequence
|
||||
|
||||
from . import errors
|
||||
from .protocol import is_renderable, rich_cast
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .console import Console, ConsoleOptions, RenderableType
|
||||
|
||||
|
||||
class Measurement(NamedTuple):
|
||||
"""Stores the minimum and maximum widths (in characters) required to render an object."""
|
||||
|
||||
minimum: int
|
||||
"""Minimum number of cells required to render."""
|
||||
maximum: int
|
||||
"""Maximum number of cells required to render."""
|
||||
|
||||
@property
|
||||
def span(self) -> int:
|
||||
"""Get difference between maximum and minimum."""
|
||||
return self.maximum - self.minimum
|
||||
|
||||
def normalize(self) -> "Measurement":
|
||||
"""Get measurement that ensures that minimum <= maximum and minimum >= 0
|
||||
|
||||
Returns:
|
||||
Measurement: A normalized measurement.
|
||||
"""
|
||||
minimum, maximum = self
|
||||
minimum = min(max(0, minimum), maximum)
|
||||
return Measurement(max(0, minimum), max(0, max(minimum, maximum)))
|
||||
|
||||
def with_maximum(self, width: int) -> "Measurement":
|
||||
"""Get a RenderableWith where the widths are <= width.
|
||||
|
||||
Args:
|
||||
width (int): Maximum desired width.
|
||||
|
||||
Returns:
|
||||
Measurement: New Measurement object.
|
||||
"""
|
||||
minimum, maximum = self
|
||||
return Measurement(min(minimum, width), min(maximum, width))
|
||||
|
||||
def with_minimum(self, width: int) -> "Measurement":
|
||||
"""Get a RenderableWith where the widths are >= width.
|
||||
|
||||
Args:
|
||||
width (int): Minimum desired width.
|
||||
|
||||
Returns:
|
||||
Measurement: New Measurement object.
|
||||
"""
|
||||
minimum, maximum = self
|
||||
width = max(0, width)
|
||||
return Measurement(max(minimum, width), max(maximum, width))
|
||||
|
||||
def clamp(
|
||||
self, min_width: Optional[int] = None, max_width: Optional[int] = None
|
||||
) -> "Measurement":
|
||||
"""Clamp a measurement within the specified range.
|
||||
|
||||
Args:
|
||||
min_width (int): Minimum desired width, or ``None`` for no minimum. Defaults to None.
|
||||
max_width (int): Maximum desired width, or ``None`` for no maximum. Defaults to None.
|
||||
|
||||
Returns:
|
||||
Measurement: New Measurement object.
|
||||
"""
|
||||
measurement = self
|
||||
if min_width is not None:
|
||||
measurement = measurement.with_minimum(min_width)
|
||||
if max_width is not None:
|
||||
measurement = measurement.with_maximum(max_width)
|
||||
return measurement
|
||||
|
||||
@classmethod
|
||||
def get(
|
||||
cls, console: "Console", options: "ConsoleOptions", renderable: "RenderableType"
|
||||
) -> "Measurement":
|
||||
"""Get a measurement for a renderable.
|
||||
|
||||
Args:
|
||||
console (~rich.console.Console): Console instance.
|
||||
options (~rich.console.ConsoleOptions): Console options.
|
||||
renderable (RenderableType): An object that may be rendered with Rich.
|
||||
|
||||
Raises:
|
||||
errors.NotRenderableError: If the object is not renderable.
|
||||
|
||||
Returns:
|
||||
Measurement: Measurement object containing range of character widths required to render the object.
|
||||
"""
|
||||
_max_width = options.max_width
|
||||
if _max_width < 1:
|
||||
return Measurement(0, 0)
|
||||
if isinstance(renderable, str):
|
||||
renderable = console.render_str(
|
||||
renderable, markup=options.markup, highlight=False
|
||||
)
|
||||
renderable = rich_cast(renderable)
|
||||
if is_renderable(renderable):
|
||||
get_console_width: Optional[
|
||||
Callable[["Console", "ConsoleOptions"], "Measurement"]
|
||||
] = getattr(renderable, "__rich_measure__", None)
|
||||
if get_console_width is not None:
|
||||
render_width = (
|
||||
get_console_width(console, options)
|
||||
.normalize()
|
||||
.with_maximum(_max_width)
|
||||
)
|
||||
if render_width.maximum < 1:
|
||||
return Measurement(0, 0)
|
||||
return render_width.normalize()
|
||||
else:
|
||||
return Measurement(0, _max_width)
|
||||
else:
|
||||
raise errors.NotRenderableError(
|
||||
f"Unable to get render width for {renderable!r}; "
|
||||
"a str, Segment, or object with __rich_console__ method is required"
|
||||
)
|
||||
|
||||
|
||||
def measure_renderables(
|
||||
console: "Console",
|
||||
options: "ConsoleOptions",
|
||||
renderables: Sequence["RenderableType"],
|
||||
) -> "Measurement":
|
||||
"""Get a measurement that would fit a number of renderables.
|
||||
|
||||
Args:
|
||||
console (~rich.console.Console): Console instance.
|
||||
options (~rich.console.ConsoleOptions): Console options.
|
||||
renderables (Iterable[RenderableType]): One or more renderable objects.
|
||||
|
||||
Returns:
|
||||
Measurement: Measurement object containing range of character widths required to
|
||||
contain all given renderables.
|
||||
"""
|
||||
if not renderables:
|
||||
return Measurement(0, 0)
|
||||
get_measurement = Measurement.get
|
||||
measurements = [
|
||||
get_measurement(console, options, renderable) for renderable in renderables
|
||||
]
|
||||
measured_width = Measurement(
|
||||
max(measurements, key=itemgetter(0)).minimum,
|
||||
max(measurements, key=itemgetter(1)).maximum,
|
||||
)
|
||||
return measured_width
|
|
@ -0,0 +1,141 @@
|
|||
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,
|
||||
)
|
||||
render_options = options.update_width(width - self.left - self.right)
|
||||
if render_options.height is not None:
|
||||
render_options = render_options.update_height(
|
||||
height=render_options.height - self.top - self.bottom
|
||||
)
|
||||
lines = console.render_lines(
|
||||
self.renderable, render_options, style=style, pad=True
|
||||
)
|
||||
_Segment = Segment
|
||||
|
||||
left = _Segment(" " * self.left, style) if self.left else None
|
||||
right = (
|
||||
[_Segment(f'{" " * self.right}', style), _Segment.line()]
|
||||
if self.right
|
||||
else [_Segment.line()]
|
||||
)
|
||||
blank_line: Optional[List[Segment]] = None
|
||||
if self.top:
|
||||
blank_line = [_Segment(f'{" " * width}\n', style)]
|
||||
yield from blank_line * self.top
|
||||
if left:
|
||||
for line in lines:
|
||||
yield left
|
||||
yield from line
|
||||
yield from right
|
||||
else:
|
||||
for line in lines:
|
||||
yield from line
|
||||
yield from right
|
||||
if self.bottom:
|
||||
blank_line = blank_line or [_Segment(f'{" " * width}\n', style)]
|
||||
yield from blank_line * self.bottom
|
||||
|
||||
def __rich_measure__(
|
||||
self, console: "Console", options: "ConsoleOptions"
|
||||
) -> "Measurement":
|
||||
max_width = options.max_width
|
||||
extra_width = self.left + self.right
|
||||
if max_width - extra_width < 1:
|
||||
return Measurement(max_width, max_width)
|
||||
measure_min, measure_max = Measurement.get(console, options, self.renderable)
|
||||
measurement = Measurement(measure_min + extra_width, measure_max + extra_width)
|
||||
measurement = measurement.with_maximum(max_width)
|
||||
return measurement
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
from pip._vendor.rich import print
|
||||
|
||||
print(Padding("Hello, World", (2, 4), style="on blue"))
|
|
@ -0,0 +1,34 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import Any
|
||||
|
||||
|
||||
class Pager(ABC):
|
||||
"""Base class for a pager."""
|
||||
|
||||
@abstractmethod
|
||||
def show(self, content: str) -> None:
|
||||
"""Show content in pager.
|
||||
|
||||
Args:
|
||||
content (str): Content to be displayed.
|
||||
"""
|
||||
|
||||
|
||||
class SystemPager(Pager):
|
||||
"""Uses the pager installed on the system."""
|
||||
|
||||
def _pager(self, content: str) -> Any: # pragma: no cover
|
||||
return __import__("pydoc").pager(content)
|
||||
|
||||
def show(self, content: str) -> None:
|
||||
"""Use the same pager used by pydoc."""
|
||||
self._pager(content)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
from .__main__ import make_test_card
|
||||
from .console import Console
|
||||
|
||||
console = Console()
|
||||
with console.pager(styles=True):
|
||||
console.print(make_test_card())
|
|
@ -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())
|
308
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/panel.py
Normal file
308
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/panel.py
Normal file
|
@ -0,0 +1,308 @@
|
|||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from .align import AlignMethod
|
||||
from .box import ROUNDED, Box
|
||||
from .cells import cell_len
|
||||
from .jupyter import JupyterMixin
|
||||
from .measure import Measurement, measure_renderables
|
||||
from .padding import Padding, PaddingDimensions
|
||||
from .segment import Segment
|
||||
from .style import Style, StyleType
|
||||
from .text import Text, TextType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .console import Console, ConsoleOptions, RenderableType, RenderResult
|
||||
|
||||
|
||||
class Panel(JupyterMixin):
|
||||
"""A console renderable that draws a border around its contents.
|
||||
|
||||
Example:
|
||||
>>> console.print(Panel("Hello, World!"))
|
||||
|
||||
Args:
|
||||
renderable (RenderableType): A console renderable object.
|
||||
box (Box, 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)
|
||||
|
||||
def align_text(
|
||||
text: Text, width: int, align: str, character: str, style: Style
|
||||
) -> Text:
|
||||
"""Gets new aligned text.
|
||||
|
||||
Args:
|
||||
text (Text): Title or subtitle text.
|
||||
width (int): Desired width.
|
||||
align (str): Alignment.
|
||||
character (str): Character for alignment.
|
||||
style (Style): Border style
|
||||
|
||||
Returns:
|
||||
Text: New text instance
|
||||
"""
|
||||
text = text.copy()
|
||||
text.truncate(width)
|
||||
excess_space = width - cell_len(text.plain)
|
||||
if excess_space:
|
||||
if align == "left":
|
||||
return Text.assemble(
|
||||
text,
|
||||
(character * excess_space, style),
|
||||
no_wrap=True,
|
||||
end="",
|
||||
)
|
||||
elif align == "center":
|
||||
left = excess_space // 2
|
||||
return Text.assemble(
|
||||
(character * left, style),
|
||||
text,
|
||||
(character * (excess_space - left), style),
|
||||
no_wrap=True,
|
||||
end="",
|
||||
)
|
||||
else:
|
||||
return Text.assemble(
|
||||
(character * excess_space, style),
|
||||
text,
|
||||
no_wrap=True,
|
||||
end="",
|
||||
)
|
||||
return text
|
||||
|
||||
title_text = self._title
|
||||
if title_text is not None:
|
||||
title_text.stylize_before(border_style)
|
||||
|
||||
child_width = (
|
||||
width - 2
|
||||
if self.expand
|
||||
else console.measure(
|
||||
renderable, options=options.update_width(width - 2)
|
||||
).maximum
|
||||
)
|
||||
child_height = self.height or options.height or None
|
||||
if child_height:
|
||||
child_height -= 2
|
||||
if title_text is not None:
|
||||
child_width = min(
|
||||
options.max_width - 2, max(child_width, title_text.cell_len + 2)
|
||||
)
|
||||
|
||||
width = child_width + 2
|
||||
child_options = options.update(
|
||||
width=child_width, height=child_height, highlight=self.highlight
|
||||
)
|
||||
lines = console.render_lines(renderable, child_options, style=style)
|
||||
|
||||
line_start = Segment(box.mid_left, border_style)
|
||||
line_end = Segment(f"{box.mid_right}", border_style)
|
||||
new_line = Segment.line()
|
||||
if title_text is None or width <= 4:
|
||||
yield Segment(box.get_top([width - 2]), border_style)
|
||||
else:
|
||||
title_text = align_text(
|
||||
title_text,
|
||||
width - 4,
|
||||
self.title_align,
|
||||
box.top,
|
||||
border_style,
|
||||
)
|
||||
yield Segment(box.top_left + box.top, border_style)
|
||||
yield from console.render(title_text, child_options.update_width(width - 4))
|
||||
yield Segment(box.top + box.top_right, border_style)
|
||||
|
||||
yield new_line
|
||||
for line in lines:
|
||||
yield line_start
|
||||
yield from line
|
||||
yield line_end
|
||||
yield new_line
|
||||
|
||||
subtitle_text = self._subtitle
|
||||
if subtitle_text is not None:
|
||||
subtitle_text.stylize_before(border_style)
|
||||
|
||||
if subtitle_text is None or width <= 4:
|
||||
yield Segment(box.get_bottom([width - 2]), border_style)
|
||||
else:
|
||||
subtitle_text = align_text(
|
||||
subtitle_text,
|
||||
width - 4,
|
||||
self.subtitle_align,
|
||||
box.bottom,
|
||||
border_style,
|
||||
)
|
||||
yield Segment(box.bottom_left + box.bottom, border_style)
|
||||
yield from console.render(
|
||||
subtitle_text, child_options.update_width(width - 4)
|
||||
)
|
||||
yield Segment(box.bottom + box.bottom_right, border_style)
|
||||
|
||||
yield new_line
|
||||
|
||||
def __rich_measure__(
|
||||
self, console: "Console", options: "ConsoleOptions"
|
||||
) -> "Measurement":
|
||||
_title = self._title
|
||||
_, right, _, left = Padding.unpack(self.padding)
|
||||
padding = left + right
|
||||
renderables = [self.renderable, _title] if _title else [self.renderable]
|
||||
|
||||
if self.width is None:
|
||||
width = (
|
||||
measure_renderables(
|
||||
console,
|
||||
options.update_width(options.max_width - padding - 2),
|
||||
renderables,
|
||||
).maximum
|
||||
+ padding
|
||||
+ 2
|
||||
)
|
||||
else:
|
||||
width = self.width
|
||||
return Measurement(width, width)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
from .console import Console
|
||||
|
||||
c = Console()
|
||||
|
||||
from .box import DOUBLE, ROUNDED
|
||||
from .padding import Padding
|
||||
|
||||
p = Panel(
|
||||
"Hello, World!",
|
||||
title="rich.Panel",
|
||||
style="white on blue",
|
||||
box=DOUBLE,
|
||||
padding=1,
|
||||
)
|
||||
|
||||
c.print()
|
||||
c.print(p)
|
994
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/pretty.py
Normal file
994
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/pretty.py
Normal file
|
@ -0,0 +1,994 @@
|
|||
import builtins
|
||||
import collections
|
||||
import dataclasses
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
from array import array
|
||||
from collections import Counter, UserDict, UserList, defaultdict, deque
|
||||
from dataclasses import dataclass, fields, is_dataclass
|
||||
from inspect import isclass
|
||||
from itertools import islice
|
||||
from types import MappingProxyType
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Callable,
|
||||
DefaultDict,
|
||||
Dict,
|
||||
Iterable,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Set,
|
||||
Tuple,
|
||||
Union,
|
||||
)
|
||||
|
||||
from pip._vendor.rich.repr import RichReprResult
|
||||
|
||||
try:
|
||||
import attr as _attr_module
|
||||
|
||||
_has_attrs = hasattr(_attr_module, "ib")
|
||||
except ImportError: # pragma: no cover
|
||||
_has_attrs = False
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
|
||||
def _is_attr_object(obj: Any) -> bool:
|
||||
"""Check if an object was created with attrs module."""
|
||||
return _has_attrs and _attr_module.has(type(obj))
|
||||
|
||||
|
||||
def _get_attr_fields(obj: Any) -> Sequence["_attr_module.Attribute[Any]"]:
|
||||
"""Get fields for an attrs object."""
|
||||
return _attr_module.fields(type(obj)) if _has_attrs 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: # pragma: no coverage
|
||||
return False
|
||||
|
||||
|
||||
_dummy_namedtuple = collections.namedtuple("_dummy_namedtuple", [])
|
||||
|
||||
|
||||
def _has_default_namedtuple_repr(obj: object) -> bool:
|
||||
"""Check if an instance of namedtuple contains the default repr
|
||||
|
||||
Args:
|
||||
obj (object): A namedtuple
|
||||
|
||||
Returns:
|
||||
bool: True if the default repr is used, False if there's a custom repr.
|
||||
"""
|
||||
obj_file = None
|
||||
try:
|
||||
obj_file = inspect.getfile(obj.__repr__)
|
||||
except (OSError, TypeError):
|
||||
# OSError handles case where object is defined in __main__ scope, e.g. REPL - no filename available.
|
||||
# TypeError trapped defensively, in case of object without filename slips through.
|
||||
pass
|
||||
default_repr_file = inspect.getfile(_dummy_namedtuple.__repr__)
|
||||
return obj_file == default_repr_file
|
||||
|
||||
|
||||
def _ipy_display_hook(
|
||||
value: Any,
|
||||
console: Optional["Console"] = None,
|
||||
overflow: "OverflowMethod" = "ignore",
|
||||
crop: bool = False,
|
||||
indent_guides: bool = False,
|
||||
max_length: Optional[int] = None,
|
||||
max_string: Optional[int] = None,
|
||||
max_depth: Optional[int] = None,
|
||||
expand_all: bool = False,
|
||||
) -> Union[str, None]:
|
||||
# needed here to prevent circular import:
|
||||
from .console import ConsoleRenderable
|
||||
|
||||
# always skip rich generated jupyter renderables or None values
|
||||
if _safe_isinstance(value, JupyterRenderable) or value is None:
|
||||
return None
|
||||
|
||||
console = console or get_console()
|
||||
|
||||
with console.capture() as capture:
|
||||
# certain renderables should start on a new line
|
||||
if _safe_isinstance(value, ConsoleRenderable):
|
||||
console.line()
|
||||
console.print(
|
||||
value
|
||||
if _safe_isinstance(value, RichRenderable)
|
||||
else Pretty(
|
||||
value,
|
||||
overflow=overflow,
|
||||
indent_guides=indent_guides,
|
||||
max_length=max_length,
|
||||
max_string=max_string,
|
||||
max_depth=max_depth,
|
||||
expand_all=expand_all,
|
||||
margin=12,
|
||||
),
|
||||
crop=crop,
|
||||
new_line_start=True,
|
||||
end="",
|
||||
)
|
||||
# strip trailing newline, not usually part of a text repr
|
||||
# I'm not sure if this should be prevented at a lower level
|
||||
return capture.get().rstrip("\n")
|
||||
|
||||
|
||||
def _safe_isinstance(
|
||||
obj: object, class_or_tuple: Union[type, Tuple[type, ...]]
|
||||
) -> bool:
|
||||
"""isinstance can fail in rare cases, for example types with no __class__"""
|
||||
try:
|
||||
return isinstance(obj, class_or_tuple)
|
||||
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,
|
||||
max_depth: 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.
|
||||
max_depth (int, optional): Maximum depth of nested data structures, or None for no maximum. 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
|
||||
|
||||
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[attr-defined]
|
||||
console.print(
|
||||
value
|
||||
if _safe_isinstance(value, RichRenderable)
|
||||
else Pretty(
|
||||
value,
|
||||
overflow=overflow,
|
||||
indent_guides=indent_guides,
|
||||
max_length=max_length,
|
||||
max_string=max_string,
|
||||
max_depth=max_depth,
|
||||
expand_all=expand_all,
|
||||
),
|
||||
crop=crop,
|
||||
)
|
||||
builtins._ = value # type: ignore[attr-defined]
|
||||
|
||||
if "get_ipython" in globals():
|
||||
ip = get_ipython() # type: ignore[name-defined]
|
||||
from IPython.core.formatters import BaseFormatter
|
||||
|
||||
class RichFormatter(BaseFormatter): # type: ignore[misc]
|
||||
pprint: bool = True
|
||||
|
||||
def __call__(self, value: Any) -> Any:
|
||||
if self.pprint:
|
||||
return _ipy_display_hook(
|
||||
value,
|
||||
console=get_console(),
|
||||
overflow=overflow,
|
||||
indent_guides=indent_guides,
|
||||
max_length=max_length,
|
||||
max_string=max_string,
|
||||
max_depth=max_depth,
|
||||
expand_all=expand_all,
|
||||
)
|
||||
else:
|
||||
return repr(value)
|
||||
|
||||
# replace plain text formatter with rich formatter
|
||||
rich_formatter = RichFormatter()
|
||||
ip.display_formatter.formatters["text/plain"] = rich_formatter
|
||||
else:
|
||||
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.
|
||||
max_depth (int, optional): Maximum depth of nested data structures, or None for no maximum. 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,
|
||||
max_depth: 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.max_depth = max_depth
|
||||
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,
|
||||
max_depth=self.max_depth,
|
||||
expand_all=self.expand_all,
|
||||
)
|
||||
pretty_text = Text.from_ansi(
|
||||
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,
|
||||
max_depth=self.max_depth,
|
||||
expand_all=self.expand_all,
|
||||
)
|
||||
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}, [", "])", f"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 (
|
||||
_safe_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
|
||||
is_namedtuple: bool = False
|
||||
children: Optional[List["Node"]] = None
|
||||
key_separator: str = ": "
|
||||
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 not self.is_namedtuple 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 _is_namedtuple(obj: Any) -> bool:
|
||||
"""Checks if an object is most likely a namedtuple. It is possible
|
||||
to craft an object that passes this check and isn't a namedtuple, but
|
||||
there is only a minuscule chance of this happening unintentionally.
|
||||
|
||||
Args:
|
||||
obj (Any): The object to test
|
||||
|
||||
Returns:
|
||||
bool: True if the object is a namedtuple. False otherwise.
|
||||
"""
|
||||
try:
|
||||
fields = getattr(obj, "_fields", None)
|
||||
except Exception:
|
||||
# Being very defensive - if we cannot get the attr then its not a namedtuple
|
||||
return False
|
||||
return isinstance(obj, tuple) and isinstance(fields, tuple)
|
||||
|
||||
|
||||
def traverse(
|
||||
_object: Any,
|
||||
max_length: Optional[int] = None,
|
||||
max_string: Optional[int] = None,
|
||||
max_depth: 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.
|
||||
max_depth (int, optional): Maximum depth of data structures, or None for no maximum.
|
||||
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 _safe_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, depth: int = 0) -> Node:
|
||||
"""Walk the object depth first."""
|
||||
|
||||
obj_id = id(obj)
|
||||
if obj_id in visited_ids:
|
||||
# Recursion detected
|
||||
return Node(value_repr="...")
|
||||
|
||||
obj_type = type(obj)
|
||||
children: List[Node]
|
||||
reached_max_depth = max_depth is not None and depth >= max_depth
|
||||
|
||||
def iter_rich_args(rich_args: Any) -> Iterable[Union[Any, Tuple[str, Any]]]:
|
||||
for arg in rich_args:
|
||||
if _safe_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:
|
||||
push_visited(obj_id)
|
||||
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 reached_max_depth:
|
||||
if angular:
|
||||
node = Node(value_repr=f"<{class_name}...>")
|
||||
else:
|
||||
node = Node(value_repr=f"{class_name}(...)")
|
||||
else:
|
||||
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 _safe_isinstance(arg, tuple):
|
||||
key, child = arg
|
||||
child_node = _traverse(child, depth=depth + 1)
|
||||
child_node.last = last
|
||||
child_node.key_repr = key
|
||||
child_node.key_separator = "="
|
||||
append(child_node)
|
||||
else:
|
||||
child_node = _traverse(arg, depth=depth + 1)
|
||||
child_node.last = last
|
||||
append(child_node)
|
||||
else:
|
||||
node = Node(
|
||||
value_repr=f"<{class_name}>" if angular else f"{class_name}()",
|
||||
children=[],
|
||||
last=root,
|
||||
)
|
||||
pop_visited(obj_id)
|
||||
elif _is_attr_object(obj) and not fake_attributes:
|
||||
push_visited(obj_id)
|
||||
children = []
|
||||
append = children.append
|
||||
|
||||
attr_fields = _get_attr_fields(obj)
|
||||
if attr_fields:
|
||||
if reached_max_depth:
|
||||
node = Node(value_repr=f"{obj.__class__.__name__}(...)")
|
||||
else:
|
||||
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, depth=depth + 1)
|
||||
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
|
||||
)
|
||||
pop_visited(obj_id)
|
||||
elif (
|
||||
is_dataclass(obj)
|
||||
and not _safe_isinstance(obj, type)
|
||||
and not fake_attributes
|
||||
and _is_dataclass_repr(obj)
|
||||
):
|
||||
push_visited(obj_id)
|
||||
children = []
|
||||
append = children.append
|
||||
if reached_max_depth:
|
||||
node = Node(value_repr=f"{obj.__class__.__name__}(...)")
|
||||
else:
|
||||
node = Node(
|
||||
open_brace=f"{obj.__class__.__name__}(",
|
||||
close_brace=")",
|
||||
children=children,
|
||||
last=root,
|
||||
empty=f"{obj.__class__.__name__}()",
|
||||
)
|
||||
|
||||
for last, field in loop_last(
|
||||
field for field in fields(obj) if field.repr
|
||||
):
|
||||
child_node = _traverse(getattr(obj, field.name), depth=depth + 1)
|
||||
child_node.key_repr = field.name
|
||||
child_node.last = last
|
||||
child_node.key_separator = "="
|
||||
append(child_node)
|
||||
|
||||
pop_visited(obj_id)
|
||||
elif _is_namedtuple(obj) and _has_default_namedtuple_repr(obj):
|
||||
push_visited(obj_id)
|
||||
class_name = obj.__class__.__name__
|
||||
if reached_max_depth:
|
||||
# If we've reached the max depth, we still show the class name, but not its contents
|
||||
node = Node(
|
||||
value_repr=f"{class_name}(...)",
|
||||
)
|
||||
else:
|
||||
children = []
|
||||
append = children.append
|
||||
node = Node(
|
||||
open_brace=f"{class_name}(",
|
||||
close_brace=")",
|
||||
children=children,
|
||||
empty=f"{class_name}()",
|
||||
)
|
||||
for last, (key, value) in loop_last(obj._asdict().items()):
|
||||
child_node = _traverse(value, depth=depth + 1)
|
||||
child_node.key_repr = key
|
||||
child_node.last = last
|
||||
child_node.key_separator = "="
|
||||
append(child_node)
|
||||
pop_visited(obj_id)
|
||||
elif _safe_isinstance(obj, _CONTAINERS):
|
||||
for container_type in _CONTAINERS:
|
||||
if _safe_isinstance(obj, container_type):
|
||||
obj_type = container_type
|
||||
break
|
||||
|
||||
push_visited(obj_id)
|
||||
|
||||
open_brace, close_brace, empty = _BRACES[obj_type](obj)
|
||||
|
||||
if reached_max_depth:
|
||||
node = Node(value_repr=f"{open_brace}...{close_brace}")
|
||||
elif 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 _safe_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, depth=depth + 1)
|
||||
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, depth=depth + 1)
|
||||
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 = _safe_isinstance(obj, tuple)
|
||||
node.is_namedtuple = _is_namedtuple(obj)
|
||||
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,
|
||||
max_depth: 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.
|
||||
max_depth (int, optional): Maximum depth of nested data structure, or None for no depth.
|
||||
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 _safe_isinstance(_object, Node):
|
||||
node = _object
|
||||
else:
|
||||
node = traverse(
|
||||
_object, max_length=max_length, max_string=max_string, max_depth=max_depth
|
||||
)
|
||||
repr_str: 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,
|
||||
max_depth: 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.
|
||||
max_depth (int, optional): Maximum depth for nested data structures, or None for unlimited depth. 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,
|
||||
max_depth=max_depth,
|
||||
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"
|
||||
|
||||
from typing import NamedTuple
|
||||
|
||||
class StockKeepingUnit(NamedTuple):
|
||||
name: str
|
||||
description: str
|
||||
price: float
|
||||
category: str
|
||||
reviews: List[str]
|
||||
|
||||
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),
|
||||
"namedtuple": StockKeepingUnit(
|
||||
"Sparkling British Spring Water",
|
||||
"Carbonated spring water",
|
||||
0.9,
|
||||
"water",
|
||||
["its amazing!", "its terrible!"],
|
||||
),
|
||||
"Broken": BrokenRepr(),
|
||||
}
|
||||
data["foo"].append(data) # type: ignore[attr-defined]
|
||||
|
||||
from pip._vendor.rich import print
|
||||
|
||||
# print(Pretty(data, indent_guides=True, max_string=20))
|
||||
|
||||
class Thing:
|
||||
def __repr__(self) -> str:
|
||||
return "Hello\x1b[38;5;239m World!"
|
||||
|
||||
print(Pretty(Thing()))
|
1702
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/progress.py
Normal file
1702
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/progress.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,224 @@
|
|||
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. Set to None to render a pulsing animation.
|
||||
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. Will pulse if a None total was passed.
|
||||
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.finished".
|
||||
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: Optional[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) -> Optional[float]:
|
||||
"""Calculate percentage complete."""
|
||||
if self.total is None:
|
||||
return None
|
||||
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
|
||||
should_pulse = self.pulse or self.total is None
|
||||
if should_pulse:
|
||||
yield from self._render_pulse(console, width, ascii=ascii)
|
||||
return
|
||||
|
||||
completed: Optional[float] = (
|
||||
min(self.total, max(0, self.completed)) if self.total is not None else None
|
||||
)
|
||||
|
||||
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 and completed is not None
|
||||
else width * 2
|
||||
)
|
||||
bar_count = complete_halves // 2
|
||||
half_bar_count = complete_halves % 2
|
||||
style = console.get_style(self.style)
|
||||
is_finished = self.total is None or self.completed >= self.total
|
||||
complete_style = console.get_style(
|
||||
self.finished_style if is_finished else self.complete_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()
|
376
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/prompt.py
Normal file
376
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/prompt.py
Normal file
|
@ -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: PromptType = 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
|
||||
|
||||
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:")
|
|
@ -0,0 +1,42 @@
|
|||
from typing import Any, cast, Set, TYPE_CHECKING
|
||||
from inspect import isclass
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pip._vendor.rich.console import RenderableType
|
||||
|
||||
_GIBBERISH = """aihwerij235234ljsdnp34ksodfipwoe234234jlskjdf"""
|
||||
|
||||
|
||||
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__")
|
||||
)
|
||||
|
||||
|
||||
def rich_cast(renderable: object) -> "RenderableType":
|
||||
"""Cast an object to a renderable by calling __rich__ if present.
|
||||
|
||||
Args:
|
||||
renderable (object): A potentially renderable object
|
||||
|
||||
Returns:
|
||||
object: The result of recursively calling __rich__.
|
||||
"""
|
||||
from pip._vendor.rich.console import RenderableType
|
||||
|
||||
rich_visited_set: Set[type] = set() # Prevent potential infinite loop
|
||||
while hasattr(renderable, "__rich__") and not isclass(renderable):
|
||||
# Detect object which claim to have all the attributes
|
||||
if hasattr(renderable, _GIBBERISH):
|
||||
return repr(renderable)
|
||||
cast_method = getattr(renderable, "__rich__")
|
||||
renderable = cast_method()
|
||||
renderable_type = type(renderable)
|
||||
if renderable_type in rich_visited_set:
|
||||
break
|
||||
rich_visited_set.add(renderable_type)
|
||||
|
||||
return cast(RenderableType, renderable)
|
|
@ -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
|
149
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/repr.py
Normal file
149
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/repr.py
Normal file
|
@ -0,0 +1,149 @@
|
|||
import inspect
|
||||
from functools import partial
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Iterable,
|
||||
List,
|
||||
Optional,
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
overload,
|
||||
)
|
||||
|
||||
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[Type[T]]) -> Type[T]:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def auto(*, angular: bool = False) -> Callable[[Type[T]], Type[T]]:
|
||||
...
|
||||
|
||||
|
||||
def auto(
|
||||
cls: Optional[Type[T]] = None, *, angular: Optional[bool] = None
|
||||
) -> Union[Type[T], Callable[[Type[T]], Type[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: T) -> str:
|
||||
"""Create repr string from __rich_repr__"""
|
||||
repr_str: List[str] = []
|
||||
append = repr_str.append
|
||||
|
||||
angular: bool = getattr(self.__rich_repr__, "angular", False) # type: ignore[attr-defined]
|
||||
for arg in self.__rich_repr__(): # type: ignore[attr-defined]
|
||||
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 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__)
|
||||
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[attr-defined]
|
||||
|
||||
auto_repr.__doc__ = "Return repr(self)"
|
||||
cls.__repr__ = auto_repr # type: ignore[assignment]
|
||||
if angular is not None:
|
||||
cls.__rich_repr__.angular = angular # type: ignore[attr-defined]
|
||||
return cls
|
||||
|
||||
if cls is None:
|
||||
return partial(do_replace, angular=angular)
|
||||
else:
|
||||
return do_replace(cls, angular=angular)
|
||||
|
||||
|
||||
@overload
|
||||
def rich_repr(cls: Optional[Type[T]]) -> Type[T]:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def rich_repr(*, angular: bool = False) -> Callable[[Type[T]], Type[T]]:
|
||||
...
|
||||
|
||||
|
||||
def rich_repr(
|
||||
cls: Optional[Type[T]] = None, *, angular: bool = False
|
||||
) -> Union[Type[T], Callable[[Type[T]], Type[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[attr-defined]
|
||||
|
||||
console.print(foo)
|
||||
|
||||
console.print(foo, width=60)
|
||||
console.print(foo, width=30)
|
130
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/rule.py
Normal file
130
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/rule.py
Normal file
|
@ -0,0 +1,130 @@
|
|||
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 .measure import Measurement
|
||||
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
|
||||
|
||||
characters = (
|
||||
"-"
|
||||
if (options.ascii_only and not self.characters.isascii())
|
||||
else self.characters
|
||||
)
|
||||
|
||||
chars_len = cell_len(characters)
|
||||
if not self.title:
|
||||
yield self._rule_line(chars_len, width)
|
||||
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()
|
||||
|
||||
required_space = 4 if self.align == "center" else 2
|
||||
truncate_width = max(0, width - required_space)
|
||||
if not truncate_width:
|
||||
yield self._rule_line(chars_len, width)
|
||||
return
|
||||
|
||||
rule_text = Text(end=self.end)
|
||||
if self.align == "center":
|
||||
title_text.truncate(truncate_width, 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(truncate_width, 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(truncate_width, 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
|
||||
|
||||
def _rule_line(self, chars_len: int, width: int) -> Text:
|
||||
rule_text = Text(self.characters * ((width // chars_len) + 1), self.style)
|
||||
rule_text.truncate(width)
|
||||
rule_text.plain = set_cell_size(rule_text.plain, width)
|
||||
return rule_text
|
||||
|
||||
def __rich_measure__(
|
||||
self, console: Console, options: ConsoleOptions
|
||||
) -> Measurement:
|
||||
return Measurement(1, 1)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
import sys
|
||||
|
||||
from pip._vendor.rich.console import Console
|
||||
|
||||
try:
|
||||
text = sys.argv[1]
|
||||
except IndexError:
|
||||
text = "Hello, World"
|
||||
console = Console()
|
||||
console.print(Rule(title=text))
|
||||
|
||||
console = Console()
|
||||
console.print(Rule("foo"), width=4)
|
|
@ -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 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.
|
||||
|
||||
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()
|
|
@ -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
|
|
@ -0,0 +1,739 @@
|
|||
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,
|
||||
Type,
|
||||
Union,
|
||||
)
|
||||
|
||||
from .cells import (
|
||||
_is_single_cell_widths,
|
||||
cached_cell_len,
|
||||
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
|
||||
SET_WINDOW_TITLE = 16
|
||||
|
||||
|
||||
ControlCode = Union[
|
||||
Tuple[ControlType],
|
||||
Tuple[ControlType, Union[int, str]],
|
||||
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.
|
||||
|
||||
Attributes:
|
||||
cell_length (int): The cell length of this Segment.
|
||||
"""
|
||||
|
||||
text: str
|
||||
style: Optional[Style] = None
|
||||
control: Optional[Sequence[ControlCode]] = None
|
||||
|
||||
@property
|
||||
def cell_length(self) -> int:
|
||||
"""The number of terminal cells required to display self.text.
|
||||
|
||||
Returns:
|
||||
int: A number of cells.
|
||||
"""
|
||||
text, _style, control = self
|
||||
return 0 if control else cell_len(text)
|
||||
|
||||
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 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"]:
|
||||
|
||||
text, style, control = segment
|
||||
_Segment = Segment
|
||||
|
||||
cell_length = segment.cell_length
|
||||
if cut >= cell_length:
|
||||
return segment, _Segment("", style, control)
|
||||
|
||||
cell_size = get_character_cell_size
|
||||
|
||||
pos = int((cut / cell_length) * (len(text) - 1))
|
||||
|
||||
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),
|
||||
)
|
||||
|
||||
raise AssertionError("Will never reach here")
|
||||
|
||||
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.
|
||||
"""
|
||||
text, style, control = self
|
||||
|
||||
if _is_single_cell_widths(text):
|
||||
# Fast path with all 1 cell characters
|
||||
if cut >= len(text):
|
||||
return self, Segment("", style, control)
|
||||
return (
|
||||
Segment(text[:cut], style, control),
|
||||
Segment(text[cut:], style, control),
|
||||
)
|
||||
|
||||
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, segment_style, _ = segment
|
||||
while text:
|
||||
_text, new_line, text = text.partition("\n")
|
||||
if _text:
|
||||
append(cls(_text, segment_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
|
||||
line.clear()
|
||||
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.
|
||||
"""
|
||||
_cell_len = cell_len
|
||||
return sum(_cell_len(text) for text, style, control in line if not control)
|
||||
|
||||
@classmethod
|
||||
def get_shape(cls, lines: List[List["Segment"]]) -> Tuple[int, int]:
|
||||
"""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.
|
||||
new_lines (bool, optional): Padded lines should include "\n". Defaults to False.
|
||||
|
||||
Returns:
|
||||
List[List[Segment]]: New list of lines.
|
||||
"""
|
||||
_height = height or len(lines)
|
||||
|
||||
blank = (
|
||||
[cls(" " * width + "\n", style)] if new_lines else [cls(" " * width, style)]
|
||||
)
|
||||
|
||||
adjust_line_length = cls.adjust_line_length
|
||||
shaped_lines = lines[:_height]
|
||||
shaped_lines[:] = [
|
||||
adjust_line_length(line, width, style=style) for line in lines
|
||||
]
|
||||
if len(shaped_lines) < _height:
|
||||
shaped_lines.extend([blank] * (_height - len(shaped_lines)))
|
||||
return shaped_lines
|
||||
|
||||
@classmethod
|
||||
def align_top(
|
||||
cls: Type["Segment"],
|
||||
lines: List[List["Segment"]],
|
||||
width: int,
|
||||
height: int,
|
||||
style: Style,
|
||||
new_lines: bool = False,
|
||||
) -> List[List["Segment"]]:
|
||||
"""Aligns lines to top (adds extra lines to bottom as required).
|
||||
|
||||
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): Style of any padding added.
|
||||
new_lines (bool, optional): Padded lines should include "\n". Defaults to False.
|
||||
|
||||
Returns:
|
||||
List[List[Segment]]: New list of lines.
|
||||
"""
|
||||
extra_lines = height - len(lines)
|
||||
if not extra_lines:
|
||||
return lines[:]
|
||||
lines = lines[:height]
|
||||
blank = cls(" " * width + "\n", style) if new_lines else cls(" " * width, style)
|
||||
lines = lines + [[blank]] * extra_lines
|
||||
return lines
|
||||
|
||||
@classmethod
|
||||
def align_bottom(
|
||||
cls: Type["Segment"],
|
||||
lines: List[List["Segment"]],
|
||||
width: int,
|
||||
height: int,
|
||||
style: Style,
|
||||
new_lines: bool = False,
|
||||
) -> List[List["Segment"]]:
|
||||
"""Aligns render to bottom (adds extra lines above as required).
|
||||
|
||||
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): 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.
|
||||
"""
|
||||
extra_lines = height - len(lines)
|
||||
if not extra_lines:
|
||||
return lines[:]
|
||||
lines = lines[:height]
|
||||
blank = cls(" " * width + "\n", style) if new_lines else cls(" " * width, style)
|
||||
lines = [[blank]] * extra_lines + lines
|
||||
return lines
|
||||
|
||||
@classmethod
|
||||
def align_middle(
|
||||
cls: Type["Segment"],
|
||||
lines: List[List["Segment"]],
|
||||
width: int,
|
||||
height: int,
|
||||
style: Style,
|
||||
new_lines: bool = False,
|
||||
) -> List[List["Segment"]]:
|
||||
"""Aligns lines to middle (adds extra lines to above and below as required).
|
||||
|
||||
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): Style of any padding added.
|
||||
new_lines (bool, optional): Padded lines should include "\n". Defaults to False.
|
||||
|
||||
Returns:
|
||||
List[List[Segment]]: New list of lines.
|
||||
"""
|
||||
extra_lines = height - len(lines)
|
||||
if not extra_lines:
|
||||
return lines[:]
|
||||
lines = lines[:height]
|
||||
blank = cls(" " * width + "\n", style) if new_lines else cls(" " * width, style)
|
||||
top_lines = extra_lines // 2
|
||||
bottom_lines = extra_lines - top_lines
|
||||
lines = [[blank]] * top_lines + lines + [[blank]] * bottom_lines
|
||||
return 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:
|
||||
cut = next(iter_cuts, -1)
|
||||
if cut == -1:
|
||||
return []
|
||||
if cut != 0:
|
||||
break
|
||||
yield []
|
||||
pos = 0
|
||||
|
||||
segments_clear = split_segments.clear
|
||||
segments_copy = split_segments.copy
|
||||
|
||||
_cell_len = cached_cell_len
|
||||
for segment in segments:
|
||||
text, _style, control = segment
|
||||
while text:
|
||||
end_pos = pos if control else pos + _cell_len(text)
|
||||
if end_pos < cut:
|
||||
add_segment(segment)
|
||||
pos = end_pos
|
||||
break
|
||||
|
||||
if end_pos == cut:
|
||||
add_segment(segment)
|
||||
yield segments_copy()
|
||||
segments_clear()
|
||||
pos = end_pos
|
||||
|
||||
cut = next(iter_cuts, -1)
|
||||
if cut == -1:
|
||||
if split_segments:
|
||||
yield segments_copy()
|
||||
return
|
||||
|
||||
break
|
||||
|
||||
else:
|
||||
before, segment = segment.split_cells(cut - pos)
|
||||
text, _style, control = segment
|
||||
add_segment(before)
|
||||
yield segments_copy()
|
||||
segments_clear()
|
||||
pos = cut
|
||||
|
||||
cut = next(iter_cuts, -1)
|
||||
if cut == -1:
|
||||
if split_segments:
|
||||
yield segments_copy()
|
||||
return
|
||||
|
||||
yield segments_copy()
|
||||
|
||||
|
||||
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__": # 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 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."
|
||||
)
|
|
@ -0,0 +1,137 @@
|
|||
from typing import cast, List, Optional, TYPE_CHECKING, Union
|
||||
|
||||
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:
|
||||
"""A spinner animation.
|
||||
|
||||
Args:
|
||||
name (str): Name of spinner (run python -m rich.spinner).
|
||||
text (RenderableType, optional): A renderable to display at the right of the spinner (str or Text typically). Defaults to "".
|
||||
style (StyleType, optional): Style for spinner animation. Defaults to None.
|
||||
speed (float, optional): Speed factor for animation. Defaults to 1.0.
|
||||
|
||||
Raises:
|
||||
KeyError: If name isn't one of the supported spinner animations.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
text: "RenderableType" = "",
|
||||
*,
|
||||
style: Optional["StyleType"] = None,
|
||||
speed: float = 1.0,
|
||||
) -> None:
|
||||
try:
|
||||
spinner = SPINNERS[name]
|
||||
except KeyError:
|
||||
raise KeyError(f"no spinner called {name!r}")
|
||||
self.text: "Union[RenderableType, 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)
|
132
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/status.py
Normal file
132
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/status.py
Normal file
|
@ -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")
|
796
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/style.py
Normal file
796
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/style.py
Normal file
|
@ -0,0 +1,796 @@
|
|||
import sys
|
||||
from functools import lru_cache
|
||||
from marshal import dumps, loads
|
||||
from random import randint
|
||||
from typing import Any, Dict, Iterable, List, Optional, Type, Union, cast
|
||||
|
||||
from . import errors
|
||||
from .color import Color, ColorParseError, ColorSystem, blend_rgb
|
||||
from .repr import Result, rich_repr
|
||||
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: Optional[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._meta = None if meta is None else dumps(meta)
|
||||
self._link_id = (
|
||||
f"{randint(0, 999999)}{hash(self._meta)}" if (link or meta) else ""
|
||||
)
|
||||
self._hash: Optional[int] = None
|
||||
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._null = not (color or bgcolor)
|
||||
style._hash = None
|
||||
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._meta = dumps(meta)
|
||||
style._link_id = f"{randint(0, 999999)}{hash(style._meta)}"
|
||||
style._hash = None
|
||||
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 (Optional[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.__hash__() == other.__hash__()
|
||||
|
||||
def __ne__(self, other: Any) -> bool:
|
||||
if not isinstance(other, Style):
|
||||
return NotImplemented
|
||||
return self.__hash__() != other.__hash__()
|
||||
|
||||
def __hash__(self) -> int:
|
||||
if self._hash is not None:
|
||||
return self._hash
|
||||
self._hash = hash(
|
||||
(
|
||||
self._color,
|
||||
self._bgcolor,
|
||||
self._attributes,
|
||||
self._set_attributes,
|
||||
self._link,
|
||||
self._meta,
|
||||
)
|
||||
)
|
||||
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._null = False
|
||||
style._meta = None
|
||||
style._hash = 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
|
||||
|
||||
@lru_cache(maxsize=128)
|
||||
def clear_meta_and_links(self) -> "Style":
|
||||
"""Get a copy of this style with link and meta information removed.
|
||||
|
||||
Returns:
|
||||
Style: New style object.
|
||||
"""
|
||||
if self._null:
|
||||
return NULL_STYLE
|
||||
style: Style = self.__new__(Style)
|
||||
style._ansi = self._ansi
|
||||
style._style_definition = self._style_definition
|
||||
style._color = self._color
|
||||
style._bgcolor = self._bgcolor
|
||||
style._attributes = self._attributes
|
||||
style._set_attributes = self._set_attributes
|
||||
style._link = None
|
||||
style._link_id = ""
|
||||
style._hash = self._hash
|
||||
style._null = False
|
||||
style._meta = None
|
||||
return style
|
||||
|
||||
def update_link(self, link: Optional[str] = None) -> "Style":
|
||||
"""Get a copy with a different value for link.
|
||||
|
||||
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 = None
|
||||
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._ansi or 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")
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def _add(self, style: Optional["Style"]) -> "Style":
|
||||
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._null = 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
|
||||
new_style._hash = None
|
||||
return new_style
|
||||
|
||||
def __add__(self, style: Optional["Style"]) -> "Style":
|
||||
combined_style = self._add(style)
|
||||
return combined_style.copy() if combined_style.link else combined_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]
|
|
@ -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)
|
948
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/syntax.py
Normal file
948
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/syntax.py
Normal file
|
@ -0,0 +1,948 @@
|
|||
import os.path
|
||||
import platform
|
||||
import re
|
||||
import sys
|
||||
import textwrap
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
Iterable,
|
||||
List,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Sequence,
|
||||
Set,
|
||||
Tuple,
|
||||
Type,
|
||||
Union,
|
||||
)
|
||||
|
||||
from pip._vendor.pygments.lexer import Lexer
|
||||
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 pip._vendor.rich.containers import Lines
|
||||
from pip._vendor.rich.padding import Padding, PaddingDimensions
|
||||
|
||||
from ._loop import loop_first
|
||||
from .cells import cell_len
|
||||
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, Segments
|
||||
from .style import Style, StyleType
|
||||
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}
|
||||
NUMBERS_COLUMN_DEFAULT_PADDING = 2
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
SyntaxPosition = Tuple[int, int]
|
||||
|
||||
|
||||
class _SyntaxHighlightRange(NamedTuple):
|
||||
"""
|
||||
A range to highlight in a Syntax object.
|
||||
`start` and `end` are 2-integers tuples, where the first integer is the line number
|
||||
(starting from 1) and the second integer is the column index (starting from 0).
|
||||
"""
|
||||
|
||||
style: StyleType
|
||||
start: SyntaxPosition
|
||||
end: SyntaxPosition
|
||||
|
||||
|
||||
class Syntax(JupyterMixin):
|
||||
"""Construct a Syntax object to render syntax highlighted code.
|
||||
|
||||
Args:
|
||||
code (str): Code to highlight.
|
||||
lexer (Lexer | 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 | None, int | None], optional): If given should be a tuple of the start and end line to render.
|
||||
A value of None in the tuple indicates the range is open in that direction.
|
||||
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.
|
||||
padding (PaddingDimensions): Padding to apply around the syntax. Defaults to 0 (no padding).
|
||||
"""
|
||||
|
||||
_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: Union[Lexer, str],
|
||||
*,
|
||||
theme: Union[str, SyntaxTheme] = DEFAULT_THEME,
|
||||
dedent: bool = False,
|
||||
line_numbers: bool = False,
|
||||
start_line: int = 1,
|
||||
line_range: Optional[Tuple[Optional[int], Optional[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,
|
||||
padding: PaddingDimensions = 0,
|
||||
) -> None:
|
||||
self.code = code
|
||||
self._lexer = lexer
|
||||
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.padding = padding
|
||||
|
||||
self._theme = self.get_theme(theme)
|
||||
self._stylized_ranges: List[_SyntaxHighlightRange] = []
|
||||
|
||||
@classmethod
|
||||
def from_path(
|
||||
cls,
|
||||
path: str,
|
||||
encoding: str = "utf-8",
|
||||
lexer: Optional[Union[Lexer, str]] = None,
|
||||
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,
|
||||
padding: PaddingDimensions = 0,
|
||||
) -> "Syntax":
|
||||
"""Construct a Syntax object from a file.
|
||||
|
||||
Args:
|
||||
path (str): Path to file to highlight.
|
||||
encoding (str): Encoding of file.
|
||||
lexer (str | Lexer, optional): Lexer to use. If None, lexer will be auto-detected from path/file content.
|
||||
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.
|
||||
padding (PaddingDimensions): Padding to apply around the syntax. Defaults to 0 (no padding).
|
||||
|
||||
Returns:
|
||||
[Syntax]: A Syntax object that may be printed to the console
|
||||
"""
|
||||
code = Path(path).read_text(encoding=encoding)
|
||||
|
||||
if not lexer:
|
||||
lexer = cls.guess_lexer(path, code=code)
|
||||
|
||||
return cls(
|
||||
code,
|
||||
lexer,
|
||||
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,
|
||||
padding=padding,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def guess_lexer(cls, path: str, code: Optional[str] = None) -> str:
|
||||
"""Guess the alias of the Pygments lexer to use based on a path and an optional string of code.
|
||||
If code is supplied, it will use a combination of the code and the filename to determine the
|
||||
best lexer to use. For example, if the file is ``index.html`` and the file contains Django
|
||||
templating syntax, then "html+django" will be returned. If the file is ``index.html``, and no
|
||||
templating language is used, the "html" lexer will be used. If no string of code
|
||||
is supplied, the lexer will be chosen based on the file extension..
|
||||
|
||||
Args:
|
||||
path (AnyStr): The path to the file containing the code you wish to know the lexer for.
|
||||
code (str, optional): Optional string of code that will be used as a fallback if no lexer
|
||||
is found for the supplied path.
|
||||
|
||||
Returns:
|
||||
str: The name of the Pygments lexer that best matches the supplied path/code.
|
||||
"""
|
||||
lexer: Optional[Lexer] = None
|
||||
lexer_name = "default"
|
||||
if code:
|
||||
try:
|
||||
lexer = guess_lexer_for_filename(path, code)
|
||||
except ClassNotFound:
|
||||
pass
|
||||
|
||||
if not lexer:
|
||||
try:
|
||||
_, ext = os.path.splitext(path)
|
||||
if ext:
|
||||
extension = ext.lstrip(".").lower()
|
||||
lexer = get_lexer_by_name(extension)
|
||||
except ClassNotFound:
|
||||
pass
|
||||
|
||||
if lexer:
|
||||
if lexer.aliases:
|
||||
lexer_name = lexer.aliases[0]
|
||||
else:
|
||||
lexer_name = lexer.name
|
||||
|
||||
return lexer_name
|
||||
|
||||
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
|
||||
|
||||
@property
|
||||
def lexer(self) -> Optional[Lexer]:
|
||||
"""The lexer for this syntax, or None if no lexer was found.
|
||||
|
||||
Tries to find the lexer by name if a string was passed to the constructor.
|
||||
"""
|
||||
|
||||
if isinstance(self._lexer, Lexer):
|
||||
return self._lexer
|
||||
try:
|
||||
return get_lexer_by_name(
|
||||
self._lexer,
|
||||
stripnl=False,
|
||||
ensurenl=True,
|
||||
tabsize=self.tab_size,
|
||||
)
|
||||
except ClassNotFound:
|
||||
return None
|
||||
|
||||
def highlight(
|
||||
self,
|
||||
code: str,
|
||||
line_range: Optional[Tuple[Optional[int], Optional[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
|
||||
|
||||
lexer = self.lexer
|
||||
|
||||
if lexer is None:
|
||||
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."""
|
||||
assert lexer # required to make MyPy happy - we know lexer is not None at this point
|
||||
|
||||
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 if line_start else 0
|
||||
|
||||
# Skip over tokens until line start
|
||||
while line_no < _line_start:
|
||||
try:
|
||||
_token_type, token = next(tokens)
|
||||
except StopIteration:
|
||||
break
|
||||
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_end and 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}")
|
||||
|
||||
if self._stylized_ranges:
|
||||
self._apply_stylized_ranges(text)
|
||||
|
||||
return text
|
||||
|
||||
def stylize_range(
|
||||
self, style: StyleType, start: SyntaxPosition, end: SyntaxPosition
|
||||
) -> None:
|
||||
"""
|
||||
Adds a custom style on a part of the code, that will be applied to the syntax display when it's rendered.
|
||||
Line numbers are 1-based, while column indexes are 0-based.
|
||||
|
||||
Args:
|
||||
style (StyleType): The style to apply.
|
||||
start (Tuple[int, int]): The start of the range, in the form `[line number, column index]`.
|
||||
end (Tuple[int, int]): The end of the range, in the form `[line number, column index]`.
|
||||
"""
|
||||
self._stylized_ranges.append(_SyntaxHighlightRange(style, start, end))
|
||||
|
||||
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")))
|
||||
+ NUMBERS_COLUMN_DEFAULT_PADDING
|
||||
)
|
||||
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":
|
||||
_, right, _, left = Padding.unpack(self.padding)
|
||||
padding = left + right
|
||||
if self.code_width is not None:
|
||||
width = self.code_width + self._numbers_column_width + padding + 1
|
||||
return Measurement(self._numbers_column_width, width)
|
||||
lines = self.code.splitlines()
|
||||
width = (
|
||||
self._numbers_column_width
|
||||
+ padding
|
||||
+ (max(cell_len(line) for line in lines) if lines else 0)
|
||||
)
|
||||
if self.line_numbers:
|
||||
width += 1
|
||||
return Measurement(self._numbers_column_width, width)
|
||||
|
||||
def __rich_console__(
|
||||
self, console: Console, options: ConsoleOptions
|
||||
) -> RenderResult:
|
||||
segments = Segments(self._get_syntax(console, options))
|
||||
if self.padding:
|
||||
yield Padding(
|
||||
segments, style=self._theme.get_background_style(), pad=self.padding
|
||||
)
|
||||
else:
|
||||
yield segments
|
||||
|
||||
def _get_syntax(
|
||||
self,
|
||||
console: Console,
|
||||
options: ConsoleOptions,
|
||||
) -> Iterable[Segment]:
|
||||
"""
|
||||
Get the Segments for the Syntax object, excluding any vertical/horizontal padding
|
||||
"""
|
||||
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
|
||||
)
|
||||
|
||||
ends_on_nl, processed_code = self._process_code(self.code)
|
||||
text = self.highlight(processed_code, self.line_range)
|
||||
|
||||
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, justify="left"),
|
||||
style=self.background_style,
|
||||
pad=True,
|
||||
new_lines=True,
|
||||
)
|
||||
for syntax_line in syntax_lines:
|
||||
yield from syntax_line
|
||||
return
|
||||
|
||||
start_line, end_line = self.line_range or (None, None)
|
||||
line_offset = 0
|
||||
if start_line:
|
||||
line_offset = max(0, start_line - 1)
|
||||
lines: Union[List[Text], Lines] = text.split("\n", allow_blank=ends_on_nl)
|
||||
if self.line_range:
|
||||
if line_offset > len(lines):
|
||||
return
|
||||
lines = lines[line_offset:end_line]
|
||||
|
||||
if self.indent_guides and not options.ascii_only:
|
||||
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 + Style(italic=False))
|
||||
.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
|
||||
new_line = _Segment("\n")
|
||||
|
||||
line_pointer = "> " if options.legacy_windows else "❱ "
|
||||
|
||||
(
|
||||
background_style,
|
||||
number_style,
|
||||
highlight_number_style,
|
||||
) = self._get_number_styles(console)
|
||||
|
||||
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, justify="left"),
|
||||
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:
|
||||
wrapped_line_left_pad = _Segment(
|
||||
" " * numbers_column_width + " ", background_style
|
||||
)
|
||||
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 wrapped_line_left_pad
|
||||
yield from wrapped_line
|
||||
yield new_line
|
||||
else:
|
||||
for wrapped_line in wrapped_lines:
|
||||
yield from wrapped_line
|
||||
yield new_line
|
||||
|
||||
def _apply_stylized_ranges(self, text: Text) -> None:
|
||||
"""
|
||||
Apply stylized ranges to a text instance,
|
||||
using the given code to determine the right portion to apply the style to.
|
||||
|
||||
Args:
|
||||
text (Text): Text instance to apply the style to.
|
||||
"""
|
||||
code = text.plain
|
||||
newlines_offsets = [
|
||||
# Let's add outer boundaries at each side of the list:
|
||||
0,
|
||||
# N.B. using "\n" here is much faster than using metacharacters such as "^" or "\Z":
|
||||
*[
|
||||
match.start() + 1
|
||||
for match in re.finditer("\n", code, flags=re.MULTILINE)
|
||||
],
|
||||
len(code) + 1,
|
||||
]
|
||||
|
||||
for stylized_range in self._stylized_ranges:
|
||||
start = _get_code_index_for_syntax_position(
|
||||
newlines_offsets, stylized_range.start
|
||||
)
|
||||
end = _get_code_index_for_syntax_position(
|
||||
newlines_offsets, stylized_range.end
|
||||
)
|
||||
if start is not None and end is not None:
|
||||
text.stylize(stylized_range.style, start, end)
|
||||
|
||||
def _process_code(self, code: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Applies various processing to a raw code string
|
||||
(normalises it so it always ends with a line return, dedents it if necessary, etc.)
|
||||
|
||||
Args:
|
||||
code (str): The raw code string to process
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: the boolean indicates whether the raw code ends with a line return,
|
||||
while the string is the processed code.
|
||||
"""
|
||||
ends_on_nl = code.endswith("\n")
|
||||
processed_code = code if ends_on_nl else code + "\n"
|
||||
processed_code = (
|
||||
textwrap.dedent(processed_code) if self.dedent else processed_code
|
||||
)
|
||||
processed_code = processed_code.expandtabs(self.tab_size)
|
||||
return ends_on_nl, processed_code
|
||||
|
||||
|
||||
def _get_code_index_for_syntax_position(
|
||||
newlines_offsets: Sequence[int], position: SyntaxPosition
|
||||
) -> Optional[int]:
|
||||
"""
|
||||
Returns the index of the code string for the given positions.
|
||||
|
||||
Args:
|
||||
newlines_offsets (Sequence[int]): The offset of each newline character found in the code snippet.
|
||||
position (SyntaxPosition): The position to search for.
|
||||
|
||||
Returns:
|
||||
Optional[int]: The index of the code string for this position, or `None`
|
||||
if the given position's line number is out of range (if it's the column that is out of range
|
||||
we silently clamp its value so that it reaches the end of the line)
|
||||
"""
|
||||
lines_count = len(newlines_offsets)
|
||||
|
||||
line_number, column_index = position
|
||||
if line_number > lines_count or len(newlines_offsets) < (line_number + 1):
|
||||
return None # `line_number` is out of range
|
||||
line_index = line_number - 1
|
||||
line_length = newlines_offsets[line_index + 1] - newlines_offsets[line_index] - 1
|
||||
# If `column_index` is out of range: let's silently clamp it:
|
||||
column_index = min(line_length, column_index)
|
||||
return newlines_offsets[line_index] + column_index
|
||||
|
||||
|
||||
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=None,
|
||||
dest="lexer_name",
|
||||
help="Lexer name",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p", "--padding", type=int, default=0, dest="padding", help="Padding"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--highlight-line",
|
||||
type=int,
|
||||
default=None,
|
||||
dest="highlight_line",
|
||||
help="The line number (not index!) to highlight",
|
||||
)
|
||||
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=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,
|
||||
padding=args.padding,
|
||||
highlight_lines={args.highlight_line},
|
||||
)
|
||||
else:
|
||||
syntax = Syntax.from_path(
|
||||
args.path,
|
||||
lexer=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,
|
||||
padding=args.padding,
|
||||
highlight_lines={args.highlight_line},
|
||||
)
|
||||
console.print(syntax, soft_wrap=args.soft_wrap)
|
1002
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/table.py
Normal file
1002
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/table.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,153 @@
|
|||
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),
|
||||
],
|
||||
)
|
||||
|
||||
MONOKAI = TerminalTheme(
|
||||
(12, 12, 12),
|
||||
(217, 217, 217),
|
||||
[
|
||||
(26, 26, 26),
|
||||
(244, 0, 95),
|
||||
(152, 224, 36),
|
||||
(253, 151, 31),
|
||||
(157, 101, 255),
|
||||
(244, 0, 95),
|
||||
(88, 209, 235),
|
||||
(196, 197, 181),
|
||||
(98, 94, 76),
|
||||
],
|
||||
[
|
||||
(244, 0, 95),
|
||||
(152, 224, 36),
|
||||
(224, 213, 97),
|
||||
(157, 101, 255),
|
||||
(244, 0, 95),
|
||||
(88, 209, 235),
|
||||
(246, 246, 239),
|
||||
],
|
||||
)
|
||||
DIMMED_MONOKAI = TerminalTheme(
|
||||
(25, 25, 25),
|
||||
(185, 188, 186),
|
||||
[
|
||||
(58, 61, 67),
|
||||
(190, 63, 72),
|
||||
(135, 154, 59),
|
||||
(197, 166, 53),
|
||||
(79, 118, 161),
|
||||
(133, 92, 141),
|
||||
(87, 143, 164),
|
||||
(185, 188, 186),
|
||||
(136, 137, 135),
|
||||
],
|
||||
[
|
||||
(251, 0, 31),
|
||||
(15, 114, 47),
|
||||
(196, 112, 51),
|
||||
(24, 109, 227),
|
||||
(251, 0, 103),
|
||||
(46, 112, 109),
|
||||
(253, 255, 185),
|
||||
],
|
||||
)
|
||||
NIGHT_OWLISH = TerminalTheme(
|
||||
(255, 255, 255),
|
||||
(64, 63, 83),
|
||||
[
|
||||
(1, 22, 39),
|
||||
(211, 66, 62),
|
||||
(42, 162, 152),
|
||||
(218, 170, 1),
|
||||
(72, 118, 214),
|
||||
(64, 63, 83),
|
||||
(8, 145, 106),
|
||||
(122, 129, 129),
|
||||
(122, 129, 129),
|
||||
],
|
||||
[
|
||||
(247, 110, 110),
|
||||
(73, 208, 197),
|
||||
(218, 194, 107),
|
||||
(92, 167, 228),
|
||||
(105, 112, 152),
|
||||
(0, 201, 144),
|
||||
(152, 159, 177),
|
||||
],
|
||||
)
|
||||
|
||||
SVG_EXPORT_THEME = TerminalTheme(
|
||||
(41, 41, 41),
|
||||
(197, 200, 198),
|
||||
[
|
||||
(75, 78, 85),
|
||||
(204, 85, 90),
|
||||
(152, 168, 75),
|
||||
(208, 179, 68),
|
||||
(96, 138, 177),
|
||||
(152, 114, 159),
|
||||
(104, 160, 179),
|
||||
(197, 200, 198),
|
||||
(154, 155, 153),
|
||||
],
|
||||
[
|
||||
(255, 38, 39),
|
||||
(0, 130, 61),
|
||||
(208, 132, 66),
|
||||
(25, 132, 233),
|
||||
(255, 44, 122),
|
||||
(57, 130, 128),
|
||||
(253, 253, 197),
|
||||
],
|
||||
)
|
1307
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/text.py
Normal file
1307
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/text.py
Normal file
File diff suppressed because it is too large
Load diff
115
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/theme.py
Normal file
115
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/theme.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
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, encoding: Optional[str] = None
|
||||
) -> "Theme":
|
||||
"""Read a theme from a path.
|
||||
|
||||
Args:
|
||||
path (str): Path to a config file readable by Python configparser module.
|
||||
inherit (bool, optional): Inherit default styles. Defaults to True.
|
||||
encoding (str, optional): Encoding of the config file. Defaults to None.
|
||||
|
||||
Returns:
|
||||
Theme: A new theme instance.
|
||||
"""
|
||||
with open(path, "rt", encoding=encoding) 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)
|
|
@ -0,0 +1,5 @@
|
|||
from .default_styles import DEFAULT_STYLES
|
||||
from .theme import Theme
|
||||
|
||||
|
||||
DEFAULT = Theme(DEFAULT_STYLES)
|
|
@ -0,0 +1,756 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import linecache
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from traceback import walk_tb
|
||||
from types import ModuleType, TracebackType
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterable,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Type,
|
||||
Union,
|
||||
)
|
||||
|
||||
from pip._vendor.pygments.lexers import guess_lexer_for_filename
|
||||
from pip._vendor.pygments.token import Comment, Keyword, Name, Number, Operator, String
|
||||
from pip._vendor.pygments.token import Text as TextToken
|
||||
from pip._vendor.pygments.token import Token
|
||||
from pip._vendor.pygments.util import ClassNotFound
|
||||
|
||||
from . import pretty
|
||||
from ._loop import 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,
|
||||
locals_max_length: int = LOCALS_MAX_LENGTH,
|
||||
locals_max_string: int = LOCALS_MAX_STRING,
|
||||
locals_hide_dunder: bool = True,
|
||||
locals_hide_sunder: Optional[bool] = None,
|
||||
indent_guides: bool = True,
|
||||
suppress: Iterable[Union[str, ModuleType]] = (),
|
||||
max_frames: int = 100,
|
||||
) -> 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.
|
||||
locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
|
||||
Defaults to 10.
|
||||
locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
|
||||
locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
|
||||
locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
|
||||
indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
|
||||
suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
|
||||
|
||||
Returns:
|
||||
Callable: The previous exception handler that was replaced.
|
||||
|
||||
"""
|
||||
traceback_console = Console(stderr=True) if console is None else console
|
||||
|
||||
locals_hide_sunder = (
|
||||
True
|
||||
if (traceback_console.is_jupyter and locals_hide_sunder is None)
|
||||
else locals_hide_sunder
|
||||
)
|
||||
|
||||
def excepthook(
|
||||
type_: Type[BaseException],
|
||||
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,
|
||||
locals_max_length=locals_max_length,
|
||||
locals_max_string=locals_max_string,
|
||||
locals_hide_dunder=locals_hide_dunder,
|
||||
locals_hide_sunder=bool(locals_hide_sunder),
|
||||
indent_guides=indent_guides,
|
||||
suppress=suppress,
|
||||
max_frames=max_frames,
|
||||
)
|
||||
)
|
||||
|
||||
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 within ipython, use customized traceback
|
||||
ip = get_ipython() # type: ignore[name-defined]
|
||||
ipy_excepthook_closure(ip)
|
||||
return sys.excepthook
|
||||
except Exception:
|
||||
# otherwise use default system hook
|
||||
old_excepthook = sys.excepthook
|
||||
sys.excepthook = excepthook
|
||||
return old_excepthook
|
||||
|
||||
|
||||
@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.
|
||||
locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
|
||||
locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
|
||||
suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
|
||||
max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
|
||||
|
||||
"""
|
||||
|
||||
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,
|
||||
locals_max_length: int = LOCALS_MAX_LENGTH,
|
||||
locals_max_string: int = LOCALS_MAX_STRING,
|
||||
locals_hide_dunder: bool = True,
|
||||
locals_hide_sunder: bool = False,
|
||||
indent_guides: bool = True,
|
||||
suppress: Iterable[Union[str, ModuleType]] = (),
|
||||
max_frames: int = 100,
|
||||
):
|
||||
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.locals_hide_dunder = locals_hide_dunder
|
||||
self.locals_hide_sunder = locals_hide_sunder
|
||||
|
||||
self.suppress: Sequence[str] = []
|
||||
for suppress_entity in suppress:
|
||||
if not isinstance(suppress_entity, str):
|
||||
assert (
|
||||
suppress_entity.__file__ is not None
|
||||
), f"{suppress_entity!r} must be a module with '__file__' attribute"
|
||||
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,
|
||||
locals_max_length: int = LOCALS_MAX_LENGTH,
|
||||
locals_max_string: int = LOCALS_MAX_STRING,
|
||||
locals_hide_dunder: bool = True,
|
||||
locals_hide_sunder: bool = False,
|
||||
indent_guides: bool = True,
|
||||
suppress: Iterable[Union[str, ModuleType]] = (),
|
||||
max_frames: int = 100,
|
||||
) -> "Traceback":
|
||||
"""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.
|
||||
locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
|
||||
locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
|
||||
suppress (Iterable[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
|
||||
max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
|
||||
|
||||
Returns:
|
||||
Traceback: A Traceback instance that may be printed.
|
||||
"""
|
||||
rich_traceback = cls.extract(
|
||||
exc_type,
|
||||
exc_value,
|
||||
traceback,
|
||||
show_locals=show_locals,
|
||||
locals_max_length=locals_max_length,
|
||||
locals_max_string=locals_max_string,
|
||||
locals_hide_dunder=locals_hide_dunder,
|
||||
locals_hide_sunder=locals_hide_sunder,
|
||||
)
|
||||
|
||||
return cls(
|
||||
rich_traceback,
|
||||
width=width,
|
||||
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,
|
||||
locals_hide_dunder=locals_hide_dunder,
|
||||
locals_hide_sunder=locals_hide_sunder,
|
||||
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,
|
||||
locals_hide_dunder: bool = True,
|
||||
locals_hide_sunder: bool = False,
|
||||
) -> 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.
|
||||
locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
|
||||
locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
|
||||
|
||||
Returns:
|
||||
Trace: A Trace instance which you can use to construct a `Traceback`.
|
||||
"""
|
||||
|
||||
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 propagate."""
|
||||
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
|
||||
|
||||
def get_locals(
|
||||
iter_locals: Iterable[Tuple[str, object]]
|
||||
) -> Iterable[Tuple[str, object]]:
|
||||
"""Extract locals from an iterator of key pairs."""
|
||||
if not (locals_hide_dunder or locals_hide_sunder):
|
||||
yield from iter_locals
|
||||
return
|
||||
for key, value in iter_locals:
|
||||
if locals_hide_dunder and key.startswith("__"):
|
||||
continue
|
||||
if locals_hide_sunder and key.startswith("_"):
|
||||
continue
|
||||
yield key, value
|
||||
|
||||
for frame_summary, line_no in walk_tb(traceback):
|
||||
filename = frame_summary.f_code.co_filename
|
||||
if filename and not filename.startswith("<"):
|
||||
if not os.path.isabs(filename):
|
||||
filename = os.path.join(_IMPORT_CWD, filename)
|
||||
if frame_summary.f_locals.get("_rich_traceback_omit", False):
|
||||
continue
|
||||
|
||||
frame = Frame(
|
||||
filename=filename or "?",
|
||||
lineno=line_no,
|
||||
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 get_locals(frame_summary.f_locals.items())
|
||||
}
|
||||
if show_locals
|
||||
else None,
|
||||
)
|
||||
append(frame)
|
||||
if frame_summary.f_locals.get("_rich_traceback_guard", False):
|
||||
del stack.frames[:]
|
||||
|
||||
cause = getattr(exc_value, "__cause__", None)
|
||||
if cause:
|
||||
exc_type = cause.__class__
|
||||
exc_value = cause
|
||||
# __traceback__ can be None, e.g. for exceptions raised by the
|
||||
# 'multiprocessing' module
|
||||
traceback = cause.__traceback__
|
||||
is_cause = True
|
||||
continue
|
||||
|
||||
cause = exc_value.__context__
|
||||
if cause and not getattr(exc_value, "__suppress_context__", False):
|
||||
exc_type = cause.__class__
|
||||
exc_value = cause
|
||||
traceback = cause.__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),
|
||||
},
|
||||
inherit=False,
|
||||
)
|
||||
|
||||
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",
|
||||
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.syntax_error",
|
||||
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>":
|
||||
if os.path.exists(syntax_error.filename):
|
||||
text = Text.assemble(
|
||||
(f" {syntax_error.filename}", "pygments.string"),
|
||||
(":", "pygments.text"),
|
||||
(str(syntax_error.lineno), "pygments.number"),
|
||||
style="pygments.text",
|
||||
)
|
||||
yield path_highlighter(text)
|
||||
syntax_error_text = highlighter(syntax_error.line.rstrip())
|
||||
syntax_error_text.no_wrap = True
|
||||
offset = min(syntax_error.offset - 1, len(syntax_error_text))
|
||||
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"
|
||||
try:
|
||||
return cls.LEXERS.get(ext) or guess_lexer_for_filename(filename, code).name
|
||||
except ClassNotFound:
|
||||
return "text"
|
||||
|
||||
@group()
|
||||
def _render_stack(self, stack: Stack) -> RenderResult:
|
||||
path_highlighter = PathHighlighter()
|
||||
theme = self.theme
|
||||
|
||||
def read_code(filename: str) -> str:
|
||||
"""Read files, and cache results on filename.
|
||||
|
||||
Args:
|
||||
filename (str): Filename to read
|
||||
|
||||
Returns:
|
||||
str: Contents of file
|
||||
"""
|
||||
return "".join(linecache.getlines(filename))
|
||||
|
||||
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 == 0
|
||||
frame_filename = frame.filename
|
||||
suppressed = any(frame_filename.startswith(path) for path in self.suppress)
|
||||
|
||||
if os.path.exists(frame.filename):
|
||||
text = Text.assemble(
|
||||
path_highlighter(Text(frame.filename, style="pygments.string")),
|
||||
(":", "pygments.text"),
|
||||
(str(frame.lineno), "pygments.number"),
|
||||
" in ",
|
||||
(frame.name, "pygments.function"),
|
||||
style="pygments.text",
|
||||
)
|
||||
else:
|
||||
text = Text.assemble(
|
||||
"in ",
|
||||
(frame.name, "pygments.function"),
|
||||
(":", "pygments.text"),
|
||||
(str(frame.lineno), "pygments.number"),
|
||||
style="pygments.text",
|
||||
)
|
||||
if not frame.filename.startswith("<") and not first:
|
||||
yield ""
|
||||
yield text
|
||||
if frame.filename.startswith("<"):
|
||||
yield from render_locals(frame)
|
||||
continue
|
||||
if not suppressed:
|
||||
try:
|
||||
code = read_code(frame.filename)
|
||||
if not code:
|
||||
# code may be an empty string if the file doesn't exist, OR
|
||||
# if the traceback filename is generated dynamically
|
||||
continue
|
||||
lexer_name = self._guess_lexer(frame.filename, code)
|
||||
syntax = Syntax(
|
||||
code,
|
||||
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[name-defined]
|
||||
except:
|
||||
console.print_exception(show_locals=True)
|
||||
|
||||
error()
|
251
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/tree.py
Normal file
251
elitebot/lib/python3.11/site-packages/pip/_vendor/rich/tree.py
Normal file
|
@ -0,0 +1,251 @@
|
|||
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: Optional[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,
|
||||
),
|
||||
pad=options.justify is not 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)
|
Loading…
Add table
Add a link
Reference in a new issue