]> granicus.if.org Git - python/commitdiff
Muchly changed and improved pprint.py:
authorFred Drake <fdrake@acm.org>
Wed, 16 Apr 1997 16:59:30 +0000 (16:59 +0000)
committerFred Drake <fdrake@acm.org>
Wed, 16 Apr 1997 16:59:30 +0000 (16:59 +0000)
- handles recursive data structures
- formatting based on a PrettyPrinter object
- allows a maximum nesting depth to be specified
- provides safe repr()-like function which does not pretty-print

Lib/pprint.py

index a39dd13dadd9b3945129b159d1ac7cb0d1236a5a..850b0f7384ce0566768b0255200271696c5f772a 100644 (file)
@@ -1,7 +1,7 @@
 #  pprint.py
 #
 #  Author:     Fred L. Drake, Jr.
-#              fdrake@cnri.reston.va.us, fdrake@intr.net
+#              fdrake@cnri.reston.va.us, fdrake@acm.org
 #
 #  This is a simple little module I wrote to make life easier.  I didn't
 #  see anything quite like it in the library, though I may have overlooked
 
 Very simple, but useful, especially in debugging data structures.
 
+Classes
+-------
+
+PrettyPrinter()
+    Handle pretty-printing operations onto a stream using a configured
+    set of formatting parameters.
+
 Functions
 ---------
 
@@ -21,123 +28,173 @@ pformat()
     Format a Python object into a pretty-printed representation.
 
 pprint()
-    Pretty-print a list, tuple or dictionary.
-
-
-
-Constants
----------
-
-INDENT_PER_LEVEL
-    Amount of indentation to use for each new recursive level.  The
-    default is 1.  This must be a non-negative integer, and may be set
-    by the caller before calling pprint().
+    Pretty-print a Python object to a stream [default is sys.sydout].
 
