他のアプリケーションへの Python の埋め込み
******************************************

前章では、 Python を拡張する方法、すなわち C 関数のライブラリを Python
に結びつけて機能を拡張する方法について述べました。同じようなことを別の
方法でも実行できます: それは、自分の C/C++ アプリケーションに Python
を埋め込んで機能を強化する、というものです。埋め込みを行うことで、アプ
リケーションの何らかの機能を C や C++ の代わりに Python で実装できるよ
うになります。埋め込みは多くの用途で利用できます; ユーザが Python でス
クリプトを書き、アプリケーションを自分好みに仕立てられるようにする、と
いうのがその一例です。プログラマが、特定の機能を Python でより楽に書け
る場合に自分自身のために埋め込みを行うこともできます。

Python の埋め込みは Python の拡張と似ていますが、全く同じというわけで
はありません。その違いは、Python を拡張した場合にはアプリケーションの
メインプログラムは依然として Python インタプリタである一方、 Python を
組み込みんだ場合には、メインプログラムには Python が関係しない --- そ
の代わりに、アプリケーションのある一部分が時折 Python インタプリタを呼
び出して何らかの Python コードを実行させる --- かもしれない、というこ
とです。

従って、 Python の埋め込みを行う場合、自作のメインプログラムを提供しな
ければなりません。メインプログラムがやらなければならないことの一つに、
Python インタプリタの初期化があります。とにかく少なくとも関数
"Py_Initialize()" を呼び出さねばなりません。オプションとして、Python
側にコマンドライン引数を渡すために関数呼び出しを行います。その後、アプ
リケーションのどこでもインタプリタを呼び出せるようになります。

インタプリタを呼び出すには、異なるいくつかの方法があります: Python 文
が入った文字列を "PyRun_SimpleString()" に渡す、 stdio ファイルポイン
タとファイル名 (これはエラーメッセージ内でコードを識別するためだけのも
のです) を "PyRun_SimpleFile()" に渡す、といった具合です。これまでの各
章で説明した低水準の操作を呼び出して、Python オブジェクトを構築したり
使用したりもできます。

参考:

  Python/C API リファレンスマニュアル
     Python C インターフェースの詳細はこのマニュアルに書かれています。
     必要な情報の大部分はここにあるはずです。


高水準の埋め込み
================

Python の埋め込みの最も簡単な形式は、超高水準インターフェースの利用で
す。このインターフェースは、アプリケーションとやり取りする必要がない
Python スクリプトを実行するためのものです。例えばこれは、一つのファイ
ル上で何らかの操作を実現するのに利用できます。

   #define PY_SSIZE_T_CLEAN
   #include <Python.h>

   int
   main(int argc, char *argv[])
   {
       PyStatus status;
       PyConfig config;
       PyConfig_InitPythonConfig(&config);

       /* 省略可能だが推奨 */
       status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]);
       if (PyStatus_Exception(status)) {
           goto exception;
       }

       status = Py_InitializeFromConfig(&config);
       if (PyStatus_Exception(status)) {
           goto exception;
       }
       PyConfig_Clear(&config);

       PyRun_SimpleString("from time import time,ctime\n"
                          "print('Today is', ctime(time()))\n");
       if (Py_FinalizeEx() < 0) {
           exit(120);
       }
       return 0;

     exception:
        PyConfig_Clear(&config);
        Py_ExitStatusException(status);
   }

注釈:

  "#define PY_SSIZE_T_CLEAN" は、いくつかの API では "int" の代わりに
  "Py_ssize_t" が使われるべきであることを示すために使われていました。
  これは Python 3.13 以降では不要となりましたが、後方互換性のために維
  持されています。このマクロの説明は 文字列とバッファ にあります。

