blob: 5d178a2a55d66e6fa07ad5ba9fefd460fc332d38 [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 Drake1ef106c2001-09-04 19:43:26 +000037from types import DictType, ListType, TupleType, StringType
38import sys
Guido van Rossum5e92aff1997-04-16 00:49:59 +000039
Fred Drakea89fda01997-04-16 16:59:30 +000040try:
41 from cStringIO import StringIO
42except ImportError:
43 from StringIO import StringIO
Guido van Rossum5e92aff1997-04-16 00:49:59 +000044
Skip Montanaroc62c81e2001-02-12 02:00:42 +000045__all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
46 "PrettyPrinter"]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000047
Fred Drake49cc01e2001-11-01 17:50:38 +000048# cache these for faster access:
49_commajoin = ", ".join
50_sys_modules = sys.modules
51_id = id
52_len = len
53_type = type
54
55
Fred Drakea89fda01997-04-16 16:59:30 +000056def pprint(object, stream=None):
57 """Pretty-print a Python object to a stream [default is sys.sydout]."""
58 printer = PrettyPrinter(stream=stream)
59 printer.pprint(object)
Guido van Rossum5e92aff1997-04-16 00:49:59 +000060
Fred Drakea89fda01997-04-16 16:59:30 +000061def pformat(object):
62 """Format a Python object into a pretty-printed representation."""
63 return PrettyPrinter().pformat(object)
Guido van Rossum5e92aff1997-04-16 00:49:59 +000064
Fred Drakea89fda01997-04-16 16:59:30 +000065def saferepr(object):
66 """Version of repr() which can handle recursive data structures."""
Fred Drake49cc01e2001-11-01 17:50:38 +000067 return _safe_repr(object, {}, None, 0)[0]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000068
Tim Petersa814db52001-05-14 07:05:58 +000069def isreadable(object):
70 """Determine if saferepr(object) is readable by eval()."""
Fred Drake49cc01e2001-11-01 17:50:38 +000071 return _safe_repr(object, {}, None, 0)[1]
Tim Petersa814db52001-05-14 07:05:58 +000072
73def isrecursive(object):
74 """Determine if object requires a recursive representation."""
Fred Drake49cc01e2001-11-01 17:50:38 +000075 return _safe_repr(object, {}, None, 0)[2]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000076
Fred Drakea89fda01997-04-16 16:59:30 +000077class PrettyPrinter:
78 def __init__(self, indent=1, width=80, depth=None, stream=None):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000079 """Handle pretty printing operations onto a stream using a set of
80 configured parameters.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000081
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000082 indent
83 Number of spaces to indent for each level of nesting.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000084
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000085 width
86 Attempted maximum number of columns in the output.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000087
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000088 depth
89 The maximum depth to print out nested structures.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000090
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000091 stream
92 The desired output stream. If omitted (or false), the standard
93 output stream available at construction will be used.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000094
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000095 """
96 indent = int(indent)
97 width = int(width)
98 assert indent >= 0
Tim Petersa814db52001-05-14 07:05:58 +000099 assert depth is None or depth > 0, "depth must be > 0"
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000100 assert width
101 self.__depth = depth
102 self.__indent_per_level = indent
103 self.__width = width
104 if stream:
105 self.__stream = stream
106 else:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000107 self.__stream = sys.stdout
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000108
Fred Drakea89fda01997-04-16 16:59:30 +0000109 def pprint(self, object):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000110 self.__stream.write(self.pformat(object) + "\n")
Fred Drakea89fda01997-04-16 16:59:30 +0000111
112 def pformat(self, object):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000113 sio = StringIO()
114 self.__format(object, sio, 0, 0, {}, 0)
115 return sio.getvalue()
Fred Drakea89fda01997-04-16 16:59:30 +0000116
Fred Drakee0ffabe1997-07-18 20:42:39 +0000117 def isrecursive(self, object):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000118 self.__recursive = 0
Fred Drake49cc01e2001-11-01 17:50:38 +0000119 self.__repr(object, {}, 0)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000120 return self.__recursive
Fred Drakee0ffabe1997-07-18 20:42:39 +0000121
122 def isreadable(self, object):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000123 self.__recursive = 0
124 self.__readable = 1
Fred Drake49cc01e2001-11-01 17:50:38 +0000125 self.__repr(object, {}, 0)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000126 return self.__readable and not self.__recursive
Fred Drakee0ffabe1997-07-18 20:42:39 +0000127
Fred Drakea89fda01997-04-16 16:59:30 +0000128 def __format(self, object, stream, indent, allowance, context, level):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000129 level = level + 1
Fred Drake49cc01e2001-11-01 17:50:38 +0000130 objid = _id(object)
131 if objid in context:
132 stream.write(_recursion(object))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000133 self.__recursive = 1
Fred Drake49cc01e2001-11-01 17:50:38 +0000134 self.__readable = 0
135 return
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000136 rep = self.__repr(object, context, level - 1)
Fred Drake49cc01e2001-11-01 17:50:38 +0000137 typ = _type(object)
138 sepLines = _len(rep) > (self.__width - 1 - indent - allowance)
139 write = stream.write
Fred Drakea89fda01997-04-16 16:59:30 +0000140
Fred Drake49cc01e2001-11-01 17:50:38 +0000141 if sepLines:
142 if typ is DictType:
143 write('{')
144 if self.__indent_per_level > 1:
145 write((self.__indent_per_level - 1) * ' ')
146 length = _len(object)
147 if length:
148 context[objid] = 1
149 indent = indent + self.__indent_per_level
150 items = object.items()
151 items.sort()
152 key, ent = items[0]
153 rep = self.__repr(key, context, level)
154 write(rep)
155 write(': ')
156 self.__format(ent, stream, indent + _len(rep) + 2,
157 allowance + 1, context, level)
158 if length > 1:
159 for key, ent in items[1:]:
160 rep = self.__repr(key, context, level)
Barry Warsaw00859c02001-11-28 05:49:39 +0000161 write(',\n%s%s: ' % (' '*indent, rep))
Fred Drake49cc01e2001-11-01 17:50:38 +0000162 self.__format(ent, stream, indent + _len(rep) + 2,
163 allowance + 1, context, level)
164 indent = indent - self.__indent_per_level
165 del context[objid]
166 write('}')
167 return
Fred Drakea89fda01997-04-16 16:59:30 +0000168
Fred Drake49cc01e2001-11-01 17:50:38 +0000169 if typ is ListType or typ is TupleType:
170 if typ is ListType:
171 write('[')
172 endchar = ']'
173 else:
174 write('(')
175 endchar = ')'
176 if self.__indent_per_level > 1:
177 write((self.__indent_per_level - 1) * ' ')
178 length = _len(object)
179 if length:
180 context[objid] = 1
181 indent = indent + self.__indent_per_level
182 self.__format(object[0], stream, indent, allowance + 1,
183 context, level)
184 if length > 1:
185 for ent in object[1:]:
186 write(',\n' + ' '*indent)
187 self.__format(ent, stream, indent,
188 allowance + 1, context, level)
189 indent = indent - self.__indent_per_level
190 del context[objid]
191 if typ is TupleType and length == 1:
192 write(',')
193 write(endchar)
194 return
Fred Drakea89fda01997-04-16 16:59:30 +0000195
Fred Drake49cc01e2001-11-01 17:50:38 +0000196 write(rep)
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000197
Fred Drakea89fda01997-04-16 16:59:30 +0000198 def __repr(self, object, context, level):
Tim Petersa814db52001-05-14 07:05:58 +0000199 repr, readable, recursive = _safe_repr(object, context,
200 self.__depth, level)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000201 if not readable:
202 self.__readable = 0
Tim Petersa814db52001-05-14 07:05:58 +0000203 if recursive:
204 self.__recursive = 1
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000205 return repr
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000206
Tim Petersa814db52001-05-14 07:05:58 +0000207# Return triple (repr_string, isreadable, isrecursive).
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000208
Fred Drake49cc01e2001-11-01 17:50:38 +0000209def _safe_repr(object, context, maxlevels, level):
210 typ = _type(object)
211 if typ is StringType:
212 if 'locale' not in _sys_modules:
Fred Drake1ef106c2001-09-04 19:43:26 +0000213 return `object`, 1, 0
214 if "'" in object and '"' not in object:
215 closure = '"'
216 quotes = {'"': '\\"'}
217 else:
218 closure = "'"
219 quotes = {"'": "\\'"}
Fred Drake49cc01e2001-11-01 17:50:38 +0000220 qget = quotes.get
Fred Drake1ef106c2001-09-04 19:43:26 +0000221 sio = StringIO()
Fred Drake49cc01e2001-11-01 17:50:38 +0000222 write = sio.write
Fred Drake1ef106c2001-09-04 19:43:26 +0000223 for char in object:
224 if char.isalpha():
Fred Drake49cc01e2001-11-01 17:50:38 +0000225 write(char)
Fred Drake1ef106c2001-09-04 19:43:26 +0000226 else:
Fred Drake49cc01e2001-11-01 17:50:38 +0000227 write(qget(char, `char`[1:-1]))
228 return ("%s%s%s" % (closure, sio.getvalue(), closure)), 1, 0
Tim Peters95b3f782001-05-14 18:39:41 +0000229
Fred Drake49cc01e2001-11-01 17:50:38 +0000230 if typ is DictType:
231 if not object:
232 return "{}", 1, 0
233 objid = _id(object)
234 if maxlevels and level > maxlevels:
235 return "{...}", 0, objid in context
236 if objid in context:
237 return _recursion(object), 0, 1
238 context[objid] = 1
239 readable = 1
240 recursive = 0
Tim Peters95b3f782001-05-14 18:39:41 +0000241 components = []
Fred Drake49cc01e2001-11-01 17:50:38 +0000242 append = components.append
243 level += 1
244 saferepr = _safe_repr
Tim Peters95b3f782001-05-14 18:39:41 +0000245 for k, v in object.iteritems():
Fred Drake49cc01e2001-11-01 17:50:38 +0000246 krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
247 vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
248 append("%s: %s" % (krepr, vrepr))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000249 readable = readable and kreadable and vreadable
Fred Drake49cc01e2001-11-01 17:50:38 +0000250 if krecur or vrecur:
251 recursive = 1
252 del context[objid]
253 return "{%s}" % _commajoin(components), readable, recursive
Tim Peters95b3f782001-05-14 18:39:41 +0000254
Fred Drake49cc01e2001-11-01 17:50:38 +0000255 if typ is ListType or typ is TupleType:
256 if typ is ListType:
257 if not object:
258 return "[]", 1, 0
259 format = "[%s]"
260 elif _len(object) == 1:
261 format = "(%s,)"
262 else:
263 if not object:
264 return "()", 1, 0
265 format = "(%s)"
266 objid = _id(object)
267 if maxlevels and level > maxlevels:
268 return format % "...", 0, objid in context
269 if objid in context:
270 return _recursion(object), 0, 1
271 context[objid] = 1
272 readable = 1
273 recursive = 0
Tim Peters95b3f782001-05-14 18:39:41 +0000274 components = []
Fred Drake49cc01e2001-11-01 17:50:38 +0000275 append = components.append
276 level += 1
277 for o in object:
278 orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level)
279 append(orepr)
280 if not oreadable:
281 readable = 0
282 if orecur:
283 recursive = 1
284 del context[objid]
285 return format % _commajoin(components), readable, recursive
Tim Peters88768482001-11-13 21:51:26 +0000286
Fred Drake49cc01e2001-11-01 17:50:38 +0000287 rep = `object`
288 return rep, (rep and not rep.startswith('<')), 0
Tim Peters95b3f782001-05-14 18:39:41 +0000289
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000290
Fred Drake49cc01e2001-11-01 17:50:38 +0000291def _recursion(object):
292 return ("<Recursion on %s with id=%s>"
293 % (_type(object).__name__, _id(object)))
Fred Drakea89fda01997-04-16 16:59:30 +0000294
Fred Drake49cc01e2001-11-01 17:50:38 +0000295
296def _perfcheck(object=None):
297 import time
298 if object is None:
299 object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
300 p = PrettyPrinter()
301 t1 = time.time()
302 _safe_repr(object, {}, None, 0)
303 t2 = time.time()
304 p.pformat(object)
305 t3 = time.time()
306 print "_safe_repr:", t2 - t1
307 print "pformat:", t3 - t2
308
309if __name__ == "__main__":
310 _perfcheck()