update pydoc topics
[python/dscho.git] / Lib / pprint.py
blob8bbc845a84d9985a7526ff2bc602575a08d6ca6a
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 PrettyPrinter:
74 def __init__(self, indent=1, width=80, depth=None, stream=None):
75 """Handle pretty printing operations onto a stream using a set of
76 configured parameters.
78 indent
79 Number of spaces to indent for each level of nesting.
81 width
82 Attempted maximum number of columns in the output.
84 depth
85 The maximum depth to print out nested structures.
87 stream
88 The desired output stream. If omitted (or false), the standard
89 output stream available at construction will be used.
91 """
92 indent = int(indent)
93 width = int(width)
94 assert indent >= 0, "indent must be >= 0"
95 assert depth is None or depth > 0, "depth must be > 0"
96 assert width, "width must be != 0"
97 self._depth = depth
98 self._indent_per_level = indent
99 self._width = width
100 if stream is not None:
101 self._stream = stream
102 else:
103 self._stream = _sys.stdout
105 def pprint(self, object):
106 self._format(object, self._stream, 0, 0, {}, 0)
107 self._stream.write("\n")
109 def pformat(self, object):
110 sio = _StringIO()
111 self._format(object, sio, 0, 0, {}, 0)
112 return sio.getvalue()
114 def isrecursive(self, object):
115 return self.format(object, {}, 0, 0)[2]
117 def isreadable(self, object):
118 s, readable, recursive = self.format(object, {}, 0, 0)
119 return readable and not recursive
121 def _format(self, object, stream, indent, allowance, context, level):
122 level = level + 1
123 objid = _id(object)
124 if objid in context:
125 stream.write(_recursion(object))
126 self._recursive = True
127 self._readable = False
128 return
129 rep = self._repr(object, context, level - 1)
130 typ = _type(object)
131 sepLines = _len(rep) > (self._width - 1 - indent - allowance)
132 write = stream.write
134 if self._depth and level > self._depth:
135 write(rep)
136 return
138 if sepLines:
139 r = getattr(typ, "__repr__", None)
140 if issubclass(typ, dict) and r is dict.__repr__:
141 write('{')
142 if self._indent_per_level > 1:
143 write((self._indent_per_level - 1) * ' ')
144 length = _len(object)
145 if length:
146 context[objid] = 1
147 indent = indent + self._indent_per_level
148 items = sorted(object.items())
149 key, ent = items[0]
150 rep = self._repr(key, context, level)
151 write(rep)
152 write(': ')
153 self._format(ent, stream, indent + _len(rep) + 2,
154 allowance + 1, context, level)
155 if length > 1:
156 for key, ent in items[1:]:
157 rep = self._repr(key, context, level)
158 write(',\n%s%s: ' % (' '*indent, rep))
159 self._format(ent, stream, indent + _len(rep) + 2,
160 allowance + 1, context, level)
161 indent = indent - self._indent_per_level
162 del context[objid]
163 write('}')
164 return
166 if ((issubclass(typ, list) and r is list.__repr__) or
167 (issubclass(typ, tuple) and r is tuple.__repr__) or
168 (issubclass(typ, set) and r is set.__repr__) or
169 (issubclass(typ, frozenset) and r is frozenset.__repr__)
171 length = _len(object)
172 if issubclass(typ, list):
173 write('[')
174 endchar = ']'
175 elif issubclass(typ, set):
176 if not length:
177 write('set()')
178 return
179 write('{')
180 endchar = '}'
181 object = sorted(object)
182 elif issubclass(typ, frozenset):
183 if not length:
184 write('frozenset()')
185 return
186 write('frozenset({')
187 endchar = '})'
188 object = sorted(object)
189 indent += 10
190 else:
191 write('(')
192 endchar = ')'
193 if self._indent_per_level > 1:
194 write((self._indent_per_level - 1) * ' ')
195 if length:
196 context[objid] = 1
197 indent = indent + self._indent_per_level
198 self._format(object[0], stream, indent, allowance + 1,
199 context, level)
200 if length > 1:
201 for ent in object[1:]:
202 write(',\n' + ' '*indent)
203 self._format(ent, stream, indent,
204 allowance + 1, context, level)
205 indent = indent - self._indent_per_level
206 del context[objid]
207 if issubclass(typ, tuple) and length == 1:
208 write(',')
209 write(endchar)
210 return
212 write(rep)
214 def _repr(self, object, context, level):
215 repr, readable, recursive = self.format(object, context.copy(),
216 self._depth, level)
217 if not readable:
218 self._readable = False
219 if recursive:
220 self._recursive = True
221 return repr
223 def format(self, object, context, maxlevels, level):
224 """Format object for a specific context, returning a string
225 and flags indicating whether the representation is 'readable'
226 and whether the object represents a recursive construct.
228 return _safe_repr(object, context, maxlevels, level)
231 # Return triple (repr_string, isreadable, isrecursive).
233 def _safe_repr(object, context, maxlevels, level):
234 typ = _type(object)
235 if typ is str:
236 if 'locale' not in _sys.modules:
237 return repr(object), True, False
238 if "'" in object and '"' not in object:
239 closure = '"'
240 quotes = {'"': '\\"'}
241 else:
242 closure = "'"
243 quotes = {"'": "\\'"}
244 qget = quotes.get
245 sio = _StringIO()
246 write = sio.write
247 for char in object:
248 if char.isalpha():
249 write(char)
250 else:
251 write(qget(char, repr(char)[1:-1]))
252 return ("%s%s%s" % (closure, sio.getvalue(), closure)), True, False
254 r = getattr(typ, "__repr__", None)
255 if issubclass(typ, dict) and r is dict.__repr__:
256 if not object:
257 return "{}", True, False
258 objid = _id(object)
259 if maxlevels and level >= maxlevels:
260 return "{...}", False, objid in context
261 if objid in context:
262 return _recursion(object), False, True
263 context[objid] = 1
264 readable = True
265 recursive = False
266 components = []
267 append = components.append
268 level += 1
269 saferepr = _safe_repr
270 items = object.items()
271 try:
272 items = sorted(items)
273 except TypeError:
274 def sortkey(item):
275 key, value = item
276 return str(type(key)), key, value
277 items = sorted(items, key=sortkey)
278 for k, v in items:
279 krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
280 vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
281 append("%s: %s" % (krepr, vrepr))
282 readable = readable and kreadable and vreadable
283 if krecur or vrecur:
284 recursive = True
285 del context[objid]
286 return "{%s}" % _commajoin(components), readable, recursive
288 if (issubclass(typ, list) and r is list.__repr__) or \
289 (issubclass(typ, tuple) and r is tuple.__repr__):
290 if issubclass(typ, list):
291 if not object:
292 return "[]", True, False
293 format = "[%s]"
294 elif _len(object) == 1:
295 format = "(%s,)"
296 else:
297 if not object:
298 return "()", True, False
299 format = "(%s)"
300 objid = _id(object)
301 if maxlevels and level >= maxlevels:
302 return format % "...", False, objid in context
303 if objid in context:
304 return _recursion(object), False, True
305 context[objid] = 1
306 readable = True
307 recursive = False
308 components = []
309 append = components.append
310 level += 1
311 for o in object:
312 orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level)
313 append(orepr)
314 if not oreadable:
315 readable = False
316 if orecur:
317 recursive = True
318 del context[objid]
319 return format % _commajoin(components), readable, recursive
321 rep = repr(object)
322 return rep, (rep and not rep.startswith('<')), False
325 def _recursion(object):
326 return ("<Recursion on %s with id=%s>"
327 % (_type(object).__name__, _id(object)))
330 def _perfcheck(object=None):
331 import time
332 if object is None:
333 object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
334 p = PrettyPrinter()
335 t1 = time.time()
336 _safe_repr(object, {}, None, 0)
337 t2 = time.time()
338 p.pformat(object)
339 t3 = time.time()
340 print("_safe_repr:", t2 - t1)
341 print("pformat:", t3 - t2)
343 if __name__ == "__main__":
344 _perfcheck()