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)