拡張と埋め込み FAQ

目次

C で独自の関数を作ることはできますか?

はい。関数、変数、例外、そして新しいタイプまで含んだビルトインモジュールを C で作れます。これはドキュメント Python インタプリタの拡張と埋め込み で説明されています。

ほとんどの中級から上級の Python 本もこの話題を扱っています。

C++ で独自の関数を作ることはできますか?

はい。C++ 内にある C 互換機能を使ってできます。 extern "C" { ... } で Python のインクルードファイルを囲み、 extern "C" を Python インタプリタから呼ぶ各関数の前に置いてください。グローバルや静的な C++ オブジェクトの構造体を持つものは良くないでしょう。

C を書くのは大変です。他の方法はありませんか?

独自の C 拡張を書くための別のやり方は、目的によっていくつかあります。

Cython とその親戚 Pyrex は、わずかに変形した Python を受け取り、対応する C コードを生成します。Cython や Pyrex を使えば Python の C API を習得することなく拡張を書けます。

今のところ Python 拡張が存在しないような C や C++ ライブラリへのインターフェイスが必要な場合、SWIG のようなツールで、そのライブラリのデータ型のラッピングを図れます。 SIPCXXBoostWeave でも C++ ライブラリをラッピングできます。

C から任意の Python 文を実行するにはどうしますか?

これを行う最高水準の関数は PyRun_SimpleString() で、一つの文字列引数を取り、モジュール __main__ のコンテキストでそれを実行し、成功なら 0、例外 (SyntaxError を含む) が発生したら -1 を返します。更に制御したければ、 PyRun_String() を使ってください。ソースは Python/pythonrun.c の 'PyRun_SimpleString() を参照してください。

C から任意の Python 式を評価するにはどうしますか?

先の質問の PyRun_String() を、スタートシンボル Py_eval_input を渡して呼び出してください。これは式を解析し、評価してその値を返します。

Python オブジェクトから C の値を取り出すにはどうしますか?

オブジェクトの型に依ります。タプルなら、 PyTuple_Size() が長さを返し、 PyTuple_GetItem() が指定されたインデックスの要素を返します。リストにも同様の関数 PyList_Size()PyList_GetItem() があります。

bytes では、 PyBytes_Size() がその長さを返し、 PyBytes_AsStringAndSize() がその値へのポインタと長さを提供します。 Python の bytes オブジェクトは null を含むこともできるので、 C の strlen() を使うべきではないことに注意してください。

オブジェクトの型を検査するには、まず最初にそれが NULL でないことを確かめた上で、 PyBytes_Check()PyTuple_Check()PyList_Check() などを使います。

いわゆる 'abstract' インターフェースから提供される、 Python オブジェクトに対する高レベル API もあります -- より詳しいことは Include/abstract.h を読んでください。これを使うと、数値型プロトコル (PyNumber_Index() など) や PyMapping API のマップ型プロトコルなどの役に立つプロトコルに加え、 PySequence_Length()PySequence_GetItem() などの任意の種類の Python シーケンスへのインターフェースにアクセスできます。

Py_BuildValue() で任意長のタプルを作るにはどうしますか?

できません。代わりに PyTuple_Pack() を使ってください。

C からオブジェクトのメソッドを呼び出すにはどうしますか?

PyObject_CallMethod() 関数でオブジェクトの任意のメソッドを呼び出せます。パラメタは、オブジェクト、呼び出すメソッドの名前、 Py_BuildValue() で使われるようなフォーマット文字列、そして引数です:

PyObject *
PyObject_CallMethod(PyObject *object, const char *method_name,
                    const char *arg_format, ...);

これはメソッドを持ついかなるオブジェクトにも有効で、組み込みかユーザ定義かは関係ありません。返り値に対して Py_DECREF() する必要があることもあります。

例えば、あるファイルオブジェクトの "seek" メソッドを 10, 0 を引数として呼ぶとき (ファイルオブジェクトのポインタを "f" とします):

res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
        ... an exception occurred ...
}
else {
        Py_DECREF(res);
}

なお、 PyObject_CallObject() の引数リストには 常に タプルが必要です。関数を引数なしで呼び出すには、フォーマットに "()" を渡し、関数を一つの引数で呼び出すには、関数を括弧でくくって例えば "(i)" としてください。

PyErr_Print() (その他 stdout/stderr に印字するもの) からの出力を受け取るにはどうしますか?

Python コード内で、 write() メソッドをサポートするオブジェクトを定義してください。そのオブジェクトを sys.stdoutsys.stderr に代入してください。print_error を呼び出すか、単に標準のトレースバック機構を作動させてください。そうすれば、出力は write() が送る任意の所に行きます。