Setting "PyConfig.program_name" should be called before
"Py_InitializeFromConfig()" to inform the interpreter about paths to
Python run-time libraries.  Next, the Python interpreter is
initialized with "Py_Initialize()", followed by the execution of a
hard-coded Python script that prints the date and time.  Afterwards,
the "Py_FinalizeEx()" call shuts the interpreter down, followed by the
end of the program.  In a real program, you may want to get the Python
script from another source, perhaps a text-editor routine, a file, or
a database.  Getting the Python code from a file can better be done by
using the "PyRun_SimpleFile()" function, which saves you the trouble
of allocating memory space and loading the file contents.


超高水準の埋め込みから踏み出す: 概要
====================================

高水準インターフェースは、断片的な Python コードをアプリケーションから
実行できるようにしてくれますが、アプリケーションと Python コードの間で
のデータのやり取りは、控えめに言っても煩わしいものです。データのやり取
りをしたいなら、より低水準のインターフェース呼び出しを利用しなくてはな
りません。より多く C コードを書かねばならない代わりに、ほぼ何でもでき
るようになります。

Python の拡張と埋め込みは、趣旨こそ違え、同じ作業であるということに注
意せねばなりません。これまでの章で議論してきたトピックのほとんどが埋め
込みでもあてはまります。これを示すために、 Python から C への拡張を行
うコードが実際には何をするか考えてみましょう:

1. データ値を Python から C に変換する。

2. 変換された値を使って C ルーチンの関数呼び出しを行い、

3. 呼び出しで得られたデータ値 C から Python に変換する。

Python を埋め込む場合には、インターフェースコードが行う作業は以下のよ
うになります:

1. データ値を C から Python に変換する。

2. 変換された値を使って Python インターフェースルーチンの関数呼び出し
   を行い、

3. 呼び出しで得られたデータ値 Python から C に変換する。

一見して分かるように、データ変換のステップは、言語間でデータを転送する
方向が変わったのに合わせて単に入れ替えただけです。唯一の相違点は、デー
タ変換の間にあるルーチンです。拡張を行う際には C ルーチンを呼び出しま
すが、埋め込みの際には Python ルーチンを呼び出します。

この章では、Python から C へ、そしてその逆へとデータを変換する方法につ
いては議論しません。また、正しい参照の使い方やエラーの扱い方についてす
でに理解しているものと仮定します。これらの側面についてはインタプリタの
拡張と何ら変わるところがないので、必要な情報については以前の章を参照で
きます。


純粋な埋め込み
==============

最初に例示するプログラムは、Python スクリプト内の関数を実行するための
ものです。超高水準インターフェースに関する節で挙げた例と同様に、Python
インタプリタはアプリケーションと直接やりとりはしません (が、次の節でや
りとりするよう変更します)。

Python スクリプト内で定義されている関数を実行するためのコードは以下の
ようになります:

   #define PY_SSIZE_T_CLEAN
   #include <Python.h>

   int
   main(int argc, char *argv[])
   {
       PyObject *pName, *pModule, *pFunc;
       PyObject *pArgs, *pValue;
       int i;

       if (argc < 3) {
           fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
           return 1;
       }

       Py_Initialize();
       pName = PyUnicode_DecodeFSDefault(argv[1]);
       /* pName のエラーチェックは省略 */

       pModule = PyImport_Import(pName);
       Py_DECREF(pName);

       if (pModule != NULL) {
           pFunc = PyObject_GetAttrString(pModule, argv[2]);
           /* pFunc は新しい参照 */

           if (pFunc && PyCallable_Check(pFunc)) {
               pArgs = PyTuple_New(argc - 3);
               for (i = 0; i < argc - 3; ++i) {
                   pValue = PyLong_FromLong(atoi(argv[i + 3]));
                   if (!pValue) {
                       Py_DECREF(pArgs);
                       Py_DECREF(pModule);
                       fprintf(stderr, "Cannot convert argument\n");
                       return 1;
                   }
                   /* ここで pValue の参照が盗まれる: */
                   PyTuple_SetItem(pArgs, i, pValue);
               }
               pValue = PyObject_CallObject(pFunc, pArgs);
               Py_DECREF(pArgs);
               if (pValue != NULL) {
                   printf("Result of call: %ld\n", PyLong_AsLong(pValue));
                   Py_DECREF(pValue);
               }
               else {
                   Py_DECREF(pFunc);
                   Py_DECREF(pModule);
                   PyErr_Print();
                   fprintf(stderr,"Call failed\n");
                   return 1;
               }
           }
           else {
               if (PyErr_Occurred())
                   PyErr_Print();
               fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
           }
           Py_XDECREF(pFunc);
           Py_DECREF(pModule);
       }
       else {
           PyErr_Print();
           fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
           return 1;
       }
       if (Py_FinalizeEx() < 0) {
           return 120;
       }
       return 0;
   }

