mirror of
				https://github.com/python/cpython.git
				synced 2025-10-25 18:54:53 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			247 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			247 lines
		
	
	
	
		
			6.9 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 modelled 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
 | |
| 
 | |
| 
 | |
| 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 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('{')
 | |
| 	    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
 | |
|     readable = 1
 | |
|     typ = type(object)
 | |
|     if not (typ in (DictType, ListType, TupleType) and object):
 | |
| 	rep = `object`
 | |
| 	if rep:
 | |
| 	    if rep[0] == '<':
 | |
| 		readable = 0
 | |
| 	else:
 | |
| 	    readable = 0
 | |
| 	return `object`, readable
 | |
|     if context.has_key(id(object)):
 | |
| 	return `_Recursion(object)`, 0
 | |
|     objid = id(object)
 | |
|     context[objid] = 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
 | |
| 	    for ent in object[1:]:
 | |
| 		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
 | 