最も簡単な方法は、 io.StringIO クラスを使うことです:

>>> import io, sys
>>> sys.stdout = io.StringIO()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(sys.stdout.getvalue())
foo
hello world!

これと同じことをするカスタムオブジェクトは次のようになるでしょう:

>>> import io, sys
>>> class StdoutCatcher(io.TextIOBase):
...     def __init__(self):
...         self.data = []
...     def write(self, stuff):
...         self.data.append(stuff)
...
>>> import sys
>>> sys.stdout = StdoutCatcher()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(''.join(sys.stdout.data))
foo
hello world!

C から Python で書かれたモジュールにアクセスするにはどうしますか?

以下のようにモジュールオブジェクトへのポインタを得られます:

module = PyImport_ImportModule("<modulename>");

そのモジュールがまだインポートされていない (つまり、まだ sys.modules に現れていない) なら、これはモジュールを初期化します。そうでなければ、単純に sys.modules["<modulename>"] の値を返します。なお、これはモジュールをいかなる名前空間にも代入しません。これはモジュールが初期化されて 'sys.modules に保管されていることを保証するだけです。

これで、モジュールの属性 (つまり、モジュールで定義された任意の名前) に以下のようにアクセスできるようになります:

attr = PyObject_GetAttrString(module, "<attrname>");

PyObject_SetAttrString() を呼んでモジュールの変数に代入することもできます。

Python から C++ へインタフェースするにはどうしますか?

やりたいことに応じて、いろいろな方法があります。手動でやるなら、 "拡張と埋め込み" ドキュメント を読むことから始めてください。なお、Python ランタイムシステムにとっては、 C と C++ はあまり変わりません。だから、C 構造体 (ポインタ)型に基づいて新しい Python の型を構築する方針は C++ オブジェクトに対しても有効です。

C++ ライブラリに関しては、 C を書くのは大変です。他の方法はありませんか? を参照してください。

セットアップファイルでモジュールを追加しようとしたらメイクに失敗しました。なぜですか?

セットアップは改行で終わらなければならなくて、改行がないと、ビルド工程は失敗します。(これを直すには、ある種の醜いシェルスクリプトハックが必要ですが、このバグは小さいものですから努力に見合う価値はないでしょう。)

拡張をデバッグするにはどうしますか?

動的にロードされた拡張に GDB を使うとき、拡張がロードされるまでブレークポイントを設定してはいけません。

.gdbinit ファイルに(または対話的に)、このコマンドを加えてください:

br _PyImport_LoadDynamicModule

そして、GDB を起動するときに:

$ gdb /local/bin/python
gdb) run myscript.py
gdb) continue # repeat until your extension is loaded
gdb) finish   # so that your extension is loaded
gdb) br myfunction.c:50
gdb) continue

Linux システムで Python モジュールをコンパイルしたいのですが、見つからないファイルがあります。なぜですか?

Python の多くのパッケージバージョンには、Python 拡張をコンパイルするのに必要な様々なファイルを含む /usr/lib/python2.x/config/ ディレクトリが含まれていません。

Red Hat では、Python RPM をインストールして必要なファイルを得てください。

Debian では、 apt-get install python-dev を実行してください。

"不完全 (incomplete) な入力" を "不適切 (invalid) な入力" から区別するにはどうしますか?

Python インタラクティブインタプリタでは、入力が不完全なとき (例えば、 "if" 文の始まりをタイプした時や、カッコや三重文字列引用符を閉じていない時など) には継続プロンプトを与えられますが、入力が不適切であるときには即座に構文エラーメッセージが与えられます。このようなふるまいを模倣したいことがあります。

Python では構文解析器のふるまいに十分に近い codeop モジュールが使えます。例えば IDLE がこれを使っています。

これを C で行う最も簡単な方法は、 PyRun_InteractiveLoop() を (必要ならば別のスレッドで) 呼び出し、Python インタプリタにあなたの入力を扱わせることです。独自の入力関数を指定するのに PyOS_ReadlineFunctionPointer() を設定することもできます。詳しいヒントは、 Modules/readline.cParser/myreadline.c を参照してください。

しかし、組み込みの Python インタプリタを他のアプリケーションと同じスレッドで実行することが必要で、 PyRun_InteractiveLoop() でユーザの入力を待っている間止められないこともあります。このような場合の解決策の一つは、 PyParser_ParseString() を呼んで e.errorE_EOF が等しいこと、つまり入力が不完全であることを確かめることです。これは、Alex Farber のコードを参考にした、未テストのサンプルコード片です:

#include <Python.h>
#include <node.h>
#include <errcode.h>
#include <grammar.h>
#include <parsetok.h>
#include <compile.h>

