tokenize — Токенизатор для источника Python¶
Исходный код: Lib/tokenize.py.
Модуль tokenize предоставляет лексический сканер для исходного кода Python, реализованный на языке Python. Сканер в этом модуле возвращает комментарии в виде лексем, что делает его полезным для реализации «красивых принтеров», включая колоризаторы для экранных дисплеев.
Для упрощения работы с потоком токенов все токены operator и delimiter, а также Ellipsis возвращаются с использованием общего типа токенов OP. Точный тип можно определить, проверив свойство exact_type на named tuple, возвращаемом из tokenize.tokenize().
Токенизация ввода¶
Первичной точкой входа является generator:
-
tokenize.tokenize(readline)¶ Генератор
tokenize()требует один аргумент, readline, который должен быть вызываемым объектом, предоставляющим тот же интерфейс, что и методio.IOBase.readline()для объектов файлов. Каждый вызов функции должен возвращать одну строку ввода в виде байтов.Генератор создает 5 кортежей с такими членами: тип маркера; строка маркера; 2-кортеж
(srow, scol)из ints, указывающий строку и столбец, с которых начинается маркер в источнике; 2-кортеж(erow, ecol)из ints, указывающий строку и столбец, на которых заканчивается маркер в источнике; и строка, на которой был найден маркер. Переданная строка (последний элемент кортежа) является физической строкой. Кортеж 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 BOM или cookie кодировки, согласно PEP 263.
-
tokenize.generate_tokens(readline)¶ Токенизировать источник, читающий строки юникода вместо байтов.
Как и в
tokenize(), аргумент readline представляет собой вызываемый объект, возвращающий одну строку ввода. Однакоgenerate_tokens()ожидает, что readline вернет объект str, а не байты.Результатом является итератор, выдающий именованные кортежи, точно так же, как
tokenize(). Он не дает токенаENCODING.
Все константы из модуля token также экспортируются из tokenize.
Еще одна функция предназначена для обратного процесса токенизации. Это полезно для создания инструментов, которые токенизируют сценарий, изменяют поток токенов и записывают обратно измененный сценарий.
-
tokenize.untokenize(iterable)¶ Преобразует лексемы обратно в исходный код Python. Последовательность iterable должна возвращать последовательности как минимум с двумя элементами: тип токена и строка токена. Любые дополнительные элементы последовательности игнорируются.
Реконструированный сценарий возвращается в виде одной строки. Результат гарантированно соответствует входным данным, так что преобразование происходит без потерь, и обходные пути гарантированы. Гарантия распространяется только на тип лексемы и строку лексемы, поскольку расстояние между лексемами (позиции столбцов) может измениться.
Он возвращает байты, закодированные с помощью маркера
ENCODING, который является первой последовательностью маркеров, выводимых командойtokenize(). Если во входных данных нет маркера кодировки, вместо него возвращается строка.
tokenize() необходимо определить кодировку исходных файлов, которые он лексирует. Функция, которую он использует для этого, доступна:
-
tokenize.detect_encoding(readline)¶ Функция
detect_encoding()используется для определения кодировки, которая должна использоваться для декодирования исходного файла Python. Она требует один аргумент, readline, так же, как и генераторtokenize().Он вызовет readline максимум два раза и вернет используемую кодировку (в виде строки) и список всех прочитанных строк (не декодированных из байтов).
Он определяет кодировку по наличию BOM UTF-8 или cookie кодировки, как указано в PEP 263. Если и BOM, и cookie присутствуют, но не совпадают, будет выдано предупреждение
SyntaxError. Обратите внимание, что если BOM найден, то в качестве кодировки будет возвращено'utf-8-sig'.Если кодировка не указана, то будет возвращено значение по умолчанию
'utf-8'.Используйте
open()для открытия исходных файлов Python: он используетdetect_encoding()для определения кодировки файла.
-
tokenize.open(filename)¶ Открыть файл в режиме только для чтения, используя кодировку, определенную командой
detect_encoding().Добавлено в версии 3.2.
-
exception
tokenize.TokenError¶ Возникает, когда doc-строка или выражение, которое может быть разбито на несколько строк, не завершается нигде в файле, например:
"""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 в объекты 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')
Пример токенизации из командной строки. Сценарий:
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)