このコードは "argv[1]" を使って Python スクリプトをロードし、
"argv[2]" 内に指定された名前の関数を呼び出します。関数の整数引数は
"argv" 配列中の他の値になります。このプログラムを コンパイルしてリンク
し (できた実行可能形式を **call** と呼びましょう)、以下のような Python
スクリプトを実行することにします:

   def multiply(a,b):
       print("Will compute", a, "times", b)
       c = 0
       for i in range(0, a):
           c = c + b
       return c

実行結果は以下のようになるはずです:

   $ call multiply multiply 3 2
   Will compute 3 times 2
   Result of call: 6

機能の割に大きなプログラムですが、コードの大部分は Python と C の間の
データ変換と、エラーの報告のためのものです。 Python の埋め込みという観
点から重要なのは、次の箇所から始まる処理です。

   Py_Initialize();
   pName = PyUnicode_DecodeFSDefault(argv[1]);
   /* pName のエラーチェックは省略 */
   pModule = PyImport_Import(pName);

インタープリタを初期化した後、"PyImport_Import()" でスクリプトをロード
します。 この関数には Python の文字列を引数として渡す必要があるため、
データ変換関数 "PyUnicode_DecodeFSDefault()" で作成します。

   pFunc = PyObject_GetAttrString(pModule, argv[2]);
   /* pFunc は新しい参照 */

   if (pFunc && PyCallable_Check(pFunc)) {
       ...
   }
   Py_XDECREF(pFunc);

ひとたびスクリプトが読み込まれると、 "PyObject_GetAttrString()" を使っ
て必要な名前を取得できます。名前がスクリプト中に存在し、取得したオブジ
ェクトが呼び出し可能オブジェクトであれば、このオブジェクトが関数である
と考えて差し支えないでしょう。そこでプログラムは定石どおりに引数のタプ
ル構築に進みます。その後、Python 関数を以下のコードで呼び出します:

   pValue = PyObject_CallObject(pFunc, pArgs);

関数が処理を戻す際、 "pValue" は "NULL" になるか、関数の戻り値への参照
が入っています。値を調べた後には忘れずに参照を解放してください。


埋め込まれた Python の拡張
==========================

ここまでは、埋め込み Python インタプリタはアプリケーション本体の機能に
アクセスする手段がありませんでした。 Python API を使うと、埋め込みイン
タプリタを拡張することでアプリケーション本体へのアクセスを可能にします
。つまり、アプリケーションで提供されているルーチンを使って、埋め込みイ
ンタプリタを拡張するのです。複雑なことのように思えますが、それほどひど
いわけではありません。さしあたって、アプリケーションが Python インタプ
リタを起動したということをちょっと忘れてみてください。その代わり、アプ
リケーションがサブルーチンの集まりで、あたかも普通の Python 拡張モジュ
ールを書くかのように、Python から各ルーチンにアクセスできるようにする
グルー(glue, 糊) コードを書くと考えてください。例えば以下のようにです:

   static int numargs=0;

   /* アプリケーションのコマンドライン引数の数を返す */
   static PyObject*
   emb_numargs(PyObject *self, PyObject *args)
   {
       if(!PyArg_ParseTuple(args, ":numargs"))
           return NULL;
       return PyLong_FromLong(numargs);
   }

   static PyMethodDef emb_module_methods[] = {
       {"numargs", emb_numargs, METH_VARARGS,
        "プロセスが受け取った引数の数を返します。"},
       {NULL, NULL, 0, NULL}
   };

   static struct PyModuleDef emb_module = {
       .m_base = PyModuleDef_HEAD_INIT,
       .m_name = "emb",
       .m_size = 0,
       .m_methods = emb_module_methods,
   };

   static PyObject*
   PyInit_emb(void)
   {
       return PyModuleDef_Init(&emb_module);
   }

