# `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étodo `io.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 tokens `OP`. Para todos los otros tipos de token, `exact_type` es el valor del campo `type` 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 token `ENCODING`.

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 por `tokenize()`. 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 generador `tokenize()`.

  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 utiliza `detect_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)