ast no Python: analise código-fonte

Publicado em: 27/08/2026
Tempo de leitura: 7 minutos
Vivid close-up of code on a computer screen showcasing programming details.

O módulo ast transforma código-fonte Python em uma árvore sintática abstrata. Em vez de tratar o programa como texto, uma ferramenta passa a trabalhar com nós que representam módulos, funções, classes, chamadas, operadores, nomes, literais e estruturas de controle. Essa camada é usada em linters, formatadores, analisadores de segurança, migrações automáticas, documentação e ferramentas educacionais.

Uma AST descreve a estrutura sintática, não o comportamento completo do programa. Imports dinâmicos, reflexão, monkey patching, descriptors, metaclasses e dados em runtime limitam o que uma análise estática pode concluir. Além disso, compilar ou executar uma árvore modificada executa código com os mesmos riscos de qualquer programa Python.

Analise uma string com parse

ast.parse() recebe código e retorna um nó Module.

import ast

codigo = "resultado = soma(2, 3)"
arvore = ast.parse(codigo, filename="exemplo.py", mode="exec")
print(type(arvore).__name__)

O nome do arquivo é usado em mensagens de erro e tracebacks. Informe um valor real quando a origem for conhecida.

Modos exec, eval e single

mode="exec" analisa um módulo com várias instruções. eval aceita uma expressão. single representa uma instrução interativa.

expressao = ast.parse("1 + 2 * 3", mode="eval")

Escolher o modo incorreto gera SyntaxError ou uma árvore incompatível com a compilação pretendida.

Visualize com dump

ast.dump() produz uma representação textual da árvore.

print(ast.dump(arvore, indent=2, include_attributes=True))

include_attributes=True inclui linhas, colunas e posições finais, úteis para diagnósticos e edição de código.

Estrutura básica

Um módulo contém uma lista body. Uma atribuição é um nó Assign; uma chamada é Call; identificadores são Name; literais modernos costumam ser Constant.

atribuicao = arvore.body[0]
print(type(atribuicao).__name__)
print(type(atribuicao.value).__name__)

Não dependa de índices fixos em ferramentas reais. Percorra e valide os tipos de nós.

Contexto Load, Store e Del

Nós Name, Attribute e Subscript possuem um contexto que indica leitura, escrita ou exclusão.

for no in ast.walk(ast.parse("x = y + 1")):
    if isinstance(no, ast.Name):
        print(no.id, type(no.ctx).__name__)

Essa distinção é essencial para análise de variáveis definidas e utilizadas.

Percorra com walk

ast.walk() produz o nó e todos os descendentes sem garantir uma ordem adequada para transformação contextual.

chamadas = [
    no for no in ast.walk(arvore)
    if isinstance(no, ast.Call)
]

Use-o para buscas simples. Para controle de entrada e saída em cada nó, use visitors.

NodeVisitor

Subclasse ast.NodeVisitor e defina métodos visit_TipoDoNo.

class ColetorFuncoes(ast.NodeVisitor):
    def __init__(self):
        self.nomes = []

    def visit_FunctionDef(self, no):
        self.nomes.append(no.name)
        self.generic_visit(no)

coletor = ColetorFuncoes()
coletor.visit(ast.parse(codigo_fonte))
print(coletor.nomes)

Chame generic_visit() quando também quiser percorrer filhos. Se omitir, a visita daquele ramo termina.

Funções assíncronas

AsyncFunctionDef é diferente de FunctionDef. Uma ferramenta que coleta funções deve tratar ambos.

def visit_AsyncFunctionDef(self, no):
    self.nomes.append(no.name)
    self.generic_visit(no)

O mesmo vale para comprehensions assíncronas e AsyncFor/AsyncWith.

Classes e escopos

ClassDef, funções, lambdas e comprehensions criam contextos de escopo distintos. Apenas percorrer nomes não resolve binding lexical.

Para uma análise de símbolos, combine AST com symtable e uma pilha explícita de escopos.

Posições no código

Muitos nós possuem lineno, col_offset, end_lineno e end_col_offset.

