El módulo tokenize convierte código fuente Python en una secuencia de tokens léxicos. Cada token describe un fragmento como nombre, número, string, operador, comentario, indentación, salto de línea o marcador final. Esta capa es útil en formatters, linters, converters, herramientas de documentación, análisis de comentarios y transformaciones que deben conservar más detalle textual que una AST.
Los tokens no representan todo el significado del programa. No resuelven scopes, imports, tipos ni comportamiento en runtime. Usa ast para estructura, symtable para símbolos y dis para bytecode. Elige tokenize cuando importen whitespace, comentarios, posiciones y grafía literal.
Tokeniza bytes
tokenize.tokenize() recibe una función readline que devuelve bytes.
from io import BytesIO
from tokenize import tokenize
codigo = b"x = 10 + 2\n"
for item in tokenize(BytesIO(codigo).readline):
print(item)
La secuencia incluye un token ENCODING, los tokens del fuente y ENDMARKER.
TokenInfo
Cada resultado es un TokenInfo con campos type, string, start, end y line.
for item in tokenize(BytesIO(codigo).readline):
print(item.type, item.string, item.start, item.end)
start y end son pares línea-columna. line contiene la línea física original.
Nombres legibles
Los tipos son enteros definidos por token. Usa token.tok_name.
import token
nombre = token.tok_name[item.type]
No persistas IDs numéricos crudos como formato estable.
exact_type para operadores
Operadores y delimitadores suelen usar el tipo general OP. TokenInfo.exact_type identifica el símbolo exacto.
import token
if item.type == token.OP:
print(token.tok_name[item.exact_type])
Así distingues +, +=, paréntesis, dos puntos y otros.
generate_tokens para texto
generate_tokens() acepta una función que devuelve strings.
from io import StringIO
from tokenize import generate_tokens
for item in generate_tokens(StringIO("x = 1\n").readline):
print(item)
No produce ENCODING. Para archivos reales, prefiere bytes o tokenize.open().
Encoding del fuente
Los archivos Python pueden declarar encoding en las primeras líneas. tokenize() lo detecta según las reglas del lenguaje.
Decodificar todo como UTF-8 antes puede fallar en proyectos legacy.
detect_encoding
detect_encoding() devuelve encoding y líneas ya consumidas.
from tokenize import detect_encoding
with open("modulo.py", "rb") as archivo:
encoding, lineas = detect_encoding(archivo.readline)
print(encoding, lineas)
Puede leer hasta dos líneas para revisar BOM y cookie.
tokenize.open
tokenize.open(filename) abre un fuente usando la codificación detectada.
import tokenize
with tokenize.open("modulo.py") as archivo:
codigo = archivo.read()
Es una opción segura para herramientas de análisis.
Comentarios
A diferencia de AST, la tokenización conserva comentarios como COMMENT.
import token
comentarios = [
item for item in tokens
if item.type == token.COMMENT
]
Permite encontrar TODOs, pragmas, type comments y directivas.
No todo comentario es directiva
Una directiva necesita prefijo, posiciones válidas y reglas de parsing. Buscar una substring produce falsos positivos.
Define una sintaxis como # herramienta:.
NL frente a NEWLINE
NEWLINE termina una instrucción lógica. NL representa un salto físico que no la termina, por ejemplo dentro de paréntesis.
resultado = (
1
+ 2
)
Formatters y analizadores de líneas deben distinguirlos.
Indentación
INDENT y DEDENT describen cambios de bloque.
if activo:
ejecutar()
INDENT contiene el whitespace real. Tabs y espacios inconsistentes pueden generar error.
Errores de indentación
Tokenización o compilación puede lanzar IndentationError o TabError. Muestra archivo, posición y contexto.
No reescribas indentación ambigua sin una política documentada.
Strings
Un literal completo suele aparecer como token STRING, incluyendo prefijo y comillas.
r"ruta\archivo"
f"valor={x}"
Los detalles de f-strings y sintaxis nueva pueden variar por versión. Para expresiones internas quizá necesites parser.
Números
Enteros, floats, complex y bases diferentes aparecen como NUMBER con la grafía original.
0xff
1_000_000
3.14e-2
Esto preserva underscores y estilo.
Nombres y keywords
Identificadores y keywords son NAME. Usa keyword.iskeyword().
import keyword
if item.type == token.NAME and keyword.iskeyword(item.string):
print("keyword", item.string)
Soft keywords dependen del contexto sintáctico.
untokenize
untokenize() reconstruye código.
from tokenize import untokenize
nuevo_codigo = untokenize(tokens)
Conserva el round trip de tipo y string, aunque el spacing exacto puede cambiar.
Transformación simple
Una herramienta puede reemplazar nombres seleccionados.
import token
from tokenize import TokenInfo, untokenize
nuevos = []
for item in tokens:
if item.type == token.NAME and item.string == "antiguo":
item = TokenInfo(
item.type, "nuevo", item.start, item.end, item.line
)
nuevos.append(item)
resultado = untokenize(nuevos)
Un rename correcto considera scopes, atributos, imports y shadowing. Los tokens solos no resuelven semántica.
Pares tipo-string
untokenize() también acepta pares (type, string) y decide el spacing.
Conservar TokenInfo aporta contexto, pero las posiciones antiguas quedan obsoletas después de editar.
Posiciones tras cambios
Cambiar la longitud de un token invalida offsets posteriores. El fuente reconstruido puede ser válido, pero los diagnósticos necesitan remapping.
Una herramienta editorial robusta mantiene un source map.
Preservación de comentarios
Transformaciones AST y ast.unparse() suelen perder comentarios. Los tokens los conservan, pero refactoring estructural complejo es difícil.
Para round trip fiel, considera una concrete syntax tree.
TokenError
TokenError aparece en strings multilínea o brackets sin cerrar.
from tokenize import TokenError
try:
tokens = list(tokenize(readline))
except TokenError as error:
print("fuente incompleto", error)
En editores, código temporalmente incompleto es normal.
ERRORTOKEN
Caracteres inválidos y casos especiales pueden aparecer como ERRORTOKEN. Inspecciona texto y posición antes de diagnosticar.
No etiquetes todo error token como entrada hostil.
Buffers parciales
Una IDE puede tokenizar mientras el usuario escribe. Implementa debounce, cancelación y resultados parciales.
No hace falta ejecutar el código.
Archivos grandes
Convertir el generator en lista consume memoria proporcional al tamaño. Procesa en streaming cuando sea posible.
Usa una ventana limitada para lookahead.
Límites contra abuso
Entrada no confiable puede contener archivos enormes, líneas gigantes y nesting extremo. Limita bytes, líneas, tokens y tiempo.
Aísla análisis pesado en otro proceso para servicios públicos.
Identificadores Unicode
Python admite identificadores Unicode. No asumas ASCII.
Las reglas de seguridad pueden detectar caracteres confusos sin rechazar idiomas legítimos.
Caracteres invisibles
Una herramienta puede reportar controles, espacios inusuales o caracteres bidireccionales. Diferencia uso legítimo de riesgo.
Muestra code points y posiciones.
Combina con AST
Usa tokens para comentarios y grafía y AST para estructura. Un flujo común asocia nodos con rangos y localiza tokens.
Consulta ast en Python.
Combina con symtable
Tokens muestran grafía; symtable identifica scopes, locals, globals y free variables.
La combinación es más segura para renames.
Formatters
Un formatter debe entender tokens, sintaxis, comentarios y reglas de layout. Añadir espacios alrededor de todo OP no basta.
Usa gramática completa y tests extensos.
Linters de comentarios
La tokenización basta para reglas sobre longitud, TODO sin responsable o pragma inválida.
Un # dentro de string no es token COMMENT.
Detección de secrets
Tokens pueden ayudar a encontrar strings asociadas a nombres como password, pero el análisis léxico genera falsos positivos.
Nunca registres el valor detectado.
Línea de comandos
El módulo ofrece una CLI para mostrar tokens.
python -m tokenize modulo.py
Es útil para aprender y depurar reglas.
Pruebas
Incluye encodings alternativos, BOM, comentarios, strings multilínea, f-strings, tabs, líneas vacías, brackets abiertos, Unicode, archivos incompletos y entrada grande.
Tras transformar, tokeniza y compila el resultado.
Errores comunes
Los fallos frecuentes son leer todo como UTF-8, confundir NL y NEWLINE, tratar keywords como tipo separado, renombrar NAME sin scope, perder comentarios al pasar a AST, confiar en posiciones antiguas, materializar listas enormes y no limitar entrada pública.
Conclusión
tokenize expone la forma léxica del código Python conservando comentarios, grafía, líneas y operadores. Usa tokenize.open() para encoding correcto, exact_type para operadores y untokenize() para reconstrucción.
Combina tokens con AST y tablas de símbolos cuando necesites contexto semántico. Consulta la documentación oficial de tokenize y la documentación de token.







