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
This commit is contained in:
Fred Drake 1997-04-16 16:59:30 +00:00
parent ab0d1afdf3
commit a89fda0fe2
1 changed files with 152 additions and 95 deletions

View 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
@ -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.
Pretty-print a Python object to a stream [default is sys.sydout].
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().
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
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
def pprint(seq, stream=None, indent=0, allowance=0):
"""Pretty-print a list, tuple, or dictionary.
def pprint(object, stream=None):
"""Pretty-print a Python object to a stream [default is sys.sydout]."""
printer = PrettyPrinter(stream=stream)
printer.pprint(object)
seq
List, tuple, or dictionary object to be pretty-printed. Other
object types are permitted by are not specially interpreted.
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
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.
The desired output stream. If omitted (or false), the standard
output stream available at construction will be used.
"""
if stream is None:
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
stream = sys.stdout
self.__stream = sys.stdout
rep = `seq`
typ = type(seq)
sepLines = len(rep) > (MAX_WIDTH - 1 - indent - allowance)
def pprint(self, object):
self.__stream.write(self.pformat(object) + "\n")
if sepLines and (typ is ListType or typ is TupleType):
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(seq)
stream.write((typ is ListType) and '[' or '(')
length = len(object)
if length:
indent = indent + INDENT_PER_LEVEL
pprint(seq[0], stream, indent, allowance + 1)
if len(seq) > 1:
for ent in seq[1:]:
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)
pprint(ent, stream, indent, allowance + 1)
indent = indent - INDENT_PER_LEVEL
self.__format(ent, stream, indent,
allowance + 1, context, level)
indent = indent - self.__indent_per_level
stream.write(((typ is ListType) and ']') or ')')
elif typ is DictType and sepLines:
elif sepLines and typ is DictType:
stream.write('{')
length = len(seq)
length = len(object)
if length:
indent = indent + INDENT_PER_LEVEL
items = seq.items()
indent = indent + self.__indent_per_level
items = object.items()
items.sort()
key, ent = items[0]
rep = `key` + ': '
rep = self.__repr(key, context, level) + ': '
stream.write(rep)
pprint(ent, stream, indent + len(rep), allowance + 1)
self.__format(ent, stream, indent + len(rep),
allowance + 1, context, level)
if len(items) > 1:
for key, ent in items[1:]:
rep = `key` + ': '
rep = self.__repr(key, context, level) + ': '
stream.write(',\n' + ' '*indent + rep)
pprint(ent, stream, indent + len(rep), allowance + 1)
indent = indent - INDENT_PER_LEVEL
self.__format(ent, stream, indent + len(rep),
allowance + 1, context, level)
indent = indent - self.__indent_per_level
stream.write('}')
else:
stream.write(rep)
del context[objid]
# Terminate the 'print' if we're not a recursive invocation.
if not indent:
stream.write('\n')
def __repr(self, object, context, level):
return _safe_repr(object, context, self.__depth, level)
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:
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