2009-09-23 9 views
7

Sembra che dovrebbe essere facile, ma non riesco a trovare la risposta da nessuna parte - né in grado di derivarne una da solo. Come si trasforma una funzione python/lambda non quotata in un AST?Come si trasforma una funzione Python/lambda non quotata in AST? 2.6

Ecco cosa mi piacerebbe essere in grado di fare.

import ast 
class Walker(ast.NodeVisitor): 
    pass 
    # ... 

# note, this doesnt work as ast.parse wants a string 
tree = ast.parse(lambda x,y: x+y) 

Walker().visit(tree) 
+0

@Ants Aasma suggerimento era più vicina a quello che speravo, anche se sembra essere molto più coinvolti di quanto mi aspettassi e immagino diverse versioni di Python WRT più fragili (se cambia bytecode). Anche GeniuSQL sembra promettente. Off per fare qualche test! – Chris

+0

Inoltre, ho appena trovato questo: http://code.activestate.com/recipes/442447/ – Chris

risposta

6

Se si ottiene solo l'accesso alla funzione/lambda, si ha solo il bytecode python compilato. L'esatto Python AST non può essere ricostruito dal bytecode perché c'è una perdita di informazioni nel processo di compilazione. Ma puoi analizzare il bytecode e creare AST per quello. C'è uno di questi analizzatori in GeniuSQL. Ho anche una piccola prova di concetto che analizza bytecode e crea clausole di SQLAlchemy da questo.

Il processo che ho usato per l'analisi è la seguente:

  1. Spalato il codice in un elenco di codici operativi con i potenziali argomenti.
  2. Trova i blocchi di base nel codice passando dagli opcode e per ogni salto crea un limite di blocco di base dopo il salto e prima del target di salto
  3. Crea un grafico del flusso di controllo dai blocchi di base.
  4. Passare attraverso tutti i blocchi di base con stack di rilevamento interpretazione astratta e assegnazioni variabili in formato SSA.
  5. Per creare l'espressione di output è sufficiente ottenere il valore di ritorno SSA calcolato.

Ho incollato il mio proof of concept e example code using it. Questo non è pulito rapidamente codice hacked insieme, ma sei libero di costruire su di esso, se vuoi. Lascia una nota se decidi di farne qualcosa di utile.

+0

Ah, capisco. Quindi prendi il codice byt compilato e crea il tuo AST adatto per generare SQL o altre grammatiche. Questo è abbastanza brillante (e ben oltre il mio livello di abilità :). Darò un ulteriore sguardo al tuo codice e GenuiuSQL. Non mi rendevo conto che c'era un altro percorso diverso dal modulo standard di '' ast'' lib, e penso che mi stesse accecando. Grazie. – Chris

+0

I collegamenti a dpaste non sono aggiornati :( –

0

tua espressione lambda è una funzione, che ha un sacco di informazioni, ma non credo che ancora ha il codice sorgente associato con. Non sono sicuro che tu possa ottenere quello che vuoi.

10

In generale, non è possibile. Ad esempio, 2 + 2 è un'espressione, ma se si passa a qualsiasi funzione o metodo, l'argomento che viene passato è solo il numero 4, non c'è modo di recuperare l'espressione da cui è stato calcolato. A volte il codice sorgente della funzione può essere ripristinato (sebbene non per un lambda), ma "un'espressione Python non quotata" ottiene valutato, quindi quello che ottieni è solo l'oggetto che è il valore dell'espressione.

Che problema stai cercando di risolvere? Potrebbero esserci altri approcci fattibili.

Modifica: tx all'OP per chiarire. Non c'è modo di farlo per lambda o di alcuni altri casi d'angolo, ma come ho detto codice sorgente funzione a volte può essere recuperato ...:

import ast 
import inspect 

def f(): 
    return 23 

tree = ast.parse(inspect.getsource(f)) 

print ast.dump(tree) 

inspect.getsource solleva IOError se non è possibile ottenere il codice sorgente per qualunque oggetto che stai passando. Vi suggerisco di racchiudere l'analisi e la chiamata getsource in una funzione ausiliaria che può accettare una stringa (e solo analizzarla) OPPURE una funzione (e prova a trovarla su di essa, probabilmente dando errori migliori nel caso IOError).

+0

Mi dispiace - Vedo che espressione è il termine sbagliato da usare. È stato rimosso dalla domanda. In generale, sto provando a trasformare un AST in un'altra grammatica. Esempi specifici: dal dato func/lambda, generare un'istruzione SQL o una javascript couchdb map/ridurre la visualizzazione o una query di mongodb dict, ecc. Suppongo che non quotato non sia un requisito rigoroso, ma sarebbe più pulito. – Chris

+0

Grazie per i vostri suggerimenti e il codice. Non ero a conoscenza di insepect.getsource. Potrei provare sia l'approccio bytecode che questo approccio inspect.getsource per confrontare. – Chris

1

Non è possibile generare AST dal codice byte compilato. Hai bisogno del codice sorgente.

4

The Meta library consente di ripristinare il codice sorgente in molti casi, con alcune eccezioni come comprensione e lambda.

import meta, ast 
source = ''' 
a = 1 
b = 2 
c = (a ** b) 
''' 

mod = ast.parse(source, '<nofile>', 'exec') 
code = compile(mod, '<nofile>', 'exec') 

mod2 = meta.decompile(code) 
source2 = meta.dump_python_source(mod2) 

assert source == source2 
Problemi correlati