17db96d56Sopenharmony_ci#  Author:      Fred L. Drake, Jr.
27db96d56Sopenharmony_ci#               fdrake@acm.org
37db96d56Sopenharmony_ci#
47db96d56Sopenharmony_ci#  This is a simple little module I wrote to make life easier.  I didn't
57db96d56Sopenharmony_ci#  see anything quite like it in the library, though I may have overlooked
67db96d56Sopenharmony_ci#  something.  I wrote this when I was trying to read some heavily nested
77db96d56Sopenharmony_ci#  tuples with fairly non-descriptive content.  This is modeled very much
87db96d56Sopenharmony_ci#  after Lisp/Scheme - style pretty-printing of lists.  If you find it
97db96d56Sopenharmony_ci#  useful, thank small children who sleep at night.
107db96d56Sopenharmony_ci
117db96d56Sopenharmony_ci"""Support to pretty-print lists, tuples, & dictionaries recursively.
127db96d56Sopenharmony_ci
137db96d56Sopenharmony_ciVery simple, but useful, especially in debugging data structures.
147db96d56Sopenharmony_ci
157db96d56Sopenharmony_ciClasses
167db96d56Sopenharmony_ci-------
177db96d56Sopenharmony_ci
187db96d56Sopenharmony_ciPrettyPrinter()
197db96d56Sopenharmony_ci    Handle pretty-printing operations onto a stream using a configured
207db96d56Sopenharmony_ci    set of formatting parameters.
217db96d56Sopenharmony_ci
227db96d56Sopenharmony_ciFunctions
237db96d56Sopenharmony_ci---------
247db96d56Sopenharmony_ci
257db96d56Sopenharmony_cipformat()
267db96d56Sopenharmony_ci    Format a Python object into a pretty-printed representation.
277db96d56Sopenharmony_ci
287db96d56Sopenharmony_cipprint()
297db96d56Sopenharmony_ci    Pretty-print a Python object to a stream [default is sys.stdout].
307db96d56Sopenharmony_ci
317db96d56Sopenharmony_cisaferepr()
327db96d56Sopenharmony_ci    Generate a 'standard' repr()-like value, but protect against recursive
337db96d56Sopenharmony_ci    data structures.
347db96d56Sopenharmony_ci
357db96d56Sopenharmony_ci"""
367db96d56Sopenharmony_ci
377db96d56Sopenharmony_ciimport collections as _collections
387db96d56Sopenharmony_ciimport dataclasses as _dataclasses
397db96d56Sopenharmony_ciimport re
407db96d56Sopenharmony_ciimport sys as _sys
417db96d56Sopenharmony_ciimport types as _types
427db96d56Sopenharmony_cifrom io import StringIO as _StringIO
437db96d56Sopenharmony_ci
447db96d56Sopenharmony_ci__all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
457db96d56Sopenharmony_ci           "PrettyPrinter", "pp"]
467db96d56Sopenharmony_ci
477db96d56Sopenharmony_ci
487db96d56Sopenharmony_cidef pprint(object, stream=None, indent=1, width=80, depth=None, *,
497db96d56Sopenharmony_ci           compact=False, sort_dicts=True, underscore_numbers=False):
507db96d56Sopenharmony_ci    """Pretty-print a Python object to a stream [default is sys.stdout]."""
517db96d56Sopenharmony_ci    printer = PrettyPrinter(
527db96d56Sopenharmony_ci        stream=stream, indent=indent, width=width, depth=depth,
537db96d56Sopenharmony_ci        compact=compact, sort_dicts=sort_dicts,
547db96d56Sopenharmony_ci        underscore_numbers=underscore_numbers)
557db96d56Sopenharmony_ci    printer.pprint(object)
567db96d56Sopenharmony_ci
577db96d56Sopenharmony_cidef pformat(object, indent=1, width=80, depth=None, *,
587db96d56Sopenharmony_ci            compact=False, sort_dicts=True, underscore_numbers=False):
597db96d56Sopenharmony_ci    """Format a Python object into a pretty-printed representation."""
607db96d56Sopenharmony_ci    return PrettyPrinter(indent=indent, width=width, depth=depth,
617db96d56Sopenharmony_ci                         compact=compact, sort_dicts=sort_dicts,
627db96d56Sopenharmony_ci                         underscore_numbers=underscore_numbers).pformat(object)
637db96d56Sopenharmony_ci
647db96d56Sopenharmony_cidef pp(object, *args, sort_dicts=False, **kwargs):
657db96d56Sopenharmony_ci    """Pretty-print a Python object"""
667db96d56Sopenharmony_ci    pprint(object, *args, sort_dicts=sort_dicts, **kwargs)
677db96d56Sopenharmony_ci
687db96d56Sopenharmony_cidef saferepr(object):
697db96d56Sopenharmony_ci    """Version of repr() which can handle recursive data structures."""
707db96d56Sopenharmony_ci    return PrettyPrinter()._safe_repr(object, {}, None, 0)[0]
717db96d56Sopenharmony_ci
727db96d56Sopenharmony_cidef isreadable(object):
737db96d56Sopenharmony_ci    """Determine if saferepr(object) is readable by eval()."""
747db96d56Sopenharmony_ci    return PrettyPrinter()._safe_repr(object, {}, None, 0)[1]
757db96d56Sopenharmony_ci
767db96d56Sopenharmony_cidef isrecursive(object):
777db96d56Sopenharmony_ci    """Determine if object requires a recursive representation."""
787db96d56Sopenharmony_ci    return PrettyPrinter()._safe_repr(object, {}, None, 0)[2]
797db96d56Sopenharmony_ci
807db96d56Sopenharmony_ciclass _safe_key:
817db96d56Sopenharmony_ci    """Helper function for key functions when sorting unorderable objects.
827db96d56Sopenharmony_ci
837db96d56Sopenharmony_ci    The wrapped-object will fallback to a Py2.x style comparison for
847db96d56Sopenharmony_ci    unorderable types (sorting first comparing the type name and then by
857db96d56Sopenharmony_ci    the obj ids).  Does not work recursively, so dict.items() must have
867db96d56Sopenharmony_ci    _safe_key applied to both the key and the value.
877db96d56Sopenharmony_ci
887db96d56Sopenharmony_ci    """
897db96d56Sopenharmony_ci
907db96d56Sopenharmony_ci    __slots__ = ['obj']
917db96d56Sopenharmony_ci
927db96d56Sopenharmony_ci    def __init__(self, obj):
937db96d56Sopenharmony_ci        self.obj = obj
947db96d56Sopenharmony_ci
957db96d56Sopenharmony_ci    def __lt__(self, other):
967db96d56Sopenharmony_ci        try:
977db96d56Sopenharmony_ci            return self.obj < other.obj
987db96d56Sopenharmony_ci        except TypeError:
997db96d56Sopenharmony_ci            return ((str(type(self.obj)), id(self.obj)) < \
1007db96d56Sopenharmony_ci                    (str(type(other.obj)), id(other.obj)))
1017db96d56Sopenharmony_ci
1027db96d56Sopenharmony_cidef _safe_tuple(t):
1037db96d56Sopenharmony_ci    "Helper function for comparing 2-tuples"
1047db96d56Sopenharmony_ci    return _safe_key(t[0]), _safe_key(t[1])
1057db96d56Sopenharmony_ci
1067db96d56Sopenharmony_ciclass PrettyPrinter:
1077db96d56Sopenharmony_ci    def __init__(self, indent=1, width=80, depth=None, stream=None, *,
1087db96d56Sopenharmony_ci                 compact=False, sort_dicts=True, underscore_numbers=False):
1097db96d56Sopenharmony_ci        """Handle pretty printing operations onto a stream using a set of
1107db96d56Sopenharmony_ci        configured parameters.
1117db96d56Sopenharmony_ci
1127db96d56Sopenharmony_ci        indent
1137db96d56Sopenharmony_ci            Number of spaces to indent for each level of nesting.
1147db96d56Sopenharmony_ci
1157db96d56Sopenharmony_ci        width
1167db96d56Sopenharmony_ci            Attempted maximum number of columns in the output.
1177db96d56Sopenharmony_ci
1187db96d56Sopenharmony_ci        depth
1197db96d56Sopenharmony_ci            The maximum depth to print out nested structures.
1207db96d56Sopenharmony_ci
1217db96d56Sopenharmony_ci        stream
1227db96d56Sopenharmony_ci            The desired output stream.  If omitted (or false), the standard
1237db96d56Sopenharmony_ci            output stream available at construction will be used.
1247db96d56Sopenharmony_ci
1257db96d56Sopenharmony_ci        compact
1267db96d56Sopenharmony_ci            If true, several items will be combined in one line.
1277db96d56Sopenharmony_ci
1287db96d56Sopenharmony_ci        sort_dicts
1297db96d56Sopenharmony_ci            If true, dict keys are sorted.
1307db96d56Sopenharmony_ci
1317db96d56Sopenharmony_ci        """
1327db96d56Sopenharmony_ci        indent = int(indent)
1337db96d56Sopenharmony_ci        width = int(width)
1347db96d56Sopenharmony_ci        if indent < 0:
1357db96d56Sopenharmony_ci            raise ValueError('indent must be >= 0')
1367db96d56Sopenharmony_ci        if depth is not None and depth <= 0:
1377db96d56Sopenharmony_ci            raise ValueError('depth must be > 0')
1387db96d56Sopenharmony_ci        if not width:
1397db96d56Sopenharmony_ci            raise ValueError('width must be != 0')
1407db96d56Sopenharmony_ci        self._depth = depth
1417db96d56Sopenharmony_ci        self._indent_per_level = indent
1427db96d56Sopenharmony_ci        self._width = width
1437db96d56Sopenharmony_ci        if stream is not None:
1447db96d56Sopenharmony_ci            self._stream = stream
1457db96d56Sopenharmony_ci        else:
1467db96d56Sopenharmony_ci            self._stream = _sys.stdout
1477db96d56Sopenharmony_ci        self._compact = bool(compact)
1487db96d56Sopenharmony_ci        self._sort_dicts = sort_dicts
1497db96d56Sopenharmony_ci        self._underscore_numbers = underscore_numbers
1507db96d56Sopenharmony_ci
1517db96d56Sopenharmony_ci    def pprint(self, object):
1527db96d56Sopenharmony_ci        if self._stream is not None:
1537db96d56Sopenharmony_ci            self._format(object, self._stream, 0, 0, {}, 0)
1547db96d56Sopenharmony_ci            self._stream.write("\n")
1557db96d56Sopenharmony_ci
1567db96d56Sopenharmony_ci    def pformat(self, object):
1577db96d56Sopenharmony_ci        sio = _StringIO()
1587db96d56Sopenharmony_ci        self._format(object, sio, 0, 0, {}, 0)
1597db96d56Sopenharmony_ci        return sio.getvalue()
1607db96d56Sopenharmony_ci
1617db96d56Sopenharmony_ci    def isrecursive(self, object):
1627db96d56Sopenharmony_ci        return self.format(object, {}, 0, 0)[2]
1637db96d56Sopenharmony_ci
1647db96d56Sopenharmony_ci    def isreadable(self, object):
1657db96d56Sopenharmony_ci        s, readable, recursive = self.format(object, {}, 0, 0)
1667db96d56Sopenharmony_ci        return readable and not recursive
1677db96d56Sopenharmony_ci
1687db96d56Sopenharmony_ci    def _format(self, object, stream, indent, allowance, context, level):
1697db96d56Sopenharmony_ci        objid = id(object)
1707db96d56Sopenharmony_ci        if objid in context:
1717db96d56Sopenharmony_ci            stream.write(_recursion(object))
1727db96d56Sopenharmony_ci            self._recursive = True
1737db96d56Sopenharmony_ci            self._readable = False
1747db96d56Sopenharmony_ci            return
1757db96d56Sopenharmony_ci        rep = self._repr(object, context, level)
1767db96d56Sopenharmony_ci        max_width = self._width - indent - allowance
1777db96d56Sopenharmony_ci        if len(rep) > max_width:
1787db96d56Sopenharmony_ci            p = self._dispatch.get(type(object).__repr__, None)
1797db96d56Sopenharmony_ci            if p is not None:
1807db96d56Sopenharmony_ci                context[objid] = 1
1817db96d56Sopenharmony_ci                p(self, object, stream, indent, allowance, context, level + 1)
1827db96d56Sopenharmony_ci                del context[objid]
1837db96d56Sopenharmony_ci                return
1847db96d56Sopenharmony_ci            elif (_dataclasses.is_dataclass(object) and
1857db96d56Sopenharmony_ci                  not isinstance(object, type) and
1867db96d56Sopenharmony_ci                  object.__dataclass_params__.repr and
1877db96d56Sopenharmony_ci                  # Check dataclass has generated repr method.
1887db96d56Sopenharmony_ci                  hasattr(object.__repr__, "__wrapped__") and
1897db96d56Sopenharmony_ci                  "__create_fn__" in object.__repr__.__wrapped__.__qualname__):
1907db96d56Sopenharmony_ci                context[objid] = 1
1917db96d56Sopenharmony_ci                self._pprint_dataclass(object, stream, indent, allowance, context, level + 1)
1927db96d56Sopenharmony_ci                del context[objid]
1937db96d56Sopenharmony_ci                return
1947db96d56Sopenharmony_ci        stream.write(rep)
1957db96d56Sopenharmony_ci
1967db96d56Sopenharmony_ci    def _pprint_dataclass(self, object, stream, indent, allowance, context, level):
1977db96d56Sopenharmony_ci        cls_name = object.__class__.__name__
1987db96d56Sopenharmony_ci        indent += len(cls_name) + 1
1997db96d56Sopenharmony_ci        items = [(f.name, getattr(object, f.name)) for f in _dataclasses.fields(object) if f.repr]
2007db96d56Sopenharmony_ci        stream.write(cls_name + '(')
2017db96d56Sopenharmony_ci        self._format_namespace_items(items, stream, indent, allowance, context, level)
2027db96d56Sopenharmony_ci        stream.write(')')
2037db96d56Sopenharmony_ci
2047db96d56Sopenharmony_ci    _dispatch = {}
2057db96d56Sopenharmony_ci
2067db96d56Sopenharmony_ci    def _pprint_dict(self, object, stream, indent, allowance, context, level):
2077db96d56Sopenharmony_ci        write = stream.write
2087db96d56Sopenharmony_ci        write('{')
2097db96d56Sopenharmony_ci        if self._indent_per_level > 1:
2107db96d56Sopenharmony_ci            write((self._indent_per_level - 1) * ' ')
2117db96d56Sopenharmony_ci        length = len(object)
2127db96d56Sopenharmony_ci        if length:
2137db96d56Sopenharmony_ci            if self._sort_dicts:
2147db96d56Sopenharmony_ci                items = sorted(object.items(), key=_safe_tuple)
2157db96d56Sopenharmony_ci            else:
2167db96d56Sopenharmony_ci                items = object.items()
2177db96d56Sopenharmony_ci            self._format_dict_items(items, stream, indent, allowance + 1,
2187db96d56Sopenharmony_ci                                    context, level)
2197db96d56Sopenharmony_ci        write('}')
2207db96d56Sopenharmony_ci
2217db96d56Sopenharmony_ci    _dispatch[dict.__repr__] = _pprint_dict
2227db96d56Sopenharmony_ci
2237db96d56Sopenharmony_ci    def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level):
2247db96d56Sopenharmony_ci        if not len(object):
2257db96d56Sopenharmony_ci            stream.write(repr(object))
2267db96d56Sopenharmony_ci            return
2277db96d56Sopenharmony_ci        cls = object.__class__
2287db96d56Sopenharmony_ci        stream.write(cls.__name__ + '(')
2297db96d56Sopenharmony_ci        self._format(list(object.items()), stream,
2307db96d56Sopenharmony_ci                     indent + len(cls.__name__) + 1, allowance + 1,
2317db96d56Sopenharmony_ci                     context, level)
2327db96d56Sopenharmony_ci        stream.write(')')
2337db96d56Sopenharmony_ci
2347db96d56Sopenharmony_ci    _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict
2357db96d56Sopenharmony_ci
2367db96d56Sopenharmony_ci    def _pprint_list(self, object, stream, indent, allowance, context, level):
2377db96d56Sopenharmony_ci        stream.write('[')
2387db96d56Sopenharmony_ci        self._format_items(object, stream, indent, allowance + 1,
2397db96d56Sopenharmony_ci                           context, level)
2407db96d56Sopenharmony_ci        stream.write(']')
2417db96d56Sopenharmony_ci
2427db96d56Sopenharmony_ci    _dispatch[list.__repr__] = _pprint_list
2437db96d56Sopenharmony_ci
2447db96d56Sopenharmony_ci    def _pprint_tuple(self, object, stream, indent, allowance, context, level):
2457db96d56Sopenharmony_ci        stream.write('(')
2467db96d56Sopenharmony_ci        endchar = ',)' if len(object) == 1 else ')'
2477db96d56Sopenharmony_ci        self._format_items(object, stream, indent, allowance + len(endchar),
2487db96d56Sopenharmony_ci                           context, level)
2497db96d56Sopenharmony_ci        stream.write(endchar)
2507db96d56Sopenharmony_ci
2517db96d56Sopenharmony_ci    _dispatch[tuple.__repr__] = _pprint_tuple
2527db96d56Sopenharmony_ci
2537db96d56Sopenharmony_ci    def _pprint_set(self, object, stream, indent, allowance, context, level):
2547db96d56Sopenharmony_ci        if not len(object):
2557db96d56Sopenharmony_ci            stream.write(repr(object))
2567db96d56Sopenharmony_ci            return
2577db96d56Sopenharmony_ci        typ = object.__class__
2587db96d56Sopenharmony_ci        if typ is set:
2597db96d56Sopenharmony_ci            stream.write('{')
2607db96d56Sopenharmony_ci            endchar = '}'
2617db96d56Sopenharmony_ci        else:
2627db96d56Sopenharmony_ci            stream.write(typ.__name__ + '({')
2637db96d56Sopenharmony_ci            endchar = '})'
2647db96d56Sopenharmony_ci            indent += len(typ.__name__) + 1
2657db96d56Sopenharmony_ci        object = sorted(object, key=_safe_key)
2667db96d56Sopenharmony_ci        self._format_items(object, stream, indent, allowance + len(endchar),
2677db96d56Sopenharmony_ci                           context, level)
2687db96d56Sopenharmony_ci        stream.write(endchar)
2697db96d56Sopenharmony_ci
2707db96d56Sopenharmony_ci    _dispatch[set.__repr__] = _pprint_set
2717db96d56Sopenharmony_ci    _dispatch[frozenset.__repr__] = _pprint_set
2727db96d56Sopenharmony_ci
2737db96d56Sopenharmony_ci    def _pprint_str(self, object, stream, indent, allowance, context, level):
2747db96d56Sopenharmony_ci        write = stream.write
2757db96d56Sopenharmony_ci        if not len(object):
2767db96d56Sopenharmony_ci            write(repr(object))
2777db96d56Sopenharmony_ci            return
2787db96d56Sopenharmony_ci        chunks = []
2797db96d56Sopenharmony_ci        lines = object.splitlines(True)
2807db96d56Sopenharmony_ci        if level == 1:
2817db96d56Sopenharmony_ci            indent += 1
2827db96d56Sopenharmony_ci            allowance += 1
2837db96d56Sopenharmony_ci        max_width1 = max_width = self._width - indent
2847db96d56Sopenharmony_ci        for i, line in enumerate(lines):
2857db96d56Sopenharmony_ci            rep = repr(line)
2867db96d56Sopenharmony_ci            if i == len(lines) - 1:
2877db96d56Sopenharmony_ci                max_width1 -= allowance
2887db96d56Sopenharmony_ci            if len(rep) <= max_width1:
2897db96d56Sopenharmony_ci                chunks.append(rep)
2907db96d56Sopenharmony_ci            else:
2917db96d56Sopenharmony_ci                # A list of alternating (non-space, space) strings
2927db96d56Sopenharmony_ci                parts = re.findall(r'\S*\s*', line)
2937db96d56Sopenharmony_ci                assert parts
2947db96d56Sopenharmony_ci                assert not parts[-1]
2957db96d56Sopenharmony_ci                parts.pop()  # drop empty last part
2967db96d56Sopenharmony_ci                max_width2 = max_width
2977db96d56Sopenharmony_ci                current = ''
2987db96d56Sopenharmony_ci                for j, part in enumerate(parts):
2997db96d56Sopenharmony_ci                    candidate = current + part
3007db96d56Sopenharmony_ci                    if j == len(parts) - 1 and i == len(lines) - 1:
3017db96d56Sopenharmony_ci                        max_width2 -= allowance
3027db96d56Sopenharmony_ci                    if len(repr(candidate)) > max_width2:
3037db96d56Sopenharmony_ci                        if current:
3047db96d56Sopenharmony_ci                            chunks.append(repr(current))
3057db96d56Sopenharmony_ci                        current = part
3067db96d56Sopenharmony_ci                    else:
3077db96d56Sopenharmony_ci                        current = candidate
3087db96d56Sopenharmony_ci                if current:
3097db96d56Sopenharmony_ci                    chunks.append(repr(current))
3107db96d56Sopenharmony_ci        if len(chunks) == 1:
3117db96d56Sopenharmony_ci            write(rep)
3127db96d56Sopenharmony_ci            return
3137db96d56Sopenharmony_ci        if level == 1:
3147db96d56Sopenharmony_ci            write('(')
3157db96d56Sopenharmony_ci        for i, rep in enumerate(chunks):
3167db96d56Sopenharmony_ci            if i > 0:
3177db96d56Sopenharmony_ci                write('\n' + ' '*indent)
3187db96d56Sopenharmony_ci            write(rep)
3197db96d56Sopenharmony_ci        if level == 1:
3207db96d56Sopenharmony_ci            write(')')
3217db96d56Sopenharmony_ci
3227db96d56Sopenharmony_ci    _dispatch[str.__repr__] = _pprint_str
3237db96d56Sopenharmony_ci
3247db96d56Sopenharmony_ci    def _pprint_bytes(self, object, stream, indent, allowance, context, level):
3257db96d56Sopenharmony_ci        write = stream.write
3267db96d56Sopenharmony_ci        if len(object) <= 4:
3277db96d56Sopenharmony_ci            write(repr(object))
3287db96d56Sopenharmony_ci            return
3297db96d56Sopenharmony_ci        parens = level == 1
3307db96d56Sopenharmony_ci        if parens:
3317db96d56Sopenharmony_ci            indent += 1
3327db96d56Sopenharmony_ci            allowance += 1
3337db96d56Sopenharmony_ci            write('(')
3347db96d56Sopenharmony_ci        delim = ''
3357db96d56Sopenharmony_ci        for rep in _wrap_bytes_repr(object, self._width - indent, allowance):
3367db96d56Sopenharmony_ci            write(delim)
3377db96d56Sopenharmony_ci            write(rep)
3387db96d56Sopenharmony_ci            if not delim:
3397db96d56Sopenharmony_ci                delim = '\n' + ' '*indent
3407db96d56Sopenharmony_ci        if parens:
3417db96d56Sopenharmony_ci            write(')')
3427db96d56Sopenharmony_ci
3437db96d56Sopenharmony_ci    _dispatch[bytes.__repr__] = _pprint_bytes
3447db96d56Sopenharmony_ci
3457db96d56Sopenharmony_ci    def _pprint_bytearray(self, object, stream, indent, allowance, context, level):
3467db96d56Sopenharmony_ci        write = stream.write
3477db96d56Sopenharmony_ci        write('bytearray(')
3487db96d56Sopenharmony_ci        self._pprint_bytes(bytes(object), stream, indent + 10,
3497db96d56Sopenharmony_ci                           allowance + 1, context, level + 1)
3507db96d56Sopenharmony_ci        write(')')
3517db96d56Sopenharmony_ci
3527db96d56Sopenharmony_ci    _dispatch[bytearray.__repr__] = _pprint_bytearray
3537db96d56Sopenharmony_ci
3547db96d56Sopenharmony_ci    def _pprint_mappingproxy(self, object, stream, indent, allowance, context, level):
3557db96d56Sopenharmony_ci        stream.write('mappingproxy(')
3567db96d56Sopenharmony_ci        self._format(object.copy(), stream, indent + 13, allowance + 1,
3577db96d56Sopenharmony_ci                     context, level)
3587db96d56Sopenharmony_ci        stream.write(')')
3597db96d56Sopenharmony_ci
3607db96d56Sopenharmony_ci    _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy
3617db96d56Sopenharmony_ci
3627db96d56Sopenharmony_ci    def _pprint_simplenamespace(self, object, stream, indent, allowance, context, level):
3637db96d56Sopenharmony_ci        if type(object) is _types.SimpleNamespace:
3647db96d56Sopenharmony_ci            # The SimpleNamespace repr is "namespace" instead of the class
3657db96d56Sopenharmony_ci            # name, so we do the same here. For subclasses; use the class name.
3667db96d56Sopenharmony_ci            cls_name = 'namespace'
3677db96d56Sopenharmony_ci        else:
3687db96d56Sopenharmony_ci            cls_name = object.__class__.__name__
3697db96d56Sopenharmony_ci        indent += len(cls_name) + 1
3707db96d56Sopenharmony_ci        items = object.__dict__.items()
3717db96d56Sopenharmony_ci        stream.write(cls_name + '(')
3727db96d56Sopenharmony_ci        self._format_namespace_items(items, stream, indent, allowance, context, level)
3737db96d56Sopenharmony_ci        stream.write(')')
3747db96d56Sopenharmony_ci
3757db96d56Sopenharmony_ci    _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace
3767db96d56Sopenharmony_ci
3777db96d56Sopenharmony_ci    def _format_dict_items(self, items, stream, indent, allowance, context,
3787db96d56Sopenharmony_ci                           level):
3797db96d56Sopenharmony_ci        write = stream.write
3807db96d56Sopenharmony_ci        indent += self._indent_per_level
3817db96d56Sopenharmony_ci        delimnl = ',\n' + ' ' * indent
3827db96d56Sopenharmony_ci        last_index = len(items) - 1
3837db96d56Sopenharmony_ci        for i, (key, ent) in enumerate(items):
3847db96d56Sopenharmony_ci            last = i == last_index
3857db96d56Sopenharmony_ci            rep = self._repr(key, context, level)
3867db96d56Sopenharmony_ci            write(rep)
3877db96d56Sopenharmony_ci            write(': ')
3887db96d56Sopenharmony_ci            self._format(ent, stream, indent + len(rep) + 2,
3897db96d56Sopenharmony_ci                         allowance if last else 1,
3907db96d56Sopenharmony_ci                         context, level)
3917db96d56Sopenharmony_ci            if not last:
3927db96d56Sopenharmony_ci                write(delimnl)
3937db96d56Sopenharmony_ci
3947db96d56Sopenharmony_ci    def _format_namespace_items(self, items, stream, indent, allowance, context, level):
3957db96d56Sopenharmony_ci        write = stream.write
3967db96d56Sopenharmony_ci        delimnl = ',\n' + ' ' * indent
3977db96d56Sopenharmony_ci        last_index = len(items) - 1
3987db96d56Sopenharmony_ci        for i, (key, ent) in enumerate(items):
3997db96d56Sopenharmony_ci            last = i == last_index
4007db96d56Sopenharmony_ci            write(key)
4017db96d56Sopenharmony_ci            write('=')
4027db96d56Sopenharmony_ci            if id(ent) in context:
4037db96d56Sopenharmony_ci                # Special-case representation of recursion to match standard
4047db96d56Sopenharmony_ci                # recursive dataclass repr.
4057db96d56Sopenharmony_ci                write("...")
4067db96d56Sopenharmony_ci            else:
4077db96d56Sopenharmony_ci                self._format(ent, stream, indent + len(key) + 1,
4087db96d56Sopenharmony_ci                             allowance if last else 1,
4097db96d56Sopenharmony_ci                             context, level)
4107db96d56Sopenharmony_ci            if not last:
4117db96d56Sopenharmony_ci                write(delimnl)
4127db96d56Sopenharmony_ci
4137db96d56Sopenharmony_ci    def _format_items(self, items, stream, indent, allowance, context, level):
4147db96d56Sopenharmony_ci        write = stream.write
4157db96d56Sopenharmony_ci        indent += self._indent_per_level
4167db96d56Sopenharmony_ci        if self._indent_per_level > 1:
4177db96d56Sopenharmony_ci            write((self._indent_per_level - 1) * ' ')
4187db96d56Sopenharmony_ci        delimnl = ',\n' + ' ' * indent
4197db96d56Sopenharmony_ci        delim = ''
4207db96d56Sopenharmony_ci        width = max_width = self._width - indent + 1
4217db96d56Sopenharmony_ci        it = iter(items)
4227db96d56Sopenharmony_ci        try:
4237db96d56Sopenharmony_ci            next_ent = next(it)
4247db96d56Sopenharmony_ci        except StopIteration:
4257db96d56Sopenharmony_ci            return
4267db96d56Sopenharmony_ci        last = False
4277db96d56Sopenharmony_ci        while not last:
4287db96d56Sopenharmony_ci            ent = next_ent
4297db96d56Sopenharmony_ci            try:
4307db96d56Sopenharmony_ci                next_ent = next(it)
4317db96d56Sopenharmony_ci            except StopIteration:
4327db96d56Sopenharmony_ci                last = True
4337db96d56Sopenharmony_ci                max_width -= allowance
4347db96d56Sopenharmony_ci                width -= allowance
4357db96d56Sopenharmony_ci            if self._compact:
4367db96d56Sopenharmony_ci                rep = self._repr(ent, context, level)
4377db96d56Sopenharmony_ci                w = len(rep) + 2
4387db96d56Sopenharmony_ci                if width < w:
4397db96d56Sopenharmony_ci                    width = max_width
4407db96d56Sopenharmony_ci                    if delim:
4417db96d56Sopenharmony_ci                        delim = delimnl
4427db96d56Sopenharmony_ci                if width >= w:
4437db96d56Sopenharmony_ci                    width -= w
4447db96d56Sopenharmony_ci                    write(delim)
4457db96d56Sopenharmony_ci                    delim = ', '
4467db96d56Sopenharmony_ci                    write(rep)
4477db96d56Sopenharmony_ci                    continue
4487db96d56Sopenharmony_ci            write(delim)
4497db96d56Sopenharmony_ci            delim = delimnl
4507db96d56Sopenharmony_ci            self._format(ent, stream, indent,
4517db96d56Sopenharmony_ci                         allowance if last else 1,
4527db96d56Sopenharmony_ci                         context, level)
4537db96d56Sopenharmony_ci
4547db96d56Sopenharmony_ci    def _repr(self, object, context, level):
4557db96d56Sopenharmony_ci        repr, readable, recursive = self.format(object, context.copy(),
4567db96d56Sopenharmony_ci                                                self._depth, level)
4577db96d56Sopenharmony_ci        if not readable:
4587db96d56Sopenharmony_ci            self._readable = False
4597db96d56Sopenharmony_ci        if recursive:
4607db96d56Sopenharmony_ci            self._recursive = True
4617db96d56Sopenharmony_ci        return repr
4627db96d56Sopenharmony_ci
4637db96d56Sopenharmony_ci    def format(self, object, context, maxlevels, level):
4647db96d56Sopenharmony_ci        """Format object for a specific context, returning a string
4657db96d56Sopenharmony_ci        and flags indicating whether the representation is 'readable'
4667db96d56Sopenharmony_ci        and whether the object represents a recursive construct.
4677db96d56Sopenharmony_ci        """
4687db96d56Sopenharmony_ci        return self._safe_repr(object, context, maxlevels, level)
4697db96d56Sopenharmony_ci
4707db96d56Sopenharmony_ci    def _pprint_default_dict(self, object, stream, indent, allowance, context, level):
4717db96d56Sopenharmony_ci        if not len(object):
4727db96d56Sopenharmony_ci            stream.write(repr(object))
4737db96d56Sopenharmony_ci            return
4747db96d56Sopenharmony_ci        rdf = self._repr(object.default_factory, context, level)
4757db96d56Sopenharmony_ci        cls = object.__class__
4767db96d56Sopenharmony_ci        indent += len(cls.__name__) + 1
4777db96d56Sopenharmony_ci        stream.write('%s(%s,\n%s' % (cls.__name__, rdf, ' ' * indent))
4787db96d56Sopenharmony_ci        self._pprint_dict(object, stream, indent, allowance + 1, context, level)
4797db96d56Sopenharmony_ci        stream.write(')')
4807db96d56Sopenharmony_ci
4817db96d56Sopenharmony_ci    _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict
4827db96d56Sopenharmony_ci
4837db96d56Sopenharmony_ci    def _pprint_counter(self, object, stream, indent, allowance, context, level):
4847db96d56Sopenharmony_ci        if not len(object):
4857db96d56Sopenharmony_ci            stream.write(repr(object))
4867db96d56Sopenharmony_ci            return
4877db96d56Sopenharmony_ci        cls = object.__class__
4887db96d56Sopenharmony_ci        stream.write(cls.__name__ + '({')
4897db96d56Sopenharmony_ci        if self._indent_per_level > 1:
4907db96d56Sopenharmony_ci            stream.write((self._indent_per_level - 1) * ' ')
4917db96d56Sopenharmony_ci        items = object.most_common()
4927db96d56Sopenharmony_ci        self._format_dict_items(items, stream,
4937db96d56Sopenharmony_ci                                indent + len(cls.__name__) + 1, allowance + 2,
4947db96d56Sopenharmony_ci                                context, level)
4957db96d56Sopenharmony_ci        stream.write('})')
4967db96d56Sopenharmony_ci
4977db96d56Sopenharmony_ci    _dispatch[_collections.Counter.__repr__] = _pprint_counter
4987db96d56Sopenharmony_ci
4997db96d56Sopenharmony_ci    def _pprint_chain_map(self, object, stream, indent, allowance, context, level):
5007db96d56Sopenharmony_ci        if not len(object.maps):
5017db96d56Sopenharmony_ci            stream.write(repr(object))
5027db96d56Sopenharmony_ci            return
5037db96d56Sopenharmony_ci        cls = object.__class__
5047db96d56Sopenharmony_ci        stream.write(cls.__name__ + '(')
5057db96d56Sopenharmony_ci        indent += len(cls.__name__) + 1
5067db96d56Sopenharmony_ci        for i, m in enumerate(object.maps):
5077db96d56Sopenharmony_ci            if i == len(object.maps) - 1:
5087db96d56Sopenharmony_ci                self._format(m, stream, indent, allowance + 1, context, level)
5097db96d56Sopenharmony_ci                stream.write(')')
5107db96d56Sopenharmony_ci            else:
5117db96d56Sopenharmony_ci                self._format(m, stream, indent, 1, context, level)
5127db96d56Sopenharmony_ci                stream.write(',\n' + ' ' * indent)
5137db96d56Sopenharmony_ci
5147db96d56Sopenharmony_ci    _dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map
5157db96d56Sopenharmony_ci
5167db96d56Sopenharmony_ci    def _pprint_deque(self, object, stream, indent, allowance, context, level):
5177db96d56Sopenharmony_ci        if not len(object):
5187db96d56Sopenharmony_ci            stream.write(repr(object))
5197db96d56Sopenharmony_ci            return
5207db96d56Sopenharmony_ci        cls = object.__class__
5217db96d56Sopenharmony_ci        stream.write(cls.__name__ + '(')
5227db96d56Sopenharmony_ci        indent += len(cls.__name__) + 1
5237db96d56Sopenharmony_ci        stream.write('[')
5247db96d56Sopenharmony_ci        if object.maxlen is None:
5257db96d56Sopenharmony_ci            self._format_items(object, stream, indent, allowance + 2,
5267db96d56Sopenharmony_ci                               context, level)
5277db96d56Sopenharmony_ci            stream.write('])')
5287db96d56Sopenharmony_ci        else:
5297db96d56Sopenharmony_ci            self._format_items(object, stream, indent, 2,
5307db96d56Sopenharmony_ci                               context, level)
5317db96d56Sopenharmony_ci            rml = self._repr(object.maxlen, context, level)
5327db96d56Sopenharmony_ci            stream.write('],\n%smaxlen=%s)' % (' ' * indent, rml))
5337db96d56Sopenharmony_ci
5347db96d56Sopenharmony_ci    _dispatch[_collections.deque.__repr__] = _pprint_deque
5357db96d56Sopenharmony_ci
5367db96d56Sopenharmony_ci    def _pprint_user_dict(self, object, stream, indent, allowance, context, level):
5377db96d56Sopenharmony_ci        self._format(object.data, stream, indent, allowance, context, level - 1)
5387db96d56Sopenharmony_ci
5397db96d56Sopenharmony_ci    _dispatch[_collections.UserDict.__repr__] = _pprint_user_dict
5407db96d56Sopenharmony_ci
5417db96d56Sopenharmony_ci    def _pprint_user_list(self, object, stream, indent, allowance, context, level):
5427db96d56Sopenharmony_ci        self._format(object.data, stream, indent, allowance, context, level - 1)
5437db96d56Sopenharmony_ci
5447db96d56Sopenharmony_ci    _dispatch[_collections.UserList.__repr__] = _pprint_user_list
5457db96d56Sopenharmony_ci
5467db96d56Sopenharmony_ci    def _pprint_user_string(self, object, stream, indent, allowance, context, level):
5477db96d56Sopenharmony_ci        self._format(object.data, stream, indent, allowance, context, level - 1)
5487db96d56Sopenharmony_ci
5497db96d56Sopenharmony_ci    _dispatch[_collections.UserString.__repr__] = _pprint_user_string
5507db96d56Sopenharmony_ci
5517db96d56Sopenharmony_ci    def _safe_repr(self, object, context, maxlevels, level):
5527db96d56Sopenharmony_ci        # Return triple (repr_string, isreadable, isrecursive).
5537db96d56Sopenharmony_ci        typ = type(object)
5547db96d56Sopenharmony_ci        if typ in _builtin_scalars:
5557db96d56Sopenharmony_ci            return repr(object), True, False
5567db96d56Sopenharmony_ci
5577db96d56Sopenharmony_ci        r = getattr(typ, "__repr__", None)
5587db96d56Sopenharmony_ci
5597db96d56Sopenharmony_ci        if issubclass(typ, int) and r is int.__repr__:
5607db96d56Sopenharmony_ci            if self._underscore_numbers:
5617db96d56Sopenharmony_ci                return f"{object:_d}", True, False
5627db96d56Sopenharmony_ci            else:
5637db96d56Sopenharmony_ci                return repr(object), True, False
5647db96d56Sopenharmony_ci
5657db96d56Sopenharmony_ci        if issubclass(typ, dict) and r is dict.__repr__:
5667db96d56Sopenharmony_ci            if not object:
5677db96d56Sopenharmony_ci                return "{}", True, False
5687db96d56Sopenharmony_ci            objid = id(object)
5697db96d56Sopenharmony_ci            if maxlevels and level >= maxlevels:
5707db96d56Sopenharmony_ci                return "{...}", False, objid in context
5717db96d56Sopenharmony_ci            if objid in context:
5727db96d56Sopenharmony_ci                return _recursion(object), False, True
5737db96d56Sopenharmony_ci            context[objid] = 1
5747db96d56Sopenharmony_ci            readable = True
5757db96d56Sopenharmony_ci            recursive = False
5767db96d56Sopenharmony_ci            components = []
5777db96d56Sopenharmony_ci            append = components.append
5787db96d56Sopenharmony_ci            level += 1
5797db96d56Sopenharmony_ci            if self._sort_dicts:
5807db96d56Sopenharmony_ci                items = sorted(object.items(), key=_safe_tuple)
5817db96d56Sopenharmony_ci            else:
5827db96d56Sopenharmony_ci                items = object.items()
5837db96d56Sopenharmony_ci            for k, v in items:
5847db96d56Sopenharmony_ci                krepr, kreadable, krecur = self.format(
5857db96d56Sopenharmony_ci                    k, context, maxlevels, level)
5867db96d56Sopenharmony_ci                vrepr, vreadable, vrecur = self.format(
5877db96d56Sopenharmony_ci                    v, context, maxlevels, level)
5887db96d56Sopenharmony_ci                append("%s: %s" % (krepr, vrepr))
5897db96d56Sopenharmony_ci                readable = readable and kreadable and vreadable
5907db96d56Sopenharmony_ci                if krecur or vrecur:
5917db96d56Sopenharmony_ci                    recursive = True
5927db96d56Sopenharmony_ci            del context[objid]
5937db96d56Sopenharmony_ci            return "{%s}" % ", ".join(components), readable, recursive
5947db96d56Sopenharmony_ci
5957db96d56Sopenharmony_ci        if (issubclass(typ, list) and r is list.__repr__) or \
5967db96d56Sopenharmony_ci           (issubclass(typ, tuple) and r is tuple.__repr__):
5977db96d56Sopenharmony_ci            if issubclass(typ, list):
5987db96d56Sopenharmony_ci                if not object:
5997db96d56Sopenharmony_ci                    return "[]", True, False
6007db96d56Sopenharmony_ci                format = "[%s]"
6017db96d56Sopenharmony_ci            elif len(object) == 1:
6027db96d56Sopenharmony_ci                format = "(%s,)"
6037db96d56Sopenharmony_ci            else:
6047db96d56Sopenharmony_ci                if not object:
6057db96d56Sopenharmony_ci                    return "()", True, False
6067db96d56Sopenharmony_ci                format = "(%s)"
6077db96d56Sopenharmony_ci            objid = id(object)
6087db96d56Sopenharmony_ci            if maxlevels and level >= maxlevels:
6097db96d56Sopenharmony_ci                return format % "...", False, objid in context
6107db96d56Sopenharmony_ci            if objid in context:
6117db96d56Sopenharmony_ci                return _recursion(object), False, True
6127db96d56Sopenharmony_ci            context[objid] = 1
6137db96d56Sopenharmony_ci            readable = True
6147db96d56Sopenharmony_ci            recursive = False
6157db96d56Sopenharmony_ci            components = []
6167db96d56Sopenharmony_ci            append = components.append
6177db96d56Sopenharmony_ci            level += 1
6187db96d56Sopenharmony_ci            for o in object:
6197db96d56Sopenharmony_ci                orepr, oreadable, orecur = self.format(
6207db96d56Sopenharmony_ci                    o, context, maxlevels, level)
6217db96d56Sopenharmony_ci                append(orepr)
6227db96d56Sopenharmony_ci                if not oreadable:
6237db96d56Sopenharmony_ci                    readable = False
6247db96d56Sopenharmony_ci                if orecur:
6257db96d56Sopenharmony_ci                    recursive = True
6267db96d56Sopenharmony_ci            del context[objid]
6277db96d56Sopenharmony_ci            return format % ", ".join(components), readable, recursive
6287db96d56Sopenharmony_ci
6297db96d56Sopenharmony_ci        rep = repr(object)
6307db96d56Sopenharmony_ci        return rep, (rep and not rep.startswith('<')), False
6317db96d56Sopenharmony_ci
6327db96d56Sopenharmony_ci_builtin_scalars = frozenset({str, bytes, bytearray, float, complex,
6337db96d56Sopenharmony_ci                              bool, type(None)})
6347db96d56Sopenharmony_ci
6357db96d56Sopenharmony_cidef _recursion(object):
6367db96d56Sopenharmony_ci    return ("<Recursion on %s with id=%s>"
6377db96d56Sopenharmony_ci            % (type(object).__name__, id(object)))
6387db96d56Sopenharmony_ci
6397db96d56Sopenharmony_ci
6407db96d56Sopenharmony_cidef _perfcheck(object=None):
6417db96d56Sopenharmony_ci    import time
6427db96d56Sopenharmony_ci    if object is None:
6437db96d56Sopenharmony_ci        object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
6447db96d56Sopenharmony_ci    p = PrettyPrinter()
6457db96d56Sopenharmony_ci    t1 = time.perf_counter()
6467db96d56Sopenharmony_ci    p._safe_repr(object, {}, None, 0, True)
6477db96d56Sopenharmony_ci    t2 = time.perf_counter()
6487db96d56Sopenharmony_ci    p.pformat(object)
6497db96d56Sopenharmony_ci    t3 = time.perf_counter()
6507db96d56Sopenharmony_ci    print("_safe_repr:", t2 - t1)
6517db96d56Sopenharmony_ci    print("pformat:", t3 - t2)
6527db96d56Sopenharmony_ci
6537db96d56Sopenharmony_cidef _wrap_bytes_repr(object, width, allowance):
6547db96d56Sopenharmony_ci    current = b''
6557db96d56Sopenharmony_ci    last = len(object) // 4 * 4
6567db96d56Sopenharmony_ci    for i in range(0, len(object), 4):
6577db96d56Sopenharmony_ci        part = object[i: i+4]
6587db96d56Sopenharmony_ci        candidate = current + part
6597db96d56Sopenharmony_ci        if i == last:
6607db96d56Sopenharmony_ci            width -= allowance
6617db96d56Sopenharmony_ci        if len(repr(candidate)) > width:
6627db96d56Sopenharmony_ci            if current:
6637db96d56Sopenharmony_ci                yield repr(current)
6647db96d56Sopenharmony_ci            current = part
6657db96d56Sopenharmony_ci        else:
6667db96d56Sopenharmony_ci            current = candidate
6677db96d56Sopenharmony_ci    if current:
6687db96d56Sopenharmony_ci        yield repr(current)
6697db96d56Sopenharmony_ci
6707db96d56Sopenharmony_ciif __name__ == "__main__":
6717db96d56Sopenharmony_ci    _perfcheck()
672