L'analisi e la modifica della struttura del codice è certamente possibile con l'aiuto del ast
modulo e lo mostrerò in un esempio tra un momento. Tuttavia, non è possibile riscrivere il codice sorgente modificato con il ast
solo modulo. Ci sono altri moduli disponibili per questo lavoro come uno qui .
NOTA: L'esempio che segue può essere trattato come un tutorial introduttivo sull'uso del ast
modulo, ma una guida più completa sull'uso del ast
modulo è disponibile qui nel tutorial sui serpenti Green Tree e nella documentazione ufficiale sul ast
modulo .
Introduzione a ast
:
>>> import ast
>>> tree = ast.parse("print 'Hello Python!!'")
>>> exec(compile(tree, filename="<ast>", mode="exec"))
Hello Python!!
Puoi analizzare il codice Python (rappresentato in stringa) semplicemente chiamando l'API ast.parse()
. Ciò restituisce l'handle alla struttura AST (Abstract Syntax Tree). È interessante notare che è possibile compilare nuovamente questa struttura ed eseguirla come mostrato sopra.
Un'altra API molto utile è quella ast.dump()
che scarica l'intero AST in una forma di stringa. Può essere utilizzato per ispezionare la struttura ad albero ed è molto utile nel debug. Per esempio,
Su Python 2.7:
>>> import ast
>>> tree = ast.parse("print 'Hello Python!!'")
>>> ast.dump(tree)
"Module(body=[Print(dest=None, values=[Str(s='Hello Python!!')], nl=True)])"
Su Python 3.5:
>>> import ast
>>> tree = ast.parse("print ('Hello Python!!')")
>>> ast.dump(tree)
"Module(body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Str(s='Hello Python!!')], keywords=[]))])"
Notare la differenza nella sintassi per l'istruzione print in Python 2.7 rispetto a Python 3.5 e la differenza nel tipo di nodo AST nei rispettivi alberi.
Come modificare il codice usando ast
:
Ora diamo un'occhiata a un esempio di modifica del codice Python per ast
modulo. Lo strumento principale per modificare la struttura AST è la ast.NodeTransformer
classe. Ogni volta che è necessario modificare l'AST, è necessario sottoclassarlo e scrivere di conseguenza la trasformazione o le trasformazioni dei nodi.
Per il nostro esempio, proviamo a scrivere una semplice utility che trasforma Python 2, stampa le istruzioni in chiamate di funzione Python 3.
Stampa l'istruzione sull'utilità di conversione delle chiamate divertenti: print2to3.py:
#!/usr/bin/env python
'''
This utility converts the python (2.7) statements to Python 3 alike function calls before running the code.
USAGE:
python print2to3.py <filename>
'''
import ast
import sys
class P2to3(ast.NodeTransformer):
def visit_Print(self, node):
new_node = ast.Expr(value=ast.Call(func=ast.Name(id='print', ctx=ast.Load()),
args=node.values,
keywords=[], starargs=None, kwargs=None))
ast.copy_location(new_node, node)
return new_node
def main(filename=None):
if not filename:
return
with open(filename, 'r') as fp:
data = fp.readlines()
data = ''.join(data)
tree = ast.parse(data)
print "Converting python 2 print statements to Python 3 function calls"
print "-" * 35
P2to3().visit(tree)
ast.fix_missing_locations(tree)
# print ast.dump(tree)
exec(compile(tree, filename="p23", mode="exec"))
if __name__ == '__main__':
if len(sys.argv) <=1:
print ("\nUSAGE:\n\t print2to3.py <filename>")
sys.exit(1)
else:
main(sys.argv[1])
Questa utility può essere provata su un piccolo file di esempio, come quello qui sotto, e dovrebbe funzionare bene.
Test file di input: py2.py
class A(object):
def __init__(self):
pass
def good():
print "I am good"
main = good
if __name__ == '__main__':
print "I am in main"
main()
Si noti che la trasformazione di cui sopra è solo a ast
scopo di esercitazione e nel caso reale si dovrà esaminare tutti i diversi scenari come print " x is %s" % ("Hello Python")
.