拡張モジュールの定義
********************

CPython の C 拡張は共有ライブラリです (例えば Linux の ".so" ファイル
や Windows の ".pyd" DLL)。 Python プロセスにロードできる必要があり (
例えば互換性のあるコンパイラ設定でコンパイルされていること)、*エクスポ
ートフック* 関数 (または旧形式の 初期化関数) をエクスポートします。

デフォルトで (つまり、 "importlib.machinery.ExtensionFileLoader" によ
って) インポート可能であるためには、共有ライブラリは "sys.path" 上で利
用可能である必要があり、モジュール名と
"importlib.machinery.EXTENSION_SUFFIXES". に含まれる拡張子で命名する必
要があります。

注釈:

  拡張モジュールのビルド、パッケージ化および配布には、サードパーティの
  ツールが最適であり、このドキュメントの範囲外です。適切なツールの一つ
  は Setuptools であり、ドキュメントは
  https://setuptools.pypa.io/en/latest/setuptools.html にあります。


拡張モジュールのエクスポートフック
==================================

Added in version 3.15: "PyModExport_*<name>*" エクスポートフックのサポ
ートは Python 3.15 で追加されました。 従来のモジュール定義方法も引き続
き利用できます。 以前の Python バージョンに対応する場合は、PyInit 関数
の節または以前のバージョンのドキュメントを参照してください。

エクスポートフックは、次のシグネチャを持つエクスポートされた関数でなけ
ればなりません:

PySlot *PyModExport_modulename(void)

名前が ASCII 文字だけのモジュールでは、エクスポートフック の名前は
"PyModExport_*<name>*" とし、"<name>" をモジュール名に置き換える必要が
あります。

モジュール名に非 ASCII 文字が含まれる場合、エクスポートフックの名前は
代わりに "PyModExportU_*<name>*" とします ("U" に注意してください)。
"<name>" には、Python の *punycode* エンコーディングでエンコードし、ハ
イフンをアンダースコアに置き換えた名前を使います。 Python では次のよう
になります:

   def hook_name(name):
       try:
           suffix = b'_' + name.encode('ascii')
       except UnicodeEncodeError:
           suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')
       return b'PyModExport' + suffix

エクスポートフックは "PySlot" エントリーの配列を返します。 この配列は
スロット ID が "0" のエントリーで終端します。 これらのスロットで、モジ
ュールの作成方法と初期化方法を記述します。

この配列はインタープリタの終了まで有効で、変更されない必要があります。
通常は "static" な記憶域を使ってください。 動的な処理には
"Py_mod_create" スロットと "Py_mod_exec" スロットを使うことを推奨しま
す。

エクスポートフックは、例外を設定して "NULL" を返すことで失敗を通知でき
ます。

エクスポートフック関数の定義には、次の補助マクロを使うことを推奨します
:

PyMODEXPORT_FUNC
    * This macro is available when compiling for the Stable ABI (バー
   ジョン 3.15 より).*

   拡張モジュールのエクスポートフックを宣言します。 このマクロは次の処
   理を行います:

   * 戻り値の型として PySlot* を指定します。

   * プラットフォームが要求する特別なリンケージ宣言を追加します。

   * C++ では、関数を "extern "C"" として宣言します。

例えば "spam" というモジュールは、次のように定義します。

   PyABIInfo_VAR(abi_info);

   static PySlot spam_slots[] = {
       PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
       PySlot_STATIC_DATA(Py_mod_name, "spam"),
       PySlot_FUNC(Py_mod_init, spam_init_function),
       ...
       PySlot_END
   };

   PyMODEXPORT_FUNC
   PyModExport_spam(void)
   {
       return spam_slots;
   }

通常、モジュールの C ソースで定義する要素のうち、"static" でないのはエ
クスポートフックだけです。

フックの処理は短くしてください。 静的な配列を "return" する以外の処理
を行う場合は、いくつか注意点があります:

* Python C API を使う必要がある場合は、最初に "PyABIInfo_Check()" を呼
  び出すことを推奨します。 これにより、よくある ABI の不一致の場合にク
  ラッシュする代わりに例外を送出できます。

* エクスポートフックのコードは *GIL* に決して依存してはいけません。
  Python の *フリースレッドビルド* では、フックから戻った後でなければ
  "Py_mod_gil" スロット (またはその不在) を確認できないためです。

* 同様に、"Py_mod_multiple_interpreters" スロット (またはその不在) を
  確認するのはフックから戻った後なので、フックはどのサブインタープリタ
  でも呼び出される可能性があります。

例えば:

   PyMODEXPORT_FUNC
   PyModExport_modulename(void)
   {
      if (PyABIInfo_Check(&abi_info, "modulename") < 0) {
         /* ABI の不一致。送出された例外を調べることは安全ではありません。 */
         return NULL;
      }

      /* Python API はできるだけ使わず、GIL に依存しないこと */

      return modulename_slots;
   }

