mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			251 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			251 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#  Author:      Fred L. Drake, Jr.
 | 
						|
#               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
 | 
						|
#  something.  I wrote this when I was trying to read some heavily nested
 | 
						|
#  tuples with fairly non-descriptive content.  This is modeled very much
 | 
						|
#  after Lisp/Scheme - style pretty-printing of lists.  If you find it
 | 
						|
#  useful, thank small children who sleep at night.
 | 
						|
 | 
						|
"""Support to pretty-print lists, tuples, & dictionaries recursively.
 | 
						|
 | 
						|
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
 | 
						|
---------
 | 
						|
 | 
						|
pformat()
 | 
						|
    Format a Python object into a pretty-printed representation.
 | 
						|
 | 
						|
pprint()
 | 
						|
    Pretty-print a Python object to a stream [default is sys.sydout].
 | 
						|
 | 
						|
saferepr()
 | 
						|
    Generate a 'standard' repr()-like value, but protect against recursive
 | 
						|
    data structures.
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
from types import DictType, ListType, TupleType
 | 
						|
 | 
						|
try:
 | 
						|
    from cStringIO import StringIO
 | 
						|
except ImportError:
 | 
						|
    from StringIO import StringIO
 | 
						|
 | 
						|
__all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
 | 
						|
           "PrettyPrinter"]
 | 
						|
 | 
						|
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 isreadable(object):
 | 
						|
    """Determine if saferepr(object) is readable by eval()."""
 | 
						|
    return PrettyPrinter().isreadable(object)
 | 
						|
 | 
						|
 | 
						|
def isrecursive(object):
 | 
						|
    """Determine if object requires a recursive representation."""
 | 
						|
    return PrettyPrinter().isrecursive(object)
 | 
						|
 | 
						|
 | 
						|
def saferepr(object):
 | 
						|
    """Version of repr() which can handle recursive data structures."""
 | 
						|
    return _safe_repr(object, {})[0]
 | 
						|
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
        """
 | 
						|
        indent = int(indent)
 | 
						|
        width = int(width)
 | 
						|
        assert indent >= 0
 | 
						|
        assert (not depth) or depth > 0, "depth may not be negative"
 | 
						|
        assert width
 | 
						|
        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 isrecursive(self, object):
 | 
						|
        self.__recursive = 0
 | 
						|
        self.pformat(object)
 | 
						|
        return self.__recursive
 | 
						|
 | 
						|
    def isreadable(self, object):
 | 
						|
        self.__recursive = 0
 | 
						|
        self.__readable = 1
 | 
						|
        self.pformat(object)
 | 
						|
        return self.__readable and not self.__recursive
 | 
						|
 | 
						|
    def __format(self, object, stream, indent, allowance, context, level):
 | 
						|
        level = level + 1
 | 
						|
        if context.has_key(id(object)):
 | 
						|
            object = _Recursion(object)
 | 
						|
            self.__recursive = 1
 | 
						|
        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 '(')
 | 
						|
            if self.__indent_per_level > 1:
 | 
						|
                stream.write((self.__indent_per_level - 1) * ' ')
 | 
						|
            length = len(object)
 | 
						|
            if length:
 | 
						|
                indent = indent + self.__indent_per_level
 | 
						|
                self.__format(object[0], stream, indent, allowance + 1,
 | 
						|
                              context, level)
 | 
						|
                if length > 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
 | 
						|
            if typ is TupleType and length == 1:
 | 
						|
                stream.write(',')
 | 
						|
            stream.write(((typ is ListType) and ']') or ')')
 | 
						|
 | 
						|
        elif sepLines and typ is DictType:
 | 
						|
            stream.write('{')
 | 
						|
            if self.__indent_per_level > 1:
 | 
						|
                stream.write((self.__indent_per_level - 1) * ' ')
 | 
						|
            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)
 | 
						|
 | 
						|
        del context[objid]
 | 
						|
 | 
						|
    def __repr(self, object, context, level):
 | 
						|
        repr, readable = _safe_repr(object, context, self.__depth, level)
 | 
						|
        if not readable:
 | 
						|
            self.__readable = 0
 | 
						|
        return repr
 | 
						|
 | 
						|
 | 
						|
def _safe_repr(object, context, maxlevels=None, level=0):
 | 
						|
    level = level + 1
 | 
						|
    typ = type(object)
 | 
						|
    if not (typ in (DictType, ListType, TupleType) and object):
 | 
						|
        rep = `object`
 | 
						|
        return rep, (rep and (rep[0] != '<'))
 | 
						|
    if context.has_key(id(object)):
 | 
						|
        return `_Recursion(object)`, 0
 | 
						|
    objid = id(object)
 | 
						|
    context[objid] = 1
 | 
						|
    readable = 1
 | 
						|
    if typ is DictType:
 | 
						|
        if maxlevels and level >= maxlevels:
 | 
						|
            s = "{...}"
 | 
						|
            readable = 0
 | 
						|
        else:
 | 
						|
            items = object.items()
 | 
						|
            k, v = items[0]
 | 
						|
            krepr, kreadable = _safe_repr(k, context, maxlevels, level)
 | 
						|
            vrepr, vreadable = _safe_repr(v, context, maxlevels, level)
 | 
						|
            readable = readable and kreadable and vreadable
 | 
						|
            s = "{%s: %s" % (krepr, vrepr)
 | 
						|
            for k, v in items[1:]:
 | 
						|
                krepr, kreadable = _safe_repr(k, context, maxlevels, level)
 | 
						|
                vrepr, vreadable = _safe_repr(v, context, maxlevels, level)
 | 
						|
                readable = readable and kreadable and vreadable
 | 
						|
                s = "%s, %s: %s" % (s, krepr, vrepr)
 | 
						|
            s = s + "}"
 | 
						|
    else:
 | 
						|
        s, term = (typ is ListType) and ('[', ']') or ('(', ')')
 | 
						|
        if maxlevels and level >= maxlevels:
 | 
						|
            s = s + "..."
 | 
						|
            readable = 0
 | 
						|
        else:
 | 
						|
            subrepr, subreadable = _safe_repr(
 | 
						|
                object[0], context, maxlevels, level)
 | 
						|
            readable = readable and subreadable
 | 
						|
            s = s + subrepr
 | 
						|
            tail = object[1:]
 | 
						|
            if not tail:
 | 
						|
                if typ is TupleType:
 | 
						|
                    s = s + ','
 | 
						|
            for ent in tail:
 | 
						|
                subrepr, subreadable = _safe_repr(
 | 
						|
                    ent, context, maxlevels, level)
 | 
						|
                readable = readable and subreadable
 | 
						|
                s = "%s, %s" % (s, subrepr)
 | 
						|
        s = s + term
 | 
						|
    del context[objid]
 | 
						|
    return s, readable
 | 
						|
 | 
						|
 | 
						|
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
 |