1 # Author: Fred L. Drake, Jr.
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.
19 Handle pretty-printing operations onto a stream using a configured
20 set of formatting parameters.
26 Format a Python object into a pretty-printed representation.
29 Pretty-print a Python object to a stream [default is sys.stdout].
32 Generate a 'standard' repr()-like value, but protect against recursive
39 from io
import StringIO
as _StringIO
41 __all__
= ["pprint","pformat","isreadable","isrecursive","saferepr",
44 # cache these for faster access:
45 _commajoin
= ", ".join
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)
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)
62 """Version of repr() which can handle recursive data structures."""
63 return _safe_repr(object, {}, None, 0)[0]
65 def isreadable(object):
66 """Determine if saferepr(object) is readable by eval()."""
67 return _safe_repr(object, {}, None, 0)[1]
69 def isrecursive(object):
70 """Determine if object requires a recursive representation."""
71 return _safe_repr(object, {}, None, 0)[2]
74 """Helper function for key functions when sorting unorderable objects.
76 The wrapped-object will fallback to an Py2.x style comparison for
77 unorderable types (sorting first comparing the type name and then by
78 the obj ids). Does not work recursively, so dict.items() must have
79 _safe_key applied to both the key and the value.
85 def __init__(self
, obj
):
88 def __lt__(self
, other
):
89 rv
= self
.obj
.__lt
__(other
.obj
)
90 if rv
is NotImplemented:
91 rv
= (str(type(self
.obj
)), id(self
.obj
)) < \
92 (str(type(other
.obj
)), id(other
.obj
))
96 "Helper function for comparing 2-tuples"
97 return _safe_key(t
[0]), _safe_key(t
[1])
100 def __init__(self
, indent
=1, width
=80, depth
=None, stream
=None):
101 """Handle pretty printing operations onto a stream using a set of
102 configured parameters.
105 Number of spaces to indent for each level of nesting.
108 Attempted maximum number of columns in the output.
111 The maximum depth to print out nested structures.
114 The desired output stream. If omitted (or false), the standard
115 output stream available at construction will be used.
120 assert indent
>= 0, "indent must be >= 0"
121 assert depth
is None or depth
> 0, "depth must be > 0"
122 assert width
, "width must be != 0"
124 self
._indent
_per
_level
= indent
126 if stream
is not None:
127 self
._stream
= stream
129 self
._stream
= _sys
.stdout
131 def pprint(self
, object):
132 self
._format
(object, self
._stream
, 0, 0, {}, 0)
133 self
._stream
.write("\n")
135 def pformat(self
, object):
137 self
._format
(object, sio
, 0, 0, {}, 0)
138 return sio
.getvalue()
140 def isrecursive(self
, object):
141 return self
.format(object, {}, 0, 0)[2]
143 def isreadable(self
, object):
144 s
, readable
, recursive
= self
.format(object, {}, 0, 0)
145 return readable
and not recursive
147 def _format(self
, object, stream
, indent
, allowance
, context
, level
):
151 stream
.write(_recursion(object))
152 self
._recursive
= True
153 self
._readable
= False
155 rep
= self
._repr
(object, context
, level
- 1)
157 sepLines
= _len(rep
) > (self
._width
- 1 - indent
- allowance
)
160 if self
._depth
and level
> self
._depth
:
165 r
= getattr(typ
, "__repr__", None)
166 if issubclass(typ
, dict) and r
is dict.__repr
__:
168 if self
._indent
_per
_level
> 1:
169 write((self
._indent
_per
_level
- 1) * ' ')
170 length
= _len(object)
173 indent
= indent
+ self
._indent
_per
_level
174 items
= sorted(object.items(), key
=_safe_tuple
)
176 rep
= self
._repr
(key
, context
, level
)
179 self
._format
(ent
, stream
, indent
+ _len(rep
) + 2,
180 allowance
+ 1, context
, level
)
182 for key
, ent
in items
[1:]:
183 rep
= self
._repr
(key
, context
, level
)
184 write(',\n%s%s: ' % (' '*indent
, rep
))
185 self
._format
(ent
, stream
, indent
+ _len(rep
) + 2,
186 allowance
+ 1, context
, level
)
187 indent
= indent
- self
._indent
_per
_level
192 if ((issubclass(typ
, list) and r
is list.__repr
__) or
193 (issubclass(typ
, tuple) and r
is tuple.__repr
__) or
194 (issubclass(typ
, set) and r
is set.__repr
__) or
195 (issubclass(typ
, frozenset) and r
is frozenset.__repr
__)
197 length
= _len(object)
198 if issubclass(typ
, list):
201 elif issubclass(typ
, set):
207 object = sorted(object, key
=_safe_key
)
208 elif issubclass(typ
, frozenset):
214 object = sorted(object, key
=_safe_key
)
219 if self
._indent
_per
_level
> 1:
220 write((self
._indent
_per
_level
- 1) * ' ')
223 indent
= indent
+ self
._indent
_per
_level
224 self
._format
(object[0], stream
, indent
, allowance
+ 1,
227 for ent
in object[1:]:
228 write(',\n' + ' '*indent
)
229 self
._format
(ent
, stream
, indent
,
230 allowance
+ 1, context
, level
)
231 indent
= indent
- self
._indent
_per
_level
233 if issubclass(typ
, tuple) and length
== 1:
240 def _repr(self
, object, context
, level
):
241 repr, readable
, recursive
= self
.format(object, context
.copy(),
244 self
._readable
= False
246 self
._recursive
= True
249 def format(self
, object, context
, maxlevels
, level
):
250 """Format object for a specific context, returning a string
251 and flags indicating whether the representation is 'readable'
252 and whether the object represents a recursive construct.
254 return _safe_repr(object, context
, maxlevels
, level
)
257 # Return triple (repr_string, isreadable, isrecursive).
259 def _safe_repr(object, context
, maxlevels
, level
):
262 if 'locale' not in _sys
.modules
:
263 return repr(object), True, False
264 if "'" in object and '"' not in object:
266 quotes
= {'"': '\\"'}
269 quotes
= {"'": "\\'"}
277 write(qget(char
, repr(char
)[1:-1]))
278 return ("%s%s%s" % (closure
, sio
.getvalue(), closure
)), True, False
280 r
= getattr(typ
, "__repr__", None)
281 if issubclass(typ
, dict) and r
is dict.__repr
__:
283 return "{}", True, False
285 if maxlevels
and level
>= maxlevels
:
286 return "{...}", False, objid
in context
288 return _recursion(object), False, True
293 append
= components
.append
295 saferepr
= _safe_repr
296 items
= sorted(object.items(), key
=_safe_tuple
)
298 krepr
, kreadable
, krecur
= saferepr(k
, context
, maxlevels
, level
)
299 vrepr
, vreadable
, vrecur
= saferepr(v
, context
, maxlevels
, level
)
300 append("%s: %s" % (krepr
, vrepr
))
301 readable
= readable
and kreadable
and vreadable
305 return "{%s}" % _commajoin(components
), readable
, recursive
307 if (issubclass(typ
, list) and r
is list.__repr
__) or \
308 (issubclass(typ
, tuple) and r
is tuple.__repr
__):
309 if issubclass(typ
, list):
311 return "[]", True, False
313 elif _len(object) == 1:
317 return "()", True, False
320 if maxlevels
and level
>= maxlevels
:
321 return format
% "...", False, objid
in context
323 return _recursion(object), False, True
328 append
= components
.append
331 orepr
, oreadable
, orecur
= _safe_repr(o
, context
, maxlevels
, level
)
338 return format
% _commajoin(components
), readable
, recursive
341 return rep
, (rep
and not rep
.startswith('<')), False
344 def _recursion(object):
345 return ("<Recursion on %s with id=%s>"
346 % (_type(object).__name
__, _id(object)))
349 def _perfcheck(object=None):
352 object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
355 _safe_repr(object, {}, None, 0)
359 print("_safe_repr:", t2
- t1
)
360 print("pformat:", t3
- t2
)
362 if __name__
== "__main__":