Analisar e modificar a estrutura do código é certamente possível com a ajuda do ast
módulo e mostrarei em um exemplo em um momento. No entanto, a gravação do código-fonte modificado não é possível apenas com o ast
módulo. Existem outros módulos disponíveis para este trabalho, como um aqui .
NOTA: O exemplo abaixo pode ser tratado como um tutorial introdutório sobre o uso do ast
módulo, mas um guia mais abrangente sobre o uso do ast
módulo está disponível aqui no tutorial do Green Tree snakes e na documentação oficial do ast
módulo .
Introdução a ast
:
>>> import ast
>>> tree = ast.parse("print 'Hello Python!!'")
>>> exec(compile(tree, filename="<ast>", mode="exec"))
Hello Python!!
Você pode analisar o código python (representado na string) simplesmente chamando a API ast.parse()
. Isso retorna o identificador para a estrutura Abstract Syntax Tree (AST). Curiosamente, você pode compilar novamente essa estrutura e executá-la como mostrado acima.
Outra API muito útil é ast.dump()
que despeja todo o AST em uma forma de string. Ele pode ser usado para inspecionar a estrutura da árvore e é muito útil na depuração. Por exemplo,
No 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)])"
No 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=[]))])"
Observe a diferença na sintaxe da declaração de impressão no Python 2.7 vs. o Python 3.5 e a diferença no tipo de nó AST nas respectivas árvores.
Como modificar o código usando ast
:
Agora, vamos dar uma olhada em um exemplo de modificação do código python por ast
módulo. A principal ferramenta para modificar a estrutura AST é a ast.NodeTransformer
classe. Sempre que é necessário modificar o AST, ele precisa subclassificar e escrever a (s) Transformação (s) de Nó de acordo.
No nosso exemplo, vamos tentar escrever um utilitário simples que transforma as instruções de impressão Python 2 em chamadas de função do Python 3.
Imprima a declaração no utilitário conversor de chamadas 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])
Este utilitário pode ser experimentado em um pequeno arquivo de exemplo, como o abaixo, e deve funcionar bem.
Arquivo de entrada de teste: 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()
Observe que a transformação acima é apenas para ast
fins de tutorial e, no caso real, será necessário analisar todos os cenários diferentes, como print " x is %s" % ("Hello Python")
.