tokenize
— Tokenizer for Python source¶
Вихідний код: Lib/tokenize.py
Модуль tokenize
забезпечує лексичний сканер вихідного коду Python, реалізований на Python. Сканер у цьому модулі також повертає коментарі як токени, що робить його корисним для реалізації «красивих принтерів», включаючи розфарбовувачі для екранних дисплеїв.
Щоб спростити обробку потоку маркерів, усі маркери operator і delimiter і Ellipsis
повертаються за допомогою загального типу токена OP
. Точний тип можна визначити, перевіривши властивість exact_type
кортежу named tuple, який повертається з tokenize.tokenize()
.
Попередження
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.
Токенізований вхід¶
Основною точкою входу є generator:
- tokenize.tokenize(readline)¶
Для генератора
tokenize()
потрібен один аргумент, readline, який має бути викликаним об’єктом, який забезпечує той самий інтерфейс, що й методio.IOBase.readline()
файлових об’єктів. Кожен виклик функції має повертати один рядок введення у вигляді байтів.Генератор створює 5-кортежі з такими членами: тип маркера; рядок маркера; 2-кортеж
(srow, scol)
int, що визначає рядок і стовпець, де починається маркер у джерелі; 2-кортеж(erow, ecol)
int, що визначає рядок і стовпець, де закінчується маркер у джерелі; і рядок, на якому було знайдено маркер. Пропущений рядок (останній елемент кортежу) є фізичним рядком. Кортеж 5 повертається як named tuple з іменами полів:type string start end line
.Повернений кортеж named tuple має додаткову властивість під назвою
exact_type
, яка містить точний тип оператора для токенівOP
. Для всіх інших типів токенівточний_тип
дорівнює іменованому полютип
кортежу.Змінено в версії 3.1: Додано підтримку іменованих кортежів.
Змінено в версії 3.3: Додано підтримку
exact_type
.tokenize()
визначає вихідне кодування файлу, шукаючи специфікацію UTF-8 або кодування cookie, відповідно до PEP 263.
- tokenize.generate_tokens(readline)¶
Токенізуйте джерело, яке читає рядки Unicode замість байтів.
Як і
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
Note that unclosed single-quoted strings do not cause an error to be
raised. They are tokenized as ERRORTOKEN
, followed by the
tokenization of their contents.
Використання командного рядка¶
Нове в версії 3.3.
Модуль tokenize
можна запустити як скрипт із командного рядка. Це так просто:
python -m tokenize [-e] [filename.py]
Приймаються такі варіанти:
- -h, --help¶
показати це довідкове повідомлення та вийти
- -e, --exact¶
відображати імена токенів, використовуючи точний тип
Якщо вказано filename.py
, його вміст токенізується до stdout. В іншому випадку токенізація виконується на stdin.
Приклади¶
Приклад сценарію переписувача, який перетворює плаваючі літерали в десяткові об’єкти:
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 ''
Приклад токенізації файлу програмним шляхом, зчитування рядків Unicode замість байтів за допомогою 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)