for no in ast.walk(arvore):
    if isinstance(no, ast.Call):
        print(no.lineno, no.col_offset, no.end_lineno, no.end_col_offset)

Offsets de coluna são relacionados à representação usada pelo parser e precisam de cuidado com Unicode ao mapear para interfaces.

Recupere o trecho original

ast.get_source_segment(codigo, no) retorna o texto correspondente quando as posições estão disponíveis.

trecho = ast.get_source_segment(codigo, chamadas[0])

O resultado preserva o trecho, mas a AST não guarda comentários e toda a formatação de maneira suficiente para edição perfeita.

Comentários e tokens

Comentários comuns não aparecem como nós da AST. Ferramentas que precisam preservar comentários, whitespace e estilo devem usar tokens ou uma concrete syntax tree.

A AST é excelente para significado estrutural, mas não para round-trip idêntico do arquivo.

Literal seguro com literal_eval

ast.literal_eval() avalia apenas estruturas literais suportadas, como strings, bytes, números, tuplas, listas, dicionários, sets, booleanos e None.

configuracao = ast.literal_eval("{'tentativas': 3, 'ativo': True}")

É mais restrito que eval(), mas não deve receber entradas gigantes ou profundamente aninhadas. Dados maliciosos podem consumir memória, CPU ou profundidade de pilha.

Não use eval em código não confiável

ast.parse() não executa o código, porém compile() e exec() executarão a árvore. Validar alguns nós não cria automaticamente um sandbox seguro.

Python possui muitas formas indiretas de acessar objetos e executar operações. Para entrada hostil, use isolamento de processo, limites e uma linguagem realmente restrita.

Transforme com NodeTransformer

NodeTransformer permite substituir nós retornando um novo objeto.

class TrocarNome(ast.NodeTransformer):
    def visit_Name(self, no):
        if no.id == "antigo":
            return ast.copy_location(
                ast.Name(id="novo", ctx=no.ctx),
                no,
            )
        return no

arvore = TrocarNome().visit(arvore)
ast.fix_missing_locations(arvore)

Preserve o contexto e copie posições para melhorar erros e debug.

Remova ou expanda nós

Um transformer pode retornar None para remover um nó de uma lista de instruções ou uma lista de nós para substituir uma instrução por várias. Nem todo campo aceita essas formas.

Valide a árvore com compilação e testes.

fix_missing_locations

Nós criados manualmente podem não possuir informações de linha. fix_missing_locations() preenche valores a partir dos pais.

Isso permite compilar, mas não produz automaticamente posições perfeitas para ferramentas editoriais.

copy_location e increment_lineno

copy_location(novo, antigo) copia localização. increment_lineno() desloca linhas de uma árvore, útil ao inserir prefixos.

Tracebacks úteis dependem de filename e posições coerentes.

Converta de volta com unparse

ast.unparse() gera código Python equivalente a partir de uma árvore.

codigo_novo = ast.unparse(arvore)

O resultado pode mudar aspas, parênteses e formatação. Ele busca equivalência sintática, não preservação textual.

Compile a árvore

compile() aceita uma AST depois que os campos obrigatórios estão corretos.

objeto = compile(arvore, "transformado.py", "exec")
namespace = {}
exec(objeto, namespace)

Execute apenas código confiável e controle o namespace. Um dicionário de globals reduz exposição acidental, mas não cria sandbox.

Versão da gramática

A estrutura aceita pelo parser muda com a linguagem. Ferramentas que suportam vários Pythons precisam testar cada versão e evitar assumir que um tipo de nó existe em todas.

feature_version pode solicitar uma aproximação de gramática anterior dentro dos limites do interpretador atual, mas não substitui testar na versão de destino.

Type comments

Algumas análises podem solicitar comentários de tipo durante o parse. Ainda assim, type hints modernos aparecem principalmente em nós de annotation.

Para semântica de tipos completa, use um type checker ou sua API, não apenas AST.

Decorators

Funções e classes possuem decorator_list. Decorators são expressões executadas e podem alterar radicalmente o objeto criado.

