blob: bf5133d119a21f3f069e3ba3f8055ec8d554a504 [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 Drakea89fda01997-04-16 16:59:30 +000056def saferepr(object):
57 """Version of repr() which can handle recursive data structures."""
58 return _safe_repr(object, {})
Guido van Rossum5e92aff1997-04-16 00:49:59 +000059
Guido van Rossum5e92aff1997-04-16 00:49:59 +000060
Fred Drakea89fda01997-04-16 16:59:30 +000061class PrettyPrinter:
62 def __init__(self, indent=1, width=80, depth=None, stream=None):
63 """Handle pretty printing operations onto a stream using a set of
64 configured parameters.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000065
Fred Drakea89fda01997-04-16 16:59:30 +000066 indent
67 Number of spaces to indent for each level of nesting.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000068
Fred Drakea89fda01997-04-16 16:59:30 +000069 width
70 Attempted maximum number of columns in the output.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000071
Fred Drakea89fda01997-04-16 16:59:30 +000072 depth
73 The maximum depth to print out nested structures.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000074
Fred Drakea89fda01997-04-16 16:59:30 +000075 stream
76 The desired output stream. If omitted (or false), the standard
77 output stream available at construction will be used.
Guido van Rossum5e92aff1997-04-16 00:49:59 +000078
Fred Drakea89fda01997-04-16 16:59:30 +000079 """
80 assert (not depth) or depth > 0, "depth may not be negative"
81 assert int(indent) or 1
82 assert int(width) or 1
83 self.__depth = depth
84 self.__indent_per_level = indent
85 self.__width = width
86 if stream:
87 self.__stream = stream
88 else:
89 import sys
90 self.__stream = sys.stdout
Guido van Rossum5e92aff1997-04-16 00:49:59 +000091
Fred Drakea89fda01997-04-16 16:59:30 +000092 def pprint(self, object):
93 self.__stream.write(self.pformat(object) + "\n")
94
95 def pformat(self, object):
96 sio = StringIO()
97 self.__format(object, sio, 0, 0, {}, 0)
98 return sio.getvalue()
99
100 def __format(self, object, stream, indent, allowance, context, level):
101 level = level + 1
102 if context.has_key(id(object)):
103 object = _Recursion(object)
Fred Drake5fd026d1997-04-18 13:54:13 +0000104 rep = self.__repr(object, context, level - 1)
Fred Drakea89fda01997-04-16 16:59:30 +0000105 objid = id(object)
106 context[objid] = 1
107 typ = type(object)
108 sepLines = len(rep) > (self.__width - 1 - indent - allowance)
109
110 if sepLines and typ in (ListType, TupleType):
111 # Pretty-print the sequence.
112 stream.write((typ is ListType) and '[' or '(')
113 length = len(object)
114 if length:
115 indent = indent + self.__indent_per_level
Fred Drake5fd026d1997-04-18 13:54:13 +0000116 self.__format(object[0], stream, indent, allowance + 1,
117 context, level)
Fred Drakea89fda01997-04-16 16:59:30 +0000118 if len(object) > 1:
119 for ent in object[1:]:
120 stream.write(',\n' + ' '*indent)
121 self.__format(ent, stream, indent,
122 allowance + 1, context, level)
123 indent = indent - self.__indent_per_level
124 stream.write(((typ is ListType) and ']') or ')')
125
126 elif sepLines and typ is DictType:
127 stream.write('{')
128 length = len(object)
129 if length:
130 indent = indent + self.__indent_per_level
131 items = object.items()
132 items.sort()
133 key, ent = items[0]
134 rep = self.__repr(key, context, level) + ': '
135 stream.write(rep)
136 self.__format(ent, stream, indent + len(rep),
137 allowance + 1, context, level)
138 if len(items) > 1:
139 for key, ent in items[1:]:
140 rep = self.__repr(key, context, level) + ': '
141 stream.write(',\n' + ' '*indent + rep)
142 self.__format(ent, stream, indent + len(rep),
143 allowance + 1, context, level)
144 indent = indent - self.__indent_per_level
145 stream.write('}')
146
147 else:
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000148 stream.write(rep)
Fred Drakea89fda01997-04-16 16:59:30 +0000149 del context[objid]
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000150
Fred Drakea89fda01997-04-16 16:59:30 +0000151 def __repr(self, object, context, level):
152 return _safe_repr(object, context, self.__depth, level)
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000153
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000154
Fred Drakef39d0511997-04-16 18:55:58 +0000155def _safe_repr(object, context, maxlevels=None, level=0):
Fred Drakea89fda01997-04-16 16:59:30 +0000156 level = level + 1
157 typ = type(object)
158 if not (typ in (DictType, ListType, TupleType) and object):
159 return `object`
Fred Drakef39d0511997-04-16 18:55:58 +0000160 if context.has_key(id(object)):
161 return `_Recursion(object)`
Fred Drakea89fda01997-04-16 16:59:30 +0000162 objid = id(object)
163 context[objid] = 1
164 if typ is DictType:
165 if maxlevels and level >= maxlevels:
166 s = "{...}"
167 else:
168 items = object.items()
169 k, v = items[0]
Fred Drakef39d0511997-04-16 18:55:58 +0000170 s = "{%s: %s" % (_safe_repr(k, context, maxlevels, level),
171 _safe_repr(v, context, maxlevels, level))
Fred Drakea89fda01997-04-16 16:59:30 +0000172 for k, v in items[1:]:
173 s = "%s, %s: %s" \
Fred Drakef39d0511997-04-16 18:55:58 +0000174 % (s, _safe_repr(k, context, maxlevels, level),
175 _safe_repr(v, context, maxlevels, level))
Fred Drakea89fda01997-04-16 16:59:30 +0000176 s = s + "}"
177 else:
178 s, term = (typ is ListType) and ('[', ']') or ('(', ')')
179 if maxlevels and level >= maxlevels:
180 s = s + "..."
181 else:
Fred Drakef39d0511997-04-16 18:55:58 +0000182 s = s + _safe_repr(object[0], context, maxlevels, level)
Fred Drakea89fda01997-04-16 16:59:30 +0000183 for ent in object[1:]:
Fred Drakef39d0511997-04-16 18:55:58 +0000184 s = "%s, %s" % (s, _safe_repr(ent, context, maxlevels, level))
Fred Drakea89fda01997-04-16 16:59:30 +0000185 s = s + term
186 del context[objid]
187 return s
Guido van Rossum5e92aff1997-04-16 00:49:59 +0000188
Fred Drakea89fda01997-04-16 16:59:30 +0000189
190class _Recursion:
191 # represent a recursive relationship; really only used for the __repr__()
192 # method...
193 def __init__(self, object):
194 self.__repr = "<Recursion on %s with id=%s>" \
195 % (type(object).__name__, id(object))
196
197 def __repr__(self):
198 return self.__repr