tokenize --- توکن‌ساز (Tokenizer) برای کد منبع پایتون

کد منبع: Lib/tokenize.py


ماژول tokenize یک پویشگر واژگانی برای کد منبع پایتون فراهم می‌کند که با پایتون پیاده‌سازی شده است. پویشگر این ماژول کامنت‌ها را نیز به‌صورت توکن برمی‌گرداند و به همین دلیل برای پیاده‌سازی «زیبانویس»ها، از جمله رنگ‌آمیزکننده‌های نمایش روی صفحه، مفید است.

To simplify token stream handling, all operator and delimiter tokens and Ellipsis are returned using the generic OP token type. The TokenInfo.exact_type property gives the exact token type.

هشدار

توجه داشته باشید که توابع این ماژول فقط برای تجزیه کد پایتون معتبر از نظر سینتکسی طراحی شده‌اند (کدی که هنگام تجزیه با استفاده از ast.parse() استثنایی پرتاب نمی‌کند). رفتار توابع این ماژول در صورت ارائه کد پایتون نامعتبر تعریف‌نشده است و ممکن است در هر زمانی تغییر کند.

توکن‌سازی ورودی

نقطه ورود اصلی یک تولیدگر است:

tokenize.tokenize(readline)

تولیدگر tokenize() به یک آرگومان، readline، نیاز دارد که باید یک شیء فراخوانی‌پذیر باشد و همان رابط متد io.IOBase.readline() اشیای پرونده را فراهم کند. هر فراخوانی این تابع باید یک سطر از ورودی را به‌صورت بایت برگرداند.

The generator produces TokenInfo objects.

تغییر یافته در نسخه‌ی 3.1: Changed to yield TokenInfo objects instead of plain tuples.

tokenize() کدگذاری منبع پرونده را با جست‌وجوی BOM UTF-8 یا کوکی کدگذاری، طبق PEP 263 تعیین می‌کند.

tokenize.generate_tokens(readline)

یک منبع را با خواندن رشته‌های یونیکد به‌جای بایت‌ها توکن‌بندی می‌کند.

مانند tokenize()، آرگومان readline یک شیء فراخوانی‌پذیر است که یک سطر ورودی را برمی‌گرداند. با این حال، generate_tokens() انتظار دارد که readline به‌جای bytes یک شیء str برگرداند.

The result is an iterator yielding TokenInfo objects, exactly like tokenize(). It does not yield an ENCODING token.

class tokenize.TokenInfo(type, string, start, end, line)

A named tuple describing a token. It has five fields and an additional exact_type property.

اضافه شده در نسخه‌ی 3.1.

type

An integer identifying the token type, as defined by the constants in token, such as NAME and STRING. Operators and delimiters have type OP; use exact_type for their specific token type.

string

The token text as a str. For STRING tokens, this includes the quotes, any prefix, and escape sequences, without evaluating them.

For an ENCODING token, this is the encoding name. Tokens inserted without corresponding source text can have an empty string. In FSTRING_MIDDLE and TSTRING_MIDDLE tokens, doubled braces are replaced by single braces.

start

A 2-tuple (srow, scol) of ints specifying the inclusive start position of the token in the source. Rows are numbered from 1 and columns from 0. Columns count Unicode code points; a tab counts as one column, without expanding it to a tab stop.

For an ENCODING token, this is (0, 0).

end

A 2-tuple (erow, ecol) of ints specifying the exclusive end position of the token. Rows are numbered from 1 and columns from 0. For a token spanning multiple lines, erow is the final row and ecol is the column on that row.

For example, the name spam at the beginning of the first line has start equal to (1, 0) and end equal to (1, 4).

For an ENCODING token, this is (0, 0). A NEWLINE or NL token inserted at the end of input without a final newline can have an end column one beyond the source text.

line

A str containing the full source line on which the token was found, including surrounding text and the line terminator if present. For a token spanning multiple lines, this can contain several source lines. It is empty for ENCODING and ENDMARKER tokens.

exact_type

An integer identifying the specific operator type for OP tokens, as defined by the constants in token. For all other token types, this equals type. For example, a + token has type equal to OP and exact_type equal to PLUS.

اضافه شده در نسخه‌ی 3.3.

تمام ثابت‌های ماژول token نیز از tokenize اکسپورت می‌شوند.

تابع دیگری برای معکوس کردن فرآیند توکن‌بندی (tokenization) ارائه شده است. این تابع برای ساخت ابزارهایی مفید است که یک اسکریپت را توکن‌بندی می‌کنند، جریان توکن را تغییر می‌دهند و اسکریپت اصلاح‌شده را بازمی‌نویسند.

