tachyon.in / kssreeram / xprint

xprint

an extensible pretty printer for Python

Printing large nested data-structures in a neatly indented fashion is a common need. Python's pprint module supports indented printing of builtin data types like list. Unfortunately there is no support for custom types. The pprint module doesn't provide any extension hooks for custom types.

To solve this, I quickly put together an extensible pretty printer - xprint.py.

xprint knows how to print builtin types like list, tuple, str, int, etc. Additionally, xprint knows how to print objects of 3 new types - XObject, XField, and XSymbol. For example, XObject("Foo", 10, 20, "bar"), is printed as Foo(10, 20, "bar"). XField instances allow you to add labels - XObject("Foo", 10, XField("n",20)) is printed as Foo(10, n=20). XSymbol can be used to print strings without any quotes - XSymbol("Foo") is printed as Foo.

The basic idea for printing custom types is simple - convert the custom type to something understood by xprint, such as an XObject. The xprint module maintains a table of converters keyed by type. The xregister procedure can be used to register a converter.

Here's a quick example.

class Person(object) :
    def __init__(self, name, age) :
        self.name = name
        self.age = age

from xprint import xprint, xregister, XObject, XField, XSymbol

def convert_person(x) :
    return XObject("Person", x.name, x.age)

xregister(Person, convert_person)
    

To print, do the following.

>>> xprint(Person("Stepanov",40))
Person('Stepanov', 40)
    

You can also add a label for age like this.

def convert_person(x) :
    return XObject("Person", x.name, XField("age",x.age))

xregister(Person, convert_person)
    
>>> xprint(Person("Stepanov",40))
Person('Stepanov', age=40)
    

Let's look at a bigger example - so that we can actually see the indentation in action!

The following code, constructs an expression tree representing the two roots of the quadratic equation ax2 + bx + c = 0.

First declare the required classes.

class BinaryOp(object) :
    def __init__(self, expr1, expr2) :
        self.expr1 = expr1
        self.expr2 = expr2

class UnaryOp(object) :
    def __init__(self, expr) :
        self.expr = expr

class Add(BinaryOp) : pass
class Subtract(BinaryOp) : pass
class Multiply(BinaryOp) : pass
class Divide(BinaryOp) : pass
class Negate(UnaryOp) : pass
class SquareRoot(UnaryOp) : pass

class Variable(object) :
    def __init__(self, name) :
        self.name = name
    

Now construct expression trees representing the roots of a quadratic equation.

a,b,c = Variable('a'), Variable('b'), Variable('c')

sroot = SquareRoot(Subtract(Multiply(b,b), Multiply(4,Multiply(a,c))))
root1 = Divide(Add(Negate(b),sroot), Multiply(2,a))
root2 = Divide(Subtract(Negate(b),sroot), Multiply(2,a))

quadratic_roots = [root1, root2]
    

Before the roots can be printed, converters need to be registered for each custom type.

def convert_binary_op(x) :
    class_name = type(x).__name__
    return XObject(class_name, x.expr1, x.expr2)

xregister(Add,      convert_binary_op)
xregister(Subtract, convert_binary_op)
xregister(Multiply, convert_binary_op)
xregister(Divide,   convert_binary_op)

def convert_unary_op(x) :
    class_name = type(x).__name__
    return XObject(class_name, x.expr)

xregister(Negate,     convert_unary_op)
xregister(SquareRoot, convert_unary_op)

xregister(Variable, lambda x : XSymbol(x.name))
    

Print the quadratic roots!

xprint(quadratic_roots)
    
[Divide(
   Add(Negate(b),
       SquareRoot(Subtract(Multiply(b, b), Multiply(4, Multiply(a, c))))),
   Multiply(2, a)),
 Divide(
   Subtract(Negate(b),
            SquareRoot(Subtract(Multiply(b, b), Multiply(4, Multiply(a, c))))),
   Multiply(2, a))]
    

Now print with 50 columns. (The default is 80).

xprint(quadratic_roots, columns=50)
    
[Divide(
   Add(Negate(b),
       SquareRoot(
         Subtract(Multiply(b, b),
                  Multiply(4, Multiply(a, c))))),
   Multiply(2, a)),
 Divide(
   Subtract(Negate(b),
            SquareRoot(
              Subtract(Multiply(b, b),
                       Multiply(4, Multiply(a, c))))),
   Multiply(2, a))]