pprint.py 20 KB

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