CUBE SUGAR CONTAINER

技術系のこと書きます。

Python: 2to3 の使い方とセットアップスクリプト (setup.py) への組み込み

以前 Python の 2.x と 3.x の違いと、その吸収方法についてまとめた記事を書いた。

blog.amedama.jp

その中で 2to3 という Python 2.x で書かれたソースコードを 3.x に移植するためのプログラムについても言及している。 記事の中では既に推奨できない方法とは解説しているけど、念のためその使い方とセットアップスクリプトに組み込んでパッケージのインストール時に動的に変換する方法を紹介しておきたい。

2to3 とは

2to3 は Python 2.x で書かれたソースコードを Python 3.x で動作するソースコードに変換するプログラム。 Python に同梱されているため、新たに何かをインストールする必要なく使うことができる。 変換に関しては一方的なもので、変換後のソースコードは Python 2.x では動作しなくなる。 また、setuptools という Python のデファクトスタンダードとなっているパッケージ用ライブラリが 2to3 とセットアップスクリプトのインテグレーションを提供している。

Python 2.x でしか動作しないスクリプトを用意する

まずは動作確認用として Python 2.x でしか動かないスクリプトを用意する。 以下のサンプルでは print が文として使われていたり、ユーザの入力を raw_input() で受け付けていたりと、あきらかに 2.x でしか動作しないことがわかる。

$ cat << EOF > example.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-


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


if __name__ == '__main__':
    main()

EOF

上記のスクリプトは 2.x であれば正しく動作する。

$ python --version
Python 2.7.10
$ python example.py
What's your name? Foo
Hello, Foo!

しかし 3.x ではシンタックスエラーになる。

$ python --version
Python 3.5.0
$ python example.py
  File "example.py", line 6
    print 'What\'s your name?',
                             ^
SyntaxError: Missing parentheses in call to 'print'

2to3 にかける

それでは、先ほどの Python 2.x でしか動作しないスクリプトを 2to3 にかけてみよう。

$ 2to3 example.py
RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: set_literal
RefactoringTool: Skipping implicit fixer: ws_comma
RefactoringTool: Refactored example.py
--- example.py  (original)
+++ example.py  (refactored)
@@ -3,9 +3,9 @@


 def main():
-  print 'What\'s your name?',
-  name = raw_input()
-  print 'Hello, {name}!'.format(name=name)
+  print('What\'s your name?', end=' ')
+  name = input()
+  print('Hello, {name}!'.format(name=name))


 if __name__ == '__main__':
RefactoringTool: Files that need to be modified:
RefactoringTool: example.py

print が括弧で囲まれた上で raw_input() 関数は input() 関数に置換された。

ファイルを変換結果に置き換える場合には -w オプションを使う。

$ 2to3 -w example.py

尚、オリジナルのファイルは .bak という名前がついて残される (-n オプションを付ければこのファイルも作成されない)。

$ ls
example.py     example.py.bak

先ほど実行した際のメッセージをみると、いくつかの変換がオプション扱いになってスキップされていることがわかる。 もし、全ての変換を適用する場合には -f all オプションをつける。 また、-f オプションでは個別に適用する fixer (変換ルールを記述したもの) を選択できる。

$ 2to3 -f all <file\>

セットアップスクリプト (setup.py) に組み込む

前述した通り 2to3 はセットアップスクリプト (setup.py) に組み込んで使うことができる。 ただし、これは Python 標準ライブラリの distutils ではなく、サードパーティ製の setuptools を使う場合に限られる点に注意が必要。

セットアップスクリプトの中で 2to3 を使うには、以下のように setup() 関数の中で use_2to3 フラグを True にする。

$ cat << EOF > setup.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from setuptools import setup
from setuptools import find_packages

setup(
    name='example',
    version = '0.0.1',
    description='This is an example',
    author='example',
    author_email='example@example.jp',
    py_modules = ['example'],
    use_2to3 = True,
)

EOF

一旦、先ほど Python 3.x で動くようにしたスクリプトをバックアップから元に戻しておこう。

$ mv example.py{.bak,}
$ ls
example.py setup.py

あとは Python 3.x の環境に、セットアップスクリプトの install コマンドでパッケージをインストールする。

$ python --version
Python 3.5.0
$ python setup.py install

すると、パッケージは Python 3.x でも動作するように変換された形でインストールされることになる。 (尚、インストールしたときと同じディレクトリで実行すると、変換前のファイルが読み込まれてしまうので注意)

$ python -c "import example;example.main()"
What's your name? Foo
Hello, Foo!

まとめ

今回は Python に同梱されている 2.x のソースコードを 3.x で動くようにする 2to3 の使い方を紹介した。 今から 2to3 をセットアップスクリプトと組み合わせて使うことは、ベースのソースコードが 2.x になってしまう点と自動変換が完璧ではないため、あまりおすすめできない。 とはいえ、プロジェクトを 3.x に一度に移行する場合には、作業の大部分を自動化できるので頼もしいツールとなるはず。