tokenize — Analisador léxico para código-fonte Python

Código-fonte: Lib/tokenize.py


O módulo tokenize fornece um scanner léxico para código-fonte Python, implementado em Python. O scanner neste módulo retorna comentários como tokens também, tornando-o útil para implementar “pretty-printers”, incluindo colorizadores para exibições na tela.

Para simplificar o tratamento do fluxo de tokens, todos os tokens operadores e delimitadores e Ellipsis são retornados usando o tipo de token genérico OP. O tipo exato pode ser determinado verificando a propriedade exact_type na tupla nomeada retornada de tokenize.tokenize().

Aviso

Observe que as funções neste módulo são projetadas apenas para analisar código Python sintaticamente válido (código que não levanta exceção quando analisado usando ast.parse()). O comportamento das funções neste módulo é indefinido ao fornecer código Python inválido e pode mudar a qualquer momento.

Tokenizando entradas

O ponto de entrada principal é um gerador:

tokenize.tokenize(readline)

O gerador tokenize() requer um argumento, readline, que deve ser um objeto chamável que fornece a mesma interface que o método io.IOBase.readline() de objetos de arquivo. Cada chamada para a função deve retornar uma linha de entrada como bytes.

O gerador produz tuplas de 5 elementos com estes membros: o tipo de token; a string de token; uma tupla de 2 elementos (srow, scol) de ints especificando a linha e a coluna onde o token começa na fonte; uma tupla de 2 elementos (erow, ecol) de ints especificando a linha e a coluna onde o token termina na fonte; e a linha na qual o token foi encontrado. A linha passada (o último item da tupla) é a linha física. A tupla 5 elementos é retornada como uma tupla nomeada com os nomes dos campos: tipo string início fim linha.

A tupla nomeada retornada tem uma propriedade adicional chamada exact_type que contém o tipo exato do operador para tokens OP. Para todos os outros tipos de token, exact_type é igual ao campo type da tupla nomeada.

Alterado na versão 3.1: Adiciona suporte para tuplas nomeadas.

Alterado na versão 3.3: Adicionado suporte para exact_type.

tokenize() determina a codificação de origem do arquivo procurando por um BOM UTF-8 ou cookie de codificação, de acordo com PEP 263.

tokenize.generate_tokens(readline)

Tokeniza uma fonte lendo strings unicode em vez de bytes.

Assim como tokenize(), o argumento readline é um chamável que retorna uma única linha de entrada. No entanto, generate_tokens() espera que readline retorne um objeto str em vez de bytes.

O resultado é um iterador produzindo tuplas nomeadas, exatamente como tokenize(). Ele não produz um token ENCODING.

Todas as constantes do módulo token também são exportadas de tokenize.

Outra função é fornecida para reverter o processo de tokenização. Isso é útil para criar ferramentas que tokenizam um script, modificam o fluxo de tokens e escrevem de volta o script modificado.

tokenize.untokenize(iterable)

Converte tokens de volta para o código-fonte Python. O iterable deve retornar sequências com pelo menos dois elementos, o tipo de token e a string do token. Quaisquer elementos de sequência adicionais são ignorados.

O resultado é garantido para tokenizar de volta para corresponder à entrada, de modo que a conversão seja sem perdas e as viagens de ida e volta sejam garantidas. A garantia se aplica somente ao tipo de token e à sequência de tokens, pois o espaçamento entre os tokens (posições de coluna) pode mudar.

Retorna bytes, codificados usando o token ENCODING, que é a primeira sequência de tokens gerada por tokenize(). Se não houver nenhum token de codificação na entrada, ele retorna um str em vez disso.

tokenize() precisa detectar a codificação dos arquivos fonte que ele tokeniza. A função que ele usa para fazer isso está disponível:

tokenize.detect_encoding(readline)

A função detect_encoding() é usada para detectar a codificação que deve ser usada para decodificar um arquivo de origem Python. Ela requer um argumento, readline, da mesma forma que o gerador tokenize().

Ele chamará readline no máximo duas vezes e retornará a codificação usada (como uma string) e uma lista de todas as linhas (não decodificadas de bytes) que ele leu.

Ele detecta a codificação a partir da presença de um BOM UTF-8 ou um cookie de codificação conforme especificado em PEP 263. Se um BOM e um cookie estiverem presentes, mas discordarem, uma exceção SyntaxError será levantada. Observe que se o BOM for encontrado, 'utf-8-sig' será retornado como uma codificação.

Se nenhuma codificação for especificada, o padrão 'utf-8' será retornado.

Use open() para abrir arquivos de código-fonte Python: ele usa detect_encoding() para detectar a codificação do arquivo.

tokenize.open(filename)

Abre um arquivo no modo somente leitura usando a codificação detectada por detect_encoding().

Adicionado na versão 3.2.

exception tokenize.TokenError

Levantada quando uma docstring ou expressão que pode ser dividida em várias linhas não é concluída em nenhum lugar do arquivo, por exemplo:

"""Início da
docstring

ou:

[1,
 2,
 3

Uso na linha de comando

Adicionado na versão 3.3.

O módulo tokenize pode ser executado como um script a partir da linha de comando. É tão simples quanto:

python -m tokenize [-e] [nome-do-arquivo.py]

As seguintes opções são aceitas:

-h, --help

mostra esta mensagem de ajuda e sai

-e, --exact

exibe nomes de tokens usando o tipo exato

Se nome-de-arquivo.py for especificado, seu conteúdo será tokenizado para stdout. Caso contrário, a tokenização será realizada em stdin.

Exemplos

Exemplo de um reescritor de script que transforma literais float em objetos Decimal:

from tokenize import tokenize, untokenize, NUMBER, STRING, NAME, OP
from io import BytesIO

def decistmt(s):
    """Substitui Decimals por floats em uma string de instruções.

    >>> from decimal import Decimal
    >>> s = 'print(+21.3e-5*-.1234/81.7)'
    >>> decistmt(s)
    "print (+Decimal ('21.3e-5')*-Decimal ('.1234')/Decimal ('81.7'))"

    O formato do expoente é herdado da biblioteca da plataforma C. Casos
    conhecidos são "e-007" (Windows) e "e-07" (não Windows). Como estamos
    mostrando apenas 12 dígitos, e o 13º não está próximo de 5, o restante
    da saída deve ser independente da plataforma.

    >>> exec(s)  #doctest: +ELLIPSIS
    -3.21716034272e-0...7

    A saída dos cálculos com Decimal deve ser idêntica em todas as
    plataformas.

    >>> 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')

Exemplo de tokenização a partir da linha de comando. O script:

def say_hello():
    print("Hello, World!")

say_hello()

será tokenizado para a seguinte saída, onde a primeira coluna é o intervalo das coordenadas da linha/coluna onde o token é encontrado, a segunda coluna é o nome do token e a coluna final é o valor do token (se houver)

$ 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      ''

Os nomes exatos dos tipos de token podem ser exibidos usando a opção -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      ''

Exemplo de tokenização de um arquivo programaticamente, lendo strings unicode em vez de bytes com generate_tokens():

import tokenize

with tokenize.open('hello.py') as f:
    tokens = tokenize.generate_tokens(f.readline)
    for token in tokens:
        print(token)

Ou lendo bytes diretamente com tokenize():

import tokenize

with open('hello.py', 'rb') as f:
    tokens = tokenize.tokenize(f.readline)
    for token in tokens:
        print(token)