tokenize --- توکنساز (Tokenizer) برای کد منبع پایتون¶
کد منبع: Lib/tokenize.py
ماژول tokenize یک پویشگر واژگانی برای کد منبع پایتون فراهم میکند که با پایتون پیادهسازی شده است. پویشگر این ماژول کامنتها را نیز بهصورت توکن برمیگرداند و به همین دلیل برای پیادهسازی «زیبانویس»ها، از جمله رنگآمیزکنندههای نمایش روی صفحه، مفید است.
برای سادهسازی مدیریت جریان توکن، تمام توکنهای عملگر و جداکننده و Ellipsis با استفاده از نوع توکن عام OP برگردانده میشوند. نوع دقیق را میتوان با بررسی ویژگی exact_type روی named tuple برگشتی از tokenize.tokenize() تعیین کرد.
هشدار
توجه داشته باشید که توابع این ماژول فقط برای تجزیه کد پایتون معتبر از نظر سینتکسی طراحی شدهاند (کدی که هنگام تجزیه با استفاده از ast.parse() استثنایی پرتاب نمیکند). رفتار توابع این ماژول در صورت ارائه کد پایتون نامعتبر تعریفنشده است و ممکن است در هر زمانی تغییر کند.
توکنبندی ورودی¶
نقطه ورود اصلی یک تولیدگر است:
- tokenize.tokenize(readline)¶
تولیدگر
tokenize()به یک آرگومان، readline، نیاز دارد که باید یک شیء فراخوانیپذیر باشد و همان رابط متدio.IOBase.readline()اشیای پرونده را فراهم کند. هر فراخوانی این تابع باید یک سطر از ورودی را بهصورت بایت برگرداند.این تولیدگر، تاپلهای ۵تایی را با این اعضا تولید میکند: نوع توکن؛ رشتهی توکن؛ یک تاپل ۲تایی
(srow, scol)از اعداد صحیح که ردیف و ستون آغاز توکن در منبع را مشخص میکند؛ یک تاپل ۲تایی(erow, ecol)از اعداد صحیح که ردیف و ستون پایان توکن در منبع را مشخص میکند؛ و سطری که توکن در آن یافت شد. سطر ارسالشده (آخرین آیتم تاپل) خط فیزیکی است. این تاپل ۵تایی بهصورت یک named tuple با نام فیلدها:type string start end lineبازگشت داده میشود.named tuple برگرداندهشده یک ویژگی اضافی به نام
exact_typeدارد که شامل نوع دقیق عملگر برای توکنهایOPاست. برای همهی انواع دیگر توکن،exact_typeبرابر با فیلدtypeدر named tuple است.تغییر یافته در نسخهی 3.1: پشتیبانی از تاپلهای نامدار (named tuples) اضافه شد.
تغییر یافته در نسخهی 3.3: پشتیبانی از
exact_typeافزوده شد.tokenize()کدگذاری منبع پرونده را با جستوجوی BOM UTF-8 یا کوکی کدگذاری، طبق PEP 263 تعیین میکند.
- tokenize.generate_tokens(readline)¶
یک منبع را با خواندن رشتههای یونیکد بهجای بایتها توکنبندی میکند.
مانند
tokenize()، آرگومان readline یک شیء فراخوانیپذیر است که یک سطر ورودی را برمیگرداند. با این حال،generate_tokens()انتظار دارد که readline بهجای bytes یک شیء str برگرداند.نتیجه یک پیمایشگر است که تاپلهای نامدار تولید میکند، دقیقاً مانند
tokenize(). این پیمایشگر توکنENCODINGرا تولید نمیکند.
تمام ثابتهای ماژول 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 انجام میشود.
مثالها¶
مثالی از یک بازنویس اسکریپت که مقادیر لفظی 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)