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

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.
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.
def pformat(object):
"""Format a Python object into a pretty-printed representation."""
return PrettyPrinter().pformat(object)
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
def saferepr(object):
"""Version of repr() which can handle recursive data structures."""
return _safe_repr(object, {})
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 '(')
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.
length = len(seq)
if length:
indent = indent + INDENT_PER_LEVEL
pprint(seq[0], stream, indent, allowance + 1)
indent
Number of spaces to indent for each level of nesting.
if len(seq) > 1:
for ent in seq[1:]:
stream.write(',\n' + ' '*indent)
pprint(ent, stream, indent, allowance + 1)
width
Attempted maximum number of columns in the output.
indent = indent - INDENT_PER_LEVEL
depth
The maximum depth to print out nested structures.
stream.write(((typ is ListType) and ']') or ')')
stream
The desired output stream. If omitted (or false), the standard
output stream available at construction will be used.
elif typ is DictType and sepLines:
stream.write('{')
"""
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
length = len(seq)
if length:
indent = indent + INDENT_PER_LEVEL
items = seq.items()
items.sort()
key, ent = items[0]
rep = `key` + ': '
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)
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
# Terminate the 'print' if we're not a recursive invocation.
if not indent:
stream.write('\n')
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