This commit was manufactured by cvs2svn to create tag 'r221c2'.
[python/dscho.git] / Lib / pprint.py
blob5d178a2a55d66e6fa07ad5ba9fefd460fc332d38
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:
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 self.__recursive = 0
119 self.__repr(object, {}, 0)
120 return self.__recursive
122 def isreadable(self, object):
123 self.__recursive = 0
124 self.__readable = 1
125 self.__repr(object, {}, 0)
126 return self.__readable and not self.__recursive
128 def __format(self, object, stream, indent, allowance, context, level):
129 level = level + 1
130 objid = _id(object)
131 if objid in context:
132 stream.write(_recursion(object))
133 self.__recursive = 1
134 self.__readable = 0
135 return
136 rep = self.__repr(object, context, level - 1)
137 typ = _type(object)
138 sepLines = _len(rep) > (self.__width - 1 - indent - allowance)
139 write = stream.write
141 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)
161 write(',\n%s%s: ' % (' '*indent, rep))
162 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
169 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
196 write(rep)
198 def __repr(self, object, context, level):
199 repr, readable, recursive = _safe_repr(object, context,
200 self.__depth, level)
201 if not readable:
202 self.__readable = 0
203 if recursive:
204 self.__recursive = 1
205 return repr
207 # Return triple (repr_string, isreadable, isrecursive).
209 def _safe_repr(object, context, maxlevels, level):
210 typ = _type(object)
211 if typ is StringType:
212 if 'locale' not in _sys_modules:
213 return `object`, 1, 0
214 if "'" in object and '"' not in object:
215 closure = '"'
216 quotes = {'"': '\\"'}
217 else:
218 closure = "'"
219 quotes = {"'": "\\'"}
220 qget = quotes.get
221 sio = StringIO()
222 write = sio.write
223 for char in object:
224 if char.isalpha():
225 write(char)
226 else:
227 write(qget(char, `char`[1:-1]))
228 return ("%s%s%s" % (closure, sio.getvalue(), closure)), 1, 0
230 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
241 components = []
242 append = components.append
243 level += 1
244 saferepr = _safe_repr
245 for k, v in object.iteritems():
246 krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
247 vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
248 append("%s: %s" % (krepr, vrepr))
249 readable = readable and kreadable and vreadable
250 if krecur or vrecur:
251 recursive = 1
252 del context[objid]
253 return "{%s}" % _commajoin(components), readable, recursive
255 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
274 components = []
275 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
287 rep = `object`
288 return rep, (rep and not rep.startswith('<')), 0
291 def _recursion(object):
292 return ("<Recursion on %s with id=%s>"
293 % (_type(object).__name__, _id(object)))
296 def _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
309 if __name__ == "__main__":
310 _perfcheck()