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 = itemtokenize 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.pyA opção -e mostra nomes exatos dos operadores.
python -m tokenize -e programa.pySem 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
NLeNEWLINE. - 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_typepara 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.







