fantástico, funciona y es realmente sencilla
class AstGraphGenerator(object):
def __init__(self):
self.graph = defaultdict(lambda: [])
def __str__(self):
return str(self.graph)
def visit(self, node):
"""Visit a node."""
method = 'visit_' + node.__class__.__name__
visitor = getattr(self, method, self.generic_visit)
return visitor(node)
def generic_visit(self, node):
"""Called if no explicit visitor function exists for a node."""
for _, value in ast.iter_fields(node):
if isinstance(value, list):
for item in value:
if isinstance(item, ast.AST):
self.visit(item)
elif isinstance(value, ast.AST):
self.graph[type(node)].append(type(value))
self.visit(value)
Por lo que es lo mismo que un NodeVisitor normal, pero tengo una defaultdict donde agrego el tipo de nodo para cada hijo. Luego paso este diccionario a pygraphviz.AGraph y obtengo mi buen resultado.
El único problema es que el tipo no dice mucho, pero por otro lado el uso de ast.dump() es demasiado detallado.
Lo mejor sería obtener el código fuente real para cada nodo, ¿es posible?
EDITAR: ahora es mucho mejor, paso en el constructor también el código fuente e intento obtener la línea de código si es posible, de lo contrario solo imprimo el tipo.
class AstGraphGenerator(object):
def __init__(self, source):
self.graph = defaultdict(lambda: [])
self.source = source # lines of the source code
def __str__(self):
return str(self.graph)
def _getid(self, node):
try:
lineno = node.lineno - 1
return "%s: %s" % (type(node), self.source[lineno].strip())
except AttributeError:
return type(node)
def visit(self, node):
"""Visit a node."""
method = 'visit_' + node.__class__.__name__
visitor = getattr(self, method, self.generic_visit)
return visitor(node)
def generic_visit(self, node):
"""Called if no explicit visitor function exists for a node."""
for _, value in ast.iter_fields(node):
if isinstance(value, list):
for item in value:
if isinstance(item, ast.AST):
self.visit(item)
elif isinstance(value, ast.AST):
node_source = self._getid(node)
value_source = self._getid(value)
self.graph[node_source].append(value_source)
# self.graph[type(node)].append(type(value))
self.visit(value)
Sí tienes razón es una aplicación muy simple, lo primero que pensé que tenía que comprobar todos los casos posibles, pero en realidad comprobar si se trata de una lista o aún no está lo suficiente como para caminar a través del árbol .. Gracias mucho –