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 列先になることがあります。

line

トークンが見つかったソース行の全体を含む str です。 前後のテキストと、行末文字があればそれも含みます。 複数行にまたがるトークンでは、複数のソース行を含むことがあります。 ENCODING と ENDMARKER のトークンでは空文字列です。

exact_type

OP トークンについて、token の定数で定義される個別の演算子の型を識別する整数です。 ほかのトークン型では type と同じ値です。 例えば + トークンでは、type は OP、exact_type は PLUS です。

Added in version 3.3.

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)