-MAX_WIDTH
-    Maximum width of the display.  This is only used if the
-    representation *can* be kept less than MAX_WIDTH characters wide.
-    May be set by the user before calling pprint() if needed.
+saferepr()
+    Generate a 'standard' repr()-like value, but protect against recursive
+    data structures.
 
 """
 
-INDENT_PER_LEVEL = 1
-
-MAX_WIDTH = 80
-
 from types import DictType, ListType, TupleType
 
-
-def pformat(seq):
-    """Format a Python object into a pretty-printed representation.
-
-    The representation is returned with no trailing newline.
-
-    """
-    import StringIO
-    sio = StringIO.StringIO()
-    pprint(seq, stream=sio)
-    str = sio.getvalue()
-    if str and str[-1] == '\n':
-       str = str[:-1]
-    return str
-
-
-def pprint(seq, stream=None, indent=0, allowance=0):
-    """Pretty-print a list, tuple, or dictionary.
-
-    seq
-       List, tuple, or dictionary object to be pretty-printed.  Other
-       object types are permitted by are not specially interpreted.
-
-    stream
-       Output stream.  If not provided, `sys.stdout' is used.  This
-       parameter must support the `write()' method with a single
-       parameter, which will always be a string.  It may be a
-       `StringIO.StringIO' object if the result is needed as a
-       string.
-
-    Indentation is done according to `INDENT_PER_LEVEL', which may be
-    set to any non-negative integer before calling this function.  The
-    output written on the stream is a perfectly valid representation
-    of the Python object passed in, with indentation to assist
-    human-readable interpretation.  The output can be used as input
-    without error, given readable representations of all elements are
-    available via `repr()'.  Output is restricted to `MAX_WIDTH'
-    columns where possible.
-
-    """
-    if stream is None:
-       import sys
-       stream = sys.stdout
-
-    rep = `seq`
-    typ = type(seq)
-    sepLines = len(rep) > (MAX_WIDTH - 1 - indent - allowance)
-
-    if sepLines and (typ is ListType or typ is TupleType):
-       #  Pretty-print the sequence.
-       stream.write(((typ is ListType) and '[') or '(')
-
-       length = len(seq)
-       if length:
-           indent = indent + INDENT_PER_LEVEL
-           pprint(seq[0], stream, indent, allowance + 1)
-
-           if len(seq) > 1:
-               for ent in seq[1:]:
-                   stream.write(',\n' + ' '*indent)
-                   pprint(ent, stream, indent, allowance + 1)
-
-           indent = indent - INDENT_PER_LEVEL
-
-       stream.write(((typ is ListType) and ']') or ')')
-
-    elif typ is DictType and sepLines:
-       stream.write('{')
-
-       length = len(seq)
-       if length:
-           indent = indent + INDENT_PER_LEVEL
-           items  = seq.items()
-           items.sort()
-           key, ent = items[0]
-           rep = `key` + ': '
+try:
+    from cStringIO import StringIO
+except ImportError:
+    from StringIO import StringIO
+
+
+def pprint(object, stream=None):
+    """Pretty-print a Python object to a stream [default is sys.sydout]."""
+    printer = PrettyPrinter(stream=stream)
+    printer.pprint(object)
+
+
+def pformat(object):
+    """Format a Python object into a pretty-printed representation."""
+    return PrettyPrinter().pformat(object)
+
+
+def saferepr(object):
+    """Version of repr() which can handle recursive data structures."""
+    return _safe_repr(object, {})
+
+
+class PrettyPrinter:
+    def __init__(self, indent=1, width=80, depth=None, stream=None):
+       """Handle pretty printing operations onto a stream using a set of
+       configured parameters.
+
+       indent
+           Number of spaces to indent for each level of nesting.
+
+       width
+           Attempted maximum number of columns in the output.
+
+       depth
+           The maximum depth to print out nested structures.
+
+       stream
+           The desired output stream.  If omitted (or false), the standard
+           output stream available at construction will be used.
+
+       """
+       assert (not depth) or depth > 0, "depth may not be negative"
+       assert int(indent) or 1
+       assert int(width) or 1
+       self.__depth = depth
+       self.__indent_per_level = indent
+       self.__width = width
+       if stream:
+           self.__stream = stream
+       else:
+           import sys
+           self.__stream = sys.stdout
+
+    def pprint(self, object):
+       self.__stream.write(self.pformat(object) + "\n")
+
+    def pformat(self, object):
+       sio = StringIO()
+       self.__format(object, sio, 0, 0, {}, 0)
+       return sio.getvalue()
+
+    def __format(self, object, stream, indent, allowance, context, level):
+       level = level + 1
+       if context.has_key(id(object)):
+           object = _Recursion(object)
+       rep = self__repr(object, context, level - 1)
+       objid = id(object)
+       context[objid] = 1
+       typ = type(object)
+       sepLines = len(rep) > (self.__width - 1 - indent - allowance)
+
+       if sepLines and typ in (ListType, TupleType):
+           #  Pretty-print the sequence.
+           stream.write((typ is ListType) and '[' or '(')
+           length = len(object)
+           if length:
+               indent = indent + self.__indent_per_level
+               pprint(object[0], stream, indent, allowance + 1)
+               if len(object) > 1:
+                   for ent in object[1:]:
+                       stream.write(',\n' + ' '*indent)
+                       self.__format(ent, stream, indent,
+                                     allowance + 1, context, level)
+               indent = indent - self.__indent_per_level
+           stream.write(((typ is ListType) and ']') or ')')
+
+       elif sepLines and typ is DictType:
+           stream.write('{')
+           length = len(object)
+           if length:
+               indent = indent + self.__indent_per_level
+               items  = object.items()
+               items.sort()
+               key, ent = items[0]
+               rep = self.__repr(key, context, level) + ': '
+               stream.write(rep)
+               self.__format(ent, stream, indent + len(rep),
+                             allowance + 1, context, level)
+               if len(items) > 1:
+                   for key, ent in items[1:]:
+                       rep = self.__repr(key, context, level) + ': '
+                       stream.write(',\n' + ' '*indent + rep)
+                       self.__format(ent, stream, indent + len(rep),
+                                     allowance + 1, context, level)
+               indent = indent - self.__indent_per_level
+           stream.write('}')
+
+       else:
            stream.write(rep)
-           pprint(ent, stream, indent + len(rep), allowance + 1)
+           del context[objid]
 
-           if len(items) > 1:
-               for key, ent in items[1:]:
-                   rep = `key` + ': '
-                   stream.write(',\n' + ' '*indent + rep)
-                   pprint(ent, stream, indent + len(rep), allowance + 1)
+    def __repr(self, object, context, level):
+       return _safe_repr(object, context, self.__depth, level)
 
-           indent = indent - INDENT_PER_LEVEL
-
-       stream.write('}')
 
+def _safe_repr(object, context=None, maxlevels=None, level=0):
+    level = level + 1
+    typ = type(object)
+    if not (typ in (DictType, ListType, TupleType) and object):
+       return `object`
+    if context is None:
+       context = {}
     else:
-       stream.write(rep)
-
-    #  Terminate the 'print' if we're not a recursive invocation.
-    if not indent:
-       stream.write('\n')
+       if context.has_key(id(object)):
+           return `_Recursion(object)`
+    objid = id(object)
+    context[objid] = 1
+    if typ is DictType:
+       if maxlevels and level >= maxlevels:
+           s = "{...}"
+       else:
+           items = object.items()
+           k, v = items[0]
+           s = "{%s: %s" % (_safe_repr(k, context), _safe_repr(v, context))
+           for k, v in items[1:]:
+               s = "%s, %s: %s" \
+                   % (s, _safe_repr(k, context), _safe_repr(v, context))
+           s = s + "}"
+    else:
+       s, term = (typ is ListType) and ('[', ']') or ('(', ')')
+       if maxlevels and level >= maxlevels:
+           s = s + "..."
+       else:
+           s = s + _safe_repr(object[0], context)
+           for ent in object[1:]:
+               s = "%s, %s" % (s, _safe_repr(ent, context))
+       s = s + term
+    del context[objid]
+    return s
+
+
+class _Recursion:
+    # represent a recursive relationship; really only used for the __repr__()
+    # method...
+    def __init__(self, object):
+       self.__repr = "<Recursion on %s with id=%s>" \
+                     % (type(object).__name__, id(object))
+
+    def __repr__(self):
+       return self.__repr