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

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


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

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

Предупреждение

Обратите внимание, что функции в этом модуле предназначены только для синтаксического анализа кода на Python (кода, который не генерируется при анализе с использованием ast.parse()). Поведение функций в этом модуле не определено при предоставлении недопустимого кода на Python и может измениться в любой момент.

Ввод токенов

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

tokenize.tokenize(readline)

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

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

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

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

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

tokenize() определяет исходную кодировку файла путем поиска спецификации UTF-8 или файла cookie с кодировкой в соответствии с PEP 263.

tokenize.generate_tokens(readline)

Обозначьте источник, считывающий строки в юникоде вместо байтов.

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

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

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

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

tokenize.untokenize(iterable)

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

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

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

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

tokenize.detect_encoding(readline)

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

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

Он определяет кодировку по наличию спецификации UTF-8 или cookie-файла с кодировкой, как указано в 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, то его содержимое преобразуется в стандартный вывод. В противном случае токенизация выполняется в стандартном вводе.

Примеры

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

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

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

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)
Вернуться на верх