blob: d95cf1a18087323ec513364d23a179f93ed0e351 [file] [log] [blame]
Guido van Rossum5e92aff1997-04-16 00:49:59 +00001# Author: Fred L. Drake, Jr.
Fred Drakea89fda01997-04-16 16:59:30 +00002# fdrake@cnri.reston.va.us, fdrake@acm.org
Guido van Rossum5e92aff1997-04-16 00:49:59 +00003#
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 modelled 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
13Very simple, but useful, especially in debugging data structures.
14
Fred Drakea89fda01997-04-16 16:59:30 +000015Classes
16-------
17
18PrettyPrinter()
19 Handle pretty-printing operations onto a stream using a configured
20 set of formatting parameters.
21
Guido van Rossum5e92aff1997-04-16 00:49:59 +000022Functions
23---------
24
25pformat()
26 Format a Python object into a pretty-printed representation.
27
28pprint()
Fred Drakea89fda01997-04-16 16:59:30 +000029 Pretty-print a Python object to a stream [default is sys.sydout].
Guido van Rossum5e92aff1997-04-16 00:49:59 +000030
Fred Drakea89fda01997-04-16 16:59:30 +000031saferepr()
32 Generate a 'standard' repr()-like value, but protect against recursive
33 data structures.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000034
35"""
36
Guido van Rossum5e92aff1997-04-16 00:49:59 +000037from types import DictType, ListType, TupleType
38
Fred Drakea89fda01997-04-16 16:59:30 +000039try:
40 from cStringIO import StringIO
41except ImportError:
42 from StringIO import StringIO
Guido van Rossum5e92aff1997-04-16 00:49:59 +000043
44
Fred Drakea89fda01997-04-16 16:59:30 +000045def pprint(object, stream=None):
46 """Pretty-print a Python object to a stream [default is sys.sydout]."""
47 printer = PrettyPrinter(stream=stream)
48 printer.pprint(object)
Guido van Rossum5e92aff1997-04-16 00:49:59 +000049
Guido van Rossum5e92aff1997-04-16 00:49:59 +000050
Fred Drakea89fda01997-04-16 16:59:30 +000051def pformat(object):
52 """Format a Python object into a pretty-printed representation."""
53 return PrettyPrinter().pformat(object)
Guido van Rossum5e92aff1997-04-16 00:49:59 +000054
Guido van Rossum5e92aff1997-04-16 00:49:59 +000055
Fred Drakee0ffabe1997-07-18 20:42:39 +000056def isreadable(object):
57 """Determine if saferepr(object) is readable by eval()."""
58 return PrettyPrinter().isreadable(object)
59
60
61def isrecursive(object):
62 """Determine if object requires a recursive representation."""
63 return PrettyPrinter().isrecursive(object)
64
65
Fred Drakea89fda01997-04-16 16:59:30 +000066def saferepr(object):
67 """Version of repr() which can handle recursive data structures."""
Fred Drakee0ffabe1997-07-18 20:42:39 +000068 return _safe_repr(object, {})[0]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000069
Guido van Rossum5e92aff1997-04-16 00:49:59 +000070
Fred Drakea89fda01997-04-16 16:59:30 +000071class PrettyPrinter:
72 def __init__(self, indent=1, width=80, depth=None, stream=None):
73 """Handle pretty printing operations onto a stream using a set of
74 configured parameters.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000075
Fred Drakea89fda01997-04-16 16:59:30 +000076 indent
77 Number of spaces to indent for each level of nesting.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000078
Fred Drakea89fda01997-04-16 16:59:30 +000079 width
80 Attempted maximum number of columns in the output.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000081
Fred Drakea89fda01997-04-16 16:59:30 +000082 depth
83 The maximum depth to print out nested structures.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000084
Fred Drakea89fda01997-04-16 16:59:30 +000085 stream
86 The desired output stream. If omitted (or false), the standard
87 output stream available at construction will be used.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000088
Fred Drakea89fda01997-04-16 16:59:30 +000089 """
Fred Drakee0ffabe1997-07-18 20:42:39 +000090 indent = int(indent)
91 width = int(width)
92 assert indent >= 0
Fred Drakea89fda01997-04-16 16:59:30 +000093 assert (not depth) or depth > 0, "depth may not be negative"
Fred Drakee0ffabe1997-07-18 20:42:39 +000094 assert width
Fred Drakea89fda01997-04-16 16:59:30 +000095 self.__depth = depth
96 self.__indent_per_level = indent
97 self.__width = width
98 if stream:
99 self.__stream = stream
100 else:
101 import sys
102 self.__stream = sys.stdout
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000103
Fred Drakea89fda01997-04-16 16:59:30 +0000104 def pprint(self, object):
105 self.__stream.write(self.pformat(object) + "\n")
106
107 def pformat(self, object):
108 sio = StringIO()
109 self.__format(object, sio, 0, 0, {}, 0)
110 return sio.getvalue()
111
Fred Drakee0ffabe1997-07-18 20:42:39 +0000112 def isrecursive(self, object):
113 self.__recursive = 0
114 self.pformat(object)
115 return self.__recursive
116
117 def isreadable(self, object):
118 self.__recursive = 0
119 self.__readable = 1
120 self.pformat(object)
121 return self.__readable and not self.__recursive
122
Fred Drakea89fda01997-04-16 16:59:30 +0000123 def __format(self, object, stream, indent, allowance, context, level):
124 level = level + 1
125 if context.has_key(id(object)):
126 object = _Recursion(object)
Fred Drakee0ffabe1997-07-18 20:42:39 +0000127 self.__recursive = 1
Fred Drake5fd026d1997-04-18 13:54:13 +0000128 rep = self.__repr(object, context, level - 1)
Fred Drakea89fda01997-04-16 16:59:30 +0000129 objid = id(object)
130 context[objid] = 1
131 typ = type(object)
132 sepLines = len(rep) > (self.__width - 1 - indent - allowance)
133
134 if sepLines and typ in (ListType, TupleType):
135 # Pretty-print the sequence.
136 stream.write((typ is ListType) and '[' or '(')
Fred Drakee0ffabe1997-07-18 20:42:39 +0000137 if self.__indent_per_level > 1:
138 stream.write((self.__indent_per_level - 1) * ' ')
Fred Drakea89fda01997-04-16 16:59:30 +0000139 length = len(object)
140 if length:
141 indent = indent + self.__indent_per_level
Fred Drake5fd026d1997-04-18 13:54:13 +0000142 self.__format(object[0], stream, indent, allowance + 1,
143 context, level)
Fred Drakea89fda01997-04-16 16:59:30 +0000144 if len(object) > 1:
145 for ent in object[1:]:
146 stream.write(',\n' + ' '*indent)
147 self.__format(ent, stream, indent,
148 allowance + 1, context, level)
149 indent = indent - self.__indent_per_level
Guido van Rossuma1dbe501997-09-14 23:21:51 +0000150 if typ is TupleType and length == 1:
151 stream.write(',')
Fred Drakea89fda01997-04-16 16:59:30 +0000152 stream.write(((typ is ListType) and ']') or ')')
153
154 elif sepLines and typ is DictType:
155 stream.write('{')
Fred Drakee0ffabe1997-07-18 20:42:39 +0000156 if self.__indent_per_level > 1:
157 stream.write((self.__indent_per_level - 1) * ' ')
Fred Drakea89fda01997-04-16 16:59:30 +0000158 length = len(object)
159 if length:
160 indent = indent + self.__indent_per_level
161 items = object.items()
162 items.sort()
163 key, ent = items[0]
164 rep = self.__repr(key, context, level) + ': '
165 stream.write(rep)
166 self.__format(ent, stream, indent + len(rep),
167 allowance + 1, context, level)
168 if len(items) > 1:
169 for key, ent in items[1:]:
170 rep = self.__repr(key, context, level) + ': '
171 stream.write(',\n' + ' '*indent + rep)
172 self.__format(ent, stream, indent + len(rep),
173 allowance + 1, context, level)
174 indent = indent - self.__indent_per_level
175 stream.write('}')
176
177 else:
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000178 stream.write(rep)
Fred Drakea89fda01997-04-16 16:59:30 +0000179 del context[objid]
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000180
Fred Drakea89fda01997-04-16 16:59:30 +0000181 def __repr(self, object, context, level):
Fred Drakee0ffabe1997-07-18 20:42:39 +0000182 repr, readable = _safe_repr(object, context, self.__depth, level)
183 if not readable:
184 self.__readable = 0
185 return repr
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000186
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000187
Fred Drakef39d0511997-04-16 18:55:58 +0000188def _safe_repr(object, context, maxlevels=None, level=0):
Fred Drakea89fda01997-04-16 16:59:30 +0000189 level = level + 1
Fred Drakee0ffabe1997-07-18 20:42:39 +0000190 readable = 1
Fred Drakea89fda01997-04-16 16:59:30 +0000191 typ = type(object)
192 if not (typ in (DictType, ListType, TupleType) and object):
Fred Drakee0ffabe1997-07-18 20:42:39 +0000193 rep = `object`
194 if rep:
195 if rep[0] == '<':
196 readable = 0
197 else:
198 readable = 0
199 return `object`, readable
Fred Drakef39d0511997-04-16 18:55:58 +0000200 if context.has_key(id(object)):
Fred Drakee0ffabe1997-07-18 20:42:39 +0000201 return `_Recursion(object)`, 0
Fred Drakea89fda01997-04-16 16:59:30 +0000202 objid = id(object)
203 context[objid] = 1
204 if typ is DictType:
205 if maxlevels and level >= maxlevels:
206 s = "{...}"
Fred Drakee0ffabe1997-07-18 20:42:39 +0000207 readable = 0
Fred Drakea89fda01997-04-16 16:59:30 +0000208 else:
209 items = object.items()
210 k, v = items[0]
Fred Drakee0ffabe1997-07-18 20:42:39 +0000211 krepr, kreadable = _safe_repr(k, context, maxlevels, level)
212 vrepr, vreadable = _safe_repr(v, context, maxlevels, level)
213 readable = readable and kreadable and vreadable
214 s = "{%s: %s" % (krepr, vrepr)
Fred Drakea89fda01997-04-16 16:59:30 +0000215 for k, v in items[1:]:
Fred Drakee0ffabe1997-07-18 20:42:39 +0000216 krepr, kreadable = _safe_repr(k, context, maxlevels, level)
217 vrepr, vreadable = _safe_repr(v, context, maxlevels, level)
218 readable = readable and kreadable and vreadable
219 s = "%s, %s: %s" % (s, krepr, vrepr)
Fred Drakea89fda01997-04-16 16:59:30 +0000220 s = s + "}"
221 else:
222 s, term = (typ is ListType) and ('[', ']') or ('(', ')')
223 if maxlevels and level >= maxlevels:
224 s = s + "..."
Fred Drakee0ffabe1997-07-18 20:42:39 +0000225 readable = 0
Fred Drakea89fda01997-04-16 16:59:30 +0000226 else:
Fred Drakee0ffabe1997-07-18 20:42:39 +0000227 subrepr, subreadable = _safe_repr(
228 object[0], context, maxlevels, level)
229 readable = readable and subreadable
230 s = s + subrepr
Guido van Rossuma1dbe501997-09-14 23:21:51 +0000231 tail = object[1:]
232 if not tail:
233 if typ is TupleType:
234 s = s + ','
235 for ent in tail:
Fred Drakee0ffabe1997-07-18 20:42:39 +0000236 subrepr, subreadable = _safe_repr(
237 ent, context, maxlevels, level)
238 readable = readable and subreadable
239 s = "%s, %s" % (s, subrepr)
Fred Drakea89fda01997-04-16 16:59:30 +0000240 s = s + term
241 del context[objid]
Fred Drakee0ffabe1997-07-18 20:42:39 +0000242 return s, readable
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000243
Fred Drakea89fda01997-04-16 16:59:30 +0000244
245class _Recursion:
246 # represent a recursive relationship; really only used for the __repr__()
247 # method...
248 def __init__(self, object):
249 self.__repr = "<Recursion on %s with id=%s>" \
250 % (type(object).__name__, id(object))
251
252 def __repr__(self):
253 return self.__repr