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.pyCom 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.