上のコードを "main()" 関数のすぐ上に挿入します。また、以下の二つの文を
"Py_Initialize()" の呼び出しの前に挿入します:

   numargs = argc;
   PyImport_AppendInittab("emb", &PyInit_emb);

この 2 行は、"numargs" 変数を初期化し、埋め込まれた Python インタープ
リタから "emb.numargs()" 関数を利用できるようにします。 この拡張によっ
て、Python スクリプトで次のような処理ができます。

   import emb
   print("Number of arguments", emb.numargs())

実際のアプリケーションでは、こうしたメソッドでアプリケーション内の API
を Python に公開することになります。


C++による Python の埋め込み
===========================

C++ プログラム中にも Python を埋め込めます; 厳密に言うと、どうやって埋
め込むかは使っているC++ 処理系の詳細に依存します; 一般的には、メインプ
ログラムをC++で書き、C++ コンパイラを使ってプログラムをコンパイル・リ
ンクする必要があるでしょう。 Python 自体を C++でコンパイルしなおす必要
はありません。


Unix 系システムにおけるコンパイルとリンク
=========================================

Python インタープリタをアプリケーションに組み込むために、コンパイラと
リンカーに渡す適切なフラグを見つけるのは、必ずしも簡単ではありません。
特に、Python にリンクされた C の動的拡張 (".so" ファイル) として実装さ
れたライブラリモジュールを、Python がロードする必要があるためです。

必要なコンパイル・リンクのオプションを知るために、
"python*X.Y*-config" スクリプトが使えます(これは Python インストール時
に生成されたもので、 "python3-config" スクリプトも利用出来るかもしれま
せん)。このスクリプトにはオプションが多くありますが、直接的に有用なの
はこれでしょう:

* "pythonX.Y-config --cflags" は推奨のコンパイルオプションを出力します
  :

     $ /opt/bin/python3.11-config --cflags
     -I/opt/include/python3.11 -I/opt/include/python3.11 -Wsign-compare  -DNDEBUG -g -fwrapv -O3 -Wall

* "pythonX.Y-config --ldflags --embed" は推奨のリンクオプションを出力
  します:

     $ /opt/bin/python3.11-config --ldflags --embed
     -L/opt/lib/python3.11/config-3.11-x86_64-linux-gnu -L/opt/lib -lpython3.11 -lpthread -ldl  -lutil -lm

注釈:

  複数 Python バージョン共存(とりわけシステムの Python とあなた自身で
  ビルドした Python)での混乱を避けるために、上での例のように
  "python*X.Y*-config" は絶対パスで起動したほうが良いです。

この手順でうまくいかない場合は、システムの動的リンクに関するドキュメン
トを読むか、Python の "Makefile" とコンパイルオプションを調べる、ある
いはその両方を行う必要があります。 すべての Unix 系プラットフォームで
動作する保証はありませんが、バグ報告 は歓迎します。 Makefile の場所は
"sysconfig.get_makefile_filename()" で調べられます。 この場合、
"sysconfig" モジュールを使うと、組み合わせたい設定値をプログラムで抽出
できて便利です。 例えば、次のように使います。

   >>> import sysconfig
   >>> sysconfig.get_config_var('LIBS')
   '-lpthread -ldl  -lutil'
   >>> sysconfig.get_config_var('LINKFORSHARED')
   '-Xlinker -export-dynamic'
