# `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 має повертати послідовності принаймні з двома елементами, типом маркера та рядком маркера. Будь-які додаткові елементи послідовності ігноруються.

  The reconstructed script is returned as a single string. The result is guaranteed to tokenize back to match the input so that the conversion is lossless and round-trips are assured. The guarantee applies only to the token type and token string as the spacing between tokens (column positions) may change.

  Він повертає байти, закодовані за допомогою маркера `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)