Quero editar programaticamente o código-fonte python. Basicamente, quero ler um .py
arquivo, gerar o AST e, em seguida, escrever novamente o código-fonte python modificado (ou seja, outro .py
arquivo).
Existem maneiras de analisar / compilar o código-fonte python usando módulos python padrão, como ast
ou compiler
. No entanto, acho que nenhum deles suporta maneiras de modificar o código-fonte (por exemplo, exclua esta declaração de função) e depois escreva novamente o código-fonte python modificado.
ATUALIZAÇÃO: A razão pela qual eu quero fazer isso é que eu gostaria de escrever uma biblioteca de teste de mutação para python, principalmente excluindo instruções / expressões, executando novamente os testes e vendo o que quebra.
Respostas:
O Pythoscope faz isso nos casos de teste que gera automaticamente, assim como a ferramenta 2to3 para o python 2.6 (converte a fonte python 2.x na fonte python 3.x).
Ambas as ferramentas usam a biblioteca lib2to3 , que é uma implementação do mecanismo de analisador / compilador python que pode preservar comentários na fonte quando ela faz ida e volta da fonte -> AST -> fonte.
O projeto de corda pode atender às suas necessidades se você quiser fazer mais refatorações como transformações.
O módulo ast é sua outra opção, e há um exemplo mais antigo de como "desparalisar" as árvores de sintaxe de volta ao código (usando o módulo analisador). Mas o
ast
módulo é mais útil ao fazer uma transformação AST no código que é transformado em um objeto de código.O projeto redbaron também pode ser um bom ajuste (ht Xavier Combelle)
fonte
unparse.py
script - pode ser realmente complicado usá-lo em outro script. Mas, existe um pacote chamado astunparse ( no github , no pypi ) que é basicamente uma versão do pacote adequadamenteunparse.py
.O módulo ast embutido não parece ter um método para converter de volta à fonte. No entanto, o módulo codegen aqui fornece uma impressora bonita para o ast que permitiria fazer isso. por exemplo.
Isso imprimirá:
Observe que você pode perder a formatação exata e os comentários, pois eles não são preservados.
No entanto, você pode não precisar. Se tudo o que você precisa é executar o AST substituído, basta fazê-lo chamando compile () no ast e executando o objeto de código resultante.
fonte
Em uma resposta diferente, sugeri o uso do
astor
pacote, mas encontrei um pacote de análise de análise AST mais atualizado chamadoastunparse
:Eu testei isso no Python 3.5.
fonte
Pode não ser necessário gerar novamente o código fonte. É um pouco perigoso para mim dizer, é claro, já que você realmente não explicou por que acha que precisa gerar um arquivo .py cheio de código; mas:
Se você deseja gerar um arquivo .py que as pessoas realmente usem, talvez para que eles possam preencher um formulário e obter um arquivo .py útil para inserir em seu projeto, não será necessário transformá-lo em um AST e de volta porque você perderá
toda a formatação (pense nas linhas em branco que tornam o Python tão legível ao agrupar conjuntos de linhas relacionados)(comentários de nóslineno
ecol_offset
atributos ). Em vez disso, você provavelmente desejará usar um mecanismo de modelagem (a linguagem de modelo do Django , por exemplo, foi projetada para facilitar a modelagem de arquivos de texto) para personalizar o arquivo .py ou usar a extensão MetaPython de Rick Copeland .Se você estiver tentando fazer uma alteração durante a compilação de um módulo, observe que não é necessário voltar ao texto; você pode compilar o AST diretamente, em vez de transformá-lo novamente em um arquivo .py.
Mas em quase todo e qualquer caso, você provavelmente está tentando fazer algo dinâmico que uma linguagem como o Python realmente facilita muito, sem escrever novos arquivos .py! Se você expandir sua pergunta para nos informar o que realmente deseja realizar, os novos arquivos .py provavelmente não estarão envolvidos na resposta; Eu já vi centenas de projetos Python fazendo centenas de coisas do mundo real, e nenhum deles precisava criar um arquivo .py. Então, devo admitir, sou um pouco cético que você tenha encontrado o primeiro bom caso de uso. :-)
Atualização: agora que você explicou o que está tentando fazer, eu ficaria tentado a operar no AST de qualquer maneira. Você deseja fazer a mutação removendo, não as linhas de um arquivo (o que poderia resultar em meias instruções que simplesmente morrem com um SyntaxError), mas em instruções inteiras - e que melhor lugar para fazer isso do que no AST?
fonte
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 oast
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 doast
módulo está disponível aqui no tutorial do Green Tree snakes e na documentação oficial doast
módulo .Introdução a
ast
: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:
No Python 3.5:
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 é aast.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:
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
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, comoprint " x is %s" % ("Hello Python")
.fonte
Eu criei recentemente um código bastante estável (o núcleo é realmente bem testado) e extensível que gera código a partir da
ast
árvore: https://github.com/paluh/code-formatter .Estou usando meu projeto como base para um pequeno plug-in vim (que uso todos os dias), então meu objetivo é gerar um código python realmente agradável e legível.
PS: Eu tentei estender,
codegen
mas sua arquitetura é baseada naast.NodeVisitor
interface, então formatadores (visitor_
métodos) são apenas funções. Achei essa estrutura bastante limitadora e difícil de otimizar (no caso de expressões longas e aninhadas, é mais fácil manter os objetos em árvore e armazenar em cache alguns resultados parciais - de outra forma, você pode atingir complexidade exponencial se quiser procurar o melhor layout). MAS,codegen
como todas as peças do trabalho de mitsuhiko (que eu li) são muito bem escritas e concisas.fonte
Uma das outras respostas recomenda
codegen
, que parece ter sido substituída porastor
. A versão doastor
PyPI (versão 0.5 até a presente data) também parece um pouco desatualizada, portanto você pode instalar a versão de desenvolvimento daastor
seguinte forma.Em seguida, você pode usar
astor.to_source
para converter um código-fonte Python AST em código-fonte Python legível por humanos:Eu testei isso no Python 3.5.
fonte
Se você estiver olhando para isso em 2019, poderá usar este pacote libcst . Possui sintaxe semelhante ao ast. Isso funciona como um encanto e preserva a estrutura do código. É basicamente útil para o projeto em que você deve preservar comentários, espaço em branco, nova linha etc.
Se você não precisa se preocupar com os comentários de preservação, espaços em branco e outros, a combinação de ast e astor funciona bem.
fonte
Tínhamos uma necessidade semelhante, que não foi resolvida por outras respostas aqui. Por isso, criamos uma biblioteca para isso, o ASTTokens , que pega uma árvore AST produzida com os módulos ast ou astroid e a marca com os intervalos de texto no código-fonte original.
Ele não faz modificações diretamente no código, mas isso não é difícil de adicionar, pois indica o intervalo de texto que você precisa modificar.
Por exemplo, isso envolve uma chamada de função
WRAP(...)
, preservando comentários e tudo o mais:Produz:
Espero que isto ajude!
fonte
Um Sistema de Transformação de Programa é uma ferramenta que analisa o texto de origem, constrói ASTs, permite modificá-lo usando transformações de fonte para fonte ("se você vir esse padrão, substitua-o por esse padrão"). Essas ferramentas são ideais para a mutação de códigos-fonte existentes, que são apenas "se você vê esse padrão, substitua por uma variante de padrão".
Obviamente, você precisa de um mecanismo de transformação de programa que possa analisar o idioma de seu interesse e ainda fazer as transformações direcionadas a padrões. Nosso DMS Software Reengineering Toolkit é um sistema que pode fazer isso e lida com Python e uma variedade de outros idiomas.
Veja esta resposta do SO para obter um exemplo de AST para Python analisado por DMS, capturando comentários com precisão. O DMS pode fazer alterações no AST e gerar novamente um texto válido, incluindo os comentários. Você pode solicitar a impressão bonita do AST, usando suas próprias convenções de formatação (você pode alterá-las) ou fazer "impressão de fidelidade", que usa as informações originais de linha e coluna para preservar ao máximo o layout original (algumas alterações no layout onde o novo código está inserido é inevitável).
Para implementar uma regra de "mutação" para Python com DMS, você pode escrever o seguinte:
Esta regra substitui "+" por "-" de uma maneira sintaticamente correta; Ele opera no AST e, portanto, não toca em seqüências ou comentários que parecem corretos. A condição extra em "mutate_this_place" é permitir que você controle com que frequência isso ocorre; você não deseja alterar todos os lugares do programa.
Obviamente, você deseja várias regras como essa que detectam várias estruturas de código e as substituem pelas versões mutadas. O DMS tem prazer em aplicar um conjunto de regras. O AST mutado é então bastante impresso.
fonte
Eu costumava usar o barão para isso, mas agora mudei para o parso porque está atualizado com o python moderno. Isso funciona muito bem.
Eu também precisava disso para um testador de mutação. É realmente muito simples criar um com parso, confira meu código em https://github.com/boxed/mutmut
fonte