Python拡張の作り方
追記: おびなたのはてな日記 - distutils with boost.pythonで詳しく説明されているように、C++とBoostを使うともっと楽になるようです。近いうちに試してみます。 また、 Cybozu Developer Network: Python調査報告 (2006/10) によれば 「PEAKの setuptools は distutils の拡張で(中略)distutils の上位互換であるため、setuptools を使用しない理由はありません。積極的に使用しましょう。」とのことなのでこちらも試してみたいと思います。
Cのコードを用意する
書き換えるべき所を明確にするために、 [[MODULE_NAME]]というように表記しました。 同じ名前の括弧には同じ文字列が入らなければいけません。 ***と書いてある部分には、文脈にあわせて適切なことを書く必要があります。#include <Python.h> static PyObject * [[FUNC_NAME]](PyObject *self, PyObject *args) { ***; // 変数の宣言 if (!PyArg_ParseTuple(args, ***)) //受け取る引数にあわせて書き換える return NULL; ***; // 肝心の処理 Py_RETURN_NONE; //値を返さないでいい場合 return Py_BuildValue(***); //返す場合 } static PyMethodDef Methods[] = { {"[[PYTHON_FUNC_NAME]]", [[FUNC_NAME]], METH_VARARGS, "[[関数の説明]]"}, {NULL, NULL, 0, NULL} }; PyMODINIT_FUNC init[[MODULE_NAME]](void) { (void) Py_InitModule("[[MODULE_NAME]]", Methods); }
setup.pyを作る
from distutils.core import setup, Extension module1 = Extension( '[[PACKAGE_NAME]].[[MODULE_NAME]]', sources = ['[[C_FILE_NAME]].c'] ) setup( name = '[[PACKAGE_NAME]]', version = '[[バージョン]]', description = '[[説明文]]', url = '[[URL]]', author = '[[NAME]]', author_email = '[[E-MAIL]]', ext_modules = [module1], packages=["[[PACKAGE_NAME]]"], )PACKAGE_NAMEはPYTHON_MODULE_NAMEと同じでもかまいません。 Extensionの引数に「A.B」と指定した場合、 Aというフォルダの中にB.pydが生成されます。
パッケージを作る
[[PACKAGE_NAME]]と同じ名前のフォルダを作り、 そこに__init__.pyやテスト用のスクリプトなどを置きます。 この位置に置いたスクリプトは[[MODULE_NAME]].pydと同じフォルダにコピーされるので import [[MODULE_NAME]]でインポートすることができます。パッケージを作るようにsetup.pyを記述したので、 ここで__init__.pyを作成して最低限下のように書いておかないと この後のリリースの時に困ります。
import [[MODULE_NAME]] __all__ = ["[[MODULE_NAME]]"]__init__.pyを作る手間が惜しければパッケージを指定しないで、 作成されたバイナリを手でパスの通ったところにコピーしても構いませんが、 あとで余計に手間がかかるかもしれません。
ビルド
コマンドプロンプトを立ち上げてsetup.pyのあるフォルダへ移動します。 基本的には「python setup.py build」でdistutilsがよしなに取りはからってくれます。 筆者の環境では「Cygwinでビルドするなら-c mingw32とつけろ」とメッセージが出るので 「python setup.py build -c mingw32」としました。Cのコードに問題がある場合はここで表示されます。
テスト
buildフォルダの中(たとえばbuild\lib.win32-2.5\[[PACKAGE_NAME]])に コンパイルされたバイナリと、その他のスクリプトがコピーされているはずです。 テスト用のスクリプトを起動して動作確認をします。コンパイルされたモジュールを一度インポートすると、 そのインタプリタが生きている間は上書きに失敗するためコンパイルができません。 delでモジュールを消しても同様です。 Cのコードを書き換えてビルドし直す場合には忘れずにインタプリタを終了しましょう。