tokenize no Python: analise código-fonte

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

Formatadores, colorizadores, analisadores de estilo e ferramentas de refatoração precisam separar código Python em unidades como nomes, números, strings, operadores, comentários e indentações. O módulo tokenize no Python fornece um scanner lexical da biblioteca padrão que preserva comentários e posições, permitindo examinar ou reescrever código-fonte sem executar o programa.

Neste guia, você aprenderá a usar tokenize(), generate_tokens(), untokenize(), detect_encoding() e tokenize.open(). O conteúdo complementa nossos artigos sobre bytecode com dis, tabelas de símbolos, entradas interativas com codeop, inspect e comparação de textos.

O que é tokenização

Tokenização é a etapa que agrupa caracteres em unidades lexicais. No código total = preco * 2, por exemplo, existem nomes, operador de atribuição, operador de multiplicação, número e marca de fim de linha.

O tokenizer não constrói uma árvore sintática completa nem resolve escopos. Ele preserva detalhes úteis para apresentação e transformação, incluindo comentários, espaços de indentação e coordenadas de linha e coluna.

Primeiro exemplo com tokenize()

tokenize.tokenize() recebe uma função readline que retorna bytes.

from io import BytesIO
from tokenize import tokenize

fonte = b"total = preco * 2  # calculo\n"

for token in tokenize(BytesIO(fonte).readline):
    print(token)

O resultado inclui tokens como ENCODING, NAME, OP, NUMBER, COMMENT, NEWLINE e ENDMARKER.

Estrutura de TokenInfo

Cada item é um named tuple com campos:

  • type: código numérico do tipo;
  • string: texto original;
  • start: linha e coluna inicial;
  • end: linha e coluna final;
  • line: linha física onde o token apareceu.
for item in tokenize(BytesIO(fonte).readline):
    print(
        item.type,
        item.string,
        item.start,
        item.end,
    )

Linhas são indexadas a partir de 1 e colunas a partir de 0. Essas coordenadas ajudam a criar diagnósticos e destaques no editor.

Nomes legíveis dos tipos

O módulo token contém o mapeamento entre códigos e nomes.

import token

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

O módulo tokenize também reexporta as constantes principais, mas o namespace token torna o propósito explícito.

OP e exact_type

Operadores e delimitadores são devolvidos com tipo genérico OP. A propriedade exact_type diferencia parênteses, dois-pontos, soma, multiplicação e outros símbolos.

import token

for item in tokenize(BytesIO(b"x += 1\n").readline):
    print(
        token.tok_name[item.type],
        token.tok_name[item.exact_type],
        item.string,
    )

A documentação oficial de tokenize recomenda consultar exact_type quando a ferramenta precisa distinguir operadores.

Comentários como tokens

Diferentemente de várias interfaces do parser, tokenize preserva comentários.

from tokenize import COMMENT

comentarios = [
    item
    for item in tokenize(BytesIO(fonte).readline)
    if item.type == COMMENT
]

for comentario in comentarios:
    print(comentario.start, comentario.string)

Isso permite construir formatadores, extratores de diretivas, verificadores de comentários especiais e ferramentas de documentação.

INDENT e DEDENT

A estrutura de blocos aparece por meio dos tokens INDENT e DEDENT.

codigo = b"if ativo:\n    executar()\nfinalizar()\n"

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

INDENT carrega o texto usado na indentação. DEDENT normalmente possui string vazia e indica retorno a um nível anterior.

NEWLINE versus 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 ou em uma linha somente com comentário.

codigo = b"resultado = (\n    1 +\n    2\n)\n"

for item in tokenize(BytesIO(codigo).readline):
    if item.type in {token.NEWLINE, token.NL}:
        print(token.tok_name[item.type], item.start)

Essa diferença é essencial para formatadores que reorganizam linhas sem alterar o significado.

Ler strings Unicode com generate_tokens()

generate_tokens() recebe uma função que retorna str, não bytes.

from io import StringIO
from tokenize import generate_tokens

