2to3 - Python 2 から 3 への自動コード変換

2to3 は、 Python 2.x のソースコードを読み込み、一連の 変換プログラム (fixer) を適用して正しい Python 3.x のコードに変換する Python プログラムです。標準ライブラリはほとんど全てのコードを取り扱うのに十分な fixer を含んでいます。ただし 2to3 を構成している lib2to3 は柔軟かつ一般的なライブラリなので、 2to3 のために独自の fixer を書くこともできます。

2to3 の使用

2to3 は大抵の場合、 Python インタープリターと共に、スクリプトとしてインストールされます。場所は、 Python のルートディレクトリにある、 Tools/scripts ディレクトリです。

2to3 に与える基本の引数は、変換対象のファイル、もしくは、ディレクトリのリストです。ディレクトリの場合は、 Python ソースコードを再帰的に探索します。

Python 2.x のサンプルコード、 example.py を示します:

def greet(name):
    print "Hello, {0}!".format(name)
print "What's your name?"
name = raw_input()
greet(name)

これは、コマンドラインから 2to3 を呼び出すことで、Python 3.x コードに変換されます:

$ 2to3 example.py

オリジナルのソースファイルに対する差分が表示されます。 2to3 は必要となる変更をソースファイルに書き込むこともできます (-n も与えたのでない限りオリジナルのバックアップも作成されます)。変更の書き戻しは -w フラグによって有効化されます:

$ 2to3 -w example.py

変換後、 example.py は以下のようになります:

def greet(name):
    print("Hello, {0}!".format(name))
print("What's your name?")
name = input()
greet(name)

変換処理を通じて、コメントと、インデントは保存されます。

デフォルトでは、 2to3 は一連の 事前定義された変換プログラム を実行します。 -l フラグは、利用可能な変換プログラムの一覧を表示します。 -f フラグにより、実行する変換プログラムを明示的に与えることもできます。同様に、 -x は変換プログラムを明示的に無効化します。下記の例では、 importshas_key 変換プログラムだけを実行します:

$ 2to3 -f imports -f has_key example.py

このコマンドは apply 以外のすべての変換プログラムを実行します:

$ 2to3 -x apply example.py

いくつかの変換プログラムは 明示的、つまり、デフォルトでは実行されず、コマンドラインで実行するものとして列記する必要があります。デフォルトの変換プログラムに idioms 変換プログラムを追加して実行するには、下記のようにします:

$ 2to3 -f all -f idioms example.py

ここで、 all を指定することで、全てのデフォルトの変換プログラムを有効化できることに注意して下さい。

2to3 がソースコードに修正すべき点を見つけても、自動的には修正できない場合もあります。この場合、 2to3 はファイルの変更点の下に警告を表示します。 3.x に準拠したコードにするために、あなたはこの警告に対処しなくてはなりません。

2to3 は doctest の修正もできます。このモードを有効化するには -d フラグを指定して下さい。 doctest だけ が修正されることに注意して下さい。これは、モジュールが有効な Python コードであることを要求しないということでもあります。例えば、 reST ドキュメント中の doctest に似たサンプルコードなども、このオプションで修正することができます。

-v は、変換処理のより詳しい情報の出力を有効化します。

いくつかの print 文は関数呼び出しとしても文としても解析できるので、 2to3 は print 関数を含むファイルを常に読めるとは限りません。 2to3 は from __future__ import print_function コンパイラディレクティブが存在することを検出すると、内部の文法を変更して print() を関数として解釈するようになります。 -p フラグによって手動でこの変更を有効化することもできます。 print 文を変換済みのコードに対して変換プログラムを適用するには -p を使用してください。同様に、exec() を関数として解釈させるには -e を使用してください。

-o または --output-dir で処理結果の出力先ディレクトリを変更出来ます。入力ファイルを上書きしないならバックアップは意味をなさないので、オプション -n フラグが要ります。

バージョン 3.2.3 で追加: -o オプションが追加されました。

-W または --write-unchanged-files により、たとえファイルに変更が必要なくても常にファイルを出力するように 2to3 に指示することが出来ます。これは、 -o とともに使って、Python ソースツリー全体を変換して別のディレクトリに書き出す際に最も有用です。

バージョン 3.2.3 で追加: -W フラグが追加されました。

