""" A (toy) tool for emitting Python ASTs as YAML formatted data. """ import ast import optparse import sys import yaml def propnames(node): """return names of attributes specific for the current node""" props = {x for x in dir(node) if not x.startswith("_")} if isinstance(node, ast.Module): props -= {"body"} if isinstance(node, (ast.Expr, ast.Attribute)): props -= {"value"} if isinstance(node, ast.Constant): props -= {"n", "s"} if isinstance(node, ast.ClassDef): props -= {"body"} return props # Note that ast.NodeTransformer exists for mutations. # This is just for reads. class TreeDumper(ast.NodeVisitor): def __init__(self): super().__init__() self._stack = [] def dump(self, node): self.visit(node) def visit(self, node): # nodetype = type(node) nodename = node.__class__.__name__ indent = " " * len(self._stack) * 2 print(indent + nodename) for n in propnames(node): print(indent + "%s: %s" % (n, node.__dict__[n])) self._stack.append(node) self.generic_visit(node) self._stack.pop() class YAMLTreeDumper(ast.NodeVisitor): def __init__(self): super().__init__() self._stack = [] def node2yml(self, node): try: # nodetype = type(node) nodename = node.__class__.__name__ return { "op": nodename, "props": {n: node.__dict__[n] for n in propnames(node)}, "children": [], } except Exception: print(repr(node), propnames(node), dir(node)) def visit(self, node): yml_node = self.node2yml(node) self._stack.append(yml_node) old_stack = self._stack self._stack = yml_node["children"] self.generic_visit(node) self._stack = old_stack return yml_node if __name__ == "__main__": parser = optparse.OptionParser(usage="%prog [options] ") opts, args = parser.parse_args() if len(args) == 0: parser.print_help() sys.exit(-1) filename = args[0] with open(filename) as f: root = ast.parse(f.read(), filename) print( yaml.dump( YAMLTreeDumper().visit(root), default_flow_style=False, sort_keys=False ) )