tokenize — Tokenizer for Python source

Вихідний код: 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) 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)