Uma análise estática deve relatar incerteza em vez de presumir que uma função decorada mantém comportamento original.

Chamadas perigosas

Um linter pode procurar padrões como eval, exec, subprocess com shell ou abertura insegura. Porém comparar apenas o texto do nome produz falsos positivos e negativos.

Aliases, imports e reassignment exigem resolução de símbolos e, mesmo assim, análise estática não garante segurança.

Imports

Import e ImportFrom expõem módulos e aliases declarados.

for no in ast.walk(arvore):
    if isinstance(no, ast.ImportFrom):
        print(no.module, [alias.name for alias in no.names])

Imports dinâmicos e condicionais precisam de tratamento separado.

Complexidade e métricas

Ferramentas podem contar branches, loops, handlers e comprehensions para estimar complexidade. Métricas são sinais para revisão, não prova de qualidade.

Documente o algoritmo e mantenha resultados estáveis entre versões.

Análise de vários arquivos

Para um projeto, descubra arquivos, leia com encoding correto, analise individualmente e agregue resultados. Erros de um arquivo não devem apagar diagnósticos dos demais.

O processamento pode ser paralelizado com limites; veja concurrent.futures no Python. Evite enviar árvores enormes entre processos quando basta retornar diagnósticos compactos.

Encoding de arquivos

Use tokenize.open() para respeitar declaração de encoding do arquivo Python. Ler tudo como UTF-8 pode falhar em código legado.

Erros de sintaxe

ast.parse() gera SyntaxError. Registre filename, linha, offset e mensagem, mas não pare todo o lote.

try:
    arvore = ast.parse(codigo, filename=caminho)
except SyntaxError as erro:
    relatar(caminho, erro.lineno, erro.offset, erro.msg)

Recursão e entradas grandes

Árvores muito profundas podem atingir limites de recursão em visitors e transformações. Arquivos gigantes também consomem memória.

Defina limites operacionais, trate exceções e não aumente o limite de recursão sem compreender o risco.

Testes de transformações

Compare comportamento antes e depois, compile a árvore, execute testes e inspecione o código gerado. Inclua comprehensions, async, pattern matching, decorators, f-strings e annotations.

Use casos negativos para garantir que a transformação não altera nomes em escopos errados.

Erros comuns

Os erros mais frequentes são esquecer generic_visit(), ignorar AsyncFunctionDef, perder contexto Load/Store, criar nós sem localização, esperar preservar comentários, usar literal_eval() sem limites, executar árvore não confiável e presumir que a AST resolve comportamento dinâmico.

Conclusão

ast transforma código Python em uma estrutura navegável e modificável. Use visitors para análise, transformers para mudanças, posições para diagnósticos e unparse() quando equivalência for suficiente.

Mantenha limites de segurança, teste por versão e trate resultados como análise estática, não verdade absoluta. Consulte a documentação oficial de ast e a documentação de symtable.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Código Python assíncrono em notebook para inspect.markcoroutinefunction
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    markcoroutinefunction: identifique wrappers async

    Aprenda inspect.markcoroutinefunction no Python para identificar wrappers assíncronos, integrar frameworks e evitar detecção incorreta de corrotinas.

    Ler mais

    Tempo de leitura: 6 minutos
    10/10/2026
    Código Python para percorrer pastas e arquivos com Path.walk
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Path.walk: percorra diretórios com segurança

    Aprenda Path.walk no Python para percorrer diretórios, filtrar arquivos, tratar erros e controlar a travessia com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    10/10/2026
    Depuração de processo Python em terminal com código
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pdb -p: depure processos Python em execução

    Aprenda a anexar o pdb a um processo Python em execução, inspecionar pilhas e diagnosticar travamentos com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    09/10/2026
    Código Python e representação de frações numéricas
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    fractions.from_number: converta números em frações

    Aprenda fractions.from_number no Python para converter números em frações exatas, controlar precisão e evitar arredondamentos inesperados.

    Ler mais

    Tempo de leitura: 5 minutos
    09/10/2026
    Desenvolvedor configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026