(root)/
Python-3.11.7/
Lib/
pprint.py
       1  #  Author:      Fred L. Drake, Jr.
       2  #               fdrake@acm.org
       3  #
       4  #  This is a simple little module I wrote to make life easier.  I didn't
       5  #  see anything quite like it in the library, though I may have overlooked
       6  #  something.  I wrote this when I was trying to read some heavily nested
       7  #  tuples with fairly non-descriptive content.  This is modeled very much
       8  #  after Lisp/Scheme - style pretty-printing of lists.  If you find it
       9  #  useful, thank small children who sleep at night.
      10  
      11  """Support to pretty-print lists, tuples, & dictionaries recursively.
      12  
      13  Very simple, but useful, especially in debugging data structures.
      14  
      15  Classes
      16  -------
      17  
      18  PrettyPrinter()
      19      Handle pretty-printing operations onto a stream using a configured
      20      set of formatting parameters.
      21  
      22  Functions
      23  ---------
      24  
      25  pformat()
      26      Format a Python object into a pretty-printed representation.
      27  
      28  pprint()
      29      Pretty-print a Python object to a stream [default is sys.stdout].
      30  
      31  saferepr()
      32      Generate a 'standard' repr()-like value, but protect against recursive
      33      data structures.
      34  
      35  """
      36  
      37  import collections as _collections
      38  import dataclasses as _dataclasses
      39  import re
      40  import sys as _sys
      41  import types as _types
      42  from io import StringIO as _StringIO
      43  
      44  __all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
      45             "PrettyPrinter", "pp"]
      46  
      47  
      48  def pprint(object, stream=None, indent=1, width=80, depth=None, *,
      49             compact=False, sort_dicts=True, underscore_numbers=False):
      50      """Pretty-print a Python object to a stream [default is sys.stdout]."""
      51      printer = PrettyPrinter(
      52          stream=stream, indent=indent, width=width, depth=depth,
      53          compact=compact, sort_dicts=sort_dicts,
      54          underscore_numbers=underscore_numbers)
      55      printer.pprint(object)
      56  
      57  def pformat(object, indent=1, width=80, depth=None, *,
      58              compact=False, sort_dicts=True, underscore_numbers=False):
      59      """Format a Python object into a pretty-printed representation."""
      60      return PrettyPrinter(indent=indent, width=width, depth=depth,
      61                           compact=compact, sort_dicts=sort_dicts,
      62                           underscore_numbers=underscore_numbers).pformat(object)
      63  
      64  def pp(object, *args, sort_dicts=False, **kwargs):
      65      """Pretty-print a Python object"""
      66      pprint(object, *args, sort_dicts=sort_dicts, **kwargs)
      67  
      68  def saferepr(object):
      69      """Version of repr() which can handle recursive data structures."""
      70      return PrettyPrinter()._safe_repr(object, {}, None, 0)[0]
      71  
      72  def isreadable(object):
      73      """Determine if saferepr(object) is readable by eval()."""
      74      return PrettyPrinter()._safe_repr(object, {}, None, 0)[1]
      75  
      76  def isrecursive(object):
      77      """Determine if object requires a recursive representation."""
      78      return PrettyPrinter()._safe_repr(object, {}, None, 0)[2]
      79  
      80  class ESC[4;38;5;81m_safe_key:
      81      """Helper function for key functions when sorting unorderable objects.
      82  
      83      The wrapped-object will fallback to a Py2.x style comparison for
      84      unorderable types (sorting first comparing the type name and then by
      85      the obj ids).  Does not work recursively, so dict.items() must have
      86      _safe_key applied to both the key and the value.
      87  
      88      """
      89  
      90      __slots__ = ['obj']
      91  
      92      def __init__(self, obj):
      93          self.obj = obj
      94  
      95      def __lt__(self, other):
      96          try:
      97              return self.obj < other.obj
      98          except TypeError:
      99              return ((str(type(self.obj)), id(self.obj)) < \
     100                      (str(type(other.obj)), id(other.obj)))
     101  
     102  def _safe_tuple(t):
     103      "Helper function for comparing 2-tuples"
     104      return _safe_key(t[0]), _safe_key(t[1])
     105  
     106  class ESC[4;38;5;81mPrettyPrinter:
     107      def __init__(self, indent=1, width=80, depth=None, stream=None, *,
     108                   compact=False, sort_dicts=True, underscore_numbers=False):
     109          """Handle pretty printing operations onto a stream using a set of
     110          configured parameters.
     111  
     112          indent
     113              Number of spaces to indent for each level of nesting.
     114  
     115          width
     116              Attempted maximum number of columns in the output.
     117  
     118          depth
     119              The maximum depth to print out nested structures.
     120  
     121          stream
     122              The desired output stream.  If omitted (or false), the standard
     123              output stream available at construction will be used.
     124  
     125          compact
     126              If true, several items will be combined in one line.
     127  
     128          sort_dicts
     129              If true, dict keys are sorted.
     130  
     131          """
     132          indent = int(indent)
     133          width = int(width)
     134          if indent < 0:
     135              raise ValueError('indent must be >= 0')
     136          if depth is not None and depth <= 0:
     137              raise ValueError('depth must be > 0')
     138          if not width:
     139              raise ValueError('width must be != 0')
     140          self._depth = depth
     141          self._indent_per_level = indent
     142          self._width = width
     143          if stream is not None:
     144              self._stream = stream
     145          else:
     146              self._stream = _sys.stdout
     147          self._compact = bool(compact)
     148          self._sort_dicts = sort_dicts
     149          self._underscore_numbers = underscore_numbers
     150  
     151      def pprint(self, object):
     152          if self._stream is not None:
     153              self._format(object, self._stream, 0, 0, {}, 0)
     154              self._stream.write("\n")
     155  
     156      def pformat(self, object):
     157          sio = _StringIO()
     158          self._format(object, sio, 0, 0, {}, 0)
     159          return sio.getvalue()
     160  
     161      def isrecursive(self, object):
     162          return self.format(object, {}, 0, 0)[2]
     163  
     164      def isreadable(self, object):
     165          s, readable, recursive = self.format(object, {}, 0, 0)
     166          return readable and not recursive
     167  
     168      def _format(self, object, stream, indent, allowance, context, level):
     169          objid = id(object)
     170          if objid in context:
     171              stream.write(_recursion(object))
     172              self._recursive = True
     173              self._readable = False
     174              return
     175          rep = self._repr(object, context, level)
     176          max_width = self._width - indent - allowance
     177          if len(rep) > max_width:
     178              p = self._dispatch.get(type(object).__repr__, None)
     179              if p is not None:
     180                  context[objid] = 1
     181                  p(self, object, stream, indent, allowance, context, level + 1)
     182                  del context[objid]
     183                  return
     184              elif (_dataclasses.is_dataclass(object) and
     185                    not isinstance(object, type) and
     186                    object.__dataclass_params__.repr and
     187                    # Check dataclass has generated repr method.
     188                    hasattr(object.__repr__, "__wrapped__") and
     189                    "__create_fn__" in object.__repr__.__wrapped__.__qualname__):
     190                  context[objid] = 1
     191                  self._pprint_dataclass(object, stream, indent, allowance, context, level + 1)
     192                  del context[objid]
     193                  return
     194          stream.write(rep)
     195  
     196      def _pprint_dataclass(self, object, stream, indent, allowance, context, level):
     197          cls_name = object.__class__.__name__
     198          indent += len(cls_name) + 1
     199          items = [(f.name, getattr(object, f.name)) for f in _dataclasses.fields(object) if f.repr]
     200          stream.write(cls_name + '(')
     201          self._format_namespace_items(items, stream, indent, allowance, context, level)
     202          stream.write(')')
     203  
     204      _dispatch = {}
     205  
     206      def _pprint_dict(self, object, stream, indent, allowance, context, level):
     207          write = stream.write
     208          write('{')
     209          if self._indent_per_level > 1:
     210              write((self._indent_per_level - 1) * ' ')
     211          length = len(object)
     212          if length:
     213              if self._sort_dicts:
     214                  items = sorted(object.items(), key=_safe_tuple)
     215              else:
     216                  items = object.items()
     217              self._format_dict_items(items, stream, indent, allowance + 1,
     218                                      context, level)
     219          write('}')
     220  
     221      _dispatch[dict.__repr__] = _pprint_dict
     222  
     223      def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level):
     224          if not len(object):
     225              stream.write(repr(object))
     226              return
     227          cls = object.__class__
     228          stream.write(cls.__name__ + '(')
     229          self._format(list(object.items()), stream,
     230                       indent + len(cls.__name__) + 1, allowance + 1,
     231                       context, level)
     232          stream.write(')')
     233  
     234      _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict
     235  
     236      def _pprint_list(self, object, stream, indent, allowance, context, level):
     237          stream.write('[')
     238          self._format_items(object, stream, indent, allowance + 1,
     239                             context, level)
     240          stream.write(']')
     241  
     242      _dispatch[list.__repr__] = _pprint_list
     243  
     244      def _pprint_tuple(self, object, stream, indent, allowance, context, level):
     245          stream.write('(')
     246          endchar = ',)' if len(object) == 1 else ')'
     247          self._format_items(object, stream, indent, allowance + len(endchar),
     248                             context, level)
     249          stream.write(endchar)
     250  
     251      _dispatch[tuple.__repr__] = _pprint_tuple
     252  
     253      def _pprint_set(self, object, stream, indent, allowance, context, level):
     254          if not len(object):
     255              stream.write(repr(object))
     256              return
     257          typ = object.__class__
     258          if typ is set:
     259              stream.write('{')
     260              endchar = '}'
     261          else:
     262              stream.write(typ.__name__ + '({')
     263              endchar = '})'
     264              indent += len(typ.__name__) + 1
     265          object = sorted(object, key=_safe_key)
     266          self._format_items(object, stream, indent, allowance + len(endchar),
     267                             context, level)
     268          stream.write(endchar)
     269  
     270      _dispatch[set.__repr__] = _pprint_set
     271      _dispatch[frozenset.__repr__] = _pprint_set
     272  
     273      def _pprint_str(self, object, stream, indent, allowance, context, level):
     274          write = stream.write
     275          if not len(object):
     276              write(repr(object))
     277              return
     278          chunks = []
     279          lines = object.splitlines(True)
     280          if level == 1:
     281              indent += 1
     282              allowance += 1
     283          max_width1 = max_width = self._width - indent
     284          for i, line in enumerate(lines):
     285              rep = repr(line)
     286              if i == len(lines) - 1:
     287                  max_width1 -= allowance
     288              if len(rep) <= max_width1:
     289                  chunks.append(rep)
     290              else:
     291                  # A list of alternating (non-space, space) strings
     292                  parts = re.findall(r'\S*\s*', line)
     293                  assert parts
     294                  assert not parts[-1]
     295                  parts.pop()  # drop empty last part
     296                  max_width2 = max_width
     297                  current = ''
     298                  for j, part in enumerate(parts):
     299                      candidate = current + part
     300                      if j == len(parts) - 1 and i == len(lines) - 1:
     301                          max_width2 -= allowance
     302                      if len(repr(candidate)) > max_width2:
     303                          if current:
     304                              chunks.append(repr(current))
     305                          current = part
     306                      else:
     307                          current = candidate
     308                  if current:
     309                      chunks.append(repr(current))
     310          if len(chunks) == 1:
     311              write(rep)
     312              return
     313          if level == 1:
     314              write('(')
     315          for i, rep in enumerate(chunks):
     316              if i > 0:
     317                  write('\n' + ' '*indent)
     318              write(rep)
     319          if level == 1:
     320              write(')')
     321  
     322      _dispatch[str.__repr__] = _pprint_str
     323  
     324      def _pprint_bytes(self, object, stream, indent, allowance, context, level):
     325          write = stream.write
     326          if len(object) <= 4:
     327              write(repr(object))
     328              return
     329          parens = level == 1
     330          if parens:
     331              indent += 1
     332              allowance += 1
     333              write('(')
     334          delim = ''
     335          for rep in _wrap_bytes_repr(object, self._width - indent, allowance):
     336              write(delim)
     337              write(rep)
     338              if not delim:
     339                  delim = '\n' + ' '*indent
     340          if parens:
     341              write(')')
     342  
     343      _dispatch[bytes.__repr__] = _pprint_bytes
     344  
     345      def _pprint_bytearray(self, object, stream, indent, allowance, context, level):
     346          write = stream.write
     347          write('bytearray(')
     348          self._pprint_bytes(bytes(object), stream, indent + 10,
     349                             allowance + 1, context, level + 1)
     350          write(')')
     351  
     352      _dispatch[bytearray.__repr__] = _pprint_bytearray
     353  
     354      def _pprint_mappingproxy(self, object, stream, indent, allowance, context, level):
     355          stream.write('mappingproxy(')
     356          self._format(object.copy(), stream, indent + 13, allowance + 1,
     357                       context, level)
     358          stream.write(')')
     359  
     360      _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy
     361  
     362      def _pprint_simplenamespace(self, object, stream, indent, allowance, context, level):
     363          if type(object) is _types.SimpleNamespace:
     364              # The SimpleNamespace repr is "namespace" instead of the class
     365              # name, so we do the same here. For subclasses; use the class name.
     366              cls_name = 'namespace'
     367          else:
     368              cls_name = object.__class__.__name__
     369          indent += len(cls_name) + 1
     370          items = object.__dict__.items()
     371          stream.write(cls_name + '(')
     372          self._format_namespace_items(items, stream, indent, allowance, context, level)
     373          stream.write(')')
     374  
     375      _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace
     376  
     377      def _format_dict_items(self, items, stream, indent, allowance, context,
     378                             level):
     379          write = stream.write
     380          indent += self._indent_per_level
     381          delimnl = ',\n' + ' ' * indent
     382          last_index = len(items) - 1
     383          for i, (key, ent) in enumerate(items):
     384              last = i == last_index
     385              rep = self._repr(key, context, level)
     386              write(rep)
     387              write(': ')
     388              self._format(ent, stream, indent + len(rep) + 2,
     389                           allowance if last else 1,
     390                           context, level)
     391              if not last:
     392                  write(delimnl)
     393  
     394      def _format_namespace_items(self, items, stream, indent, allowance, context, level):
     395          write = stream.write
     396          delimnl = ',\n' + ' ' * indent
     397          last_index = len(items) - 1
     398          for i, (key, ent) in enumerate(items):
     399              last = i == last_index
     400              write(key)
     401              write('=')
     402              if id(ent) in context:
     403                  # Special-case representation of recursion to match standard
     404                  # recursive dataclass repr.
     405                  write("...")
     406              else:
     407                  self._format(ent, stream, indent + len(key) + 1,
     408                               allowance if last else 1,
     409                               context, level)
     410              if not last:
     411                  write(delimnl)
     412  
     413      def _format_items(self, items, stream, indent, allowance, context, level):
     414          write = stream.write
     415          indent += self._indent_per_level
     416          if self._indent_per_level > 1:
     417              write((self._indent_per_level - 1) * ' ')
     418          delimnl = ',\n' + ' ' * indent
     419          delim = ''
     420          width = max_width = self._width - indent + 1
     421          it = iter(items)
     422          try:
     423              next_ent = next(it)
     424          except StopIteration:
     425              return
     426          last = False
     427          while not last:
     428              ent = next_ent
     429              try:
     430                  next_ent = next(it)
     431              except StopIteration:
     432                  last = True
     433                  max_width -= allowance
     434                  width -= allowance
     435              if self._compact:
     436                  rep = self._repr(ent, context, level)
     437                  w = len(rep) + 2
     438                  if width < w:
     439                      width = max_width
     440                      if delim:
     441                          delim = delimnl
     442                  if width >= w:
     443                      width -= w
     444                      write(delim)
     445                      delim = ', '
     446                      write(rep)
     447                      continue
     448              write(delim)
     449              delim = delimnl
     450              self._format(ent, stream, indent,
     451                           allowance if last else 1,
     452                           context, level)
     453  
     454      def _repr(self, object, context, level):
     455          repr, readable, recursive = self.format(object, context.copy(),
     456                                                  self._depth, level)
     457          if not readable:
     458              self._readable = False
     459          if recursive:
     460              self._recursive = True
     461          return repr
     462  
     463      def format(self, object, context, maxlevels, level):
     464          """Format object for a specific context, returning a string
     465          and flags indicating whether the representation is 'readable'
     466          and whether the object represents a recursive construct.
     467          """
     468          return self._safe_repr(object, context, maxlevels, level)
     469  
     470      def _pprint_default_dict(self, object, stream, indent, allowance, context, level):
     471          if not len(object):
     472              stream.write(repr(object))
     473              return
     474          rdf = self._repr(object.default_factory, context, level)
     475          cls = object.__class__
     476          indent += len(cls.__name__) + 1
     477          stream.write('%s(%s,\n%s' % (cls.__name__, rdf, ' ' * indent))
     478          self._pprint_dict(object, stream, indent, allowance + 1, context, level)
     479          stream.write(')')
     480  
     481      _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict
     482  
     483      def _pprint_counter(self, object, stream, indent, allowance, context, level):
     484          if not len(object):
     485              stream.write(repr(object))
     486              return
     487          cls = object.__class__
     488          stream.write(cls.__name__ + '({')
     489          if self._indent_per_level > 1:
     490              stream.write((self._indent_per_level - 1) * ' ')
     491          items = object.most_common()
     492          self._format_dict_items(items, stream,
     493                                  indent + len(cls.__name__) + 1, allowance + 2,
     494                                  context, level)
     495          stream.write('})')
     496  
     497      _dispatch[_collections.Counter.__repr__] = _pprint_counter
     498  
     499      def _pprint_chain_map(self, object, stream, indent, allowance, context, level):
     500          if not len(object.maps):
     501              stream.write(repr(object))
     502              return
     503          cls = object.__class__
     504          stream.write(cls.__name__ + '(')
     505          indent += len(cls.__name__) + 1
     506          for i, m in enumerate(object.maps):
     507              if i == len(object.maps) - 1:
     508                  self._format(m, stream, indent, allowance + 1, context, level)
     509                  stream.write(')')
     510              else:
     511                  self._format(m, stream, indent, 1, context, level)
     512                  stream.write(',\n' + ' ' * indent)
     513  
     514      _dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map
     515  
     516      def _pprint_deque(self, object, stream, indent, allowance, context, level):
     517          if not len(object):
     518              stream.write(repr(object))
     519              return
     520          cls = object.__class__
     521          stream.write(cls.__name__ + '(')
     522          indent += len(cls.__name__) + 1
     523          stream.write('[')
     524          if object.maxlen is None:
     525              self._format_items(object, stream, indent, allowance + 2,
     526                                 context, level)
     527              stream.write('])')
     528          else:
     529              self._format_items(object, stream, indent, 2,
     530                                 context, level)
     531              rml = self._repr(object.maxlen, context, level)
     532              stream.write('],\n%smaxlen=%s)' % (' ' * indent, rml))
     533  
     534      _dispatch[_collections.deque.__repr__] = _pprint_deque
     535  
     536      def _pprint_user_dict(self, object, stream, indent, allowance, context, level):
     537          self._format(object.data, stream, indent, allowance, context, level - 1)
     538  
     539      _dispatch[_collections.UserDict.__repr__] = _pprint_user_dict
     540  
     541      def _pprint_user_list(self, object, stream, indent, allowance, context, level):
     542          self._format(object.data, stream, indent, allowance, context, level - 1)
     543  
     544      _dispatch[_collections.UserList.__repr__] = _pprint_user_list
     545  
     546      def _pprint_user_string(self, object, stream, indent, allowance, context, level):
     547          self._format(object.data, stream, indent, allowance, context, level - 1)
     548  
     549      _dispatch[_collections.UserString.__repr__] = _pprint_user_string
     550  
     551      def _safe_repr(self, object, context, maxlevels, level):
     552          # Return triple (repr_string, isreadable, isrecursive).
     553          typ = type(object)
     554          if typ in _builtin_scalars:
     555              return repr(object), True, False
     556  
     557          r = getattr(typ, "__repr__", None)
     558  
     559          if issubclass(typ, int) and r is int.__repr__:
     560              if self._underscore_numbers:
     561                  return f"{object:_d}", True, False
     562              else:
     563                  return repr(object), True, False
     564  
     565          if issubclass(typ, dict) and r is dict.__repr__:
     566              if not object:
     567                  return "{}", True, False
     568              objid = id(object)
     569              if maxlevels and level >= maxlevels:
     570                  return "{...}", False, objid in context
     571              if objid in context:
     572                  return _recursion(object), False, True
     573              context[objid] = 1
     574              readable = True
     575              recursive = False
     576              components = []
     577              append = components.append
     578              level += 1
     579              if self._sort_dicts:
     580                  items = sorted(object.items(), key=_safe_tuple)
     581              else:
     582                  items = object.items()
     583              for k, v in items:
     584                  krepr, kreadable, krecur = self.format(
     585                      k, context, maxlevels, level)
     586                  vrepr, vreadable, vrecur = self.format(
     587                      v, context, maxlevels, level)
     588                  append("%s: %s" % (krepr, vrepr))
     589                  readable = readable and kreadable and vreadable
     590                  if krecur or vrecur:
     591                      recursive = True
     592              del context[objid]
     593              return "{%s}" % ", ".join(components), readable, recursive
     594  
     595          if (issubclass(typ, list) and r is list.__repr__) or \
     596             (issubclass(typ, tuple) and r is tuple.__repr__):
     597              if issubclass(typ, list):
     598                  if not object:
     599                      return "[]", True, False
     600                  format = "[%s]"
     601              elif len(object) == 1:
     602                  format = "(%s,)"
     603              else:
     604                  if not object:
     605                      return "()", True, False
     606                  format = "(%s)"
     607              objid = id(object)
     608              if maxlevels and level >= maxlevels:
     609                  return format % "...", False, objid in context
     610              if objid in context:
     611                  return _recursion(object), False, True
     612              context[objid] = 1
     613              readable = True
     614              recursive = False
     615              components = []
     616              append = components.append
     617              level += 1
     618              for o in object:
     619                  orepr, oreadable, orecur = self.format(
     620                      o, context, maxlevels, level)
     621                  append(orepr)
     622                  if not oreadable:
     623                      readable = False
     624                  if orecur:
     625                      recursive = True
     626              del context[objid]
     627              return format % ", ".join(components), readable, recursive
     628  
     629          rep = repr(object)
     630          return rep, (rep and not rep.startswith('<')), False
     631  
     632  _builtin_scalars = frozenset({str, bytes, bytearray, float, complex,
     633                                bool, type(None)})
     634  
     635  def _recursion(object):
     636      return ("<Recursion on %s with id=%s>"
     637              % (type(object).__name__, id(object)))
     638  
     639  
     640  def _perfcheck(object=None):
     641      import time
     642      if object is None:
     643          object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
     644      p = PrettyPrinter()
     645      t1 = time.perf_counter()
     646      p._safe_repr(object, {}, None, 0, True)
     647      t2 = time.perf_counter()
     648      p.pformat(object)
     649      t3 = time.perf_counter()
     650      print("_safe_repr:", t2 - t1)
     651      print("pformat:", t3 - t2)
     652  
     653  def _wrap_bytes_repr(object, width, allowance):
     654      current = b''
     655      last = len(object) // 4 * 4
     656      for i in range(0, len(object), 4):
     657          part = object[i: i+4]
     658          candidate = current + part
     659          if i == last:
     660              width -= allowance
     661          if len(repr(candidate)) > width:
     662              if current:
     663                  yield repr(current)
     664              current = part
     665          else:
     666              current = candidate
     667      if current:
     668          yield repr(current)
     669  
     670  if __name__ == "__main__":
     671      _perfcheck()