注釈:

  複数のエクスポートフックを定義すれば、1 つの共有ライブラリから複数の
  モジュールをエクスポートできます。 ただし、Python のインポート機構は
  ファイル名に対応する関数だけを探すため、これらをインポートするには、
  独自のインポーターか、適切な名前を付けた拡張ファイルのコピーまたはリ
  ンクが必要です。 詳細は **PEP 489** の Multiple modules in one
  library の節を参照してください。


多段階初期化
============

拡張モジュールの作成はいくつかの段階に分かれています:

* Python はエクスポートフックを探して呼び出し、モジュールの作成方法に
  関する情報を取得します。

* 実質的な処理を行うコードが実行される前に、Python はモジュールが対応
  する機能を確認し、環境を調整したり、互換性のない拡張モジュールのロー
  ドを拒否したりできます。 "Py_mod_abi"、"Py_mod_gil"、
  "Py_mod_multiple_interpreters" などのスロットがこの段階に影響します
  。

* 次に、デフォルトでは Python 自身がモジュールオブジェクトを作成します
  。 これは、オブジェクトを作成するときの "__new__()" の呼び出しに相当
  します。 この段階の処理は "Py_mod_create" スロットでオーバーライドで
  きます。

* Python は "__package__" や "__loader__" などのモジュール属性の初期値
  を設定し、モジュールオブジェクトを "sys.modules" に挿入します。

* その後、拡張モジュール固有の方法でモジュールオブジェクトを初期化しま
  す。 これはオブジェクト作成時の "__init__()" や、Python で書かれたモ
  ジュールのトップレベルのコード実行に相当します。 動作は
  "Py_mod_exec" スロットで指定します。

これを *多段階初期化* と呼び、初期化関数が完全に構築済みのモジュールを
返す従来の 一段階初期化 (現在もサポートされています) と区別します。

バージョン 3.5 で変更: 多段階初期化 (**PEP 489**) のサポートを追加しま
した。


複数のモジュールインスタンス
============================

デフォルトでは、拡張モジュールはシングルトンではありません。 例えば
"sys.modules" のエントリーを削除してモジュールを再インポートすると、新
しいモジュールオブジェクトを作成し、通常は新しいメソッドオブジェクトや
型オブジェクトを設定します。 古いモジュールは通常のガベージコレクショ
ンの対象になります。 これは純粋な Python モジュールと同じ動作です。

サブインタープリタ 内や、Python ランタイムの再初期化 ("Py_Finalize()"
と "Py_Initialize()") 後にも、追加のモジュールインスタンスが作成される
可能性があります。 これらの場合、モジュールインスタンス間で Python オ
ブジェクトを共有すると、クラッシュや未定義の動作を引き起こしやすくなり
ます。

このような問題を避けるには、拡張モジュールの各インスタンスを *分離* す
る必要があります。 あるインスタンスの変更が暗黙に他のインスタンスに影
響してはいけません。 また、Python オブジェクトへの参照を含む、モジュー
ルが所有するすべての状態は、特定のモジュールインスタンスだけのものでな
ければなりません。 詳細と実践的な手引きは 拡張モジュールを分離する を
参照してください。

これらの問題を避けるより簡単な方法は、初期化を繰り返すとエラーを送出す
る ことです。

すべてのモジュールは サブインタープリタ に対応するか、対応していないこ
とを明示的に通知することが期待されます。 通常は、上述したように分離を
行うか、初期化の繰り返しを禁止します。 "Py_mod_multiple_interpreters"
スロットで、モジュールをメインインタープリタに限定することもできます。


"PyInit" 関数
=============

バージョン 3.15 で ソフト非推奨 <Soft deprecated>: この機能に新しい機
能は追加されませんが、削除する予定はありません。

拡張モジュールは "PyModExport_modulename()" の代わりに、次のシグネチャ
を持つ旧形式の *初期化関数* を定義できます:

PyObject *PyInit_modulename(void)

名前は "PyInit_*<name>*" とし、"<name>" をモジュール名に置き換えます。
モジュール名に非 ASCII 文字が含まれる場合は、代わりに
"PyInitU_*<name>*" を使い、"<name>" を エクスポートフック と同じ方法 (
アンダースコアを使う Punycode) でエンコードします。

モジュールが "PyInit_*<name>*" と "PyModExport_*<name>*" の両方をエク
スポートする場合、"PyInit_*<name>*" 関数は無視されます。

"PyMODEXPORT_FUNC" と同様に、初期化関数の定義には補助マクロを使うこと
を推奨します:

PyMODINIT_FUNC

   拡張モジュールの初期化関数を宣言します。 このマクロは次の処理を行い
   ます:

   * 戻り値の型として PyObject* を指定します。

   * プラットフォームが要求する特別なリンケージ宣言を追加します。

   * C++ では、関数を "extern "C"" として宣言します。

通常、初期化関数 ("PyInit_modulename") は、"m_slots" が "NULL" でない
"PyModuleDef" インスタンスを返します。 これにより Python は 多段階初期
化 を利用できます。

返す前に、次の関数を使って "PyModuleDef" インスタンスを初期化する必要
があります:

