| #  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 | 
 | 	    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 | 
 |     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 | 
 | 	    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 |