オプション --add-suffix で、全ての出力ファイル名に与えた文字列を追加します。これを指定するのであれば別名で書き出されるためバックアップは必要ないので、オプション -n フラグが要ります。例えば:

$ 2to3 -n -W --add-suffix=3 example.py

とすれば変換後ファイルは example.py3 として書き出されます。

バージョン 3.2.3 で追加: --add-suffix オプションが追加されました。

ひとつのディレクトリツリーからプロジェクト全体を変換したければこのように使います:

$ 2to3 --output-dir=python3-version/mycode -W -n python2-version/mycode

変換プログラム

コード変形の各ステップは変換プログラムに隠蔽されます。 2to3 -l コマンドは変換プログラムのリストを表示します。 上記 の通り、それぞれの変換プログラムを個別に有効化したり無効化したりすることができます。ここではそれらをさらに詳細に説明します。

apply

apply() の使用を削除します。例えば apply(function, *args, **kwargs)function(*args, **kwargs) に変換されます。

asserts

廃止になった unittest メソッド名を新しい名前に置換します。

対象

変換先

failUnlessEqual(a, b)

assertEqual(a, b)

assertEquals(a, b)

assertEqual(a, b)

failIfEqual(a, b)

assertNotEqual(a, b)

assertNotEquals(a, b)

assertNotEqual(a, b)

failUnless(a)

assertTrue(a)

assert_(a)

assertTrue(a)

failIf(a)

assertFalse(a)

failUnlessRaises(exc, cal)

assertRaises(exc, cal)

failUnlessAlmostEqual(a, b)

assertAlmostEqual(a, b)

assertAlmostEquals(a, b)

assertAlmostEqual(a, b)

failIfAlmostEqual(a, b)

assertNotAlmostEqual(a, b)

assertNotAlmostEquals(a, b)

assertNotAlmostEqual(a, b)

basestring

basestringstr に変換します。

buffer

buffermemoryview に変換します。 memoryview API は buffer と似ているものの厳密に同じではないので、この変換プログラムはオプションです。

dict

辞書をイテレートするメソッドを修正します。 dict.iteritems()dict.items() に、 dict.iterkeys()dict.keys() に、 dict.itervalues()dict.values() に変換されます。同様に dict.viewitems(), dict.viewkeys() dict.viewvalues() はそれぞれ dict.items(), dict.keys(), dict.values() に変換されます。また、 list の呼び出しの中で dict.items(), dict.keys(), dict.values() を使用している場合はそれをラップします。

except

except X, Texcept X as T に変換します。

exec

exec 文を exec() 関数に変換します。

execfile

execfile() の使用を削除します。 execfile() への引数は open(), compile(), exec() の呼び出しでラップされます。

exitfunc

sys.exitfunc への代入を atexit モジュールの使用に変更します。

filter

list 呼び出しの中で filter() を使用している部分をラップします。

funcattrs

名前が変更された関数の属性を修正します。例えば my_function.func_closuremy_function.__closure__ に変換されます。

future

from __future__ import new_feature 文を削除します。

getcwdu

os.getcwdu()os.getcwd() に置き換えます。

has_key

dict.has_key(key)key in dict に変更します。

idioms

このオプションの変換プログラムは、 Python コードをより Python らしい書き方にするいくつかの変形を行います。 type(x) is SomeClasstype(x) == SomeClass のような型の比較は isinstance(x, SomeClass) に変換されます。 while 1while True になります。また、適切な場所では sorted() が使われるようにします。例えば、このブロックは

L = list(some_iterable)
L.sort()

次のように変更されます

L = sorted(some_iterable)
import

暗黙の相対インポート (sibling imports) を検出して、明示的な相対インポート (relative imports) に変換します。

imports

標準ライブラリ中のモジュール名の変更を扱います。

imports2

標準ライブラリ中の別のモジュール名の変更を扱います。単に技術的な制約のために imports とは別になっています。

input

input(prompt)eval(input(prompt)) に変換します。

intern

intern()sys.intern() に変換します。

isinstance

isinstance() の第 2 引数の重複を修正します。例えば isinstance(x, (int, int))isinstance(x, int)isinstance(x, (int, float, int))isinstance(x, (int, float)) に変換されます。

itertools_imports

