tokenize no Python: analise código-fonte

Publicado em: 05/08/2026
Tempo de leitura: 6 minutos
Código-fonte em tela representando análise com tokenize no Python

Ferramentas de lint, formatadores, analisadores de segurança e editores precisam entender o código Python sem executá-lo. O módulo tokenize no Python transforma o texto-fonte em uma sequência de tokens como nomes, números, operadores, strings, comentários, quebras de linha e indentação. Essa camada fica entre o arquivo bruto e estruturas mais sofisticadas, como a AST.

Neste guia, você aprenderá a ler tokens, preservar codificação, reconstruir código, localizar comentários, medir posições, lidar com erros e combinar tokenize com outras APIs. O conteúdo complementa nossos artigos sobre inspect, symtable, dis, expressões regulares e Ruff.

O que é um token

Um token representa uma unidade léxica reconhecida pelo analisador. A palavra def, o nome de uma função, os parênteses, uma string e um comentário são tokens distintos. Ao contrário de uma busca simples por texto, a tokenização respeita strings multilinhas, comentários, escapes, indentação e operadores compostos.

import tokenize
from io import BytesIO

codigo = b"x = 10  # valor inicial\n"
for token in tokenize.tokenize(BytesIO(codigo).readline):
    print(token)

A função trabalha com bytes porque precisa detectar a codificação antes de interpretar o texto.

Campos de TokenInfo

Cada item retornado é um TokenInfo com tipo, texto, posição inicial, posição final e linha física original.

for token in tokenize.tokenize(BytesIO(codigo).readline):
    print(token.type, token.string, token.start, token.end)

As posições usam pares (linha, coluna). Linhas começam em 1 e colunas em 0. Isso permite destacar exatamente uma região em um editor, relatório ou mensagem de erro.

Nomes legíveis dos tipos

O número do tipo pode ser convertido para um nome com token.tok_name.

import token

for item in tokenize.tokenize(BytesIO(codigo).readline):
    print(token.tok_name[item.type], repr(item.string))

Você verá nomes como ENCODING, NAME, OP, NUMBER, COMMENT, NEWLINE e ENDMARKER.

Detectando a codificação

Arquivos Python podem declarar a codificação nas duas primeiras linhas. detect_encoding() implementa as regras usadas pelo interpretador.

with open("programa.py", "rb") as arquivo:
    encoding, linhas = tokenize.detect_encoding(arquivo.readline)

print(encoding)

A documentação oficial de tokenize explica que um BOM UTF-8 e um cookie de codificação incompatível geram erro. Não tente adivinhar a codificação com heurísticas próprias quando estiver analisando arquivos Python.

Usando tokenize.open()

Para abrir um arquivo de código com a codificação correta, use tokenize.open().

with tokenize.open("programa.py") as arquivo:
    fonte = arquivo.read()

Isso é mais seguro do que fixar UTF-8 em ferramentas que também precisam processar projetos antigos.

Gerando tokens a partir de texto

Quando o código já está em uma string, use generate_tokens(). Essa API aceita uma função que retorna texto, mas não gera o token ENCODING.

from io import StringIO

fonte = "total = preco * quantidade\n"
for item in tokenize.generate_tokens(StringIO(fonte).readline):
    print(item)

Para novas ferramentas que leem arquivos, prefira a API de bytes. Use a variante textual quando a codificação já foi resolvida.

Encontrando comentários

Comentários são tokens próprios, portanto não é necessário procurar o caractere # manualmente.

import token

comentarios = []
for item in tokenize.generate_tokens(StringIO(fonte).readline):
    if item.type == token.COMMENT:
        comentarios.append((item.start, item.string))

Uma busca textual falharia quando # aparece dentro de uma string. A tokenização distingue os dois casos corretamente.

Extraindo nomes sem executar

Você pode coletar identificadores para criar índices, estatísticas ou recursos educacionais.

