tokenize --- Python ソース用のトークナイザー¶
ソースコード: Lib/tokenize.py
The tokenize module provides a lexical scanner for Python source code,
implemented in Python. The scanner in this module returns comments as tokens
as well, making it useful for implementing "pretty-printers", including
colorizers for on-screen displays.
トークン列を扱いやすくするため、すべての 演算子 と 区切り文字 のトークン、および Ellipsis を、汎用の OP トークン型で返します。
TokenInfo.exact_type プロパティで、個別のトークン型を取得できます。
警告
このモジュールの関数は、構文的に有効な Python コード (ast.parse() で解析しても例外を送出しないコード) を解析するためだけに設計されています。
不正な Python コードを与えた場合の動作は 未定義 であり、いつでも変わる可能性があります。
入力のトークン化¶
第一のエントリポイントは ジェネレータ です:
- tokenize.tokenize(readline)¶
tokenize()ジェネレータは一つの引数 readline を必要とします。この引数は呼び出し可能オブジェクトで、ファイルオブジェクトのio.IOBase.readline()メソッドと同じインタフェースを提供している必要があります。この関数は呼び出しのたびに入力の一行を bytes で返さなければなりません。このジェネレータは
TokenInfoオブジェクトを生成します。バージョン 3.1 で変更: 通常のタプルではなく
TokenInfoオブジェクトを yield するようになりました。tokenize()は PEP 263 にしたがって、ソースのエンコーディングを UTF-8 BOM か encoding cookie を見つけて決定します。
- tokenize.generate_tokens(readline)¶
バイト列ではなく Unicode 文字列を読み取り、ソースをトークン化します。
tokenize()と同じく、readline 引数は入力を 1 行返す呼び出し可能オブジェクトです。 ただしgenerate_tokens()では、readline は bytes ではなく str オブジェクトを返す必要があります。結果は
tokenize()と同じく、TokenInfoオブジェクトを yield するイテレータです。ENCODINGトークンは生成しません。
- class tokenize.TokenInfo(type, string, start, end, line)¶
トークンを表す 名前付きタプル です。 5 つのフィールドと、追加の
exact_typeプロパティを持ちます。Added in version 3.1.
- type¶
NAMEやSTRINGなど、tokenの定数で定義される、トークン型を識別する整数です。 演算子と区切り文字の型はOPです。 その個別の型を調べるにはexact_typeを使ってください。
- string¶
strで表したトークンのテキストです。STRINGトークンでは、引用符、接頭辞、エスケープシーケンスも、評価せずに含めます。ENCODINGトークンでは、エンコーディング名です。 対応するソーステキストなしに挿入されるトークンでは、空文字列になることがあります。FSTRING_MIDDLEとTSTRING_MIDDLEのトークンでは、二重の波括弧を 1 つの波括弧に置き換えます。
- start¶
ソース内でトークンが始まる位置 (その位置を含む) を指定する、整数の 2 要素タプル
(srow, scol)です。 行番号は 1 から、列番号は 0 から数えます。 列は Unicode コードポイント単位で数え、タブはタブストップまで展開せず、1 列として数えます。ENCODINGトークンでは(0, 0)です。
- end¶
トークンの終端位置 (その位置は含まない) を指定する、整数の 2 要素タプル
(erow, ecol)です。 行番号は 1 から、列番号は 0 から数えます。 複数行にまたがるトークンでは、erow は最後の行、ecol はその行内の列です。例えば、最初の行の先頭にある名前
spamでは、startは(1, 0)、endは(1, 4)です。ENCODINGトークンでは(0, 0)です。 末尾に改行がない入力の終わりに挿入するNEWLINEやNLトークンでは、終端の列がソーステキストより 1 列先になることがあります。
token モジュールのすべての定数は、tokenize からも公開されています。
もう一つの関数がトークン化プロセスを逆転するために提供されています。これは、スクリプトを字句解析し、トークンのストリームに変更を加え、変更されたスクリプトを書き戻すようなツールを作成する際に便利です。
- tokenize.untokenize(iterable)¶
トークンの列を Python ソースコードに変換します。 iterable は少なくとも二つの要素、トークンタイプおよびトークン文字列、からなるシーケンスを返さなければいけません。 その他のシーケンスの要素は無視されます。
結果を再びトークン化すると入力と一致することが保証されるため、情報を失わずに往復変換できます。 ただし保証されるのはトークン型とトークン文字列だけで、トークン間の空白 (列位置) は変わることがあります。
tokenize()が最初に出力するトークンであるENCODINGを使ってエンコードしたバイト列を返します。 入力にエンコーディングのトークンがなければ、代わりに str を返します。
tokenize() はトークナイズしようとしているソースファイルのエンコーディングを検出する必要があります。これを行うために使っている関数が公開されています:
- tokenize.detect_encoding(readline)¶
detect_encoding()関数は Python のソースファイルをデコードするのに使うエンコーディングを検出するために使われます。tokenize()ジェネレータと同じ readline を引数として取ります。readlineを最大 2 回呼び出し、使うエンコーディング (文字列) と、読み込んだ行のリスト (バイト列からデコードしないまま) を返します。UTF-8 BOM か PEP 263 で定義されている encoding cookie からエンコーディングを検出します。BOMと cookie の両方が存在し、一致していない場合、
SyntaxErrorが送出されます。 BOM が見つかった場合はエンコーディングとして'utf-8-sig'が返されます。エンコーディングが指定されていない場合、デフォルトの
'utf-8'が返されます。Python のソースファイルを開くには
open()を使ってください。これはdetect_encoding()を利用してファイルエンコーディングを検出します。
- tokenize.open(filename)¶
detect_encoding()を使って検出したエンコーディングを利用して、ファイルを読み出し専用モードで開きます。Added in version 3.2.
- exception tokenize.TokenError¶
docstring や複数行にわたることが許される式がファイル内のどこかで終わっていない場合に送出されます。例えば:
"""docstring の 始まり
もしくは:
[1, 2, 3
コマンドラインでの使用¶
Added in version 3.3.
tokenize モジュールは、コマンドラインからスクリプトとして実行できます。
次のようにするだけです:
python -m tokenize [-e] [filename.py]
以下のオプションが使用できます:
- -h, --help¶
このヘルプメッセージを出力して終了します
- -e, --exact¶
exact type を使ってトークン名を表示します
filename.py が指定された場合、その内容がトークナイズされ stdout に出力されます。指定されなかった場合は stdin からトークナイズします。
Added in version 3.15: 出力はデフォルトで色付きになり、環境変数で制御 できます。
使用例¶
スクリプト書き換えの例で、浮動小数点数リテラルを 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')
コマンドラインからトークナイズする例。次のスクリプトが:
def say_hello():
print("Hello, World!")
say_hello()
トークン化すると次の出力になります。 最初の列はトークンが見つかった行と列の座標の範囲、2 列目はトークン名、最後の列はトークンの値 (ある場合) です:
$ 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 ''
トークンの exact_type 名は -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)