CPythonをC言語で拡張(1): モジュールを作る

PythonからC言語で書いた関数を呼び出すには、CPythonにCのコードを追加する。 具体的には以下の公式記事を参考にすればいい:

しかし、微妙に詰まった点があったのでここでは具体的なステップで拡張の書き方を紹介する。

1. 関数を書く

まずは関数の中身を書く

// 以下の2行は必ず必要
#define PY_SSIZE_T_CLEAN
#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 PyLong_FromLong(sts);
}

この部分は、PythonAPIを使うこと以外は比較的素直に書ける。

2. 関数を登録情報を書く

インタプリタがアクセスできるように、関数の情報を"メソッドテーブル"に書く必要がある。

static PyMethodDef SpamMethods[] = {
  // METH_VARARGS: C関数が使う呼び出し規約をインタプリタに教える
  // METH_VAARGS もしくは METH_VAARGS | METH_KEYWORDS
  {"system", spam_system, METH_VARARGS,
    "Execute a shell command."},
  {NULL, NULL, 0, NULL}
};

基本的には、名前、関数、呼び出し規約、説明(任意) を入れれば良い。 最後の行は番兵として必要なだけらしい。

3. モジュール定義の構造体を作る

以下のようにモジュール定義用の構造体を作り、メソッドテーブルを参照させる:

static struct PyModuleDef spammodule = {
  PyModuleDef_HEAD_INIT,
  "spam",
  NULL,
  -1,
  SpamMethods
};

4. Pythonインタプリタの初期化時にモジュールを登録する

インタプリタの初期化時に、モジュールを登録する。

PyMODINIT_FUNC PyInit_spam(void)
{
  return PyModule_Create(&spammodule);
}

上記までのプログラムをspammodule.cのファイルとして保存しておく。

5. CPythonのコンパイルとリンク

ここがさらにトリッキーで、CPythonのModule/ディレクトリに上記のプログラムを入れる必要がある。 具体的には、以下のようにコンパイルする:

git clone --depth 1 https://github.com/python/cpython.git
cp spammodule.c  cpython/Modules/
vim Modules/setup.local

setup.localは以下のような新しいモジュールの説明を書く:

spam spammodule.o

このファイルはPythonコンパイル時に自動で見つけてくれる。

そしてコンパイルする:

cd cpython
./configure --prefix=`pwd`/built
make -j 4
make install

6. 新しいモジュールを試す

ここまで行けば、あとは単純に試せる

$ cpython/built/bin/python3
Python 3.14.0a0 on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import spam
>>> spam.system("echo hi")
hi
0

終わり

PythonからC言語で書いたmoduleを呼び出せた。もっと高度なAPIは以下に書いてある。

Python/C API リファレンスマニュアル — Python 3.12.5 ドキュメント