nomes = {
    item.string
    for item in tokenize.generate_tokens(StringIO(fonte).readline)
    if item.type == token.NAME
}

Essa lista inclui palavras-chave e nomes. Use keyword.iskeyword() para separá-los.

import keyword

identificadores = {nome for nome in nomes if not keyword.iskeyword(nome)}

Indentação e blocos

Python representa mudanças de nível com tokens INDENT e DEDENT.

fonte = "def dobro(x):\n    return x * 2\n"
for item in tokenize.generate_tokens(StringIO(fonte).readline):
    if item.type in {token.INDENT, token.DEDENT}:
        print(token.tok_name[item.type], repr(item.string))

Isso ajuda a construir visualizações de blocos ou verificar padrões de estilo sem executar o código.

NEWLINE e NL

NEWLINE encerra uma instrução lógica. NL representa uma quebra física que não encerra a instrução, como dentro de parênteses.

valores = [
    1,
    2,
]

As quebras internas aparecem como NL. Essa diferença é essencial para formatadores e ferramentas que preservam comentários.

Operadores e exact_type

Operadores são normalmente emitidos como OP, mas exact_type informa o operador específico.

for item in tokenize.generate_tokens(StringIO("a += 1\n").readline):
    if item.type == token.OP:
        print(token.tok_name[item.exact_type])

Assim você distingue soma, atribuição aumentada, setas e outros símbolos sem comparar strings manualmente.

Reconstruindo código

untokenize() reconstrói texto ou bytes a partir dos tokens.

tokens = list(tokenize.tokenize(BytesIO(codigo).readline))
reconstruido = tokenize.untokenize(tokens)
print(reconstruido)

A garantia principal é que a sequência de tipos e textos pode ser tokenizada novamente de forma equivalente. O espaçamento exato pode mudar.

Renomeação simples

Transformações podem substituir o texto de tokens NAME específicos.

resultado = []
for item in tokenize.generate_tokens(StringIO("valor = valor + 1\n").readline):
    if item.type == token.NAME and item.string == "valor":
        item = item._replace(string="contador")
    resultado.append(item)

novo_codigo = tokenize.untokenize(resultado)

Esse exemplo é léxico, não semântico. Ele também renomearia um parâmetro ou variável de outro escopo com o mesmo nome. Para refatoração real, combine AST, symtable e análise de projeto.

Tratando TokenError

Entrada incompleta pode gerar TokenError, por exemplo uma string tripla não fechada ou parênteses abertos até o fim do arquivo.

try:
    list(tokenize.generate_tokens(StringIO("x = '''texto").readline))
except tokenize.TokenError as erro:
    mensagem, posicao = erro.args
    print(mensagem, posicao)

Apresente linha e coluna ao usuário e preserve o arquivo original. Não tente corrigir automaticamente sem entender o contexto.

Erros de indentação

Algumas inconsistências podem gerar IndentationError durante a tokenização. Ferramentas devem capturar tanto esse erro quanto TokenError.

try:
    tokens = list(tokenize.generate_tokens(StringIO(fonte).readline))
except (tokenize.TokenError, IndentationError) as erro:
    print(f"Fonte inválida: {erro}")

Tokenização não valida toda a sintaxe

Um código pode ser tokenizável e ainda conter sintaxe inválida. Para validação sintática, use ast.parse() depois da tokenização.

import ast

ast.parse(fonte)

Tokenize responde “quais unidades léxicas existem”; AST responde “como essas unidades formam expressões e instruções”.

Combinando tokenize e AST

Uma ferramenta pode usar AST para compreender funções e chamadas, enquanto usa tokens para preservar comentários e espaçamento. A AST tradicional não representa todos os comentários, por isso formatadores e codemods frequentemente mantêm as duas visões.

Segurança ao analisar projetos

Tokenizar não executa o código, o que reduz riscos em comparação com importar módulos. Ainda assim, um arquivo enorme ou cuidadosamente construído pode consumir memória e CPU. Defina limites de tamanho, tempo e quantidade de arquivos.

