2011-12-01 15 views
7

Sto analizzando l'AST generato dal codice Python per "divertimento e profitto", e mi piacerebbe avere qualcosa di più grafico di "ast.dump" per vedere effettivamente l'AST generato.Grafico da python a punto

In teoria è già un albero, quindi non dovrebbe essere troppo difficile creare un grafico, ma non capisco come potrei farlo.

ast.walk sembra camminare con una strategia BFS, ed i metodi visitX non posso davvero vedere il genitore o che non sembrano trovare un modo per creare un grafico ...

Sembra come se l'unico modo fosse scrivere la mia funzione DFS walk, è logico?

risposta

6

Se si guarda ast.NodeVisitor, è una classe abbastanza banale. Puoi sottoclassi o semplicemente reimplementare la sua strategia di camminata per qualsiasi cosa ti serva. Ad esempio, mantenere i riferimenti al genitore quando i nodi sono visitati è molto semplice da implementare in questo modo, basta aggiungere un metodo visit che accetta anche il genitore come argomento e passarlo dal proprio generic_visit.

P.S. A proposito, sembra che NodeVisitor.generic_visit implementa DFS, quindi tutto ciò che devi fare è aggiungere il nodo genitore che passa.

+0

Sì hai ragione è molto semplice implementazione, ho pensato che ho dovuto controllare tutti i casi possibili, ma in realtà controllando se si tratta di una lista o non è già abbastanza per camminare attraverso l'albero .. Grazie Un sacco –

6

fantastico, funziona ed è davvero semplice

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) 

Quindi è lo stesso di un NodeVisitor normale, ma ho un defaultdict cui aggiungo il tipo di nodo per ogni figlio. Poi passo questo dizionario a pygraphviz.AGraph e ottengo il mio bel risultato.

L'unico problema è che il tipo non dice molto, ma d'altra parte usare ast.dump() è troppo prolisso.

La cosa migliore sarebbe ottenere il codice sorgente effettivo per ciascun nodo, è possibile?

EDIT: ora è molto meglio, ho passato nel costruttore anche il codice sorgente e cerco di ottenere la riga di codice, se possibile, altrimenti basta stampare il 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) 
Problemi correlati