Welcome, guest | Sign In | My Account | Store | Cart

A function that outputs a human-readable version of a Python AST.

Python, 47 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
"""Python AST pretty-printer.

To me, it is totally unf*ckinbelievable that the standard Python compiler module
does not come with a pretty-printer for the AST. Here is one.
"""

import sys
from compiler.ast import Node


def pprintAst(ast, indent='  ', stream=sys.stdout):
    "Pretty-print an AST to the given output stream."
    rec_node(ast, 0, indent, stream.write)

def rec_node(node, level, indent, write):
    "Recurse through a node, pretty-printing it."
    pfx = indent * level
    if isinstance(node, Node):
        write(pfx)
        write(node.__class__.__name__)
        write('(')

        if any(isinstance(child, Node) for child in node.getChildren()):
            for i, child in enumerate(node.getChildren()):
                if i != 0:
                    write(',')
                write('\n')
                rec_node(child, level+1, indent, write)
            write('\n')
            write(pfx)
        else:
            # None of the children as nodes, simply join their repr on a single
            # line.
            write(', '.join(repr(child) for child in node.getChildren()))

        write(')')

    else:
        write(pfx)
        write(repr(node))


if __name__ == '__main__':
    def test():
        import compiler
        pprintAst(compiler.parseFile(__file__))
    test()

To me, it is totally unbelievable that the standard Python compiler module does not come with a pretty-printer for the AST. It just blows my mind, completely. I don't know how many times I have been hacking around one until today.

You can find the latest version of this code in the Snakefood project source. This really should belong in the standard compiler module (and that code should be improved too, the visitors are always a PIA to use).

1 comment

Ian Barnard 14 years ago  # | flag

had to define an any() function for this to work with python 2.4.3. like this:

def any(l):
    for item in l:
        if item:
            return True
    return False