blob: 16d8eae57ff59d89b859f54c072e24a8c522bb50 [file] [log] [blame]
Guido van Rossum45e2fbc1998-03-26 21:13:24 +00001# Author: Fred L. Drake, Jr.
Fred Drake3e5e6612001-10-09 20:53:48 +00002# fdrake@acm.org
Guido van Rossum5e92aff1997-04-16 00:49:59 +00003#
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
Thomas Wouters7e474022000-07-16 12:04:32 +00007# tuples with fairly non-descriptive content. This is modeled very much
Guido van Rossum5e92aff1997-04-16 00:49:59 +00008# 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
13Very simple, but useful, especially in debugging data structures.
14
Fred Drakea89fda01997-04-16 16:59:30 +000015Classes
16-------
17
18PrettyPrinter()
19 Handle pretty-printing operations onto a stream using a configured
20 set of formatting parameters.
21
Guido van Rossum5e92aff1997-04-16 00:49:59 +000022Functions
23---------
24
25pformat()
26 Format a Python object into a pretty-printed representation.
27
28pprint()
Fred Drakea89fda01997-04-16 16:59:30 +000029 Pretty-print a Python object to a stream [default is sys.sydout].
Guido van Rossum5e92aff1997-04-16 00:49:59 +000030
Fred Drakea89fda01997-04-16 16:59:30 +000031saferepr()
32 Generate a 'standard' repr()-like value, but protect against recursive
33 data structures.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000034
35"""
36
Fred Drake397b6152002-12-31 07:14:18 +000037import sys as _sys
Guido van Rossum5e92aff1997-04-16 00:49:59 +000038
Fred Drake397b6152002-12-31 07:14:18 +000039from cStringIO import StringIO as _StringIO
Guido van Rossum5e92aff1997-04-16 00:49:59 +000040
Skip Montanaroc62c81e2001-02-12 02:00:42 +000041__all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
42 "PrettyPrinter"]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000043
Fred Drake49cc01e2001-11-01 17:50:38 +000044# cache these for faster access:
45_commajoin = ", ".join
Fred Drake49cc01e2001-11-01 17:50:38 +000046_id = id
47_len = len
48_type = type
49
50
Fred Drakea89fda01997-04-16 16:59:30 +000051def pprint(object, stream=None):
52 """Pretty-print a Python object to a stream [default is sys.sydout]."""
53 printer = PrettyPrinter(stream=stream)
54 printer.pprint(object)
Guido van Rossum5e92aff1997-04-16 00:49:59 +000055
Fred Drakea89fda01997-04-16 16:59:30 +000056def pformat(object):
57 """Format a Python object into a pretty-printed representation."""
58 return PrettyPrinter().pformat(object)
Guido van Rossum5e92aff1997-04-16 00:49:59 +000059
Fred Drakea89fda01997-04-16 16:59:30 +000060def saferepr(object):
61 """Version of repr() which can handle recursive data structures."""
Fred Drake49cc01e2001-11-01 17:50:38 +000062 return _safe_repr(object, {}, None, 0)[0]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000063
Tim Petersa814db52001-05-14 07:05:58 +000064def isreadable(object):
65 """Determine if saferepr(object) is readable by eval()."""
Fred Drake49cc01e2001-11-01 17:50:38 +000066 return _safe_repr(object, {}, None, 0)[1]
Tim Petersa814db52001-05-14 07:05:58 +000067
68def isrecursive(object):
69 """Determine if object requires a recursive representation."""
Fred Drake49cc01e2001-11-01 17:50:38 +000070 return _safe_repr(object, {}, None, 0)[2]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000071
Fred Drakea89fda01997-04-16 16:59:30 +000072class PrettyPrinter:
73 def __init__(self, indent=1, width=80, depth=None, stream=None):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000074 """Handle pretty printing operations onto a stream using a set of
75 configured parameters.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000076
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000077 indent
78 Number of spaces to indent for each level of nesting.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000079
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000080 width
81 Attempted maximum number of columns in the output.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000082
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000083 depth
84 The maximum depth to print out nested structures.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000085
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000086 stream
87 The desired output stream. If omitted (or false), the standard
88 output stream available at construction will be used.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000089
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000090 """
91 indent = int(indent)
92 width = int(width)
93 assert indent >= 0
Tim Petersa814db52001-05-14 07:05:58 +000094 assert depth is None or depth > 0, "depth must be > 0"
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000095 assert width
Fred Drakee6691ef2002-07-08 12:28:06 +000096 self._depth = depth
97 self._indent_per_level = indent
98 self._width = width
Raymond Hettinger16e3c422002-06-01 16:07:16 +000099 if stream is not None:
Fred Drakee6691ef2002-07-08 12:28:06 +0000100 self._stream = stream
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000101 else:
Fred Drake397b6152002-12-31 07:14:18 +0000102 self._stream = _sys.stdout
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000103
Fred Drakea89fda01997-04-16 16:59:30 +0000104 def pprint(self, object):
Fred Drakee6691ef2002-07-08 12:28:06 +0000105 self._stream.write(self.pformat(object) + "\n")
Fred Drakea89fda01997-04-16 16:59:30 +0000106
107 def pformat(self, object):
Fred Drake397b6152002-12-31 07:14:18 +0000108 sio = _StringIO()
Fred Drakee6691ef2002-07-08 12:28:06 +0000109 self._format(object, sio, 0, 0, {}, 0)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000110 return sio.getvalue()
Fred Drakea89fda01997-04-16 16:59:30 +0000111
Fred Drakee0ffabe1997-07-18 20:42:39 +0000112 def isrecursive(self, object):
Fred Drake397b6152002-12-31 07:14:18 +0000113 return self.format(object, {}, 0, 0)[2]
Fred Drakee0ffabe1997-07-18 20:42:39 +0000114
115 def isreadable(self, object):
Fred Drake397b6152002-12-31 07:14:18 +0000116 s, readable, recursive = self.format(object, {}, 0, 0)
Fred Drakeaee113d2002-04-02 05:08:35 +0000117 return readable and not recursive
Fred Drakee0ffabe1997-07-18 20:42:39 +0000118
Fred Drakee6691ef2002-07-08 12:28:06 +0000119 def _format(self, object, stream, indent, allowance, context, level):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000120 level = level + 1
Fred Drake49cc01e2001-11-01 17:50:38 +0000121 objid = _id(object)
122 if objid in context:
123 stream.write(_recursion(object))
Fred Drakee6691ef2002-07-08 12:28:06 +0000124 self._recursive = True
125 self._readable = False
Fred Drake49cc01e2001-11-01 17:50:38 +0000126 return
Fred Drakee6691ef2002-07-08 12:28:06 +0000127 rep = self._repr(object, context, level - 1)
Fred Drake49cc01e2001-11-01 17:50:38 +0000128 typ = _type(object)
Fred Drakee6691ef2002-07-08 12:28:06 +0000129 sepLines = _len(rep) > (self._width - 1 - indent - allowance)
Fred Drake49cc01e2001-11-01 17:50:38 +0000130 write = stream.write
Fred Drakea89fda01997-04-16 16:59:30 +0000131
Fred Drake49cc01e2001-11-01 17:50:38 +0000132 if sepLines:
Martin v. Löwisd02879d2003-06-07 20:47:37 +0000133 if typ is dict:
Fred Drake49cc01e2001-11-01 17:50:38 +0000134 write('{')
Fred Drakee6691ef2002-07-08 12:28:06 +0000135 if self._indent_per_level > 1:
136 write((self._indent_per_level - 1) * ' ')
Fred Drake49cc01e2001-11-01 17:50:38 +0000137 length = _len(object)
138 if length:
139 context[objid] = 1
Fred Drakee6691ef2002-07-08 12:28:06 +0000140 indent = indent + self._indent_per_level
Fred Drake49cc01e2001-11-01 17:50:38 +0000141 items = object.items()
142 items.sort()
143 key, ent = items[0]
Fred Drakee6691ef2002-07-08 12:28:06 +0000144 rep = self._repr(key, context, level)
Fred Drake49cc01e2001-11-01 17:50:38 +0000145 write(rep)
146 write(': ')
Fred Drakee6691ef2002-07-08 12:28:06 +0000147 self._format(ent, stream, indent + _len(rep) + 2,
Fred Drake49cc01e2001-11-01 17:50:38 +0000148 allowance + 1, context, level)
149 if length > 1:
150 for key, ent in items[1:]:
Fred Drakee6691ef2002-07-08 12:28:06 +0000151 rep = self._repr(key, context, level)
Barry Warsaw00859c02001-11-28 05:49:39 +0000152 write(',\n%s%s: ' % (' '*indent, rep))
Fred Drakee6691ef2002-07-08 12:28:06 +0000153 self._format(ent, stream, indent + _len(rep) + 2,
Fred Drake49cc01e2001-11-01 17:50:38 +0000154 allowance + 1, context, level)
Fred Drakee6691ef2002-07-08 12:28:06 +0000155 indent = indent - self._indent_per_level
Fred Drake49cc01e2001-11-01 17:50:38 +0000156 del context[objid]
157 write('}')
158 return
Fred Drakea89fda01997-04-16 16:59:30 +0000159
Martin v. Löwisd02879d2003-06-07 20:47:37 +0000160 if typ is list or typ is tuple:
161 if typ is list:
Fred Drake49cc01e2001-11-01 17:50:38 +0000162 write('[')
163 endchar = ']'
164 else:
165 write('(')
166 endchar = ')'
Fred Drakee6691ef2002-07-08 12:28:06 +0000167 if self._indent_per_level > 1:
168 write((self._indent_per_level - 1) * ' ')
Fred Drake49cc01e2001-11-01 17:50:38 +0000169 length = _len(object)
170 if length:
171 context[objid] = 1
Fred Drakee6691ef2002-07-08 12:28:06 +0000172 indent = indent + self._indent_per_level
173 self._format(object[0], stream, indent, allowance + 1,
174 context, level)
Fred Drake49cc01e2001-11-01 17:50:38 +0000175 if length > 1:
176 for ent in object[1:]:
177 write(',\n' + ' '*indent)
Fred Drakee6691ef2002-07-08 12:28:06 +0000178 self._format(ent, stream, indent,
Fred Drake49cc01e2001-11-01 17:50:38 +0000179 allowance + 1, context, level)
Fred Drakee6691ef2002-07-08 12:28:06 +0000180 indent = indent - self._indent_per_level
Fred Drake49cc01e2001-11-01 17:50:38 +0000181 del context[objid]
Martin v. Löwisd02879d2003-06-07 20:47:37 +0000182 if typ is tuple and length == 1:
Fred Drake49cc01e2001-11-01 17:50:38 +0000183 write(',')
184 write(endchar)
185 return
Fred Drakea89fda01997-04-16 16:59:30 +0000186
Fred Drake49cc01e2001-11-01 17:50:38 +0000187 write(rep)
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000188
Fred Drakee6691ef2002-07-08 12:28:06 +0000189 def _repr(self, object, context, level):
Fred Drakeaee113d2002-04-02 05:08:35 +0000190 repr, readable, recursive = self.format(object, context.copy(),
Fred Drakee6691ef2002-07-08 12:28:06 +0000191 self._depth, level)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000192 if not readable:
Fred Drakee6691ef2002-07-08 12:28:06 +0000193 self._readable = False
Tim Petersa814db52001-05-14 07:05:58 +0000194 if recursive:
Fred Drakee6691ef2002-07-08 12:28:06 +0000195 self._recursive = True
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000196 return repr
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000197
Fred Drakeaee113d2002-04-02 05:08:35 +0000198 def format(self, object, context, maxlevels, level):
199 """Format object for a specific context, returning a string
200 and flags indicating whether the representation is 'readable'
201 and whether the object represents a recursive construct.
202 """
203 return _safe_repr(object, context, maxlevels, level)
204
205
Tim Petersa814db52001-05-14 07:05:58 +0000206# Return triple (repr_string, isreadable, isrecursive).
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000207
Fred Drake49cc01e2001-11-01 17:50:38 +0000208def _safe_repr(object, context, maxlevels, level):
209 typ = _type(object)
Martin v. Löwisd02879d2003-06-07 20:47:37 +0000210 if typ is str:
Fred Drake397b6152002-12-31 07:14:18 +0000211 if 'locale' not in _sys.modules:
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000212 return `object`, True, False
Fred Drake1ef106c2001-09-04 19:43:26 +0000213 if "'" in object and '"' not in object:
214 closure = '"'
215 quotes = {'"': '\\"'}
216 else:
217 closure = "'"
218 quotes = {"'": "\\'"}
Fred Drake49cc01e2001-11-01 17:50:38 +0000219 qget = quotes.get
Fred Drake397b6152002-12-31 07:14:18 +0000220 sio = _StringIO()
Fred Drake49cc01e2001-11-01 17:50:38 +0000221 write = sio.write
Fred Drake1ef106c2001-09-04 19:43:26 +0000222 for char in object:
223 if char.isalpha():
Fred Drake49cc01e2001-11-01 17:50:38 +0000224 write(char)
Fred Drake1ef106c2001-09-04 19:43:26 +0000225 else:
Fred Drake49cc01e2001-11-01 17:50:38 +0000226 write(qget(char, `char`[1:-1]))
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000227 return ("%s%s%s" % (closure, sio.getvalue(), closure)), True, False
Tim Peters95b3f782001-05-14 18:39:41 +0000228
Martin v. Löwisd02879d2003-06-07 20:47:37 +0000229 if typ is dict:
Fred Drake49cc01e2001-11-01 17:50:38 +0000230 if not object:
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000231 return "{}", True, False
Fred Drake49cc01e2001-11-01 17:50:38 +0000232 objid = _id(object)
233 if maxlevels and level > maxlevels:
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000234 return "{...}", False, objid in context
Fred Drake49cc01e2001-11-01 17:50:38 +0000235 if objid in context:
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000236 return _recursion(object), False, True
Fred Drake49cc01e2001-11-01 17:50:38 +0000237 context[objid] = 1
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000238 readable = True
239 recursive = False
Tim Peters95b3f782001-05-14 18:39:41 +0000240 components = []
Fred Drake49cc01e2001-11-01 17:50:38 +0000241 append = components.append
242 level += 1
243 saferepr = _safe_repr
Tim Peters95b3f782001-05-14 18:39:41 +0000244 for k, v in object.iteritems():
Fred Drake49cc01e2001-11-01 17:50:38 +0000245 krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
246 vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
247 append("%s: %s" % (krepr, vrepr))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000248 readable = readable and kreadable and vreadable
Fred Drake49cc01e2001-11-01 17:50:38 +0000249 if krecur or vrecur:
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000250 recursive = True
Fred Drake49cc01e2001-11-01 17:50:38 +0000251 del context[objid]
252 return "{%s}" % _commajoin(components), readable, recursive
Tim Peters95b3f782001-05-14 18:39:41 +0000253
Martin v. Löwisd02879d2003-06-07 20:47:37 +0000254 if typ is list or typ is tuple:
255 if typ is list:
Fred Drake49cc01e2001-11-01 17:50:38 +0000256 if not object:
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000257 return "[]", True, False
Fred Drake49cc01e2001-11-01 17:50:38 +0000258 format = "[%s]"
259 elif _len(object) == 1:
260 format = "(%s,)"
261 else:
262 if not object:
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000263 return "()", True, False
Fred Drake49cc01e2001-11-01 17:50:38 +0000264 format = "(%s)"
265 objid = _id(object)
266 if maxlevels and level > maxlevels:
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000267 return format % "...", False, objid in context
Fred Drake49cc01e2001-11-01 17:50:38 +0000268 if objid in context:
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000269 return _recursion(object), False, True
Fred Drake49cc01e2001-11-01 17:50:38 +0000270 context[objid] = 1
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000271 readable = True
272 recursive = False
Tim Peters95b3f782001-05-14 18:39:41 +0000273 components = []
Fred Drake49cc01e2001-11-01 17:50:38 +0000274 append = components.append
275 level += 1
276 for o in object:
277 orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level)
278 append(orepr)
279 if not oreadable:
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000280 readable = False
Fred Drake49cc01e2001-11-01 17:50:38 +0000281 if orecur:
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000282 recursive = True
Fred Drake49cc01e2001-11-01 17:50:38 +0000283 del context[objid]
284 return format % _commajoin(components), readable, recursive
Tim Peters88768482001-11-13 21:51:26 +0000285
Fred Drake49cc01e2001-11-01 17:50:38 +0000286 rep = `object`
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000287 return rep, (rep and not rep.startswith('<')), False
Tim Peters95b3f782001-05-14 18:39:41 +0000288
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000289
Fred Drake49cc01e2001-11-01 17:50:38 +0000290def _recursion(object):
291 return ("<Recursion on %s with id=%s>"
292 % (_type(object).__name__, _id(object)))
Fred Drakea89fda01997-04-16 16:59:30 +0000293
Fred Drake49cc01e2001-11-01 17:50:38 +0000294
295def _perfcheck(object=None):
296 import time
297 if object is None:
298 object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
299 p = PrettyPrinter()
300 t1 = time.time()
301 _safe_repr(object, {}, None, 0)
302 t2 = time.time()
303 p.pformat(object)
304 t3 = time.time()
305 print "_safe_repr:", t2 - t1
306 print "pformat:", t3 - t2
307
308if __name__ == "__main__":
309 _perfcheck()