Загрузить файлы в «venv/Lib/site-packages/pip/_vendor/rich»
This commit is contained in:
115
venv/Lib/site-packages/pip/_vendor/rich/theme.py
Normal file
115
venv/Lib/site-packages/pip/_vendor/rich/theme.py
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import configparser
|
||||||
|
from typing import IO, Dict, List, 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, 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)
|
||||||
5
venv/Lib/site-packages/pip/_vendor/rich/themes.py
Normal file
5
venv/Lib/site-packages/pip/_vendor/rich/themes.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from .default_styles import DEFAULT_STYLES
|
||||||
|
from .theme import Theme
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT = Theme(DEFAULT_STYLES)
|
||||||
899
venv/Lib/site-packages/pip/_vendor/rich/traceback.py
Normal file
899
venv/Lib/site-packages/pip/_vendor/rich/traceback.py
Normal file
@@ -0,0 +1,899 @@
|
|||||||
|
import inspect
|
||||||
|
import linecache
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from itertools import islice
|
||||||
|
from traceback import walk_tb
|
||||||
|
from types import ModuleType, TracebackType
|
||||||
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
Dict,
|
||||||
|
Iterable,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Sequence,
|
||||||
|
Set,
|
||||||
|
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_first_last, loop_last
|
||||||
|
from .columns import Columns
|
||||||
|
from .console import (
|
||||||
|
Console,
|
||||||
|
ConsoleOptions,
|
||||||
|
ConsoleRenderable,
|
||||||
|
Group,
|
||||||
|
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, SyntaxPosition
|
||||||
|
from .text import Text
|
||||||
|
from .theme import Theme
|
||||||
|
|
||||||
|
WINDOWS = sys.platform == "win32"
|
||||||
|
|
||||||
|
LOCALS_MAX_LENGTH = 10
|
||||||
|
LOCALS_MAX_STRING = 80
|
||||||
|
|
||||||
|
|
||||||
|
def _iter_syntax_lines(
|
||||||
|
start: SyntaxPosition, end: SyntaxPosition
|
||||||
|
) -> Iterable[Tuple[int, int, int]]:
|
||||||
|
"""Yield start and end positions per line.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
start: Start position.
|
||||||
|
end: End position.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Iterable of (LINE, COLUMN1, COLUMN2).
|
||||||
|
"""
|
||||||
|
|
||||||
|
line1, column1 = start
|
||||||
|
line2, column2 = end
|
||||||
|
|
||||||
|
if line1 == line2:
|
||||||
|
yield line1, column1, column2
|
||||||
|
else:
|
||||||
|
for first, last, line_no in loop_first_last(range(line1, line2 + 1)):
|
||||||
|
if first:
|
||||||
|
yield line_no, column1, -1
|
||||||
|
elif last:
|
||||||
|
yield line_no, 0, column2
|
||||||
|
else:
|
||||||
|
yield line_no, 0, -1
|
||||||
|
|
||||||
|
|
||||||
|
def install(
|
||||||
|
*,
|
||||||
|
console: Optional[Console] = None,
|
||||||
|
width: Optional[int] = 100,
|
||||||
|
code_width: Optional[int] = 88,
|
||||||
|
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.
|
||||||
|
code_width (Optional[int], optional): Code width (in characters) of traceback. Defaults to 88.
|
||||||
|
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:
|
||||||
|
exception_traceback = Traceback.from_exception(
|
||||||
|
type_,
|
||||||
|
value,
|
||||||
|
traceback,
|
||||||
|
width=width,
|
||||||
|
code_width=code_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,
|
||||||
|
)
|
||||||
|
traceback_console.print(exception_traceback)
|
||||||
|
|
||||||
|
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")
|
||||||
|
if tb_offset is None:
|
||||||
|
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
|
||||||
|
last_instruction: Optional[Tuple[Tuple[int, int], Tuple[int, int]]] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class _SyntaxError:
|
||||||
|
offset: int
|
||||||
|
filename: str
|
||||||
|
line: str
|
||||||
|
lineno: int
|
||||||
|
msg: str
|
||||||
|
notes: List[str] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
@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)
|
||||||
|
notes: List[str] = field(default_factory=list)
|
||||||
|
is_group: bool = False
|
||||||
|
exceptions: List["Trace"] = 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.
|
||||||
|
code_width (Optional[int], optional): Number of code characters used to traceback. Defaults to 88.
|
||||||
|
extra_lines (int, optional): Additional lines of code to render. Defaults to 3.
|
||||||
|
theme (str, optional): Override pygments theme used in traceback.
|
||||||
|
word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.
|
||||||
|
show_locals (bool, optional): Enable display of local variables. Defaults to False.
|
||||||
|
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,
|
||||||
|
code_width: Optional[int] = 88,
|
||||||
|
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.code_width = code_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,
|
||||||
|
code_width: Optional[int] = 88,
|
||||||
|
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.
|
||||||
|
code_width (Optional[int], optional): Number of code characters used to traceback. Defaults to 88.
|
||||||
|
extra_lines (int, optional): Additional lines of code to render. Defaults to 3.
|
||||||
|
theme (str, optional): Override pygments theme used in traceback.
|
||||||
|
word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.
|
||||||
|
show_locals (bool, optional): Enable display of local variables. Defaults to False.
|
||||||
|
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,
|
||||||
|
code_width=code_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,
|
||||||
|
_visited_exceptions: Optional[Set[BaseException]] = None,
|
||||||
|
) -> 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
|
||||||
|
|
||||||
|
notes: List[str] = getattr(exc_value, "__notes__", None) or []
|
||||||
|
|
||||||
|
grouped_exceptions: Set[BaseException] = (
|
||||||
|
set() if _visited_exceptions is None else _visited_exceptions
|
||||||
|
)
|
||||||
|
|
||||||
|
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,
|
||||||
|
notes=notes,
|
||||||
|
)
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 11):
|
||||||
|
if isinstance(exc_value, (BaseExceptionGroup, ExceptionGroup)):
|
||||||
|
stack.is_group = True
|
||||||
|
for exception in exc_value.exceptions:
|
||||||
|
if exception in grouped_exceptions:
|
||||||
|
continue
|
||||||
|
grouped_exceptions.add(exception)
|
||||||
|
stack.exceptions.append(
|
||||||
|
Traceback.extract(
|
||||||
|
type(exception),
|
||||||
|
exception,
|
||||||
|
exception.__traceback__,
|
||||||
|
show_locals=show_locals,
|
||||||
|
locals_max_length=locals_max_length,
|
||||||
|
locals_hide_dunder=locals_hide_dunder,
|
||||||
|
locals_hide_sunder=locals_hide_sunder,
|
||||||
|
_visited_exceptions=grouped_exceptions,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
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,
|
||||||
|
notes=notes,
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
last_instruction: Optional[Tuple[Tuple[int, int], Tuple[int, int]]]
|
||||||
|
last_instruction = None
|
||||||
|
if sys.version_info >= (3, 11):
|
||||||
|
instruction_index = frame_summary.f_lasti // 2
|
||||||
|
instruction_position = next(
|
||||||
|
islice(
|
||||||
|
frame_summary.f_code.co_positions(),
|
||||||
|
instruction_index,
|
||||||
|
instruction_index + 1,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(
|
||||||
|
start_line,
|
||||||
|
end_line,
|
||||||
|
start_column,
|
||||||
|
end_column,
|
||||||
|
) = instruction_position
|
||||||
|
if (
|
||||||
|
start_line is not None
|
||||||
|
and end_line is not None
|
||||||
|
and start_column is not None
|
||||||
|
and end_column is not None
|
||||||
|
):
|
||||||
|
last_instruction = (
|
||||||
|
(start_line, start_column),
|
||||||
|
(end_line, end_column),
|
||||||
|
)
|
||||||
|
|
||||||
|
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 not (inspect.isfunction(value) or inspect.isclass(value))
|
||||||
|
}
|
||||||
|
if show_locals
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
last_instruction=last_instruction,
|
||||||
|
)
|
||||||
|
append(frame)
|
||||||
|
if frame_summary.f_locals.get("_rich_traceback_guard", False):
|
||||||
|
del stack.frames[:]
|
||||||
|
|
||||||
|
if not grouped_exceptions:
|
||||||
|
cause = getattr(exc_value, "__cause__", None)
|
||||||
|
if cause is not None and cause is not exc_value:
|
||||||
|
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 is not None 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()
|
||||||
|
|
||||||
|
@group()
|
||||||
|
def render_stack(stack: Stack, last: bool) -> RenderResult:
|
||||||
|
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"))
|
||||||
|
|
||||||
|
for note in stack.notes:
|
||||||
|
yield Text.assemble(("[NOTE] ", "traceback.note"), highlighter(note))
|
||||||
|
|
||||||
|
if stack.is_group:
|
||||||
|
for group_no, group_exception in enumerate(stack.exceptions, 1):
|
||||||
|
grouped_exceptions: List[Group] = []
|
||||||
|
for group_last, group_stack in loop_last(group_exception.stacks):
|
||||||
|
grouped_exceptions.append(render_stack(group_stack, group_last))
|
||||||
|
yield ""
|
||||||
|
yield Constrain(
|
||||||
|
Panel(
|
||||||
|
Group(*grouped_exceptions),
|
||||||
|
title=f"Sub-exception #{group_no}",
|
||||||
|
border_style="traceback.group.border",
|
||||||
|
),
|
||||||
|
self.width,
|
||||||
|
)
|
||||||
|
|
||||||
|
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",
|
||||||
|
)
|
||||||
|
|
||||||
|
for last, stack in loop_last(reversed(self.trace.stacks)):
|
||||||
|
yield render_stack(stack, last)
|
||||||
|
|
||||||
|
@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 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_lines = linecache.getlines(frame.filename)
|
||||||
|
code = "".join(code_lines)
|
||||||
|
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=self.code_width,
|
||||||
|
indent_guides=self.indent_guides,
|
||||||
|
dedent=False,
|
||||||
|
)
|
||||||
|
yield ""
|
||||||
|
except Exception as error:
|
||||||
|
yield Text.assemble(
|
||||||
|
(f"\n{error}", "traceback.error"),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if frame.last_instruction is not None:
|
||||||
|
start, end = frame.last_instruction
|
||||||
|
|
||||||
|
# Stylize a line at a time
|
||||||
|
# So that indentation isn't underlined (which looks bad)
|
||||||
|
for line1, column1, column2 in _iter_syntax_lines(start, end):
|
||||||
|
try:
|
||||||
|
if column1 == 0:
|
||||||
|
line = code_lines[line1 - 1]
|
||||||
|
column1 = len(line) - len(line.lstrip())
|
||||||
|
if column2 == -1:
|
||||||
|
column2 = len(code_lines[line1 - 1])
|
||||||
|
except IndexError:
|
||||||
|
# Being defensive here
|
||||||
|
# If last_instruction reports a line out-of-bounds, we don't want to crash
|
||||||
|
continue
|
||||||
|
|
||||||
|
syntax.stylize_range(
|
||||||
|
style="traceback.error_range",
|
||||||
|
start=(line1, column1),
|
||||||
|
end=(line1, column2),
|
||||||
|
)
|
||||||
|
yield (
|
||||||
|
Columns(
|
||||||
|
[
|
||||||
|
syntax,
|
||||||
|
*render_locals(frame),
|
||||||
|
],
|
||||||
|
padding=1,
|
||||||
|
)
|
||||||
|
if frame.locals
|
||||||
|
else syntax
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": # pragma: no cover
|
||||||
|
install(show_locals=True)
|
||||||
|
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:
|
||||||
|
foo(0)
|
||||||
|
|
||||||
|
error()
|
||||||
257
venv/Lib/site-packages/pip/_vendor/rich/tree.py
Normal file
257
venv/Lib/site-packages/pip/_vendor/rich/tree.py
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
GuideType = Tuple[str, str, str, str]
|
||||||
|
|
||||||
|
|
||||||
|
class Tree(JupyterMixin):
|
||||||
|
"""A renderable for a tree structure.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
ASCII_GUIDES (GuideType): Guide lines used when Console.ascii_only is True.
|
||||||
|
TREE_GUIDES (List[GuideType, GuideType, GuideType]): Default guide lines.
|
||||||
|
|
||||||
|
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.
|
||||||
|
hide_root (bool, optional): Hide the root node. Defaults to False.
|
||||||
|
"""
|
||||||
|
|
||||||
|
ASCII_GUIDES = (" ", "| ", "+-- ", "`-- ")
|
||||||
|
TREE_GUIDES = [
|
||||||
|
(" ", "│ ", "├── ", "└── "),
|
||||||
|
(" ", "┃ ", "┣━━ ", "┗━━ "),
|
||||||
|
(" ", "║ ", "╠══ ", "╚══ "),
|
||||||
|
]
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
_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 = self.ASCII_GUIDES[index]
|
||||||
|
else:
|
||||||
|
guide = 1 if style.bold else (2 if style.underline2 else 0)
|
||||||
|
line = self.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)
|
||||||
Reference in New Issue
Block a user