fonte_texto = "mensagem = 'olá'\n"

for item in generate_tokens(StringIO(fonte_texto).readline):
    print(item)

Essa API não gera o token ENCODING. É conveniente quando o texto já foi decodificado corretamente.

Detectar encoding

detect_encoding() examina no máximo duas linhas para localizar BOM UTF-8 ou cookie conforme a PEP 263.

from tokenize import detect_encoding

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

print(encoding)
print(linhas)

Se BOM e cookie discordarem, a função lança SyntaxError. Sem declaração, o padrão é UTF-8.

Abrir código-fonte corretamente

tokenize.open() usa a mesma detecção e devolve o arquivo em modo texto.

import tokenize

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

Prefira essa função ao analisar arquivos Python de origem desconhecida, pois ela respeita o encoding declarado pelo próprio arquivo.

Reconstruir fonte com untokenize()

untokenize() recebe pares de tipo e string e reconstrói código-fonte.

from tokenize import untokenize

pares = [
    (item.type, item.string)
    for item in tokenize(BytesIO(fonte).readline)
]

reconstruido = untokenize(pares)
print(reconstruido.decode("utf-8"))

O round-trip garante equivalência dos tipos e strings dos tokens, mas espaços e posições de coluna podem mudar.

Transformar literais com segurança lexical

Uma ferramenta pode substituir somente tokens numéricos, evitando alterar strings e comentários.

from tokenize import NUMBER, NAME, OP, STRING

resultado = []

for item in tokenize(BytesIO(b"taxa = 1.25\n").readline):
    if item.type == NUMBER and "." in item.string:
        resultado.extend([
            (NAME, "Decimal"),
            (OP, "("),
            (STRING, repr(item.string)),
            (OP, ")"),
        ])
    else:
        resultado.append((item.type, item.string))

print(untokenize(resultado).decode("utf-8"))

A transformação ainda precisa inserir o import necessário e considerar formatos como exponenciais, complexos e underscores.

Renomear identificadores

É possível substituir tokens NAME, mas um renomeador correto precisa compreender escopos.

from tokenize import NAME

for item in tokenize(BytesIO(b"valor = valor + 1\n").readline):
    if item.type == NAME and item.string == "valor":
        novo = item._replace(string="contador")
    else:
        novo = item

tokenize sozinho não sabe se um nome é variável, atributo, import, parâmetro ou parte de um padrão. Combine-o com AST e symtable para refatoração semântica.

Colorização de código

Um colorizador pode associar classes visuais aos tokens.

CLASSES = {
    token.NAME: "nome",
    token.NUMBER: "numero",
    token.STRING: "string",
    token.OP: "operador",
    token.COMMENT: "comentario",
}

for item in generate_tokens(StringIO(fonte_texto).readline):
    classe = CLASSES.get(item.type, "outro")
    renderizar(item.string, classe)

Keywords também chegam como NAME. Use o módulo keyword para diferenciá-las.

TokenError

TokenError aparece quando uma string multilinha ou expressão entre delimitadores não termina até o fim do arquivo.

from tokenize import TokenError

try:
    list(tokenize(BytesIO(b"lista = [1, 2\n").readline))
except TokenError as erro:
    print("Código incompleto:", erro)

Outros erros de sintaxe podem não ser detectados nessa etapa porque tokenização não substitui parsing.

Código sintaticamente inválido

A documentação alerta que o módulo foi projetado para código que também seria aceito por ast.parse(). O comportamento diante de código inválido é indefinido e pode mudar.

Para editores que precisam trabalhar com arquivos temporariamente incompletos, implemente recuperação tolerante, trate exceções e não dependa de uma sequência específica de tokens após o ponto inválido.

Executar pela linha de comando

O módulo possui uma interface simples:

python -m tokenize programa.py

A opção -e mostra nomes exatos dos operadores.

python -m tokenize -e programa.py

Sem arquivo, a entrada é lida de stdin. Essa interface é útil para estudar e depurar ferramentas.

