tokenize — Токенизатор для исходного кода Python

Исходный код: Lib/tokenize.py


Модуль tokenize предоставляет лексический сканер для исходного кода Python , реализованного в Python. Сканер в этом модуле также возвращает комментарии в качестве маркеров, что делает его полезным для реализации «красивых принтов», включая краски для экранных дисплеев.

Чтобы упростить символическую обработку потока, весь оператор, символы делимитера и Ellipsis - возвращенный, используя универсальный символический тип OP. Точный тип можно определить, проверив свойство exact_type на именованный кортеж возвращенный из tokenize.tokenize().

Токенизация входа

Основной точкой входа является генератором:

tokenize.tokenize(readline)

Для tokenize() генератор требуется один аргумент readline, который должен быть вызываемым объектом, обеспечивающим тот же интерфейс, что и для метода io.IOBase.readline() файловых объектов. Каждый вызов функции должен возвращает одну строку ввода в байтах.

Генератор производит 5-кортежи с этими членами: тип токена; строка маркера; 2-кортежный (srow, scol) ints, указывающий строку и столбец, где начинается маркер в источнике; 2-кортежный (erow, ecol) ints, указывающий строку и столбец, где маркер заканчивается в источнике; и строку, на которой был найден маркер. Переданная строка (последний элемент кортежа) - это физическая строка. 5 кортеж возвращенный как именованный кортеж с именами полей: type string start end line.

У возвращенный именованный кортеж есть дополнительное свойство с именем exact_type, содержащее точный тип оператора для маркеров OP. Для всех остальных типов маркеров exact_type равно именованному полю type кортежа.

Изменено в версии 3.1: Добавлена поддержка именованных кортежей.

Изменено в версии 3.3: Добавлена поддержка exact_type.

tokenize() определяет источник кодировка файла, ища BOM UTF-8 или печенье кодировки, согласно PEP 263.

tokenize.generate_tokens(readline)

Маркировать исходный строки Юникода вместо байтов.

Как и tokenize(), аргумент readline является вызываемым, возвращающим одну строку ввода. Однако generate_tokens() ожидает, что readline возвращает объект str, а не байты.

В результате получается итератор, дающий именованные кортежи, точно как tokenize(). Он не yield токен ENCODING.

Все константы из модуля token также экспортируются из tokenize.

Предусмотрена еще одна функция для обращения процесса токенизации. Это полезно для создания инструментов для токенизации сценария, изменения потока маркеров и обратной записи измененного сценария.

tokenize.untokenize(iterable)

Преобразует маркеры обратно в исходный код Python. В iterable должны возвращает последовательности, содержащие по крайней мере два элемента, тип маркера и строка маркера. Любые дополнительные элементы последовательности игнорируются.

Реконструированный сценарий возвращенный как единый строка. Результат гарантированно получает токенизацию для соответствия входному сигналу, так что преобразование происходит без потерь и обеспечивается передача в оба конца. Гарантия применяется только к типу маркера и маркеру строка так как интервал между маркерами (позициями столбцов) может измениться.

Он возвращает байты, кодированный используя токен ENCODING, который является первой последовательностью токенов, выводимой tokenize(). Если во входном файле нет маркера кодировка, он возвращает str.

tokenize() должен определить кодировку исходных файлов, которые он маркирует. Функция, используемая для этого, доступна:

tokenize.detect_encoding(readline)

Функция detect_encoding() используемый для обнаружения кодировка, которые должны быть используемый для декодирования исходного файла Python. Он требует одного аргумента, readline, так же, как и tokenize() генератор.

Он вызовет readline максимум два раза, и возвращает кодировка используемый (как строка) и список любых строк (не декодированных из байтов), которые он прочитал.

Это обнаруживает кодировка от присутствия BOM UTF-8 или печенья кодировка, как определено в PEP 263. Если и спецификация, и cookie присутствуют, но не согласны, SyntaxError будет поднят. Обратите внимание, что при обнаружении спецификации 'utf-8-sig' будет возвращенный как кодировка.

Если кодировка не указана, то значение по умолчанию 'utf-8' будет возвращенный.

Используйте open(), чтобы открыть исходные файлы Python: это использует detect_encoding(), чтобы обнаружить файл кодировка.

tokenize.open(filename)

Открыть файл в режиме только для чтения с помощью кодировки, обнаруженного detect_encoding().

Добавлено в версии 3.2.

exception tokenize.TokenError

Возникает, когда докстринг или выражение, которое может быть разделено на несколько строк, не завершено нигде в файле, например:

"""Beginning of
docstring

или:

[1,
 2,
 3

Следует отметить, что незакрытые строки с одной кавычкой не вызывают возникновения ошибки. Они маркируются как ERRORTOKEN, после чего происходит токенизация их содержимого.

Использование командной строки

Добавлено в версии 3.3.

Модуль tokenize может быть выполнен в виде сценария из командной строки. Это так же просто, как:

python -m tokenize [-e] [filename.py]

Принимаются следующие опции:

-h, --help

показать это сообщение справки и выйти

-e, --exact

отображение имен маркеров с использованием точного типа

Если указано filename.py, его содержимое маркируется как stdout. В противном случае токенизация выполняется на stdin.

Примеры

Пример переписчика сценария, преобразующего float литералы в десятичные объекты:

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

def decistmt(s):
    """Заменитm десятичные дроби на числа с плавающей точкой в строке операторов.

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

    Формат экспоненты наследуется из библиотеки платформы C. Известны случаи "e-007"
    (Windows) и "e-07" (не Windows). Поскольку мы показываем только 12 цифры, а 13-е
    не близко к 5, остальные выходные данные должны быть не зависящими от платформы.

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

    Выходные данные вычислений с десятичной запятой должны быть одинаковыми для всех
    платформ.

    >>> exec(decistmt(s))
    -3.217160342717258261933904529E-7
    """
    result = []
    g = tokenize(BytesIO(s.encode('utf-8')).readline)  # токенизировать строку
    for toknum, tokval, _, _, _ in g:
        if toknum == NUMBER and '.' in tokval:  # заменить NUMBER токены
            result.extend([
                (NAME, 'Decimal'),
                (OP, '('),
                (STRING, repr(tokval)),
                (OP, ')')
            ])
        else:
            result.append((toknum, tokval))
    return untokenize(result).decode('utf-8')

Пример токенизации из командной строки. Сценарий:

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

say_hello()

будет помечен следующим выводом, где первый столбец является диапазоном координат строки/столбца, где находится маркер, второй столбец является именем маркера, а последний столбец является значение маркера (если имеется)

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

Точные имена типов маркеров можно отобразить с помощью опции -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      ''

Пример токенизации файла программно, считывание строки юникода вместо байтов с помощью generate_tokens():

import tokenize

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

Или считывание байтов непосредственно с помощью tokenize():

import tokenize

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