Keep on moving

あんまりまとまってないことを書きますよ

pyuvをPython3対応するメモ

ネットワーク系プログラミングの勉強もかねて、↓のライブラリで近頃よくあそんでいます。
saghul/pyuv · GitHub
これ現状、Python3系は未対応でして、日頃の感謝もこめて対応してみました。
ちなみにPython3系は今回初めてさわりました。
軽い気持ちではじめて割とはまったので備忘録的にまとめておきます。多分もっといい方法はあるはず。

はまった箇所

Python3系のCAPI対応は [twitter:mopemope] さんのエントリが参考になりました。

CAPIでのモジュールの初期化

Cでモジュールを初期化する場合には以下のように書きます。

#if PY_MAJOR_VERSION >= 3
PyInit_<yourmodulename>(void)
#else
init<yourmodulename>(void)
#endif

はまったのはPython2系だと独自に initerrno って名前の関数をつくっても、自分のモジュールが読み込まれますが、
Python3ではPyInit_errnoって名前にすると Pythonのビルトインのerrnoモジュール
が読み込まれます。動きは絶対Python3が正しいと思います。
とりあえず、関数名をビルトインモジュールとかぶらない名前に変えました。
ちなみに"PyModuleDef"で設定する名前とPyInit_~ の名前は合わせなくても動くんですね。。こういうしようなのかな。
要はこんな感じにかけます。が、本当にいいのか?

    static struct PyModuleDef moduledef = {
        PyModuleDef_HEAD_INIT,
        "pyuv.themodulename",     /* m_name */
        "This is a module",  /* m_doc */
        -1,                  /* m_size */
        NULL,                /* m_methods */
    };
/* m_name と違う名前でもOK */
PyInit_themodulename(void)
try/catchの書き方が変わった

2to3でうまくいくんだけど、一応

# before2.5
except IOError, exc:
# after 2.6
except IOError as exc:
bytes/Unicode対応が難しい

2系ではstring/unicode型だったのが、3系ではbytes/unicode型になりました。
2to3ではうまく動かない場合が多いです。
詳しくは清水川さんの記事を見るといいかも。

えーと、要は地道にどちらになるべきかをきちんとおさえて、書いてくしかなさそうですね。
テストがきちんと書かれていていたので、読んである程度理解できたので助かりました。単体テスト大事ですね。*1

とりあえず、pyuvの作者は2.5以前は対応しない方針らしいので、テストで出てくる文字はきちんと型分けしといた

b'test' # bytes
u'test' # unicode

あと os.linesep (改行コード)がPython2系だと Stringで Python3系だと Unicodeになってるのが
わりと困りました。

成果

とりあえずPull Requestしてみた。さてどうなるかな。
https://github.com/saghul/pyuv/pull/5

結果

2012/1/18 追記

正式に取り込まれました。Python3.0/3.1/3.2でlibuv のPython wrapperが使えるようになりました。
Authorに名前をいれてもらえました。Pull/Requestが取り込まれるのってうれしいものですね!!
多バージョンのテストにはtoxを使いました。
非常に便利だったので、後日ブログ記事にします。

pyuv/AUTHORS at master · saghul/pyuv · GitHub

*1: きっとテストを読み間違えて勘違いしてるかもなー