summaryrefslogtreecommitdiffstats
path: root/Lib/pprint.py
diff options
context:
space:
mode:
authorFred Drake <fdrake@acm.org>1997-04-16 16:59:30 (GMT)
committerFred Drake <fdrake@acm.org>1997-04-16 16:59:30 (GMT)
commita89fda0fe2b494e424f422f12633f9082a146909 (patch)
tree42432743afddcb1d3de7ad0a3c4f574cdde34946 /Lib/pprint.py
parentab0d1afdf3f29bbc4e822b75dc93999c61c67b2d (diff)
downloadcpython-a89fda0fe2b494e424f422f12633f9082a146909.zip
cpython-a89fda0fe2b494e424f422f12633f9082a146909.tar.gz
cpython-a89fda0fe2b494e424f422f12633f9082a146909.tar.bz2
Muchly changed and improved pprint.py:
- 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
Diffstat (limited to 'Lib/pprint.py')
-rw-r--r--Lib/pprint.py277
1 files changed, 167 insertions, 110 deletions
diff --git a/Lib/pprint.py b/Lib/pprint.py
index a39dd13..850b0f7 100644
--- a/Lib/pprint.py
+++ b/Lib/pprint.py
@@ -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
@@ -14,6 +14,13 @@
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