L'analyse et la modification de la structure du code est certainement possible à l'aide du ast
module et je vais le montrer dans un exemple dans un instant. Cependant, la réécriture du code source modifié n'est pas possible avec le ast
module seul. Il existe d'autres modules disponibles pour ce travail, comme un ici .
REMARQUE: l'exemple ci-dessous peut être traité comme un didacticiel d'introduction sur l'utilisation du ast
module, mais un guide plus complet sur l'utilisation du ast
module est disponible ici sur le didacticiel Green Tree serpents et la documentation officielle sur le ast
module .
Introduction à ast
:
>>> import ast
>>> tree = ast.parse("print 'Hello Python!!'")
>>> exec(compile(tree, filename="<ast>", mode="exec"))
Hello Python!!
Vous pouvez analyser le code python (représenté sous forme de chaîne) en appelant simplement l'API ast.parse()
. Cela renvoie le descripteur à la structure AST (Abstract Syntax Tree). Il est intéressant de noter que vous pouvez recompiler cette structure et l'exécuter comme indiqué ci-dessus.
Une autre API très utile est de ast.dump()
vider tout l'AST sous forme de chaîne. Il peut être utilisé pour inspecter l'arborescence et est très utile pour le débogage. Par exemple,
Sur 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)])"
Sur 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=[]))])"
Notez la différence de syntaxe pour l'instruction d'impression dans Python 2.7 par rapport à Python 3.5 et la différence de type de nœud AST dans les arbres respectifs.
Comment modifier le code en utilisant ast
:
Voyons maintenant un exemple de modification de code python par ast
module. Le principal outil de modification de la structure AST est la ast.NodeTransformer
classe. Chaque fois que l'on a besoin de modifier l'AST, il / elle doit en faire une sous-classe et écrire la (les) transformation (s) de nœud en conséquence.
Pour notre exemple, essayons d'écrire un utilitaire simple qui transforme les instructions Python 2, print en appels de fonction Python 3.
Imprimer l'instruction dans l'utilitaire de conversion d'appel Fun: 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])
Cet utilitaire peut être essayé sur un petit fichier d'exemple, tel que celui ci-dessous, et il devrait fonctionner correctement.
Fichier d'entrée de test: 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()
Veuillez noter que la transformation ci-dessus est uniquement à ast
des fins de didacticiel et que dans un scénario réel, il faudra examiner tous les différents scénarios tels que print " x is %s" % ("Hello Python")
.