append(): Fixing the test for convertability after consultation with
[python/dscho.git] / Lib / pprint.py
blobd7d0405b2282dd815c091ec4f2b11ee6dff82faf
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.sydout].
31 saferepr()
32 Generate a 'standard' repr()-like value, but protect against recursive
33 data structures.
35 """
37 from types import DictType, ListType, TupleType, StringType
38 import sys
40 try:
41 from cStringIO import StringIO
42 except ImportError:
43 from StringIO import StringIO
45 __all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
46 "PrettyPrinter"]
48 # cache these for faster access:
49 _commajoin = ", ".join
50 _sys_modules = sys.modules
51 _id = id
52 _len = len
53 _type = type
56 def 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)
61 def pformat(object):
62 """Format a Python object into a pretty-printed representation."""
63 return PrettyPrinter().pformat(object)
65 def saferepr(object):
66 """Version of repr() which can handle recursive data structures."""
67 return _safe_repr(object, {}, None, 0)[0]
69 def isreadable(object):
70 """Determine if saferepr(object) is readable by eval()."""
71 return _safe_repr(object, {}, None, 0)[1]
73 def isrecursive(object):
74 """Determine if object requires a recursive representation."""
75 return _safe_repr(object, {}, None, 0)[2]
77 class PrettyPrinter:
78 def __init__(self, indent=1, width=80, depth=None, stream=None):
79 """Handle pretty printing operations onto a stream using a set of
80 configured parameters.
82 indent
83 Number of spaces to indent for each level of nesting.
85 width
86 Attempted maximum number of columns in the output.
88 depth
89 The maximum depth to print out nested structures.
91 stream
92 The desired output stream. If omitted (or false), the standard
93 output stream available at construction will be used.
95 """
96 indent = int(indent)
97 width = int(width)
98 assert indent >= 0
99 assert depth is None or depth > 0, "depth must be > 0"
100 assert width
101 self._depth = depth
102 self._indent_per_level = indent
103 self._width = width
104 if stream is not None:
105 self._stream = stream
106 else:
107 self._stream = sys.stdout
109 def pprint(self, object):
110 self._stream.write(self.pformat(object) + "\n")
112 def pformat(self, object):
113 sio = StringIO()
114 self._format(object, sio, 0, 0, {}, 0)
115 return sio.getvalue()
117 def isrecursive(self, object):
118 return self.format(object, {}, 0)[2]
120 def isreadable(self, object):
121 s, readable, recursive = self.format(object, {}, 0)
122 return readable and not recursive
124 def _format(self, object, stream, indent, allowance, context, level):
125 level = level + 1
126 objid = _id(object)
127 if objid in context:
128 stream.write(_recursion(object))
129 self._recursive = True
130 self._readable = False
131 return
132 rep = self._repr(object, context, level - 1)
133 typ = _type(object)
134 sepLines = _len(rep) > (self._width - 1 - indent - allowance)
135 write = stream.write
137 if sepLines:
138 if typ is DictType:
139 write('{')
140 if self._indent_per_level > 1:
141 write((self._indent_per_level - 1) * ' ')
142 length = _len(object)
143 if length:
144 context[objid] = 1
145 indent = indent + self._indent_per_level
146 items = object.items()
147 items.sort()
148 key, ent = items[0]
149 rep = self._repr(key, context, level)
150 write(rep)
151 write(': ')
152 self._format(ent, stream, indent + _len(rep) + 2,
153 allowance + 1, context, level)
154 if length > 1:
155 for key, ent in items[1:]:
156 rep = self._repr(key, context, level)
157 write(',\n%s%s: ' % (' '*indent, rep))
158 self._format(ent, stream, indent + _len(rep) + 2,
159 allowance + 1, context, level)
160 indent = indent - self._indent_per_level
161 del context[objid]
162 write('}')
163 return
165 if typ is ListType or typ is TupleType:
166 if typ is ListType:
167 write('[')
168 endchar = ']'
169 else:
170 write('(')
171 endchar = ')'
172 if self._indent_per_level > 1:
173 write((self._indent_per_level - 1) * ' ')
174 length = _len(object)
175 if length:
176 context[objid] = 1
177 indent = indent + self._indent_per_level
178 self._format(object[0], stream, indent, allowance + 1,
179 context, level)
180 if length > 1:
181 for ent in object[1:]:
182 write(',\n' + ' '*indent)
183 self._format(ent, stream, indent,
184 allowance + 1, context, level)
185 indent = indent - self._indent_per_level
186 del context[objid]
187 if typ is TupleType and length == 1:
188 write(',')
189 write(endchar)
190 return
192 write(rep)
194 def _repr(self, object, context, level):
195 repr, readable, recursive = self.format(object, context.copy(),
196 self._depth, level)
197 if not readable:
198 self._readable = False
199 if recursive:
200 self._recursive = True
201 return repr
203 def format(self, object, context, maxlevels, level):
204 """Format object for a specific context, returning a string
205 and flags indicating whether the representation is 'readable'
206 and whether the object represents a recursive construct.
208 return _safe_repr(object, context, maxlevels, level)
211 # Return triple (repr_string, isreadable, isrecursive).
213 def _safe_repr(object, context, maxlevels, level):
214 typ = _type(object)
215 if typ is StringType:
216 if 'locale' not in _sys_modules:
217 return `object`, True, False
218 if "'" in object and '"' not in object:
219 closure = '"'
220 quotes = {'"': '\\"'}
221 else:
222 closure = "'"
223 quotes = {"'": "\\'"}
224 qget = quotes.get
225 sio = StringIO()
226 write = sio.write
227 for char in object:
228 if char.isalpha():
229 write(char)
230 else:
231 write(qget(char, `char`[1:-1]))
232 return ("%s%s%s" % (closure, sio.getvalue(), closure)), True, False
234 if typ is DictType:
235 if not object:
236 return "{}", True, False
237 objid = _id(object)
238 if maxlevels and level > maxlevels:
239 return "{...}", False, objid in context
240 if objid in context:
241 return _recursion(object), False, True
242 context[objid] = 1
243 readable = True
244 recursive = False
245 components = []
246 append = components.append
247 level += 1
248 saferepr = _safe_repr
249 for k, v in object.iteritems():
250 krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
251 vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
252 append("%s: %s" % (krepr, vrepr))
253 readable = readable and kreadable and vreadable
254 if krecur or vrecur:
255 recursive = True
256 del context[objid]
257 return "{%s}" % _commajoin(components), readable, recursive
259 if typ is ListType or typ is TupleType:
260 if typ is ListType:
261 if not object:
262 return "[]", True, False
263 format = "[%s]"
264 elif _len(object) == 1:
265 format = "(%s,)"
266 else:
267 if not object:
268 return "()", True, False
269 format = "(%s)"
270 objid = _id(object)
271 if maxlevels and level > maxlevels:
272 return format % "...", False, objid in context
273 if objid in context:
274 return _recursion(object), False, True
275 context[objid] = 1
276 readable = True
277 recursive = False
278 components = []
279 append = components.append
280 level += 1
281 for o in object:
282 orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level)
283 append(orepr)
284 if not oreadable:
285 readable = False
286 if orecur:
287 recursive = True
288 del context[objid]
289 return format % _commajoin(components), readable, recursive
291 rep = `object`
292 return rep, (rep and not rep.startswith('<')), False
295 def _recursion(object):
296 return ("<Recursion on %s with id=%s>"
297 % (_type(object).__name__, _id(object)))
300 def _perfcheck(object=None):
301 import time
302 if object is None:
303 object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
304 p = PrettyPrinter()
305 t1 = time.time()
306 _safe_repr(object, {}, None, 0)
307 t2 = time.time()
308 p.pformat(object)
309 t3 = time.time()
310 print "_safe_repr:", t2 - t1
311 print "pformat:", t3 - t2
313 if __name__ == "__main__":
314 _perfcheck()