Загрузить файлы в «venv/Lib/site-packages/pip/_vendor/rich»
This commit is contained in:
19
venv/Lib/site-packages/pip/_vendor/rich/LICENSE
Normal file
19
venv/Lib/site-packages/pip/_vendor/rich/LICENSE
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2020 Will McGugan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
400
venv/Lib/site-packages/pip/_vendor/rich/live.py
Normal file
400
venv/Lib/site-packages/pip/_vendor/rich/live.py
Normal file
@@ -0,0 +1,400 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from threading import Event, RLock, Thread
|
||||
from types import TracebackType
|
||||
from typing import IO, TYPE_CHECKING, Any, Callable, List, Optional, TextIO, Type, cast
|
||||
|
||||
from . import get_console
|
||||
from .console import Console, ConsoleRenderable, Group, 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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# Can be replaced with `from typing import Self` in Python 3.11+
|
||||
from typing_extensions import Self # pragma: no cover
|
||||
|
||||
|
||||
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. Defaults to 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
|
||||
)
|
||||
self._nested = False
|
||||
|
||||
@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._started = True
|
||||
|
||||
if not self.console.set_live(self):
|
||||
self._nested = True
|
||||
return
|
||||
|
||||
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._started = False
|
||||
self.console.clear_live()
|
||||
if self._nested:
|
||||
if not self.transient:
|
||||
self.console.print(self.renderable)
|
||||
return
|
||||
|
||||
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) -> Self:
|
||||
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.
|
||||
"""
|
||||
live_stack = self.console._live_stack
|
||||
renderable: RenderableType
|
||||
if live_stack and self is live_stack[0]:
|
||||
# The first Live instance will render everything in the Live stack
|
||||
renderable = Group(*[live.get_renderable() for live in live_stack])
|
||||
else:
|
||||
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._nested:
|
||||
if self.console._live_stack:
|
||||
self.console._live_stack[0].refresh()
|
||||
return
|
||||
|
||||
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))
|
||||
106
venv/Lib/site-packages/pip/_vendor/rich/live_render.py
Normal file
106
venv/Lib/site-packages/pip/_vendor/rich/live_render.py
Normal file
@@ -0,0 +1,106 @@
|
||||
from typing import Optional, Tuple, Literal
|
||||
|
||||
|
||||
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
|
||||
297
venv/Lib/site-packages/pip/_vendor/rich/logging.py
Normal file
297
venv/Lib/site-packages/pip/_vendor/rich/logging.py
Normal file
@@ -0,0 +1,297 @@
|
||||
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_code_width (int, optional): Number of code characters used to render tracebacks, or None for full width. Defaults to 88.
|
||||
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.
|
||||
tracebacks_max_frames (int, optional): Optional maximum number of frames returned by 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_code_width: Optional[int] = 88,
|
||||
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]] = (),
|
||||
tracebacks_max_frames: int = 100,
|
||||
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.tracebacks_max_frames = tracebacks_max_frames
|
||||
self.tracebacks_code_width = tracebacks_code_width
|
||||
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,
|
||||
code_width=self.tracebacks_code_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,
|
||||
max_frames=self.tracebacks_max_frames,
|
||||
)
|
||||
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))
|
||||
251
venv/Lib/site-packages/pip/_vendor/rich/markup.py
Normal file
251
venv/Lib/site-packages/pip/_vendor/rich/markup.py
Normal file
@@ -0,0 +1,251 @@
|
||||
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)
|
||||
if markup.endswith("\\") and not markup.endswith("\\\\"):
|
||||
return 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.
|
||||
style: (Union[str, Style]): The style to use.
|
||||
emoji (bool, optional): Also render emoji code. Defaults to True.
|
||||
emoji_variant (str, optional): Optional emoji variant, either "text" or "emoji". Defaults to None.
|
||||
|
||||
|
||||
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)
|
||||
Reference in New Issue
Block a user