pyrtm の Python 3 対応

(第14回)Python mini Hack-a-thon - connpass に参加してきました。

PythonRemember The Milk API を扱う pyrtm 0.4 をリリースしました *1

やったこと

Python 2/3 両対応です。

たまたま Python3 対応の Pull リクエスト が来ていました。やろうと思えば、いつでもできる類いの修正ですが、こういうリクエストが来るということそのものが嬉しいですね。リクエストをもらったことをきっかけに取り組みました。

Pull リクエストの内容は、2to3 をベースに Python 3 で動作するようにした修正でした。Python 2/3 両対応するにあたって、

  • 2.x 系と 3.x 系のソースコードを完全に分離する
  • 2.6 以上を対象に Python 3 の構文を使う (2.5 以下はサポートしない)

の2通りのやり方があります。周りの開発者にどっちが良いのさ?と聞いてみたら後者の方を支持する意見が多かったのでそちらで対応しました。

Python 3 対応については ライブラリをPython3対応に書き換える がとても参考になります。

実際に pyrtm でやってみて遭遇した 2 と 3 の違いをいくつか取り上げます。Python 2.6 は Python 3 の構文をサポートしているとは言っても、何かしらエラーが起きることもあるので、ちゃんと両方でテストする必要がありそうです。

  • ライブラリ名/構造の違いによるインポート系の修正
try:                                                                                
    from urllib.request import urlopen                                              
    from urllib.parse import urlencode                                              
except ImportError:                                                                 
    from urllib import urlencode, urlopen

こういうのはどうしようもないんですよね?

  • print 関数への file 引数
-        print('Usage: rtm_appsample APIKEY SECRET [TOKEN]', file=sys.stderr)
+        sys.stderr.write('Usage: rtm_appsample APIKEY SECRET [TOKEN]\n')

sys.stderr じゃないといけない理由はないのですが、オリジナルのコードを尊重してみました。

  • sort メソッドの key 引数
-    keys.sort()
+    keys.sort(key=str)

Python 3 だと、異なるオブジェクトが入ったリストで sort() メソッドを呼び出すと TypeError になります。key 引数で比較関数へ渡すキーの変換方法を明示する必要があるようです。

>>> [1, "a"].sort()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: str() < int()
まとめ

Mac OS X と Fedora15 の Python 2.6 と 3.2 でテストしました。pyrtm ぐらいのツールなら、対応そのものは難しくありませんが、テストするのがちょっと面倒ですね。

2012/2/4 追記

上述した例で print() 関数の file 引数がエラーになっているのは、Python 2.6 では print 文として解釈されているからでした。Python 2.6 以降で print() 関数を使うには、以下の記述が必要でした。

from __future__ import print_function