int testcomplete(char *code)
  /* code should end in \n */
  /* return -1 for error, 0 for incomplete, 1 for complete */
{
  node *n;
  perrdetail e;

  n = PyParser_ParseString(code, &_PyParser_Grammar,
                           Py_file_input, &e);
  if (n == NULL) {
    if (e.error == E_EOF)
      return 0;
    return -1;
  }

  PyNode_Free(n);
  return 1;
}

別の解決策は、受け取られた文字列を Py_CompileString() でコンパイルすることを試みることです。エラー無くコンパイルされたら、返されたコードオブジェクトを PyEval_EvalCode() を呼んで実行することを試みてください。そうでなければ、入力を後のために保存してください。コンパイルが失敗したなら、それがエラーなのか入力の続きが求められているだけなのか調べてください。そのためには、例外タプルからメッセージ文字列を展開し、それを文字列 "unexpected EOF while parsing" と比較します。ここに GNU readline library を使った完全な例があります (readline() を読んでいる間は SIGINT を無視したいかもしれません):

#include <stdio.h>
#include <readline.h>

#include <Python.h>
#include <object.h>
#include <compile.h>
#include <eval.h>

int main (int argc, char* argv[])
{
  int i, j, done = 0;                          /* lengths of line, code */
  char ps1[] = ">>> ";
  char ps2[] = "... ";
  char *prompt = ps1;
  char *msg, *line, *code = NULL;
  PyObject *src, *glb, *loc;
  PyObject *exc, *val, *trb, *obj, *dum;

  Py_Initialize ();
  loc = PyDict_New ();
  glb = PyDict_New ();
  PyDict_SetItemString (glb, "__builtins__", PyEval_GetBuiltins ());

  while (!done)
  {
    line = readline (prompt);

    if (NULL == line)                          /* Ctrl-D pressed */
    {
      done = 1;
    }
    else
    {
      i = strlen (line);

      if (i > 0)
        add_history (line);                    /* save non-empty lines */

      if (NULL == code)                        /* nothing in code yet */
        j = 0;
      else
        j = strlen (code);

      code = realloc (code, i + j + 2);
      if (NULL == code)                        /* out of memory */
        exit (1);

      if (0 == j)                              /* code was empty, so */
        code[0] = '\0';                        /* keep strncat happy */

      strncat (code, line, i);                 /* append line to code */
      code[i + j] = '\n';                      /* append '\n' to code */
      code[i + j + 1] = '\0';

      src = Py_CompileString (code, "<stdin>", Py_single_input);

      if (NULL != src)                         /* compiled just fine - */
      {
        if (ps1  == prompt ||                  /* ">>> " or */
            '\n' == code[i + j - 1])           /* "... " and double '\n' */
        {                                               /* so execute it */
          dum = PyEval_EvalCode (src, glb, loc);
          Py_XDECREF (dum);
          Py_XDECREF (src);
          free (code);
          code = NULL;
          if (PyErr_Occurred ())
            PyErr_Print ();
          prompt = ps1;
        }
      }                                        /* syntax error or E_EOF? */
      else if (PyErr_ExceptionMatches (PyExc_SyntaxError))
      {
        PyErr_Fetch (&exc, &val, &trb);        /* clears exception! */

        if (PyArg_ParseTuple (val, "sO", &msg, &obj) &&
            !strcmp (msg, "unexpected EOF while parsing")) /* E_EOF */
        {
          Py_XDECREF (exc);
          Py_XDECREF (val);
          Py_XDECREF (trb);
          prompt = ps2;
        }
        else                                   /* some other syntax error */
        {
          PyErr_Restore (exc, val, trb);
          PyErr_Print ();
          free (code);
          code = NULL;
          prompt = ps1;
        }
      }
      else                                     /* some non-syntax error */
      {
        PyErr_Print ();
        free (code);
        code = NULL;
        prompt = ps1;
      }

      free (line);
    }
  }

  Py_XDECREF(glb);
  Py_XDECREF(loc);
  Py_Finalize();
  exit(0);
}

未定義の g++ シンボル __builtin_new や __pure_virtual を見つけるにはどうしますか?

g++ モジュールを動的にロードするには、Python を再コンパイルし、それを g++ で再リンク (Python Modules Makefile 内の LINKCC を変更) し、拡張を g++ でリンク (例えば g++ -shared -o mymodule.so mymodule.o) しなければなりません。

メソッドのいくつかは C で、その他は Python で実装されたオブジェクトクラスを (継承などで) 作ることはできますか?

はい、 intlistdict などのビルトインクラスから継承できます。

The Boost Python Library (BPL, http://www.boost.org/libs/python/doc/index.html) を使えば、これを C++ からできます。 (すなわち、BPL を使って C++ で書かれた拡張クラスを継承できます).