#  pprint.py
#
#  Author:	Fred L. Drake, Jr.
#		fdrake@cnri.reston.va.us, fdrake@intr.net
#
#  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.

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().

TAB_WIDTH
    The width represented by a single tab.  This value is typically 8,
    but 4 is the default under MacOS.  Can be changed by the user if
    desired, but is probably not a good idea.
"""

INDENT_PER_LEVEL = 1

MAX_WIDTH = 80

import os
TAB_WIDTH = (os.name == 'mac' and 4) or 8
del os

from types import DictType, ListType, TupleType


def _indentation(cols):
    """Create tabbed indentation string.

    cols
	Width of the indentation, in columns.
    """
    return ((cols / TAB_WIDTH) * '\t') + ((cols % TAB_WIDTH) * ' ')


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' + _indentation(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` + ': '
	    stream.write(rep)
	    pprint(ent, stream, indent + len(rep), allowance + 1)

	    if len(items) > 1:
		for key, ent in items[1:]:
		    rep = `key` + ': '
		    stream.write(',\n' + _indentation(indent) + rep)
		    pprint(ent, stream, indent + len(rep), allowance + 1)

	    indent = indent - INDENT_PER_LEVEL

	stream.write('}')

    else:
	stream.write(rep)

    #  Terminate the 'print' if we're not a recursive invocation.
    if not indent:
	stream.write('\n')


#
#  end of file
