PageRenderTime 1158ms CodeModel.GetById 173ms app.highlight 651ms RepoModel.GetById 196ms app.codeStats 1ms

/Lib/pprint.py

http://unladen-swallow.googlecode.com/
Python | 343 lines | 312 code | 1 blank | 30 comment | 0 complexity | 9fdf869c0d1381d80f9bef0b4f2bb526 MD5 | raw file
  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
 13Very simple, but useful, especially in debugging data structures.
 14
 15Classes
 16-------
 17
 18PrettyPrinter()
 19    Handle pretty-printing operations onto a stream using a configured
 20    set of formatting parameters.
 21
 22Functions
 23---------
 24
 25pformat()
 26    Format a Python object into a pretty-printed representation.
 27
 28pprint()
 29    Pretty-print a Python object to a stream [default is sys.stdout].
 30
 31saferepr()
 32    Generate a 'standard' repr()-like value, but protect against recursive
 33    data structures.
 34
 35"""
 36
 37import sys as _sys
 38
 39from 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
 51def 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
 57def 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
 61def saferepr(object):
 62    """Version of repr() which can handle recursive data structures."""
 63    return _safe_repr(object, {}, None, 0)[0]
 64
 65def isreadable(object):
 66    """Determine if saferepr(object) is readable by eval()."""
 67    return _safe_repr(object, {}, None, 0)[1]
 68
 69def isrecursive(object):
 70    """Determine if object requires a recursive representation."""
 71    return _safe_repr(object, {}, None, 0)[2]
 72
 73class 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 self._depth and level > self._depth:
135            write(rep)
136            return
137
138        r = getattr(typ, "__repr__", None)
139        if issubclass(typ, dict) and r is dict.__repr__:
140            write('{')
141            if self._indent_per_level > 1:
142                write((self._indent_per_level - 1) * ' ')
143            length = _len(object)
144            if length:
145                context[objid] = 1
146                indent = indent + self._indent_per_level
147                items  = object.items()
148                items.sort()
149                key, ent = items[0]
150                rep = self._repr(key, context, level)
151                write(rep)
152                write(': ')
153                self._format(ent, stream, indent + _len(rep) + 2,
154                              allowance + 1, context, level)
155                if length > 1:
156                    for key, ent in items[1:]:
157                        rep = self._repr(key, context, level)
158                        if sepLines:
159                            write(',\n%s%s: ' % (' '*indent, rep))
160                        else:
161                            write(', %s: ' % rep)
162                        self._format(ent, stream, indent + _len(rep) + 2,
163                                      allowance + 1, context, level)
164                indent = indent - self._indent_per_level
165                del context[objid]
166            write('}')
167            return
168
169        if ((issubclass(typ, list) and r is list.__repr__) or
170            (issubclass(typ, tuple) and r is tuple.__repr__) or
171            (issubclass(typ, set) and r is set.__repr__) or
172            (issubclass(typ, frozenset) and r is frozenset.__repr__)
173           ):
174            length = _len(object)
175            if issubclass(typ, list):
176                write('[')
177                endchar = ']'
178            elif issubclass(typ, set):
179                if not length:
180                    write('set()')
181                    return
182                write('set([')
183                endchar = '])'
184                object = sorted(object)
185                indent += 4
186            elif issubclass(typ, frozenset):
187                if not length:
188                    write('frozenset()')
189                    return
190                write('frozenset([')
191                endchar = '])'
192                object = sorted(object)
193                indent += 10
194            else:
195                write('(')
196                endchar = ')'
197            if self._indent_per_level > 1 and sepLines:
198                write((self._indent_per_level - 1) * ' ')
199            if length:
200                context[objid] = 1
201                indent = indent + self._indent_per_level
202                self._format(object[0], stream, indent, allowance + 1,
203                             context, level)
204                if length > 1:
205                    for ent in object[1:]:
206                        if sepLines:
207                            write(',\n' + ' '*indent)
208                        else:
209                            write(', ')
210                        self._format(ent, stream, indent,
211                                      allowance + 1, context, level)
212                indent = indent - self._indent_per_level
213                del context[objid]
214            if issubclass(typ, tuple) and length == 1:
215                write(',')
216            write(endchar)
217            return
218
219        write(rep)
220
221    def _repr(self, object, context, level):
222        repr, readable, recursive = self.format(object, context.copy(),
223                                                self._depth, level)
224        if not readable:
225            self._readable = False
226        if recursive:
227            self._recursive = True
228        return repr
229
230    def format(self, object, context, maxlevels, level):
231        """Format object for a specific context, returning a string
232        and flags indicating whether the representation is 'readable'
233        and whether the object represents a recursive construct.
234        """
235        return _safe_repr(object, context, maxlevels, level)
236
237
238# Return triple (repr_string, isreadable, isrecursive).
239
240def _safe_repr(object, context, maxlevels, level):
241    typ = _type(object)
242    if typ is str:
243        if 'locale' not in _sys.modules:
244            return repr(object), True, False
245        if "'" in object and '"' not in object:
246            closure = '"'
247            quotes = {'"': '\\"'}
248        else:
249            closure = "'"
250            quotes = {"'": "\\'"}
251        qget = quotes.get
252        sio = _StringIO()
253        write = sio.write
254        for char in object:
255            if char.isalpha():
256                write(char)
257            else:
258                write(qget(char, repr(char)[1:-1]))
259        return ("%s%s%s" % (closure, sio.getvalue(), closure)), True, False
260
261    r = getattr(typ, "__repr__", None)
262    if issubclass(typ, dict) and r is dict.__repr__:
263        if not object:
264            return "{}", True, False
265        objid = _id(object)
266        if maxlevels and level >= maxlevels:
267            return "{...}", False, objid in context
268        if objid in context:
269            return _recursion(object), False, True
270        context[objid] = 1
271        readable = True
272        recursive = False
273        components = []
274        append = components.append
275        level += 1
276        saferepr = _safe_repr
277        for k, v in sorted(object.items()):
278            krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
279            vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
280            append("%s: %s" % (krepr, vrepr))
281            readable = readable and kreadable and vreadable
282            if krecur or vrecur:
283                recursive = True
284        del context[objid]
285        return "{%s}" % _commajoin(components), readable, recursive
286
287    if (issubclass(typ, list) and r is list.__repr__) or \
288       (issubclass(typ, tuple) and r is tuple.__repr__):
289        if issubclass(typ, list):
290            if not object:
291                return "[]", True, False
292            format = "[%s]"
293        elif _len(object) == 1:
294            format = "(%s,)"
295        else:
296            if not object:
297                return "()", True, False
298            format = "(%s)"
299        objid = _id(object)
300        if maxlevels and level >= maxlevels:
301            return format % "...", False, objid in context
302        if objid in context:
303            return _recursion(object), False, True
304        context[objid] = 1
305        readable = True
306        recursive = False
307        components = []
308        append = components.append
309        level += 1
310        for o in object:
311            orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level)
312            append(orepr)
313            if not oreadable:
314                readable = False
315            if orecur:
316                recursive = True
317        del context[objid]
318        return format % _commajoin(components), readable, recursive
319
320    rep = repr(object)
321    return rep, (rep and not rep.startswith('<')), False
322
323
324def _recursion(object):
325    return ("<Recursion on %s with id=%s>"
326            % (_type(object).__name__, _id(object)))
327
328
329def _perfcheck(object=None):
330    import time
331    if object is None:
332        object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
333    p = PrettyPrinter()
334    t1 = time.time()
335    _safe_repr(object, {}, None, 0)
336    t2 = time.time()
337    p.pformat(object)
338    t3 = time.time()
339    print "_safe_repr:", t2 - t1
340    print "pformat:", t3 - t2
341
342if __name__ == "__main__":
343    _perfcheck()