pprint.py 24 KB

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