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

    Pastas organizadas representando aplicações empacotadas em arquivos .pyz com zipapp no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    zipapp no Python: crie executáveis .pyz

    Aprenda zipapp no Python para empacotar aplicações em arquivos .pyz, definir entry points, incluir dependências e distribuir com segurança.

    Ler mais

    Tempo de leitura: 7 minutos
    13/08/2026
    Editor de código representando autocompletar em REPL com rlcompleter no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    rlcompleter no Python: autocompletar REPL

    Aprenda rlcompleter no Python para adicionar autocompletar a REPLs, consoles e editores, controlar namespaces e evitar efeitos colaterais.

    Ler mais

    Tempo de leitura: 6 minutos
    13/08/2026
    Janela de terminal representando console interativo criado com cmd no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    cmd no Python: crie consoles interativos

    Aprenda cmd no Python para criar consoles interativos com comandos, ajuda, histórico, autocompletar, testes e controle seguro de ações.

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026
    Terminal interativo representando um REPL customizado com o módulo code no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    code no Python: crie um REPL customizado

    Aprenda o módulo code no Python para criar REPLs customizados, controlar namespaces, prompts, saída, blocos incompletos e encerramento seguro.

    Ler mais

    Tempo de leitura: 6 minutos
    12/08/2026
    Código de aplicação web representando WSGI com wsgiref no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    wsgiref no Python: aplicações WSGI

    Aprenda wsgiref no Python para criar e validar aplicações WSGI, testar environ, headers, rotas e servidores locais sem usar em

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026
    Protocolo seguro na internet representando preparação Unicode com stringprep no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    stringprep no Python: prepare Unicode

    Aprenda stringprep no Python para aplicar tabelas do RFC 3454, mapear Unicode, bloquear caracteres proibidos e validar regras bidirecionais.

    Ler mais

    Tempo de leitura: 7 minutos
    12/08/2026