Evite escrever saídas sobre o arquivo original antes de validar o resultado. Grave em arquivo temporário, tokenize novamente, execute testes e faça substituição atômica.

Exemplo de relatório

from collections import Counter

def resumo(fonte):
    contagem = Counter()
    for item in tokenize.generate_tokens(StringIO(fonte).readline):
        contagem[token.tok_name[item.type]] += 1
    return dict(contagem)

Esse relatório pode mostrar quantidade de comentários, strings, nomes e operadores sem depender da execução.

Ferramenta de linha de comando

O módulo pode ser executado diretamente:

python -m tokenize programa.py

Com a opção -e, a saída usa os tipos exatos dos operadores. Isso é útil para aprender e depurar rapidamente.

Erros frequentes

  • Procurar comentários com regex e confundir strings.
  • Ignorar ENCODING ao ler bytes.
  • Tratar NL e NEWLINE como equivalentes.
  • Usar renomeação léxica como refatoração sem analisar escopos.
  • Presumir que untokenize preserva todos os espaços.
  • Executar ou importar código quando tokenização seria suficiente.
  • Não limitar arquivos de entrada não confiáveis.

Boas práticas

  • Leia arquivos em modo binário com tokenize.tokenize.
  • Use tokenize.open para respeitar a codificação.
  • Compare tipos usando o módulo token.
  • Use exact_type para operadores.
  • Combine tokens, AST e symtable conforme a tarefa.
  • Valide o código reconstruído antes de substituir arquivos.
  • Teste strings multilinhas, comentários, Unicode e entradas incompletas.

Conclusão

O módulo tokenize no Python oferece uma visão precisa da camada léxica do código-fonte. Ele identifica nomes, números, operadores, comentários, strings, quebras de linha e mudanças de indentação, preservando posições úteis para editores e relatórios.

Use tokenize para análise estática leve, extração de comentários, métricas, ensino e transformações controladas. Para compreender semântica e escopos, combine-o com AST e symtable. Com limites de entrada, tratamento de erros e validação após reconstruções, você pode criar ferramentas de código confiáveis sem executar os arquivos analisados.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Desenvolvedor trabalhando em automação de build e compilação de diretórios com compileall no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    compileall no Python: compile diretórios

    Aprenda compileall no Python para compilar diretórios, gerar pyc em paralelo, filtrar arquivos e controlar otimização e invalidação.

    Ler mais

    Tempo de leitura: 7 minutos
    04/08/2026
    Monitor com código binário representando geração de arquivos pyc com py_compile no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    py_compile no Python: gere arquivos pyc

    Aprenda py_compile no Python para gerar arquivos pyc, validar sintaxe e controlar otimização e invalidação por timestamp ou hash.

    Ler mais

    Tempo de leitura: 7 minutos
    04/08/2026
    Editor de código representando correção de tabs e espaços com tabnanny no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tabnanny no Python: corrija indentação

    Aprenda tabnanny no Python para detectar tabs e espaços ambíguos, verificar projetos e evitar TabError e IndentationError.

    Ler mais

    Tempo de leitura: 7 minutos
    04/08/2026
    Código-fonte e sintaxe representando análise lexical com tokenize no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tokenize no Python: analise código-fonte

    Aprenda tokenize no Python para analisar tokens, comentários, encoding, posições e reconstruir código-fonte com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    04/08/2026
    Terminal de programação representando compilação de entradas interativas com codeop no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    codeop no Python: compile entradas interativas

    Aprenda codeop no Python para detectar entradas completas, compilar comandos de REPL e preservar __future__ com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    04/08/2026
    Desenvolvedor analisando estrutura de código e tabelas de símbolos com symtable no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    symtable no Python: escopos e símbolos

    Aprenda symtable no Python para analisar escopos, símbolos, globals, nonlocals, closures, imports, annotations e type parameters.

    Ler mais

    Tempo de leitura: 7 minutos
    03/08/2026