Posições e caracteres Unicode

As colunas representam posições na string Python decodificada, não necessariamente offsets de bytes no arquivo original. Ferramentas que aplicam edições em bytes devem manter um mapeamento cuidadoso de encoding.

Também considere tabs, caracteres combinantes e finais de linha diferentes. Teste a interface de destaque com código real em várias plataformas.

Preservar formatação original

untokenize() não promete preservar espaços exatamente. Para um formatador isso pode ser aceitável; para uma correção mínima talvez seja melhor aplicar edições diretamente nos intervalos do texto original.

Use coordenadas dos tokens para construir patches, processe edições do fim para o início e valide o resultado com ast.parse().

Segurança

Tokenizar não executa o código, o que é mais seguro que importar um módulo. Ainda assim, um arquivo enorme ou criado para consumo excessivo pode gastar memória e CPU.

Imponha limites de tamanho, tempo e quantidade de tokens. Nunca conclua que código é seguro apenas porque foi tokenizado ou parseado.

Exemplo de relatório

def relatorio(fonte: bytes):
    for item in tokenize(BytesIO(fonte).readline):
        yield {
            "tipo": token.tok_name[item.type],
            "tipo_exato": token.tok_name[item.exact_type],
            "texto": item.string,
            "inicio": item.start,
            "fim": item.end,
        }

Esse formato pode alimentar uma tabela, visualizador de código ou teste de um gerador.

Erros frequentes

  • Usar generate_tokens() com bytes.
  • Ignorar o token de encoding.
  • Tratar todos os operadores apenas como OP.
  • Confundir NL e NEWLINE.
  • Esperar que untokenize() preserve espaços exatos.
  • Fazer renomeação sem compreender escopos.
  • Confiar no comportamento para código inválido.
  • Usar tokenização como validação de segurança.

Boas práticas

  • Use bytes e tokenize() para arquivos completos.
  • Abra fontes com tokenize.open().
  • Consulte exact_type para operadores.
  • Combine com AST e symtable para semântica.
  • Valide o código reconstruído.
  • Teste comentários, Unicode, tabs e multilinhas.
  • Limite recursos para entradas não confiáveis.
  • Documente quais detalhes de formatação são preservados.

Conclusão

O módulo tokenize no Python transforma código-fonte em uma sequência rica de tokens, incluindo comentários, indentações, encoding e posições. Ele é uma base útil para colorizadores, formatadores, analisadores de estilo e transformações lexicais.

Seu limite é igualmente importante: tokens descrevem a forma lexical, não o significado completo. Ao combinar tokenize com AST, symtable e validação posterior, você pode modificar código com precisão sem executar o programa e sem confundir strings ou comentários com sintaxe ativa.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    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
    Monitor com código binário representando análise de bytecode com dis no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    dis no Python: entenda o bytecode

    Aprenda dis no Python para desmontar bytecode, analisar instruções, caches adaptativos, posições, tracebacks e detalhes do CPython.

    Ler mais

    Tempo de leitura: 7 minutos
    03/08/2026
    Tela de erro representando diagnóstico de crashes e deadlocks com faulthandler no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    faulthandler no Python: diagnostique travamentos

    Aprenda faulthandler no Python para diagnosticar crashes, deadlocks e timeouts com pilhas de threads e código nativo.

    Ler mais

    Tempo de leitura: 8 minutos
    03/08/2026
    Notebook com código representando análise de traceback e depuração no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    traceback no Python: erros e pilha

    Aprenda traceback no Python para capturar, formatar e registrar pilhas de erro com segurança, sem vazar dados ou reter memória.

    Ler mais

    Tempo de leitura: 6 minutos
    03/08/2026
    Análise de software representando introspecção de objetos com inspect no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    inspect no Python: introspecção de objetos

    Aprenda inspect no Python para analisar funções, classes, assinaturas, código-fonte, decorators, generators e frames com segurança.

    Ler mais

    Tempo de leitura: 8 minutos
    02/08/2026