PyObject *PyModuleDef_Init(PyModuleDef *def)
    * 次に属します: Stable ABI (バージョン 3.5 より).*

   モジュール定義を、自身の型と参照カウントを正しく示す、適切に初期化
   された Python オブジェクトにします。

   *def* を "PyObject*" にキャストして返します。 エラーが発生した場合
   は "NULL" を返します。

   モジュール初期化関数から "PyModuleDef" を返す前に、この関数を呼び出
   す必要があります。 他の場面では使わないでください。

   Python は "PyModuleDef" 構造体が静的に割り当てられていると仮定する
   ことに注意してください。 この関数は新しい参照または借用参照を返しま
   すが、その参照を解放してはいけません。

   Added in version 3.5.

例えば "spam" というモジュールは、次のように定義します。

   static struct PyModuleDef spam_module = {
       .m_base = PyModuleDef_HEAD_INIT,
       .m_name = "spam",
       ...
   };

   PyMODINIT_FUNC
   PyInit_spam(void)
   {
       return PyModuleDef_Init(&spam_module);
   }


従来の一段階初期化
------------------

バージョン 3.15 で ソフト非推奨 <Soft deprecated>: 一段階初期化は拡張
モジュールを初期化する従来の仕組みであり、既知の欠点や設計上の問題があ
ります。 拡張モジュールの作者には、代わりに多段階初期化を使うことを推
奨します。ただし、一段階初期化のサポートを削除する予定はありません。

一段階初期化では、旧形式の 初期化関数 ("PyInit_modulename") がモジュー
ルオブジェクトを作成し、内容を設定して返す必要があります。 通常は
"PyModule_Create()" と "PyModule_AddObjectRef()" などの関数を使います
。

一段階初期化は、次の点で デフォルトの方式 と異なります:

* "PyModuleDef.m_size" を -1 に設定した一段階初期化モジュールは「シン
  グルトン」です。 より正確には、シングルトンを *含みます*。

  このようなモジュールを初めて初期化すると、Python はモジュールの
  "__dict__" の内容 (通常はモジュールの関数と型) を保存します。

  それ以降のインポートでは、Python は初期化関数を再び呼び出しません。
  代わりに、新しい "__dict__" を持つ新しいモジュールオブジェクトを作成
  し、保存しておいた内容をコピーします。 モジュールと保存した内容は、
  次の (*module_name*, *file_name*) タプルで対応づけられます:

  * *module_name* はモジュールのドット区切りの完全な名前で、
    "ModuleSpec.name" (またはインポートを行う関数の同等の引数) から取
    得します。 初期化関数がモジュールの作成に使う名前 (例えば
    "PyModuleDef.m_name") は考慮しないことに注意してください。

  * *file_name* は拡張モジュールのファイル名です。 組み込みモジュール
    や、"PyImport_CreateModuleFromInitfunc()" または
    "PyImport_AppendInittab()" で追加したモジュールなど、ファイルから
    ロードしないモジュールでは設定されません。

  初回の初期化の一部として、Python はモジュールをその名前で
  "sys.modules" にも追加します。 例えば、関数 "sum" と例外クラス
  "error" を定義する一段階初期化モジュール "_testsinglephase" [1] を考
  えます:

     >>> import sys
     >>> import _testsinglephase as one
     >>> del sys.modules['_testsinglephase']
     >>> import _testsinglephase as two
     >>> one is two
     False
     >>> one.__dict__ is two.__dict__
     False
     >>> one.sum is two.sum
     True
     >>> one.error is two.error
     True

  厳密な動作は CPython の実装の詳細とみなしてください。

* "PyModuleDef.m_size" が非負の一段階初期化モジュールでは、"m_size ==
  -1" の場合と同じ (*module_name*, *file_name*) キーで、Python が初期
  化関数へのポインタをキャッシュする場合があります。

  それ以降のインポートで別の初期化関数を指定した場合 (例えば
  "PyImport_CreateModuleFromInitfunc()" を使った場合)、キャッシュ済み
  の関数か、指定した「新しい」関数のどちらかが呼び出される可能性があり
  ます。

  この場合も、厳密な動作は CPython の実装の詳細とみなしてください。

* "PyInit_modulename" が *spec* 引数を受け取らないことに対処するため、
  インポート機構の状態の一部を保存し、"PyInit_modulename" の呼び出し中
  に最初に作成された適切なモジュールに適用します。 具体的には、サブモ
  ジュールをインポートするとき、この仕組みがモジュール名の先頭に親パッ
  ケージ名を付加します。

  一段階初期化の "PyInit_modulename" 関数は、他のモジュールオブジェク
  トが作成される前に、できるだけ早く「自身の」モジュールオブジェクトを
  作成してください。

* 非 ASCII のモジュール名 ("PyInitU_modulename") はサポートされません
  。

* 一段階初期化モジュールは、"PyState_FindModule()" などのモジュール検
  索関数をサポートします。

* モジュールの "PyModuleDef.m_slots" は "NULL" でなければなりません。

[1] "_testsinglephase" は CPython 自身のテストスイートで使う内部モジュ
    ールです。 利用しているインストール環境には含まれていない場合もあ
    ります。