tokenize.untokenize(iterable)

توکن‌ها را دوباره به کد منبع پایتون تبدیل می‌کند. iterable باید دنباله‌هایی با دست‌کم دو عنصر برگرداند: نوع توکن و رشته‌ی توکن. سایر عناصر دنباله نادیده گرفته می‌شوند.

تضمین می‌شود که نتیجه دوباره توکن‌بندی شود (tokenize back) تا با ورودی مطابقت داشته باشد، به‌گونه‌ای که تبدیل بدون از دست رفتن داده باشد و رفت‌وبرگشت‌ها تضمین شوند. این تضمین فقط به نوع توکن و رشته‌ی توکن اعمال می‌شود، زیرا فاصله‌گذاری بین توکن‌ها (موقعیت‌های ستونی) ممکن است تغییر کند.

این تابع بایت‌هایی را برمی‌گرداند که با استفاده از توکن ENCODING کدگذاری شده‌اند؛ این توکن نخستین دنباله‌ی توکنی است که توسط tokenize() خروجی داده می‌شود. اگر توکن کدگذاری در ورودی وجود نداشته باشد، در عوض یک str برمی‌گرداند.

tokenize() باید کدگذاری پرونده‌های منبعی را که توکن‌بندی (tokenize) می‌کند، تشخیص دهد. تابعی که برای این کار استفاده می‌کند، در دسترس است:

tokenize.detect_encoding(readline)

تابع detect_encoding() برای تشخیص کدگذاری‌ای استفاده می‌شود که باید برای کدگشایی یک پرونده منبع پایتون به کار رود. این تابع، همانند تولیدگر tokenize()، به یک آرگومان، readline، نیاز دارد.

readline را حداکثر دو بار فراخوانی می‌کند و کدگذاری استفاده‌شده (به‌صورت یک رشته) و فهرستی از سطرهایی را که خوانده است (که از بایت‌ها کدگشایی نشده‌اند) برمی‌گرداند.

کدگذاری بر اساس وجود نشانگر ترتیب بایت (BOM) مربوط به UTF-8 یا کوکی کدگذاری (encoding cookie)، همان‌طور که در PEP 263 مشخص شده است، تشخیص داده می‌شود. اگر هر دو BOM و کوکی موجود باشند، اما با یکدیگر مغایرت داشته باشند، یک SyntaxError پرتاب خواهد شد. توجه داشته باشید که اگر BOM یافت شود، 'utf-8-sig' به‌عنوان کدگذاری برگردانده خواهد شد.

اگر هیچ کدگذاری‌ای مشخص نشده باشد، مقدار پیش‌فرض 'utf-8' برگردانده خواهد شد.

برای باز کردن پرونده‌های منبع پایتون، از open() استفاده کنید: این تابع از detect_encoding() برای تشخیص کدگذاری پرونده استفاده می‌کند.

tokenize.open(filename)

باز کردن یک پرونده در حالت فقط‌خواندنی با استفاده از کدگذاری تشخیص‌داده‌شده توسط detect_encoding().

اضافه شده در نسخه‌ی 3.2.

exception tokenize.TokenError

هنگامی پرتاب می‌شود که یا یک رشته مستندسازی یا عبارتی که ممکن است در چند سطر تقسیم شود، در هیچ جای پرونده تکمیل نشده باشد، برای مثال:

"""Beginning of
docstring

یا:

[1,
 2,
 3

نحوه‌ی استفاده از خط فرمان

اضافه شده در نسخه‌ی 3.3.

ماژول tokenize می‌تواند به‌عنوان یک اسکریپت از خط فرمان اجرا شود. این کار به سادگی زیر است:

python -m tokenize [-e] [filename.py]

گزینه‌های زیر پذیرفته می‌شوند:

-h, --help

این پیام راهنما را نمایش می‌دهد و خارج می‌شود

-e, --exact

نمایش نام‌های توکن با استفاده از نوع دقیق

اگر filename.py مشخص شده باشد، محتوای آن به stdout توکن‌سازی می‌شود. در غیر این صورت، توکن‌سازی روی stdin انجام می‌شود.

اضافه شده در نسخه‌ی 3.15: خروجی به‌طور پیش‌فرض رنگی است و می‌تواند با استفاده از متغیرهای محیطی کنترل شود.

مثال‌ها

مثالی از یک بازنویس اسکریپت که مقادیر لفظی 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')

مثالی از توکن‌بندی (tokenizing) از خط فرمان. اسکریپت:

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)