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

    Rustic exposed brick wall featuring aged electrical sockets and metal conduit.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    socket no Python: redes TCP e UDP

    Aprenda socket no Python para clientes e servidores TCP, UDP, framing, timeouts, IPv6, concorrência, TLS e segurança de rede.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    multiprocessing no Python: vários núcleos

    Aprenda multiprocessing no Python com processos, pools, filas, pipes, memória compartilhada, cancelamento, segurança e shutdown correto.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    Monochrome image showcasing concentric circles in a tunnel-like structure creating a sense of depth.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    concurrent.futures: threads e processos em paralelo

    Aprenda concurrent.futures no Python com threads, processos, Future, timeouts, cancelamento, backpressure e prevenção de deadlocks.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    Striking image of a red-bellied python showcasing its vibrant scales in dramatic lighting.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    winsound no Python: sons no Windows

    Aprenda winsound no Python para tocar WAV, sons do sistema, beeps, loops e notificações assíncronas com segurança no Windows.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    winreg no Python: Registro do Windows

    Aprenda winreg no Python para ler e gravar o Registro do Windows, controlar permissões, tipos, WOW64, exclusões e segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    26/08/2026
    Macro shot capturing detailed patterns of a python in its natural surroundings.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    posix no Python: chamadas Unix diretas

    Entenda posix no Python, chamadas Unix, descritores, permissões, processos, segurança e quando usar os em vez do módulo direto.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026