Merged revisions 79260 via svnmerge from
[python/dscho.git] / Lib / pprint.py
blobb3a644649e5ae88d1d3fd1dcca918d159c0c3385
1 # Author: Fred L. Drake, Jr.
2 # fdrake@acm.org
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.
11 """Support to pretty-print lists, tuples, & dictionaries recursively.
13 Very simple, but useful, especially in debugging data structures.
15 Classes
16 -------
18 PrettyPrinter()
19 Handle pretty-printing operations onto a stream using a configured
20 set of formatting parameters.
22 Functions
23 ---------
25 pformat()
26 Format a Python object into a pretty-printed representation.
28 pprint()
29 Pretty-print a Python object to a stream [default is sys.stdout].
31 saferepr()
32 Generate a 'standard' repr()-like value, but protect against recursive
33 data structures.
35 """
37 import sys as _sys
39 from io import StringIO as _StringIO
41 __all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
42 "PrettyPrinter"]
44 # cache these for faster access:
45 _commajoin = ", ".join
46 _id = id
47 _len = len
48 _type = type
51 def pprint(object, stream=None, indent=1, width=80, depth=None):
52 """Pretty-print a Python object to a stream [default is sys.stdout]."""
53 printer = PrettyPrinter(
54 stream=stream, indent=indent, width=width, depth=depth)
55 printer.pprint(object)
57 def pformat(object, indent=1, width=80, depth=None):
58 """Format a Python object into a pretty-printed representation."""
59 return PrettyPrinter(indent=indent, width=width, depth=depth).pformat(object)
61 def saferepr(object):
62 """Version of repr() which can handle recursive data structures."""
63 return _safe_repr(object, {}, None, 0)[0]
65 def isreadable(object):
66 """Determine if saferepr(object) is readable by eval()."""
67 return _safe_repr(object, {}, None, 0)[1]
69 def isrecursive(object):
70 """Determine if object requires a recursive representation."""
71 return _safe_repr(object, {}, None, 0)[2]
73 class _safe_key:
74 """Helper function for key functions when sorting unorderable objects.
76 The wrapped-object will fallback to an Py2.x style comparison for
77 unorderable types (sorting first comparing the type name and then by
78 the obj ids). Does not work recursively, so dict.items() must have
79 _safe_key applied to both the key and the value.
81 """
83 __slots__ = ['obj']
85 def __init__(self, obj):
86 self.obj = obj
88 def __lt__(self, other):
89 rv = self.obj.__lt__(other.obj)
90 if rv is NotImplemented:
91 rv = (str(type(self.obj)), id(self.obj)) < \
92 (str(type(other.obj)), id(other.obj))
93 return rv
95 def _safe_tuple(t):
96 "Helper function for comparing 2-tuples"
97 return _safe_key(t[0]), _safe_key(t[1])
99 class PrettyPrinter:
100 def __init__(self, indent=1, width=80, depth=None, stream=None):
101 """Handle pretty printing operations onto a stream using a set of
102 configured parameters.
104 indent
105 Number of spaces to indent for each level of nesting.
107 width
108 Attempted maximum number of columns in the output.
110 depth
111 The maximum depth to print out nested structures.
113 stream
114 The desired output stream. If omitted (or false), the standard
115 output stream available at construction will be used.
118 indent = int(indent)
119 width = int(width)
120 assert indent >= 0, "indent must be >= 0"
121 assert depth is None or depth > 0, "depth must be > 0"
122 assert width, "width must be != 0"
123 self._depth = depth
124 self._indent_per_level = indent
125 self._width = width
126 if stream is not None:
127 self._stream = stream
128 else:
129 self._stream = _sys.stdout
131 def pprint(self, object):
132 self._format(object, self._stream, 0, 0, {}, 0)
133 self._stream.write("\n")
135 def pformat(self, object):
136 sio = _StringIO()
137 self._format(object, sio, 0, 0, {}, 0)
138 return sio.getvalue()
140 def isrecursive(self, object):
141 return self.format(object, {}, 0, 0)[2]
143 def isreadable(self, object):
144 s, readable, recursive = self.format(object, {}, 0, 0)
145 return readable and not recursive
147 def _format(self, object, stream, indent, allowance, context, level):
148 level = level + 1
149 objid = _id(object)
150 if objid in context:
151 stream.write(_recursion(object))
152 self._recursive = True
153 self._readable = False
154 return
155 rep = self._repr(object, context, level - 1)
156 typ = _type(object)
157 sepLines = _len(rep) > (self._width - 1 - indent - allowance)
158 write = stream.write
160 if self._depth and level > self._depth:
161 write(rep)
162 return
164 if sepLines:
165 r = getattr(typ, "__repr__", None)
166 if issubclass(typ, dict) and r is dict.__repr__:
167 write('{')
168 if self._indent_per_level > 1:
169 write((self._indent_per_level - 1) * ' ')
170 length = _len(object)
171 if length:
172 context[objid] = 1
173 indent = indent + self._indent_per_level
174 items = sorted(object.items(), key=_safe_tuple)
175 key, ent = items[0]
176 rep = self._repr(key, context, level)
177 write(rep)
178 write(': ')
179 self._format(ent, stream, indent + _len(rep) + 2,
180 allowance + 1, context, level)
181 if length > 1:
182 for key, ent in items[1:]:
183 rep = self._repr(key, context, level)
184 write(',\n%s%s: ' % (' '*indent, rep))
185 self._format(ent, stream, indent + _len(rep) + 2,
186 allowance + 1, context, level)
187 indent = indent - self._indent_per_level
188 del context[objid]
189 write('}')
190 return
192 if ((issubclass(typ, list) and r is list.__repr__) or
193 (issubclass(typ, tuple) and r is tuple.__repr__) or
194 (issubclass(typ, set) and r is set.__repr__) or
195 (issubclass(typ, frozenset) and r is frozenset.__repr__)
197 length = _len(object)
198 if issubclass(typ, list):
199 write('[')
200 endchar = ']'
201 elif issubclass(typ, set):
202 if not length:
203 write('set()')
204 return
205 write('{')
206 endchar = '}'
207 object = sorted(object, key=_safe_key)
208 elif issubclass(typ, frozenset):
209 if not length:
210 write('frozenset()')
211 return
212 write('frozenset({')
213 endchar = '})'
214 object = sorted(object, key=_safe_key)
215 indent += 10
216 else:
217 write('(')
218 endchar = ')'
219 if self._indent_per_level > 1:
220 write((self._indent_per_level - 1) * ' ')
221 if length:
222 context[objid] = 1
223 indent = indent + self._indent_per_level
224 self._format(object[0], stream, indent, allowance + 1,
225 context, level)
226 if length > 1:
227 for ent in object[1:]:
228 write(',\n' + ' '*indent)
229 self._format(ent, stream, indent,
230 allowance + 1, context, level)
231 indent = indent - self._indent_per_level
232 del context[objid]
233 if issubclass(typ, tuple) and length == 1:
234 write(',')
235 write(endchar)
236 return
238 write(rep)
240 def _repr(self, object, context, level):
241 repr, readable, recursive = self.format(object, context.copy(),
242 self._depth, level)
243 if not readable:
244 self._readable = False
245 if recursive:
246 self._recursive = True
247 return repr
249 def format(self, object, context, maxlevels, level):
250 """Format object for a specific context, returning a string
251 and flags indicating whether the representation is 'readable'
252 and whether the object represents a recursive construct.
254 return _safe_repr(object, context, maxlevels, level)
257 # Return triple (repr_string, isreadable, isrecursive).
259 def _safe_repr(object, context, maxlevels, level):
260 typ = _type(object)
261 if typ is str:
262 if 'locale' not in _sys.modules:
263 return repr(object), True, False
264 if "'" in object and '"' not in object:
265 closure = '"'
266 quotes = {'"': '\\"'}
267 else:
268 closure = "'"
269 quotes = {"'": "\\'"}
270 qget = quotes.get
271 sio = _StringIO()
272 write = sio.write
273 for char in object:
274 if char.isalpha():
275 write(char)
276 else:
277 write(qget(char, repr(char)[1:-1]))
278 return ("%s%s%s" % (closure, sio.getvalue(), closure)), True, False
280 r = getattr(typ, "__repr__", None)
281 if issubclass(typ, dict) and r is dict.__repr__:
282 if not object:
283 return "{}", True, False
284 objid = _id(object)
285 if maxlevels and level >= maxlevels:
286 return "{...}", False, objid in context
287 if objid in context:
288 return _recursion(object), False, True
289 context[objid] = 1
290 readable = True
291 recursive = False
292 components = []
293 append = components.append
294 level += 1
295 saferepr = _safe_repr
296 items = sorted(object.items(), key=_safe_tuple)
297 for k, v in items:
298 krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
299 vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
300 append("%s: %s" % (krepr, vrepr))
301 readable = readable and kreadable and vreadable
302 if krecur or vrecur:
303 recursive = True
304 del context[objid]
305 return "{%s}" % _commajoin(components), readable, recursive
307 if (issubclass(typ, list) and r is list.__repr__) or \
308 (issubclass(typ, tuple) and r is tuple.__repr__):
309 if issubclass(typ, list):
310 if not object:
311 return "[]", True, False
312 format = "[%s]"
313 elif _len(object) == 1:
314 format = "(%s,)"
315 else:
316 if not object:
317 return "()", True, False
318 format = "(%s)"
319 objid = _id(object)
320 if maxlevels and level >= maxlevels:
321 return format % "...", False, objid in context
322 if objid in context:
323 return _recursion(object), False, True
324 context[objid] = 1
325 readable = True
326 recursive = False
327 components = []
328 append = components.append
329 level += 1
330 for o in object:
331 orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level)
332 append(orepr)
333 if not oreadable:
334 readable = False
335 if orecur:
336 recursive = True
337 del context[objid]
338 return format % _commajoin(components), readable, recursive
340 rep = repr(object)
341 return rep, (rep and not rep.startswith('<')), False
344 def _recursion(object):
345 return ("<Recursion on %s with id=%s>"
346 % (_type(object).__name__, _id(object)))
349 def _perfcheck(object=None):
350 import time
351 if object is None:
352 object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
353 p = PrettyPrinter()
354 t1 = time.time()
355 _safe_repr(object, {}, None, 0)
356 t2 = time.time()
357 p.pformat(object)
358 t3 = time.time()
359 print("_safe_repr:", t2 - t1)
360 print("pformat:", t3 - t2)
362 if __name__ == "__main__":
363 _perfcheck()