123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903 |
- import builtins
- import dataclasses
- import inspect
- import os
- import re
- 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,
- Set,
- Tuple,
- Union,
- )
- from pip._vendor.rich.repr import RichReprResult
- try:
- import attr as _attr_module
- except ImportError: # pragma: no cover
- _attr_module = None # type: ignore
- 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 _attr_module is not None and _attr_module.has(type(obj))
- def _get_attr_fields(obj: Any) -> Iterable["_attr_module.Attribute[Any]"]:
- """Get fields for an attrs object."""
- return _attr_module.fields(type(obj)) if _attr_module is not None else []
- def _is_dataclass_repr(obj: object) -> bool:
- """Check if an instance of a dataclass contains the default repr.
- Args:
- obj (object): A dataclass instance.
- Returns:
- bool: True if the default repr is used, False if there is a custom repr.
- """
- # Digging in to a lot of internals here
- # Catching all exceptions in case something is missing on a non CPython implementation
- try:
- return obj.__repr__.__code__.co_filename == dataclasses.__file__
- except Exception: # pragma: no coverage
- return False
- 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,
- expand_all: bool = False,
- ) -> None:
- from .console import ConsoleRenderable # needed here to prevent circular import
- # always skip rich generated jupyter renderables or None values
- if isinstance(value, JupyterRenderable) or value is None:
- return
- console = console or get_console()
- if console.is_jupyter:
- # Delegate rendering to IPython if the object (and IPython) supports it
- # https://ipython.readthedocs.io/en/stable/config/integrating.html#rich-display
- ipython_repr_methods = [
- "_repr_html_",
- "_repr_markdown_",
- "_repr_json_",
- "_repr_latex_",
- "_repr_jpeg_",
- "_repr_png_",
- "_repr_svg_",
- "_repr_mimebundle_",
- ]
- for repr_method in ipython_repr_methods:
- method = getattr(value, repr_method, None)
- if inspect.ismethod(method):
- # Calling the method ourselves isn't ideal. The interface for the `_repr_*_` methods
- # specifies that if they return None, then they should not be rendered
- # by the notebook.
- try:
- repr_result = method()
- except Exception:
- continue # If the method raises, treat it as if it doesn't exist, try any others
- if repr_result is not None:
- return # Delegate rendering to IPython
- # certain renderables should start on a new line
- if isinstance(value, ConsoleRenderable):
- console.line()
- console.print(
- value
- if isinstance(value, RichRenderable)
- else Pretty(
- value,
- overflow=overflow,
- indent_guides=indent_guides,
- max_length=max_length,
- max_string=max_string,
- expand_all=expand_all,
- margin=12,
- ),
- crop=crop,
- new_line_start=True,
- )
- def install(
- console: Optional["Console"] = None,
- overflow: "OverflowMethod" = "ignore",
- crop: bool = False,
- indent_guides: bool = False,
- max_length: Optional[int] = None,
- max_string: Optional[int] = None,
- expand_all: bool = False,
- ) -> None:
- """Install automatic pretty printing in the Python REPL.
- Args:
- console (Console, optional): Console instance or ``None`` to use global console. Defaults to None.
- overflow (Optional[OverflowMethod], optional): Overflow method. Defaults to "ignore".
- crop (Optional[bool], optional): Enable cropping of long lines. Defaults to False.
- indent_guides (bool, optional): Enable indentation guides. Defaults to False.
- max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
- Defaults to None.
- max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to None.
- expand_all (bool, optional): Expand all containers. Defaults to False.
- max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
- """
- from pip._vendor.rich import get_console
- console = console or get_console()
- assert console is not None
- def display_hook(value: Any) -> None:
- """Replacement sys.displayhook which prettifies objects with Rich."""
- if value is not None:
- assert console is not None
- builtins._ = None # type: ignore
- console.print(
- value
- if isinstance(value, RichRenderable)
- else Pretty(
- value,
- overflow=overflow,
- indent_guides=indent_guides,
- max_length=max_length,
- max_string=max_string,
- expand_all=expand_all,
- ),
- crop=crop,
- )
- builtins._ = value # type: ignore
- try: # pragma: no cover
- ip = get_ipython() # type: ignore
- from IPython.core.formatters import BaseFormatter
- class RichFormatter(BaseFormatter): # type: ignore
- pprint: bool = True
- def __call__(self, value: Any) -> Any:
- if self.pprint:
- return _ipy_display_hook(
- value,
- console=get_console(),
- overflow=overflow,
- indent_guides=indent_guides,
- max_length=max_length,
- max_string=max_string,
- 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
- except Exception:
- sys.displayhook = display_hook
- class Pretty(JupyterMixin):
- """A rich renderable that pretty prints an object.
- Args:
- _object (Any): An object to pretty print.
- highlighter (HighlighterType, optional): Highlighter object to apply to result, or None for ReprHighlighter. Defaults to None.
- indent_size (int, optional): Number of spaces in indent. Defaults to 4.
- justify (JustifyMethod, optional): Justify method, or None for default. Defaults to None.
- overflow (OverflowMethod, optional): Overflow method, or None for default. Defaults to None.
- no_wrap (Optional[bool], optional): Disable word wrapping. Defaults to False.
- indent_guides (bool, optional): Enable indentation guides. Defaults to False.
- max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
- Defaults to None.
- max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to None.
- 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(
- pretty_str,
- justify=self.justify or options.justify,
- overflow=self.overflow or options.overflow,
- no_wrap=pick_bool(self.no_wrap, options.no_wrap),
- style="pretty",
- )
- pretty_text = (
- self.highlighter(pretty_text)
- if pretty_text
- else Text(
- f"{type(self._object)}.__repr__ returned empty string",
- style="dim italic",
- )
- )
- if self.indent_guides and not options.ascii_only:
- pretty_text = pretty_text.with_indent_guides(
- self.indent_size, style="repr.indent"
- )
- if self.insert_line and "\n" in pretty_text:
- yield ""
- yield pretty_text
- def __rich_measure__(
- self, console: "Console", options: "ConsoleOptions"
- ) -> "Measurement":
- pretty_str = pretty_repr(
- self._object,
- max_width=options.max_width,
- indent_size=self.indent_size,
- max_length=self.max_length,
- max_string=self.max_string,
- )
- text_width = (
- max(cell_len(line) for line in pretty_str.splitlines()) if pretty_str else 0
- )
- return Measurement(text_width, text_width)
- def _get_braces_for_defaultdict(_object: DefaultDict[Any, Any]) -> Tuple[str, str, str]:
- return (
- f"defaultdict({_object.default_factory!r}, {{",
- "})",
- f"defaultdict({_object.default_factory!r}, {{}})",
- )
- def _get_braces_for_array(_object: "array[Any]") -> Tuple[str, str, str]:
- return (f"array({_object.typecode!r}, [", "])", "array({_object.typecode!r})")
- _BRACES: Dict[type, Callable[[Any], Tuple[str, str, str]]] = {
- os._Environ: lambda _object: ("environ({", "})", "environ({})"),
- array: _get_braces_for_array,
- defaultdict: _get_braces_for_defaultdict,
- Counter: lambda _object: ("Counter({", "})", "Counter()"),
- deque: lambda _object: ("deque([", "])", "deque()"),
- dict: lambda _object: ("{", "}", "{}"),
- UserDict: lambda _object: ("{", "}", "{}"),
- frozenset: lambda _object: ("frozenset({", "})", "frozenset()"),
- list: lambda _object: ("[", "]", "[]"),
- UserList: lambda _object: ("[", "]", "[]"),
- set: lambda _object: ("{", "}", "set()"),
- tuple: lambda _object: ("(", ")", "()"),
- MappingProxyType: lambda _object: ("mappingproxy({", "})", "mappingproxy({})"),
- }
- _CONTAINERS = tuple(_BRACES.keys())
- _MAPPING_CONTAINERS = (dict, os._Environ, MappingProxyType, UserDict)
- def is_expandable(obj: Any) -> bool:
- """Check if an object may be expanded by pretty print."""
- return (
- isinstance(obj, _CONTAINERS)
- or (is_dataclass(obj))
- or (hasattr(obj, "__rich_repr__"))
- or _is_attr_object(obj)
- ) and not isclass(obj)
- @dataclass
- class Node:
- """A node in a repr tree. May be atomic or a container."""
- key_repr: str = ""
- value_repr: str = ""
- open_brace: str = ""
- close_brace: str = ""
- empty: str = ""
- last: bool = False
- is_tuple: bool = False
- children: Optional[List["Node"]] = None
- key_separator = ": "
- separator: str = ", "
- def iter_tokens(self) -> Iterable[str]:
- """Generate tokens for this node."""
- if self.key_repr:
- yield self.key_repr
- yield self.key_separator
- if self.value_repr:
- yield self.value_repr
- elif self.children is not None:
- if self.children:
- yield self.open_brace
- if self.is_tuple and len(self.children) == 1:
- yield from self.children[0].iter_tokens()
- yield ","
- else:
- for child in self.children:
- yield from child.iter_tokens()
- if not child.last:
- yield self.separator
- yield self.close_brace
- else:
- yield self.empty
- def check_length(self, start_length: int, max_length: int) -> bool:
- """Check the length fits within a limit.
- Args:
- start_length (int): Starting length of the line (indent, prefix, suffix).
- max_length (int): Maximum length.
- Returns:
- bool: True if the node can be rendered within max length, otherwise False.
- """
- total_length = start_length
- for token in self.iter_tokens():
- total_length += cell_len(token)
- if total_length > max_length:
- return False
- return True
- def __str__(self) -> str:
- repr_text = "".join(self.iter_tokens())
- return repr_text
- def render(
- self, max_width: int = 80, indent_size: int = 4, expand_all: bool = False
- ) -> str:
- """Render the node to a pretty repr.
- Args:
- max_width (int, optional): Maximum width of the repr. Defaults to 80.
- indent_size (int, optional): Size of indents. Defaults to 4.
- expand_all (bool, optional): Expand all levels. Defaults to False.
- Returns:
- str: A repr string of the original object.
- """
- lines = [_Line(node=self, is_root=True)]
- line_no = 0
- while line_no < len(lines):
- line = lines[line_no]
- if line.expandable and not line.expanded:
- if expand_all or not line.check_length(max_width):
- lines[line_no : line_no + 1] = line.expand(indent_size)
- line_no += 1
- repr_str = "\n".join(str(line) for line in lines)
- return repr_str
- @dataclass
- class _Line:
- """A line in repr output."""
- parent: Optional["_Line"] = None
- is_root: bool = False
- node: Optional[Node] = None
- text: str = ""
- suffix: str = ""
- whitespace: str = ""
- expanded: bool = False
- last: bool = False
- @property
- def expandable(self) -> bool:
- """Check if the line may be expanded."""
- return bool(self.node is not None and self.node.children)
- def check_length(self, max_length: int) -> bool:
- """Check this line fits within a given number of cells."""
- start_length = (
- len(self.whitespace) + cell_len(self.text) + cell_len(self.suffix)
- )
- assert self.node is not None
- return self.node.check_length(start_length, max_length)
- def expand(self, indent_size: int) -> Iterable["_Line"]:
- """Expand this line by adding children on their own line."""
- node = self.node
- assert node is not None
- whitespace = self.whitespace
- assert node.children
- if node.key_repr:
- new_line = yield _Line(
- text=f"{node.key_repr}{node.key_separator}{node.open_brace}",
- whitespace=whitespace,
- )
- else:
- new_line = yield _Line(text=node.open_brace, whitespace=whitespace)
- child_whitespace = self.whitespace + " " * indent_size
- tuple_of_one = node.is_tuple and len(node.children) == 1
- for last, child in loop_last(node.children):
- separator = "," if tuple_of_one else node.separator
- line = _Line(
- parent=new_line,
- node=child,
- whitespace=child_whitespace,
- suffix=separator,
- last=last and not tuple_of_one,
- )
- yield line
- yield _Line(
- text=node.close_brace,
- whitespace=whitespace,
- suffix=self.suffix,
- last=self.last,
- )
- def __str__(self) -> str:
- if self.last:
- return f"{self.whitespace}{self.text}{self.node or ''}"
- else:
- return (
- f"{self.whitespace}{self.text}{self.node or ''}{self.suffix.rstrip()}"
- )
- def traverse(
- _object: Any,
- max_length: Optional[int] = None,
- max_string: Optional[int] = None,
- 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 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_type = type(obj)
- py_version = (sys.version_info.major, sys.version_info.minor)
- 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 isinstance(arg, tuple):
- if len(arg) == 3:
- key, child, default = arg
- if default == child:
- continue
- yield key, child
- elif len(arg) == 2:
- key, child = arg
- yield key, child
- elif len(arg) == 1:
- yield arg[0]
- else:
- yield arg
- try:
- fake_attributes = hasattr(
- obj, "awehoi234_wdfjwljet234_234wdfoijsdfmmnxpi492"
- )
- except Exception:
- fake_attributes = False
- rich_repr_result: Optional[RichReprResult] = None
- if not fake_attributes:
- try:
- if hasattr(obj, "__rich_repr__") and not isclass(obj):
- rich_repr_result = obj.__rich_repr__()
- except Exception:
- pass
- if rich_repr_result is not None:
- angular = getattr(obj.__rich_repr__, "angular", False)
- args = list(iter_rich_args(rich_repr_result))
- class_name = obj.__class__.__name__
- if args:
- children = []
- append = children.append
- if reached_max_depth:
- node = Node(value_repr=f"...")
- 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 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,
- )
- elif _is_attr_object(obj) and not fake_attributes:
- children = []
- append = children.append
- attr_fields = _get_attr_fields(obj)
- if attr_fields:
- if reached_max_depth:
- node = Node(value_repr=f"...")
- 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
- )
- elif (
- is_dataclass(obj)
- and not isinstance(obj, type)
- and not fake_attributes
- and (_is_dataclass_repr(obj) or py_version == (3, 6))
- ):
- obj_id = id(obj)
- if obj_id in visited_ids:
- # Recursion detected
- return Node(value_repr="...")
- push_visited(obj_id)
- children = []
- append = children.append
- if reached_max_depth:
- node = Node(value_repr=f"...")
- else:
- node = Node(
- open_brace=f"{obj.__class__.__name__}(",
- close_brace=")",
- children=children,
- last=root,
- )
- for last, field in loop_last(
- field for field in fields(obj) if field.repr
- ):
- 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 isinstance(obj, _CONTAINERS):
- for container_type in _CONTAINERS:
- if isinstance(obj, container_type):
- obj_type = container_type
- break
- obj_id = id(obj)
- if obj_id in visited_ids:
- # Recursion detected
- return Node(value_repr="...")
- push_visited(obj_id)
- open_brace, close_brace, empty = _BRACES[obj_type](obj)
- if reached_max_depth:
- node = Node(value_repr=f"...", last=root)
- 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 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 = isinstance(obj, tuple)
- return node
- node = _traverse(_object, root=True)
- return node
- def pretty_repr(
- _object: Any,
- *,
- max_width: int = 80,
- indent_size: int = 4,
- max_length: Optional[int] = None,
- max_string: Optional[int] = None,
- 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 isinstance(_object, Node):
- node = _object
- else:
- node = traverse(
- _object, max_length=max_length, max_string=max_string, max_depth=max_depth
- )
- repr_str = node.render(
- max_width=max_width, indent_size=indent_size, expand_all=expand_all
- )
- return repr_str
- def pprint(
- _object: Any,
- *,
- console: Optional["Console"] = None,
- indent_guides: bool = True,
- max_length: Optional[int] = None,
- max_string: Optional[int] = None,
- 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"
- d = defaultdict(int)
- d["foo"] = 5
- data = {
- "foo": [
- 1,
- "Hello World!",
- 100.123,
- 323.232,
- 432324.0,
- {5, 6, 7, (1, 2, 3, 4), 8},
- ],
- "bar": frozenset({1, 2, 3}),
- "defaultdict": defaultdict(
- list, {"crumble": ["apple", "rhubarb", "butter", "sugar", "flour"]}
- ),
- "counter": Counter(
- [
- "apple",
- "orange",
- "pear",
- "kumquat",
- "kumquat",
- "durian" * 100,
- ]
- ),
- "atomic": (False, True, None),
- "Broken": BrokenRepr(),
- }
- data["foo"].append(data) # type: ignore
- from pip._vendor.rich import print
- print(Pretty(data, indent_guides=True, max_string=20))
|