diff options
author | Fred Drake <fdrake@acm.org> | 1997-04-16 16:59:30 (GMT) |
---|---|---|
committer | Fred Drake <fdrake@acm.org> | 1997-04-16 16:59:30 (GMT) |
commit | a89fda0fe2b494e424f422f12633f9082a146909 (patch) | |
tree | 42432743afddcb1d3de7ad0a3c4f574cdde34946 | |
parent | ab0d1afdf3f29bbc4e822b75dc93999c61c67b2d (diff) | |
download | cpython-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
-rw-r--r-- | Lib/pprint.py | 277 |
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 |