itertools.ifilter(), itertools.izip(), itertools.imap() のインポートを削除します。また itertools.ifilterfalse() のインポートを itertools.filterfalse() に変換します。

itertools

itertools.ifilter(), itertools.izip(), itertools.imap() を使っている箇所を同等の組み込み関数で置き換えます。 itertools.ifilterfalse()itertools.filterfalse() に変換されます。

long

longint に変更します。

map

list 呼び出しの中の map() をラップします。また、 map(None, x)list(x) に変換します。 from future_builtins import map を使うと、この変換プログラムを無効にできます。

metaclass

古いメタクラス構文 (クラス定義中の __metaclass__ = Meta) を、新しい構文 (class X(metaclass=Meta)) に変換します。

methodattrs

古いメソッドの属性名を修正します。例えば meth.im_funcmeth.__func__ に変換されます。

ne

古い不等号の構文 <>!= に変換します。

next

イテレータの next() メソッドの使用を next() 関数に変換します。また next() メソッドを __next__() に変更します。

nonzero

Renames definitions of methods called __nonzero__() to __bool__().

numliterals

8 進数リテラルを新しい構文に変換します。

operator

operator モジュール内のさまざまな関数呼び出しを、他の、しかし機能的には同等の関数呼び出しに変換します。必要に応じて、import collections.abc などの適切な import ステートメントが追加されます。以下のマッピングが行われます。

対象

変換先

operator.isCallable(obj)

callable(obj)

operator.sequenceIncludes(obj)

operator.contains(obj)

operator.isSequenceType(obj)

isinstance(obj, collections.abc.Sequence)

operator.isMappingType(obj)

isinstance(obj, collections.abc.Mapping)

operator.isNumberType(obj)

isinstance(obj, numbers.Number)

operator.repeat(obj, n)

operator.mul(obj, n)

operator.irepeat(obj, n)

operator.imul(obj, n)

paren

リスト内包表記で必要になる括弧を追加します。例えば [x for x in 1, 2][x for x in (1, 2)] になります。

print

print 文を print() 関数に変換します。

raise

raise E, Vraise E(V) に、raise E, V, Traise E(V).with_traceback(T) に変換します。例外の代わりにタプルを使用することは 3.0 で削除されたので、E がタプルならこの変換は不正確になります。

raw_input

raw_input()input() に変換します。

reduce

reduce()functools.reduce() に移動されたことを扱います。

reload

reload()importlib.reload() に変換します。

renames

sys.maxintsys.maxsize に変更します。

repr

バッククォートを使った repr を repr() 関数に置き換えます。

set_literal

set コンストラクタの使用を set リテラルに置換します。この変換プログラムはオプションです。

standarderror

StandardErrorException に変更します。

sys_exc

廃止された sys.exc_value, sys.exc_type, sys.exc_traceback の代わりに sys.exc_info() を使うように変更します。

throw

ジェネレータの throw() メソッドの API 変更を修正します。

tuple_params

関数定義における暗黙的なタプルパラメータの展開を取り除きます。この変換プログラムによって一時変数が追加されます。

types

types モジュールのいくつかのメンバオブジェクトが削除されたことによって壊れたコードを修正します。

unicode

unicodestr に変更します。

urllib

urlliburllib2urllib パッケージに変更されたことを扱います。

ws_comma

コンマ区切りの要素から余計な空白を取り除きます。この変換プログラムはオプションです。

xrange

xrange()range() に変更して、既存の range() 呼び出しを list でラップします。

xreadlines

for x in file.xreadlines()for x in file に変更します。

zip

list 呼び出しの中で使われている zip() をラップします。これは from future_builtins import zip が見つかった場合は無効にされます。

lib2to3 - 2to3's library

ソースコード: Lib/lib2to3/


バージョン 3.10 で非推奨: Python 3.9 から構文解析器が PEG パーサに変更になり (PEP 617 を参照のこと)、Python 3.10 には lib2to3 の使用する LL(1) パーサでは解析できない文法が導入される可能性があります。Python の将来のバージョンでは lib2to3 モジュールが標準ライブラリから削除される可能性があります。その場合、代替として LibCSTparso のようなサードパーティ・ライブラリの使用を検討してください。

注釈

lib2to3 API は安定しておらず、将来、劇的に変更されるかも知れないと考えるべきです。