]> git.lizzy.rs Git - plan9front.git/blob - sys/lib/python/pprint.py
cdproto: add spim
[plan9front.git] / sys / lib / python / pprint.py
1 #  Author:      Fred L. Drake, Jr.
2 #               fdrake@acm.org
3 #
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.
10
11 """Support to pretty-print lists, tuples, & dictionaries recursively.
12
13 Very simple, but useful, especially in debugging data structures.
14
15 Classes
16 -------
17
18 PrettyPrinter()
19     Handle pretty-printing operations onto a stream using a configured
20     set of formatting parameters.
21
22 Functions
23 ---------
24
25 pformat()
26     Format a Python object into a pretty-printed representation.
27
28 pprint()
29     Pretty-print a Python object to a stream [default is sys.stdout].
30
31 saferepr()
32     Generate a 'standard' repr()-like value, but protect against recursive
33     data structures.
34
35 """
36
37 import sys as _sys
38
39 from cStringIO import StringIO as _StringIO
40
41 __all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
42            "PrettyPrinter"]
43
44 # cache these for faster access:
45 _commajoin = ", ".join
46 _id = id
47 _len = len
48 _type = type
49
50
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)
56
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)
60
61 def saferepr(object):
62     """Version of repr() which can handle recursive data structures."""
63     return _safe_repr(object, {}, None, 0)[0]
64
65 def isreadable(object):
66     """Determine if saferepr(object) is readable by eval()."""
67     return _safe_repr(object, {}, None, 0)[1]
68
69 def isrecursive(object):
70     """Determine if object requires a recursive representation."""
71     return _safe_repr(object, {}, None, 0)[2]
72
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.
77
78         indent
79             Number of spaces to indent for each level of nesting.
80
81         width
82             Attempted maximum number of columns in the output.
83
84         depth
85             The maximum depth to print out nested structures.
86
87         stream
88             The desired output stream.  If omitted (or false), the standard
89             output stream available at construction will be used.
90
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
104
105     def pprint(self, object):
106         self._format(object, self._stream, 0, 0, {}, 0)
107         self._stream.write("\n")
108
109     def pformat(self, object):
110         sio = _StringIO()
111         self._format(object, sio, 0, 0, {}, 0)
112         return sio.getvalue()
113
114     def isrecursive(self, object):
115         return self.format(object, {}, 0, 0)[2]
116
117     def isreadable(self, object):
118         s, readable, recursive = self.format(object, {}, 0, 0)
119         return readable and not recursive
120
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
133
134         if sepLines:
135             r = getattr(typ, "__repr__", None)
136             if issubclass(typ, dict) and r is dict.__repr__:
137                 write('{')
138                 if self._indent_per_level > 1:
139                     write((self._indent_per_level - 1) * ' ')
140                 length = _len(object)
141                 if length:
142                     context[objid] = 1
143                     indent = indent + self._indent_per_level
144                     items  = object.items()
145                     items.sort()
146                     key, ent = items[0]
147                     rep = self._repr(key, context, level)
148                     write(rep)
149                     write(': ')
150                     self._format(ent, stream, indent + _len(rep) + 2,
151                                   allowance + 1, context, level)
152                     if length > 1:
153                         for key, ent in items[1:]:
154                             rep = self._repr(key, context, level)
155                             write(',\n%s%s: ' % (' '*indent, rep))
156                             self._format(ent, stream, indent + _len(rep) + 2,
157                                           allowance + 1, context, level)
158                     indent = indent - self._indent_per_level
159                     del context[objid]
160                 write('}')
161                 return
162
163             if (issubclass(typ, list) and r is list.__repr__) or \
164                (issubclass(typ, tuple) and r is tuple.__repr__):
165                 if issubclass(typ, list):
166                     write('[')
167                     endchar = ']'
168                 else:
169                     write('(')
170                     endchar = ')'
171                 if self._indent_per_level > 1:
172                     write((self._indent_per_level - 1) * ' ')
173                 length = _len(object)
174                 if length:
175                     context[objid] = 1
176                     indent = indent + self._indent_per_level
177                     self._format(object[0], stream, indent, allowance + 1,
178                                  context, level)
179                     if length > 1:
180                         for ent in object[1:]:
181                             write(',\n' + ' '*indent)
182                             self._format(ent, stream, indent,
183                                           allowance + 1, context, level)
184                     indent = indent - self._indent_per_level
185                     del context[objid]
186                 if issubclass(typ, tuple) and length == 1:
187                     write(',')
188                 write(endchar)
189                 return
190
191         write(rep)
192
193     def _repr(self, object, context, level):
194         repr, readable, recursive = self.format(object, context.copy(),
195                                                 self._depth, level)
196         if not readable:
197             self._readable = False
198         if recursive:
199             self._recursive = True
200         return repr
201
202     def format(self, object, context, maxlevels, level):
203         """Format object for a specific context, returning a string
204         and flags indicating whether the representation is 'readable'
205         and whether the object represents a recursive construct.
206         """
207         return _safe_repr(object, context, maxlevels, level)
208
209
210 # Return triple (repr_string, isreadable, isrecursive).
211
212 def _safe_repr(object, context, maxlevels, level):
213     typ = _type(object)
214     if typ is str:
215         if 'locale' not in _sys.modules:
216             return repr(object), True, False
217         if "'" in object and '"' not in object:
218             closure = '"'
219             quotes = {'"': '\\"'}
220         else:
221             closure = "'"
222             quotes = {"'": "\\'"}
223         qget = quotes.get
224         sio = _StringIO()
225         write = sio.write
226         for char in object:
227             if char.isalpha():
228                 write(char)
229             else:
230                 write(qget(char, repr(char)[1:-1]))
231         return ("%s%s%s" % (closure, sio.getvalue(), closure)), True, False
232
233     r = getattr(typ, "__repr__", None)
234     if issubclass(typ, dict) and r is dict.__repr__:
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 sorted(object.items()):
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
258
259     if (issubclass(typ, list) and r is list.__repr__) or \
260        (issubclass(typ, tuple) and r is tuple.__repr__):
261         if issubclass(typ, list):
262             if not object:
263                 return "[]", True, False
264             format = "[%s]"
265         elif _len(object) == 1:
266             format = "(%s,)"
267         else:
268             if not object:
269                 return "()", True, False
270             format = "(%s)"
271         objid = _id(object)
272         if maxlevels and level > maxlevels:
273             return format % "...", False, objid in context
274         if objid in context:
275             return _recursion(object), False, True
276         context[objid] = 1
277         readable = True
278         recursive = False
279         components = []
280         append = components.append
281         level += 1
282         for o in object:
283             orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level)
284             append(orepr)
285             if not oreadable:
286                 readable = False
287             if orecur:
288                 recursive = True
289         del context[objid]
290         return format % _commajoin(components), readable, recursive
291
292     rep = repr(object)
293     return rep, (rep and not rep.startswith('<')), False
294
295
296 def _recursion(object):
297     return ("<Recursion on %s with id=%s>"
298             % (_type(object).__name__, _id(object)))
299
300
301 def _perfcheck(object=None):
302     import time
303     if object is None:
304         object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
305     p = PrettyPrinter()
306     t1 = time.time()
307     _safe_repr(object, {}, None, 0)
308     t2 = time.time()
309     p.pformat(object)
310     t3 = time.time()
311     print "_safe_repr:", t2 - t1
312     print "pformat:", t3 - t2
313
314 if __name__ == "__main__":
315     _perfcheck()