CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
pretty.py1017 linesDownload Raw Back to rich
1import builtins2import collections3import dataclasses4import inspect5import os6import reprlib7import sys8from array import array9from collections import Counter, UserDict, UserList, defaultdict, deque10from dataclasses import dataclass, fields, is_dataclass11from inspect import isclass12from itertools import islice13from types import MappingProxyType14from typing import (15    TYPE_CHECKING,16    Any,17    Callable,18    DefaultDict,19    Deque,20    Dict,21    Iterable,22    List,23    Optional,24    Sequence,25    Set,26    Tuple,27    Union,28)29 30from pip._vendor.rich.repr import RichReprResult31 32try:33    import attr as _attr_module34 35    _has_attrs = hasattr(_attr_module, "ib")36except ImportError:  # pragma: no cover37    _has_attrs = False38 39from . import get_console40from ._loop import loop_last41from ._pick import pick_bool42from .abc import RichRenderable43from .cells import cell_len44from .highlighter import ReprHighlighter45from .jupyter import JupyterMixin, JupyterRenderable46from .measure import Measurement47from .text import Text48 49if TYPE_CHECKING:50    from .console import (51        Console,52        ConsoleOptions,53        HighlighterType,54        JustifyMethod,55        OverflowMethod,56        RenderResult,57    )58 59 60def _is_attr_object(obj: Any) -> bool:61    """Check if an object was created with attrs module."""62    return _has_attrs and _attr_module.has(type(obj))63 64 65def _get_attr_fields(obj: Any) -> Sequence["_attr_module.Attribute[Any]"]:66    """Get fields for an attrs object."""67    return _attr_module.fields(type(obj)) if _has_attrs else []68 69 70def _is_dataclass_repr(obj: object) -> bool:71    """Check if an instance of a dataclass contains the default repr.72 73    Args:74        obj (object): A dataclass instance.75 76    Returns:77        bool: True if the default repr is used, False if there is a custom repr.78    """79    # Digging in to a lot of internals here80    # Catching all exceptions in case something is missing on a non CPython implementation81    try:82        return obj.__repr__.__code__.co_filename in (83            dataclasses.__file__,84            reprlib.__file__,85        )86    except Exception:  # pragma: no coverage87        return False88 89 90_dummy_namedtuple = collections.namedtuple("_dummy_namedtuple", [])91 92 93def _has_default_namedtuple_repr(obj: object) -> bool:94    """Check if an instance of namedtuple contains the default repr95 96    Args:97        obj (object): A namedtuple98 99    Returns:100        bool: True if the default repr is used, False if there's a custom repr.101    """102    obj_file = None103    try:104        obj_file = inspect.getfile(obj.__repr__)105    except (OSError, TypeError):106        # OSError handles case where object is defined in __main__ scope, e.g. REPL - no filename available.107        # TypeError trapped defensively, in case of object without filename slips through.108        pass109    default_repr_file = inspect.getfile(_dummy_namedtuple.__repr__)110    return obj_file == default_repr_file111 112 113def _ipy_display_hook(114    value: Any,115    console: Optional["Console"] = None,116    overflow: "OverflowMethod" = "ignore",117    crop: bool = False,118    indent_guides: bool = False,119    max_length: Optional[int] = None,120    max_string: Optional[int] = None,121    max_depth: Optional[int] = None,122    expand_all: bool = False,123) -> Union[str, None]:124    # needed here to prevent circular import:125    from .console import ConsoleRenderable126 127    # always skip rich generated jupyter renderables or None values128    if _safe_isinstance(value, JupyterRenderable) or value is None:129        return None130 131    console = console or get_console()132 133    with console.capture() as capture:134        # certain renderables should start on a new line135        if _safe_isinstance(value, ConsoleRenderable):136            console.line()137        console.print(138            (139                value140                if _safe_isinstance(value, RichRenderable)141                else Pretty(142                    value,143                    overflow=overflow,144                    indent_guides=indent_guides,145                    max_length=max_length,146                    max_string=max_string,147                    max_depth=max_depth,148                    expand_all=expand_all,149                    margin=12,150                )151            ),152            crop=crop,153            new_line_start=True,154            end="",155        )156    # strip trailing newline, not usually part of a text repr157    # I'm not sure if this should be prevented at a lower level158    return capture.get().rstrip("\n")159 160 161def _safe_isinstance(162    obj: object, class_or_tuple: Union[type, Tuple[type, ...]]163) -> bool:164    """isinstance can fail in rare cases, for example types with no __class__"""165    try:166        return isinstance(obj, class_or_tuple)167    except Exception:168        return False169 170 171def install(172    console: Optional["Console"] = None,173    overflow: "OverflowMethod" = "ignore",174    crop: bool = False,175    indent_guides: bool = False,176    max_length: Optional[int] = None,177    max_string: Optional[int] = None,178    max_depth: Optional[int] = None,179    expand_all: bool = False,180) -> None:181    """Install automatic pretty printing in the Python REPL.182 183    Args:184        console (Console, optional): Console instance or ``None`` to use global console. Defaults to None.185        overflow (Optional[OverflowMethod], optional): Overflow method. Defaults to "ignore".186        crop (Optional[bool], optional): Enable cropping of long lines. Defaults to False.187        indent_guides (bool, optional): Enable indentation guides. Defaults to False.188        max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.189            Defaults to None.190        max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to None.191        max_depth (int, optional): Maximum depth of nested data structures, or None for no maximum. Defaults to None.192        expand_all (bool, optional): Expand all containers. Defaults to False.193        max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.194    """195    from pip._vendor.rich import get_console196 197    console = console or get_console()198    assert console is not None199 200    def display_hook(value: Any) -> None:201        """Replacement sys.displayhook which prettifies objects with Rich."""202        if value is not None:203            assert console is not None204            builtins._ = None  # type: ignore[attr-defined]205            console.print(206                (207                    value208                    if _safe_isinstance(value, RichRenderable)209                    else Pretty(210                        value,211                        overflow=overflow,212                        indent_guides=indent_guides,213                        max_length=max_length,214                        max_string=max_string,215                        max_depth=max_depth,216                        expand_all=expand_all,217                    )218                ),219                crop=crop,220            )221            builtins._ = value  # type: ignore[attr-defined]222 223    try:224        ip = get_ipython()  # type: ignore[name-defined]225    except NameError:226        sys.displayhook = display_hook227    else:228        from IPython.core.formatters import BaseFormatter229 230        class RichFormatter(BaseFormatter):  # type: ignore[misc]231            pprint: bool = True232 233            def __call__(self, value: Any) -> Any:234                if self.pprint:235                    return _ipy_display_hook(236                        value,237                        console=get_console(),238                        overflow=overflow,239                        indent_guides=indent_guides,240                        max_length=max_length,241                        max_string=max_string,242                        max_depth=max_depth,243                        expand_all=expand_all,244                    )245                else:246                    return repr(value)247 248        # replace plain text formatter with rich formatter249        rich_formatter = RichFormatter()250        ip.display_formatter.formatters["text/plain"] = rich_formatter251 252 253class Pretty(JupyterMixin):254    """A rich renderable that pretty prints an object.255 256    Args:257        _object (Any): An object to pretty print.258        highlighter (HighlighterType, optional): Highlighter object to apply to result, or None for ReprHighlighter. Defaults to None.259        indent_size (int, optional): Number of spaces in indent. Defaults to 4.260        justify (JustifyMethod, optional): Justify method, or None for default. Defaults to None.261        overflow (OverflowMethod, optional): Overflow method, or None for default. Defaults to None.262        no_wrap (Optional[bool], optional): Disable word wrapping. Defaults to False.263        indent_guides (bool, optional): Enable indentation guides. Defaults to False.264        max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.265            Defaults to None.266        max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to None.267        max_depth (int, optional): Maximum depth of nested data structures, or None for no maximum. Defaults to None.268        expand_all (bool, optional): Expand all containers. Defaults to False.269        margin (int, optional): Subtrace a margin from width to force containers to expand earlier. Defaults to 0.270        insert_line (bool, optional): Insert a new line if the output has multiple new lines. Defaults to False.271    """272 273    def __init__(274        self,275        _object: Any,276        highlighter: Optional["HighlighterType"] = None,277        *,278        indent_size: int = 4,279        justify: Optional["JustifyMethod"] = None,280        overflow: Optional["OverflowMethod"] = None,281        no_wrap: Optional[bool] = False,282        indent_guides: bool = False,283        max_length: Optional[int] = None,284        max_string: Optional[int] = None,285        max_depth: Optional[int] = None,286        expand_all: bool = False,287        margin: int = 0,288        insert_line: bool = False,289    ) -> None:290        self._object = _object291        self.highlighter = highlighter or ReprHighlighter()292        self.indent_size = indent_size293        self.justify: Optional["JustifyMethod"] = justify294        self.overflow: Optional["OverflowMethod"] = overflow295        self.no_wrap = no_wrap296        self.indent_guides = indent_guides297        self.max_length = max_length298        self.max_string = max_string299        self.max_depth = max_depth300        self.expand_all = expand_all301        self.margin = margin302        self.insert_line = insert_line303 304    def __rich_console__(305        self, console: "Console", options: "ConsoleOptions"306    ) -> "RenderResult":307        pretty_str = pretty_repr(308            self._object,309            max_width=options.max_width - self.margin,310            indent_size=self.indent_size,311            max_length=self.max_length,312            max_string=self.max_string,313            max_depth=self.max_depth,314            expand_all=self.expand_all,315        )316        pretty_text = Text.from_ansi(317            pretty_str,318            justify=self.justify or options.justify,319            overflow=self.overflow or options.overflow,320            no_wrap=pick_bool(self.no_wrap, options.no_wrap),321            style="pretty",322        )323        pretty_text = (324            self.highlighter(pretty_text)325            if pretty_text326            else Text(327                f"{type(self._object)}.__repr__ returned empty string",328                style="dim italic",329            )330        )331        if self.indent_guides and not options.ascii_only:332            pretty_text = pretty_text.with_indent_guides(333                self.indent_size, style="repr.indent"334            )335        if self.insert_line and "\n" in pretty_text:336            yield ""337        yield pretty_text338 339    def __rich_measure__(340        self, console: "Console", options: "ConsoleOptions"341    ) -> "Measurement":342        pretty_str = pretty_repr(343            self._object,344            max_width=options.max_width,345            indent_size=self.indent_size,346            max_length=self.max_length,347            max_string=self.max_string,348            max_depth=self.max_depth,349            expand_all=self.expand_all,350        )351        text_width = (352            max(cell_len(line) for line in pretty_str.splitlines()) if pretty_str else 0353        )354        return Measurement(text_width, text_width)355 356 357def _get_braces_for_defaultdict(_object: DefaultDict[Any, Any]) -> Tuple[str, str, str]:358    return (359        f"defaultdict({_object.default_factory!r}, {{",360        "})",361        f"defaultdict({_object.default_factory!r}, {{}})",362    )363 364 365def _get_braces_for_deque(_object: Deque[Any]) -> Tuple[str, str, str]:366    if _object.maxlen is None:367        return ("deque([", "])", "deque()")368    return (369        "deque([",370        f"], maxlen={_object.maxlen})",371        f"deque(maxlen={_object.maxlen})",372    )373 374 375def _get_braces_for_array(_object: "array[Any]") -> Tuple[str, str, str]:376    return (f"array({_object.typecode!r}, [", "])", f"array({_object.typecode!r})")377 378 379_BRACES: Dict[type, Callable[[Any], Tuple[str, str, str]]] = {380    os._Environ: lambda _object: ("environ({", "})", "environ({})"),381    array: _get_braces_for_array,382    defaultdict: _get_braces_for_defaultdict,383    Counter: lambda _object: ("Counter({", "})", "Counter()"),384    deque: _get_braces_for_deque,385    dict: lambda _object: ("{", "}", "{}"),386    UserDict: lambda _object: ("{", "}", "{}"),387    frozenset: lambda _object: ("frozenset({", "})", "frozenset()"),388    list: lambda _object: ("[", "]", "[]"),389    UserList: lambda _object: ("[", "]", "[]"),390    set: lambda _object: ("{", "}", "set()"),391    tuple: lambda _object: ("(", ")", "()"),392    MappingProxyType: lambda _object: ("mappingproxy({", "})", "mappingproxy({})"),393}394_CONTAINERS = tuple(_BRACES.keys())395_MAPPING_CONTAINERS = (dict, os._Environ, MappingProxyType, UserDict)396 397 398def is_expandable(obj: Any) -> bool:399    """Check if an object may be expanded by pretty print."""400    return (401        _safe_isinstance(obj, _CONTAINERS)402        or (is_dataclass(obj))403        or (hasattr(obj, "__rich_repr__"))404        or _is_attr_object(obj)405    ) and not isclass(obj)406 407 408@dataclass409class Node:410    """A node in a repr tree. May be atomic or a container."""411 412    key_repr: str = ""413    value_repr: str = ""414    open_brace: str = ""415    close_brace: str = ""416    empty: str = ""417    last: bool = False418    is_tuple: bool = False419    is_namedtuple: bool = False420    children: Optional[List["Node"]] = None421    key_separator: str = ": "422    separator: str = ", "423 424    def iter_tokens(self) -> Iterable[str]:425        """Generate tokens for this node."""426        if self.key_repr:427            yield self.key_repr428            yield self.key_separator429        if self.value_repr:430            yield self.value_repr431        elif self.children is not None:432            if self.children:433                yield self.open_brace434                if self.is_tuple and not self.is_namedtuple and len(self.children) == 1:435                    yield from self.children[0].iter_tokens()436                    yield ","437                else:438                    for child in self.children:439                        yield from child.iter_tokens()440                        if not child.last:441                            yield self.separator442                yield self.close_brace443            else:444                yield self.empty445 446    def check_length(self, start_length: int, max_length: int) -> bool:447        """Check the length fits within a limit.448 449        Args:450            start_length (int): Starting length of the line (indent, prefix, suffix).451            max_length (int): Maximum length.452 453        Returns:454            bool: True if the node can be rendered within max length, otherwise False.455        """456        total_length = start_length457        for token in self.iter_tokens():458            total_length += cell_len(token)459            if total_length > max_length:460                return False461        return True462 463    def __str__(self) -> str:464        repr_text = "".join(self.iter_tokens())465        return repr_text466 467    def render(468        self, max_width: int = 80, indent_size: int = 4, expand_all: bool = False469    ) -> str:470        """Render the node to a pretty repr.471 472        Args:473            max_width (int, optional): Maximum width of the repr. Defaults to 80.474            indent_size (int, optional): Size of indents. Defaults to 4.475            expand_all (bool, optional): Expand all levels. Defaults to False.476 477        Returns:478            str: A repr string of the original object.479        """480        lines = [_Line(node=self, is_root=True)]481        line_no = 0482        while line_no < len(lines):483            line = lines[line_no]484            if line.expandable and not line.expanded:485                if expand_all or not line.check_length(max_width):486                    lines[line_no : line_no + 1] = line.expand(indent_size)487            line_no += 1488 489        repr_str = "\n".join(str(line) for line in lines)490        return repr_str491 492 493@dataclass494class _Line:495    """A line in repr output."""496 497    parent: Optional["_Line"] = None498    is_root: bool = False499    node: Optional[Node] = None500    text: str = ""501    suffix: str = ""502    whitespace: str = ""503    expanded: bool = False504    last: bool = False505 506    @property507    def expandable(self) -> bool:508        """Check if the line may be expanded."""509        return bool(self.node is not None and self.node.children)510 511    def check_length(self, max_length: int) -> bool:512        """Check this line fits within a given number of cells."""513        start_length = (514            len(self.whitespace) + cell_len(self.text) + cell_len(self.suffix)515        )516        assert self.node is not None517        return self.node.check_length(start_length, max_length)518 519    def expand(self, indent_size: int) -> Iterable["_Line"]:520        """Expand this line by adding children on their own line."""521        node = self.node522        assert node is not None523        whitespace = self.whitespace524        assert node.children525        if node.key_repr:526            new_line = yield _Line(527                text=f"{node.key_repr}{node.key_separator}{node.open_brace}",528                whitespace=whitespace,529            )530        else:531            new_line = yield _Line(text=node.open_brace, whitespace=whitespace)532        child_whitespace = self.whitespace + " " * indent_size533        tuple_of_one = node.is_tuple and len(node.children) == 1534        for last, child in loop_last(node.children):535            separator = "," if tuple_of_one else node.separator536            line = _Line(537                parent=new_line,538                node=child,539                whitespace=child_whitespace,540                suffix=separator,541                last=last and not tuple_of_one,542            )543            yield line544 545        yield _Line(546            text=node.close_brace,547            whitespace=whitespace,548            suffix=self.suffix,549            last=self.last,550        )551 552    def __str__(self) -> str:553        if self.last:554            return f"{self.whitespace}{self.text}{self.node or ''}"555        else:556            return (557                f"{self.whitespace}{self.text}{self.node or ''}{self.suffix.rstrip()}"558            )559 560 561def _is_namedtuple(obj: Any) -> bool:562    """Checks if an object is most likely a namedtuple. It is possible563    to craft an object that passes this check and isn't a namedtuple, but564    there is only a minuscule chance of this happening unintentionally.565 566    Args:567        obj (Any): The object to test568 569    Returns:570        bool: True if the object is a namedtuple. False otherwise.571    """572    try:573        fields = getattr(obj, "_fields", None)574    except Exception:575        # Being very defensive - if we cannot get the attr then its not a namedtuple576        return False577    return isinstance(obj, tuple) and isinstance(fields, tuple)578 579 580def traverse(581    _object: Any,582    max_length: Optional[int] = None,583    max_string: Optional[int] = None,584    max_depth: Optional[int] = None,585) -> Node:586    """Traverse object and generate a tree.587 588    Args:589        _object (Any): Object to be traversed.590        max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.591            Defaults to None.592        max_string (int, optional): Maximum length of string before truncating, or None to disable truncating.593            Defaults to None.594        max_depth (int, optional): Maximum depth of data structures, or None for no maximum.595            Defaults to None.596 597    Returns:598        Node: The root of a tree structure which can be used to render a pretty repr.599    """600 601    def to_repr(obj: Any) -> str:602        """Get repr string for an object, but catch errors."""603        if (604            max_string is not None605            and _safe_isinstance(obj, (bytes, str))606            and len(obj) > max_string607        ):608            truncated = len(obj) - max_string609            obj_repr = f"{obj[:max_string]!r}+{truncated}"610        else:611            try:612                obj_repr = repr(obj)613            except Exception as error:614                obj_repr = f"<repr-error {str(error)!r}>"615        return obj_repr616 617    visited_ids: Set[int] = set()618    push_visited = visited_ids.add619    pop_visited = visited_ids.remove620 621    def _traverse(obj: Any, root: bool = False, depth: int = 0) -> Node:622        """Walk the object depth first."""623 624        obj_id = id(obj)625        if obj_id in visited_ids:626            # Recursion detected627            return Node(value_repr="...")628 629        obj_type = type(obj)630        children: List[Node]631        reached_max_depth = max_depth is not None and depth >= max_depth632 633        def iter_rich_args(rich_args: Any) -> Iterable[Union[Any, Tuple[str, Any]]]:634            for arg in rich_args:635                if _safe_isinstance(arg, tuple):636                    if len(arg) == 3:637                        key, child, default = arg638                        if default == child:639                            continue640                        yield key, child641                    elif len(arg) == 2:642                        key, child = arg643                        yield key, child644                    elif len(arg) == 1:645                        yield arg[0]646                else:647                    yield arg648 649        try:650            fake_attributes = hasattr(651                obj, "awehoi234_wdfjwljet234_234wdfoijsdfmmnxpi492"652            )653        except Exception:654            fake_attributes = False655 656        rich_repr_result: Optional[RichReprResult] = None657        if not fake_attributes:658            try:659                if hasattr(obj, "__rich_repr__") and not isclass(obj):660                    rich_repr_result = obj.__rich_repr__()661            except Exception:662                pass663 664        if rich_repr_result is not None:665            push_visited(obj_id)666            angular = getattr(obj.__rich_repr__, "angular", False)667            args = list(iter_rich_args(rich_repr_result))668            class_name = obj.__class__.__name__669 670            if args:671                children = []672                append = children.append673 674                if reached_max_depth:675                    if angular:676                        node = Node(value_repr=f"<{class_name}...>")677                    else:678                        node = Node(value_repr=f"{class_name}(...)")679                else:680                    if angular:681                        node = Node(682                            open_brace=f"<{class_name} ",683                            close_brace=">",684                            children=children,685                            last=root,686                            separator=" ",687                        )688                    else:689                        node = Node(690                            open_brace=f"{class_name}(",691                            close_brace=")",692                            children=children,693                            last=root,694                        )695                    for last, arg in loop_last(args):696                        if _safe_isinstance(arg, tuple):697                            key, child = arg698                            child_node = _traverse(child, depth=depth + 1)699                            child_node.last = last700                            child_node.key_repr = key701                            child_node.key_separator = "="702                            append(child_node)703                        else:704                            child_node = _traverse(arg, depth=depth + 1)705                            child_node.last = last706                            append(child_node)707            else:708                node = Node(709                    value_repr=f"<{class_name}>" if angular else f"{class_name}()",710                    children=[],711                    last=root,712                )713            pop_visited(obj_id)714        elif _is_attr_object(obj) and not fake_attributes:715            push_visited(obj_id)716            children = []717            append = children.append718 719            attr_fields = _get_attr_fields(obj)720            if attr_fields:721                if reached_max_depth:722                    node = Node(value_repr=f"{obj.__class__.__name__}(...)")723                else:724                    node = Node(725                        open_brace=f"{obj.__class__.__name__}(",726                        close_brace=")",727                        children=children,728                        last=root,729                    )730 731                    def iter_attrs() -> (732                        Iterable[Tuple[str, Any, Optional[Callable[[Any], str]]]]733                    ):734                        """Iterate over attr fields and values."""735                        for attr in attr_fields:736                            if attr.repr:737                                try:738                                    value = getattr(obj, attr.name)739                                except Exception as error:740                                    # Can happen, albeit rarely741                                    yield (attr.name, error, None)742                                else:743                                    yield (744                                        attr.name,745                                        value,746                                        attr.repr if callable(attr.repr) else None,747                                    )748 749                    for last, (name, value, repr_callable) in loop_last(iter_attrs()):750                        if repr_callable:751                            child_node = Node(value_repr=str(repr_callable(value)))752                        else:753                            child_node = _traverse(value, depth=depth + 1)754                        child_node.last = last755                        child_node.key_repr = name756                        child_node.key_separator = "="757                        append(child_node)758            else:759                node = Node(760                    value_repr=f"{obj.__class__.__name__}()", children=[], last=root761                )762            pop_visited(obj_id)763        elif (764            is_dataclass(obj)765            and not _safe_isinstance(obj, type)766            and not fake_attributes767            and _is_dataclass_repr(obj)768        ):769            push_visited(obj_id)770            children = []771            append = children.append772            if reached_max_depth:773                node = Node(value_repr=f"{obj.__class__.__name__}(...)")774            else:775                node = Node(776                    open_brace=f"{obj.__class__.__name__}(",777                    close_brace=")",778                    children=children,779                    last=root,780                    empty=f"{obj.__class__.__name__}()",781                )782 783                for last, field in loop_last(784                    field785                    for field in fields(obj)786                    if field.repr and hasattr(obj, field.name)787                ):788                    child_node = _traverse(getattr(obj, field.name), depth=depth + 1)789                    child_node.key_repr = field.name790                    child_node.last = last791                    child_node.key_separator = "="792                    append(child_node)793 794            pop_visited(obj_id)795        elif _is_namedtuple(obj) and _has_default_namedtuple_repr(obj):796            push_visited(obj_id)797            class_name = obj.__class__.__name__798            if reached_max_depth:799                # If we've reached the max depth, we still show the class name, but not its contents800                node = Node(801                    value_repr=f"{class_name}(...)",802                )803            else:804                children = []805                append = children.append806                node = Node(807                    open_brace=f"{class_name}(",808                    close_brace=")",809                    children=children,810                    empty=f"{class_name}()",811                )812                for last, (key, value) in loop_last(obj._asdict().items()):813                    child_node = _traverse(value, depth=depth + 1)814                    child_node.key_repr = key815                    child_node.last = last816                    child_node.key_separator = "="817                    append(child_node)818            pop_visited(obj_id)819        elif _safe_isinstance(obj, _CONTAINERS):820            for container_type in _CONTAINERS:821                if _safe_isinstance(obj, container_type):822                    obj_type = container_type823                    break824 825            push_visited(obj_id)826 827            open_brace, close_brace, empty = _BRACES[obj_type](obj)828 829            if reached_max_depth:830                node = Node(value_repr=f"{open_brace}...{close_brace}")831            elif obj_type.__repr__ != type(obj).__repr__:832                node = Node(value_repr=to_repr(obj), last=root)833            elif obj:834                children = []835                node = Node(836                    open_brace=open_brace,837                    close_brace=close_brace,838                    children=children,839                    last=root,840                )841                append = children.append842                num_items = len(obj)843                last_item_index = num_items - 1844 845                if _safe_isinstance(obj, _MAPPING_CONTAINERS):846                    iter_items = iter(obj.items())847                    if max_length is not None:848                        iter_items = islice(iter_items, max_length)849                    for index, (key, child) in enumerate(iter_items):850                        child_node = _traverse(child, depth=depth + 1)851                        child_node.key_repr = to_repr(key)852                        child_node.last = index == last_item_index853                        append(child_node)854                else:855                    iter_values = iter(obj)856                    if max_length is not None:857                        iter_values = islice(iter_values, max_length)858                    for index, child in enumerate(iter_values):859                        child_node = _traverse(child, depth=depth + 1)860                        child_node.last = index == last_item_index861                        append(child_node)862                if max_length is not None and num_items > max_length:863                    append(Node(value_repr=f"... +{num_items - max_length}", last=True))864            else:865                node = Node(empty=empty, children=[], last=root)866 867            pop_visited(obj_id)868        else:869            node = Node(value_repr=to_repr(obj), last=root)870        node.is_tuple = type(obj) == tuple871        node.is_namedtuple = _is_namedtuple(obj)872        return node873 874    node = _traverse(_object, root=True)875    return node876 877 878def pretty_repr(879    _object: Any,880    *,881    max_width: int = 80,882    indent_size: int = 4,883    max_length: Optional[int] = None,884    max_string: Optional[int] = None,885    max_depth: Optional[int] = None,886    expand_all: bool = False,887) -> str:888    """Prettify repr string by expanding on to new lines to fit within a given width.889 890    Args:891        _object (Any): Object to repr.892        max_width (int, optional): Desired maximum width of repr string. Defaults to 80.893        indent_size (int, optional): Number of spaces to indent. Defaults to 4.894        max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.895            Defaults to None.896        max_string (int, optional): Maximum length of string before truncating, or None to disable truncating.897            Defaults to None.898        max_depth (int, optional): Maximum depth of nested data structure, or None for no depth.899            Defaults to None.900        expand_all (bool, optional): Expand all containers regardless of available width. Defaults to False.901 902    Returns:903        str: A possibly multi-line representation of the object.904    """905 906    if _safe_isinstance(_object, Node):907        node = _object908    else:909        node = traverse(910            _object, max_length=max_length, max_string=max_string, max_depth=max_depth911        )912    repr_str: str = node.render(913        max_width=max_width, indent_size=indent_size, expand_all=expand_all914    )915    return repr_str916 917 918def pprint(919    _object: Any,920    *,921    console: Optional["Console"] = None,922    indent_guides: bool = True,923    max_length: Optional[int] = None,924    max_string: Optional[int] = None,925    max_depth: Optional[int] = None,926    expand_all: bool = False,927) -> None:928    """A convenience function for pretty printing.929 930    Args:931        _object (Any): Object to pretty print.932        console (Console, optional): Console instance, or None to use default. Defaults to None.933        max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.934            Defaults to None.935        max_string (int, optional): Maximum length of strings before truncating, or None to disable. Defaults to None.936        max_depth (int, optional): Maximum depth for nested data structures, or None for unlimited depth. Defaults to None.937        indent_guides (bool, optional): Enable indentation guides. Defaults to True.938        expand_all (bool, optional): Expand all containers. Defaults to False.939    """940    _console = get_console() if console is None else console941    _console.print(942        Pretty(943            _object,944            max_length=max_length,945            max_string=max_string,946            max_depth=max_depth,947            indent_guides=indent_guides,948            expand_all=expand_all,949            overflow="ignore",950        ),951        soft_wrap=True,952    )953 954 955if __name__ == "__main__":  # pragma: no cover956 957    class BrokenRepr:958        def __repr__(self) -> str:959            1 / 0960            return "this will fail"961 962    from typing import NamedTuple963 964    class StockKeepingUnit(NamedTuple):965        name: str966        description: str967        price: float968        category: str969        reviews: List[str]970 971    d = defaultdict(int)972    d["foo"] = 5973    data = {974        "foo": [975            1,976            "Hello World!",977            100.123,978            323.232,979            432324.0,980            {5, 6, 7, (1, 2, 3, 4), 8},981        ],982        "bar": frozenset({1, 2, 3}),983        "defaultdict": defaultdict(984            list, {"crumble": ["apple", "rhubarb", "butter", "sugar", "flour"]}985        ),986        "counter": Counter(987            [988                "apple",989                "orange",990                "pear",991                "kumquat",992                "kumquat",993                "durian" * 100,994            ]995        ),996        "atomic": (False, True, None),997        "namedtuple": StockKeepingUnit(998            "Sparkling British Spring Water",999            "Carbonated spring water",1000            0.9,1001            "water",1002            ["its amazing!", "its terrible!"],1003        ),1004        "Broken": BrokenRepr(),1005    }1006    data["foo"].append(data)  # type: ignore[attr-defined]1007 1008    from pip._vendor.rich import print1009 1010    print(Pretty(data, indent_guides=True, max_string=20))1011 1012    class Thing:1013        def __repr__(self) -> str:1014            return "Hello\x1b[38;5;239m World!"1015 1016    print(Pretty(Thing()))1017 
Aluode/PerceptionLabPortable · CoolFace