tokenize
— Conversor a tokens para código Python¶
Código fuente: Lib/tokenize.py
El módulo tokenize
provee un analizador léxico para código fuente Python, implementado en Python. Este analizador también retorna comentarios como tokens, siendo útil para implementar «pretty-printers», como colorizers para impresiones en pantalla.
Para simplificar el manejo de flujos de tokens, todos los tokens operator y delimiter y Ellipsis
se retorna usando el tipo genérico OP
. El tipo exacto se puede determinar usando la propiedad exact_type
en la named tuple retornada por tokenize.tokenize()
.
Advertencia
Note that the functions in this module are only designed to parse
syntactically valid Python code (code that does not raise when parsed
using ast.parse()
). The behavior of the functions in this module is
undefined when providing invalid Python code and it can change at any
point.
Convirtiendo la entrada en tokens¶
El punto de entrada principal es un generador:
- tokenize.tokenize(readline)¶
El generador
tokenize()
requiere un argumento, readline, que debe ser un objeto invocable que provee la misma interfaz que el métodoio.IOBase.readline()
de los objetos archivos. Cada llamada a la función debe retornar una línea de entrada como bytes.El generador produce una tupla con los siguientes 5 miembros: El tipo de token, la cadena del token, una tupla
(srow, scol)
de enteros especificando la fila y columna donde el token empieza en el código, una(erow, ecol)
de enteros especificando la fila y columna donde el token acaba en el código, y la línea en la que se encontró el token. La línea pasada (el último elemento de la tupla) es la línea física. La tupla se retorna como una named tuple con los campos:type string start end line
.La named tuple retorna tiene una propiedad adicional llamada
exact_type
que contiene el tipo de operador exacto para tokensOP
. Para todos los otros tipos de token,exact_type
es el valor del campotype
de la tupla con su respectivo nombre.Distinto en la versión 3.1: Añadido soporte para tuplas con nombre.
Distinto en la versión 3.3: Añadido soporte para
exact_type
.tokenize()
determina la codificación del fichero buscando una marca BOM UTF-8 o una cookie de codificación, de acuerdo con PEP 263.
- tokenize.generate_tokens(readline)¶
Convertir a tokens una fuente leyendo cadenas unicode en lugar de bytes.
Como
tokenize()
, el argumento readline es un invocable que retorna una sola línea de entrada. Sin embargo,generate_tokens()
espera que readline retorne un objeto str en lugar de bytes.El resultado es un iterador que produce tuplas con nombre, exactamente como
tokenize()
. No produce un tokenENCODING
.
Todas las constantes del módulo token
se exportan también desde tokenize
.
Otra función se encarga de invertir el proceso de conversión. Esto es útil para crear herramientas que convierten a tokens un script, modificar el flujo de token, y escribir el script modificado.
- tokenize.untokenize(iterable)¶
Convierte los tokens de vuelta en código fuente Python. El iterable debe retornar secuencias con al menos dos elementos, el tipo de token y la cadena del token. Cualquier otro elemento de la secuencia es ignorado.
El script reconstruido se retorna como una cadena simple. El resultado está garantizado de que se convierte en tokens de vuelta a la misma entrada, de modo que la conversión no tiene pérdida y que las conversiones de ida y de vuelta están aseguradas. La garantía aplica sólo al tipo y la cadena del token, ya que el espaciado entre tokens (posiciones de columna) pueden variar.
Retorna bytes, codificados usando el token
ENCODING
, que es el primer elemento de la secuencia retornada portokenize()
. Si no hay un token de codificación en la entrada, retorna una cadena.
tokenize()
necesita detectar la codificación de los ficheros fuente que convierte a tokens. La función que utiliza para hacer esto está disponible como:
- tokenize.detect_encoding(readline)¶
La función
detect_encoding()
se usa para detectar la codificación que debería usarse al leer un fichero fuente Python. Requiere un argumento, readline, del mismo modo que el generadortokenize()
.Llamará a readline un máximo de dos veces, retornando la codificación usada, como cadena, y una lista del resto de líneas leídas, no descodificadas de bytes.
Detecta la codificación a partir de la presencia de una marca BOM UTF-8 o una cookie de codificación, como se especifica en PEP 263. Si ambas están presentes pero en desacuerdo, se lanzará un
SyntaxError
. Resaltar que si se encuentra la marca BOM, se retornará'utf-8-sig'
como codificación.Si no se especifica una codificación, por defecto se retornará
'utf-8'
.Usa
open()
para abrir ficheros fuente Python: Ésta utilizadetect_encoding()
para detectar la codificación del fichero.
- tokenize.open(filename)¶
Abrir un fichero en modo sólo lectura usando la codificación detectada por
detect_encoding()
.Nuevo en la versión 3.2.
- exception tokenize.TokenError¶
Lanzada cuando una expresión o docstring que puede separarse en más líneas no está completa en el fichero, por ejemplo:
"""Beginning of docstring
o:
[1, 2, 3
Destacar que cadenas con comillas simples sin finalizar no lanzan un error. Se convertirán en tokens como ERRORTOKEN
, seguido de la conversión de su contenido.
Uso como línea de comandos¶
Nuevo en la versión 3.3.
El módulo tokenize
se puede ejecutar como script desde la línea de comandos. Es tan simple como:
python -m tokenize [-e] [filename.py]
Se aceptan las siguientes opciones:
- -h, --help¶
muestra el mensaje de ayuda y sale
- -e, --exact¶
muestra los nombres de token usando el tipo exacto
Si se especifica filename.py
, se convierte su contenido a tokens por la salida estándar. En otro caso, se convierte la entrada estándar.
Ejemplos¶
Ejemplo de un script que transforma literales float en objetos Decimal:
from tokenize import tokenize, untokenize, NUMBER, STRING, NAME, OP
from io import BytesIO
def decistmt(s):
"""Substitute Decimals for floats in a string of statements.
>>> from decimal import Decimal
>>> s = 'print(+21.3e-5*-.1234/81.7)'
>>> decistmt(s)
"print (+Decimal ('21.3e-5')*-Decimal ('.1234')/Decimal ('81.7'))"
The format of the exponent is inherited from the platform C library.
Known cases are "e-007" (Windows) and "e-07" (not Windows). Since
we're only showing 12 digits, and the 13th isn't close to 5, the
rest of the output should be platform-independent.
>>> exec(s) #doctest: +ELLIPSIS
-3.21716034272e-0...7
Output from calculations with Decimal should be identical across all
platforms.
>>> exec(decistmt(s))
-3.217160342717258261933904529E-7
"""
result = []
g = tokenize(BytesIO(s.encode('utf-8')).readline) # tokenize the string
for toknum, tokval, _, _, _ in g:
if toknum == NUMBER and '.' in tokval: # replace NUMBER tokens
result.extend([
(NAME, 'Decimal'),
(OP, '('),
(STRING, repr(tokval)),
(OP, ')')
])
else:
result.append((toknum, tokval))
return untokenize(result).decode('utf-8')
Ejemplo de conversión desde la línea de comandos. El script:
def say_hello():
print("Hello, World!")
say_hello()
se convertirá en la salida siguiente, donde la primera columna es el rango de coordenadas línea/columna donde se encuentra el token, la segunda columna es el nombre del token, y la última columna es el valor del token, si lo hay
$ python -m tokenize hello.py
0,0-0,0: ENCODING 'utf-8'
1,0-1,3: NAME 'def'
1,4-1,13: NAME 'say_hello'
1,13-1,14: OP '('
1,14-1,15: OP ')'
1,15-1,16: OP ':'
1,16-1,17: NEWLINE '\n'
2,0-2,4: INDENT ' '
2,4-2,9: NAME 'print'
2,9-2,10: OP '('
2,10-2,25: STRING '"Hello, World!"'
2,25-2,26: OP ')'
2,26-2,27: NEWLINE '\n'
3,0-3,1: NL '\n'
4,0-4,0: DEDENT ''
4,0-4,9: NAME 'say_hello'
4,9-4,10: OP '('
4,10-4,11: OP ')'
4,11-4,12: NEWLINE '\n'
5,0-5,0: ENDMARKER ''
Los nombres de tipos de token exactos se pueden mostrar con la opción -e
:
$ python -m tokenize -e hello.py
0,0-0,0: ENCODING 'utf-8'
1,0-1,3: NAME 'def'
1,4-1,13: NAME 'say_hello'
1,13-1,14: LPAR '('
1,14-1,15: RPAR ')'
1,15-1,16: COLON ':'
1,16-1,17: NEWLINE '\n'
2,0-2,4: INDENT ' '
2,4-2,9: NAME 'print'
2,9-2,10: LPAR '('
2,10-2,25: STRING '"Hello, World!"'
2,25-2,26: RPAR ')'
2,26-2,27: NEWLINE '\n'
3,0-3,1: NL '\n'
4,0-4,0: DEDENT ''
4,0-4,9: NAME 'say_hello'
4,9-4,10: LPAR '('
4,10-4,11: RPAR ')'
4,11-4,12: NEWLINE '\n'
5,0-5,0: ENDMARKER ''
Ejemplo de tokenización de un fichero programáticamente, leyendo cadenas unicode en lugar de bytes con generate_tokens()
:
import tokenize
with tokenize.open('hello.py') as f:
tokens = tokenize.generate_tokens(f.readline)
for token in tokens:
print(token)
O leyendo bytes directamente con tokenize()
:
import tokenize
with open('hello.py', 'rb') as f:
tokens = tokenize.tokenize(f.readline)
for token in tokens:
print(token)