Explorando o Python AST: Como Ler e Modificar Código Programaticamente
Linters e ferramentas de análise estática
Refatoração automatizada de código
Geração de código
Ferramentas de instrumentação e profiling
Neste post, vamos explorar como usar o módulo ast do Python para ler, interpretar e modificar código, com exemplos práticos, dicas e boas práticas.
1. O que é AST?
AST (Abstract Syntax Tree) é uma representação em árvore de um código fonte, onde cada nó representa uma construção sintática do Python:
Funções, classes, loops e condicionais
Expressões matemáticas e operações lógicas
Chamadas de funções e atribuições
Exemplo simples:
x = 10 + 5
AST correspondente:
Module(
body=[
Assign(
targets=[Name(id='x', ctx=Store())],
value=BinOp(left=Constant(value=10), op=Add(), right=Constant(value=5))
)
]
)
2. Analisando código com ast
O módulo ast permite converter código em uma árvore de sintaxe:
import ast
code = "x = 10 + 5"
tree = ast.parse(code)
print(ast.dump(tree, indent=4))
Saída:
Module(
body=[
Assign(
targets=[Name(id='x', ctx=Store())],
value=BinOp(left=Constant(value=10), op=Add(), right=Constant(value=5))
)
],
type_ignores=[]
)
Explicação:
Module: representa o arquivo/códigoAssign: operação de atribuiçãoBinOp: operação binária (+)Constant: valores literais
3. Percorrendo a AST
Para inspecionar cada nó, use ast.walk ou visitores personalizados.
3.1 Exemplo com ast.walk
for node in ast.walk(tree):
print(type(node))
Isso imprime o tipo de cada nó (Module, Assign, Name, BinOp, Constant), útil para análise estática.
3.2 Criando um visitante com NodeVisitor
class Analyzer(ast.NodeVisitor):
def visit_Assign(self, node):
print("Encontrada atribuição para:", [t.id for t in node.targets])
self.generic_visit(node)
analyzer = Analyzer()
analyzer.visit(tree)
Saída:
Encontrada atribuição para: ['x']
Vantagem: você pode criar lógicas específicas para cada tipo de nó.
4. Modificando código com AST
Além de ler, podemos modificar a árvore e gerar código novo.
4.1 Criando um NodeTransformer
class AddTenTransformer(ast.NodeTransformer):
def visit_BinOp(self, node):
# Adiciona 10 a qualquer soma
if isinstance(node.op, ast.Add):
return ast.BinOp(left=node.left, op=node.op, right=ast.Constant(value=10))
return node
transformer = AddTenTransformer()
new_tree = transformer.visit(tree)
4.2 Gerando código Python de volta
Python 3.9+ possui ast.unparse:
new_code = ast.unparse(new_tree)
print(new_code)
Saída:
x = 10 + 10
5. Exemplos práticos de uso do AST
5.1 Contando funções em um arquivo Python
class FunctionCounter(ast.NodeVisitor):
def __init__(self):
self.count = 0
def visit_FunctionDef(self, node):
self.count += 1
self.generic_visit(node)
with open("script.py") as f:
tree = ast.parse(f.read())
counter = FunctionCounter()
counter.visit(tree)
print(f"Total de funções: {counter.count}")
5.2 Detectando imports desnecessários
class ImportAnalyzer(ast.NodeVisitor):
def visit_Import(self, node):
for alias in node.names:
print("Import encontrado:", alias.name)
def visit_ImportFrom(self, node):
print("From import:", node.module)
tree = ast.parse(open("script.py").read())
ImportAnalyzer().visit(tree)
5.3 Inserindo logging automático em funções
class LoggerInjector(ast.NodeTransformer):
def visit_FunctionDef(self, node):
# Adiciona print no início de cada função
print_stmt = ast.parse(f'print("Entrando na função {node.name}")').body[0]
node.body.insert(0, print_stmt)
return node
tree = ast.parse(open("script.py").read())
tree = LoggerInjector().visit(tree)
exec(compile(tree, filename="<ast>", mode="exec"))
Resultado: todas as funções do script imprimirão uma mensagem ao serem chamadas.
6. Boas práticas ao usar AST
Nunca execute código de origem não confiável diretamente com
exec.Mantenha separação entre análise, transformação e execução do código.
Use
NodeVisitorpara análise eNodeTransformerpara alterações.Teste as transformações para evitar gerar código inválido.
Versionamento de scripts: manter histórico das mudanças AST é útil em refatoração automática.
Performance: AST é ótimo para análise e geração de código, mas não substitui otimizações de runtime.
7. Ferramentas avançadas que usam AST
mypyepyright: análise de tipos estáticosblackeautopep8: formatação automática de códigopylinteflake8: linters e análise estáticaFerramentas de refatoração automática: adicionam logs, instrumentação ou otimizações
Python AST é a base para todas essas ferramentas, permitindo manipulação programática do código fonte.
8. Conclusão
O Python AST é uma ferramenta extremamente poderosa para ler, analisar e modificar código programaticamente. Com AST, você pode:
Criar linters e verificações personalizadas
Automatizar refatorações
Gerar código de forma dinâmica
Instrumentar funções para logging ou métricas
Desenvolver ferramentas avançadas de análise de código
Próximos passos sugeridos:
Explorar AST em scripts maiores para análise de dependências ou métricas.
Combinar NodeTransformer com geração de código para refatorações automáticas.
Integrar AST com frameworks como
astorouredbaronpara manipulação mais amigável.Criar ferramentas de linting ou análise customizada para projetos específicos.

Comentários
Postar um comentário