blob: 79b17b3ca16e81f8a639e00b726ba1a76f9c1978 [file] [log] [blame]
Guido van Rossum45e2fbc1998-03-26 21:13:24 +00001# Author: Fred L. Drake, Jr.
Fred Drake3e5e6612001-10-09 20:53:48 +00002# 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
Thomas Wouters7e474022000-07-16 12:04:32 +00007# tuples with fairly non-descriptive content. This is modeled very much
Guido van Rossum5e92aff1997-04-16 00:49:59 +00008# 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
Fred Drake1ef106c2001-09-04 19:43:26 +000037from types import DictType, ListType, TupleType, StringType
38import sys
Guido van Rossum5e92aff1997-04-16 00:49:59 +000039
Fred Drakea89fda01997-04-16 16:59:30 +000040try:
41 from cStringIO import StringIO
42except ImportError:
43 from StringIO import StringIO
Guido van Rossum5e92aff1997-04-16 00:49:59 +000044
Skip Montanaroc62c81e2001-02-12 02:00:42 +000045__all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
46 "PrettyPrinter"]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000047
Fred Drakea89fda01997-04-16 16:59:30 +000048def pprint(object, stream=None):
49 """Pretty-print a Python object to a stream [default is sys.sydout]."""
50 printer = PrettyPrinter(stream=stream)
51 printer.pprint(object)
Guido van Rossum5e92aff1997-04-16 00:49:59 +000052
Fred Drakea89fda01997-04-16 16:59:30 +000053def pformat(object):
54 """Format a Python object into a pretty-printed representation."""
55 return PrettyPrinter().pformat(object)
Guido van Rossum5e92aff1997-04-16 00:49:59 +000056
Fred Drakea89fda01997-04-16 16:59:30 +000057def saferepr(object):
58 """Version of repr() which can handle recursive data structures."""
Fred Drakee0ffabe1997-07-18 20:42:39 +000059 return _safe_repr(object, {})[0]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000060
Tim Petersa814db52001-05-14 07:05:58 +000061def isreadable(object):
62 """Determine if saferepr(object) is readable by eval()."""
63 return _safe_repr(object, {})[1]
64
65def isrecursive(object):
66 """Determine if object requires a recursive representation."""
67 return _safe_repr(object, {})[2]
Guido van Rossum5e92aff1997-04-16 00:49:59 +000068
Fred Drakea89fda01997-04-16 16:59:30 +000069class PrettyPrinter:
70 def __init__(self, indent=1, width=80, depth=None, stream=None):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000071 """Handle pretty printing operations onto a stream using a set of
72 configured parameters.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000073
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000074 indent
75 Number of spaces to indent for each level of nesting.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000076
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000077 width
78 Attempted maximum number of columns in the output.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000079
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000080 depth
81 The maximum depth to print out nested structures.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000082
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000083 stream
84 The desired output stream. If omitted (or false), the standard
85 output stream available at construction will be used.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000086
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000087 """
88 indent = int(indent)
89 width = int(width)
90 assert indent >= 0
Tim Petersa814db52001-05-14 07:05:58 +000091 assert depth is None or depth > 0, "depth must be > 0"
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000092 assert width
93 self.__depth = depth
94 self.__indent_per_level = indent
95 self.__width = width
96 if stream:
97 self.__stream = stream
98 else:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000099 self.__stream = sys.stdout
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000100
Fred Drakea89fda01997-04-16 16:59:30 +0000101 def pprint(self, object):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000102 self.__stream.write(self.pformat(object) + "\n")
Fred Drakea89fda01997-04-16 16:59:30 +0000103
104 def pformat(self, object):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000105 sio = StringIO()
106 self.__format(object, sio, 0, 0, {}, 0)
107 return sio.getvalue()
Fred Drakea89fda01997-04-16 16:59:30 +0000108
Fred Drakee0ffabe1997-07-18 20:42:39 +0000109 def isrecursive(self, object):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000110 self.__recursive = 0
111 self.pformat(object)
112 return self.__recursive
Fred Drakee0ffabe1997-07-18 20:42:39 +0000113
114 def isreadable(self, object):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000115 self.__recursive = 0
116 self.__readable = 1
117 self.pformat(object)
118 return self.__readable and not self.__recursive
Fred Drakee0ffabe1997-07-18 20:42:39 +0000119
Fred Drakea89fda01997-04-16 16:59:30 +0000120 def __format(self, object, stream, indent, allowance, context, level):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000121 level = level + 1
122 if context.has_key(id(object)):
123 object = _Recursion(object)
124 self.__recursive = 1
125 rep = self.__repr(object, context, level - 1)
126 objid = id(object)
127 context[objid] = 1
128 typ = type(object)
129 sepLines = len(rep) > (self.__width - 1 - indent - allowance)
Fred Drakea89fda01997-04-16 16:59:30 +0000130
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000131 if sepLines and typ in (ListType, TupleType):
132 # Pretty-print the sequence.
133 stream.write((typ is ListType) and '[' or '(')
134 if self.__indent_per_level > 1:
135 stream.write((self.__indent_per_level - 1) * ' ')
136 length = len(object)
137 if length:
138 indent = indent + self.__indent_per_level
139 self.__format(object[0], stream, indent, allowance + 1,
140 context, level)
Fred Drakefbff97a1999-12-22 21:52:32 +0000141 if length > 1:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000142 for ent in object[1:]:
143 stream.write(',\n' + ' '*indent)
144 self.__format(ent, stream, indent,
145 allowance + 1, context, level)
146 indent = indent - self.__indent_per_level
147 if typ is TupleType and length == 1:
148 stream.write(',')
149 stream.write(((typ is ListType) and ']') or ')')
Fred Drakea89fda01997-04-16 16:59:30 +0000150
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000151 elif sepLines and typ is DictType:
152 stream.write('{')
153 if self.__indent_per_level > 1:
154 stream.write((self.__indent_per_level - 1) * ' ')
155 length = len(object)
156 if length:
157 indent = indent + self.__indent_per_level
158 items = object.items()
159 items.sort()
160 key, ent = items[0]
161 rep = self.__repr(key, context, level) + ': '
162 stream.write(rep)
163 self.__format(ent, stream, indent + len(rep),
164 allowance + 1, context, level)
165 if len(items) > 1:
166 for key, ent in items[1:]:
167 rep = self.__repr(key, context, level) + ': '
168 stream.write(',\n' + ' '*indent + rep)
169 self.__format(ent, stream, indent + len(rep),
170 allowance + 1, context, level)
171 indent = indent - self.__indent_per_level
172 stream.write('}')
Fred Drakea89fda01997-04-16 16:59:30 +0000173
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000174 else:
175 stream.write(rep)
Guido van Rossum183fd401999-09-02 15:09:44 +0000176
177 del context[objid]
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000178
Fred Drakea89fda01997-04-16 16:59:30 +0000179 def __repr(self, object, context, level):
Tim Petersa814db52001-05-14 07:05:58 +0000180 repr, readable, recursive = _safe_repr(object, context,
181 self.__depth, level)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000182 if not readable:
183 self.__readable = 0
Tim Petersa814db52001-05-14 07:05:58 +0000184 if recursive:
185 self.__recursive = 1
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000186 return repr
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000187
Tim Petersa814db52001-05-14 07:05:58 +0000188# Return triple (repr_string, isreadable, isrecursive).
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000189
Fred Drake1ef106c2001-09-04 19:43:26 +0000190_have_module = sys.modules.has_key
191
Fred Drakef39d0511997-04-16 18:55:58 +0000192def _safe_repr(object, context, maxlevels=None, level=0):
Tim Peters95b3f782001-05-14 18:39:41 +0000193 level += 1
Fred Drakea89fda01997-04-16 16:59:30 +0000194 typ = type(object)
Fred Drake1ef106c2001-09-04 19:43:26 +0000195 if not (typ in (DictType, ListType, TupleType, StringType) and object):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000196 rep = `object`
Tim Petersa814db52001-05-14 07:05:58 +0000197 return rep, (rep and (rep[0] != '<')), 0
Fred Drake1ef106c2001-09-04 19:43:26 +0000198 elif typ is StringType:
199 if not _have_module('locale'):
200 return `object`, 1, 0
201 if "'" in object and '"' not in object:
202 closure = '"'
203 quotes = {'"': '\\"'}
204 else:
205 closure = "'"
206 quotes = {"'": "\\'"}
207 sio = StringIO()
208 for char in object:
209 if char.isalpha():
210 sio.write(char)
211 else:
212 sio.write(quotes.get(char, `char`[1:-1]))
213 return closure + sio.getvalue() + closure, 1, 0
Tim Peters95b3f782001-05-14 18:39:41 +0000214
Fred Drakef39d0511997-04-16 18:55:58 +0000215 if context.has_key(id(object)):
Tim Petersa814db52001-05-14 07:05:58 +0000216 return `_Recursion(object)`, 0, 1
Fred Drakea89fda01997-04-16 16:59:30 +0000217 objid = id(object)
218 context[objid] = 1
Tim Peters95b3f782001-05-14 18:39:41 +0000219
Fred Draked804f4e1999-02-17 17:30:52 +0000220 readable = 1
Tim Petersa814db52001-05-14 07:05:58 +0000221 recursive = 0
Tim Peters95b3f782001-05-14 18:39:41 +0000222 startchar, endchar = {ListType: "[]",
223 TupleType: "()",
224 DictType: "{}"}[typ]
225 if maxlevels and level > maxlevels:
226 with_commas = "..."
227 readable = 0
228
229 elif typ is DictType:
230 components = []
231 for k, v in object.iteritems():
Tim Petersa814db52001-05-14 07:05:58 +0000232 krepr, kreadable, krecur = _safe_repr(k, context, maxlevels,
233 level)
234 vrepr, vreadable, vrecur = _safe_repr(v, context, maxlevels,
235 level)
Tim Peters95b3f782001-05-14 18:39:41 +0000236 components.append("%s: %s" % (krepr, vrepr))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000237 readable = readable and kreadable and vreadable
Tim Petersa814db52001-05-14 07:05:58 +0000238 recursive = recursive or krecur or vrecur
Tim Peters95b3f782001-05-14 18:39:41 +0000239 with_commas = ", ".join(components)
240
241 else: # list or tuple
242 assert typ in (ListType, TupleType)
243 components = []
244 for element in object:
Tim Petersa814db52001-05-14 07:05:58 +0000245 subrepr, subreadable, subrecur = _safe_repr(
Tim Peters95b3f782001-05-14 18:39:41 +0000246 element, context, maxlevels, level)
247 components.append(subrepr)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000248 readable = readable and subreadable
Tim Petersa814db52001-05-14 07:05:58 +0000249 recursive = recursive or subrecur
Tim Peters95b3f782001-05-14 18:39:41 +0000250 if len(components) == 1 and typ is TupleType:
251 components[0] += ","
252 with_commas = ", ".join(components)
253
254 s = "%s%s%s" % (startchar, with_commas, endchar)
Fred Drakea89fda01997-04-16 16:59:30 +0000255 del context[objid]
Tim Petersa814db52001-05-14 07:05:58 +0000256 return s, readable and not recursive, recursive
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000257
Fred Drakea89fda01997-04-16 16:59:30 +0000258class _Recursion:
259 # represent a recursive relationship; really only used for the __repr__()
260 # method...
261 def __init__(self, object):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000262 self.__repr = "<Recursion on %s with id=%s>" \
263 % (type(object).__name__, id(object))
Fred Drakea89fda01997-04-16 16:59:30 +0000264
265 def __repr__(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000266 return self.__repr