Python拡張モジュールの作成(MinGW)

以下のような動作をするPython拡張モジュールの作成方法です。拡張モジュール名はspamとし、system()というメソッドを有するものとします。環境はPython 2.7、コンパイラMinGW上のgcc 4.5.2です。サンプルコードの出典はhttp://docs.python.org/extending/です。

>>> import spam
>>> status = spam.system("dir /w")

拡張モジュールのソースコードを下記に示します。受け取った文字列を引数としてsystem()をコールするだけの単純な処理です。

  • spammodule.c
#include <Python.h>

static PyObject *
spam_system(PyObject *self, PyObject *args)
{
    const char *command;
    int sts;

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = system(command);
    return Py_BuildValue("i", sts);
}

static PyMethodDef SpamMethods[] = {
    {"system",  spam_system, METH_VARARGS, "Execute a shell command."},
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

PyMODINIT_FUNC
initspam(void)
{
    (void) Py_InitModule("spam", SpamMethods);
}

pydに必要となるdefファイルを作成します。詳細はわかりませんが、「init+モジュール名」なる関数(このサンプルの場合はinitspam)の外部公開は必須のようです。

LIBRARY spam.pyd
EXPORTS
initspam

コンパイルしてspam.pydの作成を行います。

$ gcc -c spammodule.c -I/c/python27/include
$ dllwrap -def spam.def -driver-name gcc --dllname spam.pyd -o spam.pyd \
          --target=i386-mingw32 --mno-cygwin spammodule.o \
          -L/c/Python27/Libs -lpython27 -static-libgcc

dllwrapのオプションに-static-libgccをつけると生成されたspam.pydがlibgcc_s_dw2-1.dllに外部依存しなくなるので便利です。(外部依存させる場合、Pythonのsys.pathにlibgcc_s_dw2-1.dllを置く必要があります。)また、libstdc++-6.dllに外部依存させないようにするには-static-libstdc++を指定するとよいようです。pydがどのDLLに依存しているかを見るにはDependency Walkerが役立ちました。