C API の安定性¶
Python の C API は、 Backwards Compatibility Policy, PEP 387 によって保護されています。C API はマイナーリリースごとに変更されますが(例えば 3.9 から 3.10 へ)、ほとんどの変更はソース互換であり、通常は新しい API を追加するだけです。既存の API を変更したり、 API を削除したりするのは、非推奨期間の後か、深刻な問題を修正する場合のみです。
CPython の Application Binary Interface(ABI)は、マイナーリリース間で前方および後方互換性があります(これらが同じ方法でコンパイルされた場合。以下の プラットフォームで考慮すべき点 を参照)。そのため、Python 3.10.0 用にコンパイルされたコードは 3.10.8 で動作し、その逆も同様ですが、 3.9.x と 3.10.x では別々にコンパイルする必要があります。
_Py_InternalState のようにアンダースコアが前につくものは、パッチリリースでも予告なく変更される可能性があるプライベートAPIです。
安定 ABI (Stable Appliction Binary Interface)¶
Python 3.2 では、 Python の C API のサブセットである Limited API が導入されました。Limited API のみを使用する拡張機能は、一度コンパイルすれば、複数のバージョンの Python で動作させることができます。Limited API の内容は listed below です。
これを可能にするために、 Python は Stable ABI を提供しています。これは、Python 3.x のバージョン間で互換性を保つシンボルの集合です。Stable ABI には、 Limited API で公開されているシンボルだけでなく、 Limited API の古いバージョンをサポートするために必要な関数など、他のシンボルも含まれています。
(簡単にするため、このドキュメントでは 拡張機能 について話していますが、 Limited API と Stable ABI は、 API のすべての使用、たとえばPythonの組み込みに対して同じように動作します)
-
Py_LIMITED_API¶ このマクロを
Python.hをインクルードする前に定義することで、 Limited API のみを使用することを選択し、 Limited API バージョンを選択することができます。Py_LIMITED_APIをPY_VERSION_HEXの値として定義し、拡張機能がサポートする Python の最低バージョンに対応します。この拡張機能は、指定されたバージョン以降のすべての Python 3 リリースで再コンパイルせずに動作し、そのバージョンまでに導入された Limited API を使用することができます。PY_VERSION_HEXマクロを直接使うのではなく、将来の Python のバージョンでコンパイルするときの安定性のために、最小のマイナーバージョン(例えば、 Python 3.10なら0x030A0000)をハードコードします。また、
Py_LIMITED_APIを3に定義することができます。これは0x03020000(Python 3.2, Limited API が導入されたバージョン)と同じように動作します。
Windows では、 Stable ABI を使用する拡張機能は、 python39.dll のようなバージョン固有のライブラリではなく、 python3.dll に対してリンクする必要があります。
いくつかのプラットフォームでは、 Python は abi3 タグで名付けられた共有ライブラリファイルを探して読み込みます(例: mymodule.abi3.so)。このような拡張モジュールが Stable ABI に適合しているかどうかはチェックされません。ユーザー(またはそのパッケージングツール)は、たとえば 3.10+ Limited API でビルドされた拡張モジュールが、それ以下のバージョンの Python にインストールされないことを確認する必要があります。
Stable ABI に含まれるすべての関数は、マクロとしてだけでなく、 Python の共有ライブラリの関数として存在します。そのため、Cプリプロセッサを使用しない言語から使用することができます。
APIスコープとパフォーマンスの制限¶
Limited API の目標は、フル C API で可能なすべてのことを実現することですが、おそらく性能上の制約があります。
例えば、 PyList_GetItem() は利用可能ですが、その “unsafe” マクロの変種 PyList_GET_ITEM() は利用できません。このマクロは、リストオブジェクトのバージョン固有の実装の詳細に依存することができるため、より高速に処理することができます。
Py_LIMITED_API を定義しないと、いくつかの C API 関数がインライン化されたり、マクロに置き換わったりします。Py_LIMITED_API を定義すると、このインライン化が無効になり、 Python のデータ構造が改善されても安定した動作が可能になりますが、性能が低下する可能性があります。
Py_LIMITED_API の定義を省くことで、 Limited API 拡張をバージョン固有の ABI でコンパイルすることが可能です。これにより、その Python のバージョンでパフォーマンスを向上させることができますが、互換性は制限されます。Py_LIMITED_API でコンパイルすると、バージョンに依存しない拡張機能が利用できない場合、例えば、次期 Python バージョンのプレリリースに対応した拡張モジュールを配布することができるようになります。
制限付きAPIの注意点¶
Py_LIMITED_API でコンパイルすることは、 Limited API や Stable ABI に準拠したコードであることを完全に保証するものではないことに注意してください。Py_LIMITED_API は定義だけをカバーしていますが、 API には期待されるセマンティクスのような他の課題も含まれています。
Py_LIMITED_API が防げない問題の1つは、 Python の下位バージョンでは無効な引数を持つ関数を呼び出すことです。例えば、引数に NULL を受け取る関数を考えてみましょう。Python 3.9 では NULL はデフォルトの挙動を選択しますが、Python 3.8 ではこの引数は直接使用され、 NULL の参照外れを起こしクラッシュします。同様の引数は、構造体のフィールドに対しても機能します。
もう一つの問題は、一部の構造体フィールドがLimited APIの一部であるにもかかわらず、 Py_LIMITED_API が定義されたときに現在非表示になっていないことです。
これらの理由から、私たちは拡張モジュールがサポートする すべての マイナーな Python バージョンでテストすること、そしてできれば 最も低い バージョンでビルドすることを推奨します。
また、使用するすべての API のドキュメントを確認し、それが明示的に Limited API の一部であるかどうかをチェックすることをお勧めします。Py_LIMITED_API が定義されていても、技術的な理由で(あるいはバグとして意図せず)いくつかのプライベート宣言が公開されることがあります。
Python 3.8 で Py_LIMITED_API をコンパイルすると、その拡張モジュールは Python 3.12 で動作しますが、必ずしも Python 3.12 で コンパイル できるとは限らないことに注意してください。特に、Limited API の一部は、 Stable ABI が安定している限り、非推奨で削除されるかもしれません。
プラットフォームで考慮すべき点¶
ABI の安定性は Python だけでなく、使用するコンパイラ、低レベルのライブラリ、コンパイラのオプションにも依存します。安定した ABI の目的では、これらの詳細が「プラットフォーム」を定義します。通常、 OS の種類とプロセッサのアーキテクチャに依存します。
特定のプラットフォーム上のすべての Python バージョンが安定版 ABI を破壊しない方法でビルドされていることを保証するのは、 Python の各特定配布者の責任です。これは python.org や多くのサードパーティーの配布元からの Windows と macOS のリリースの場合です。
限定版APIの内容¶
現在、Limited APIには以下の項目が含まれています:
PyBaseObject_TypePyBool_TypePyByteArrayIter_TypePyBytesIter_TypePyBytes_DecodeEscape()PyBytes_Repr()PyCFunction_Call()PyCFunction_GetFlags()PyCFunction_GetFunction()PyCFunction_GetSelf()PyCFunction_New()PyCFunction_NewEx()PyCFunction_TypePyCMethod_New()PyCapsule_TypePyClassMethodDescr_TypePyDictItems_TypePyDictIterItem_TypePyDictIterKey_TypePyDictIterValue_TypePyDictKeys_TypePyDictProxy_TypePyDictRevIterItem_TypePyDictRevIterKey_TypePyDictRevIterValue_TypePyDictValues_TypePyEllipsis_TypePyEnum_TypePyErr_Display()PyErr_ProgramText()PyEval_CallFunction()PyEval_CallMethod()PyEval_CallObjectWithKeywords()PyExc_ArithmeticErrorPyExc_AssertionErrorPyExc_AttributeErrorPyExc_BaseExceptionPyExc_BlockingIOErrorPyExc_BrokenPipeErrorPyExc_BufferErrorPyExc_BytesWarningPyExc_ChildProcessErrorPyExc_ConnectionAbortedErrorPyExc_ConnectionErrorPyExc_ConnectionRefusedErrorPyExc_ConnectionResetErrorPyExc_DeprecationWarningPyExc_EOFErrorPyExc_EncodingWarningPyExc_EnvironmentErrorPyExc_ExceptionPyExc_FileExistsErrorPyExc_FileNotFoundErrorPyExc_FloatingPointErrorPyExc_FutureWarningPyExc_GeneratorExitPyExc_IOErrorPyExc_ImportErrorPyExc_ImportWarningPyExc_IndentationErrorPyExc_IndexErrorPyExc_InterruptedErrorPyExc_IsADirectoryErrorPyExc_KeyErrorPyExc_KeyboardInterruptPyExc_LookupErrorPyExc_MemoryErrorPyExc_ModuleNotFoundErrorPyExc_NameErrorPyExc_NotADirectoryErrorPyExc_NotImplementedErrorPyExc_OSErrorPyExc_OverflowErrorPyExc_PendingDeprecationWarningPyExc_PermissionErrorPyExc_ProcessLookupErrorPyExc_RecursionErrorPyExc_ReferenceErrorPyExc_ResourceWarningPyExc_RuntimeErrorPyExc_RuntimeWarningPyExc_StopAsyncIterationPyExc_StopIterationPyExc_SyntaxErrorPyExc_SyntaxWarningPyExc_SystemErrorPyExc_SystemExitPyExc_TabErrorPyExc_TimeoutErrorPyExc_TypeErrorPyExc_UnboundLocalErrorPyExc_UnicodeDecodeErrorPyExc_UnicodeEncodeErrorPyExc_UnicodeErrorPyExc_UnicodeTranslateErrorPyExc_UnicodeWarningPyExc_UserWarningPyExc_ValueErrorPyExc_WarningPyExc_WindowsErrorPyExc_ZeroDivisionErrorPyExceptionClass_Name()PyFilter_TypePyGILState_STATEPyGetSetDescr_TypePyListIter_TypePyListRevIter_TypePyLongRangeIter_TypePyLong_GetInfo()PyMap_TypePyMemberDescr_TypePyMemoryView_TypePyMethodDescr_TypePyModuleDef_BasePyModuleDef_TypePyOS_InterruptOccurred()PyOS_mystricmp()PyOS_mystrnicmp()PyOS_sighandler_tPyOS_strtol()PyOS_strtoul()PyObject_DelItemString()PyObject_SelfIter()PyRangeIter_TypePyRange_TypePyReversed_TypePySequence_In()PySetIter_TypePySuper_TypePySys_HasWarnOptions()PyThread_GetInfo()PyThread_acquire_lock()PyThread_acquire_lock_timed()PyThread_allocate_lock()PyThread_exit_thread()PyThread_free_lock()PyThread_get_stacksize()PyThread_get_thread_ident()PyThread_get_thread_native_id()PyThread_init_thread()PyThread_release_lock()PyThread_set_stacksize()PyThread_start_new_thread()PyTraceBack_Here()PyTraceBack_Print()PyTraceBack_TypePyTupleIter_TypePyUnicodeIter_TypePyUnicode_Append()PyUnicode_AppendAndDel()PyUnicode_AsDecodedObject()PyUnicode_AsDecodedUnicode()PyUnicode_AsEncodedObject()PyUnicode_AsEncodedUnicode()PyUnicode_BuildEncodingMap()PyUnicode_DecodeCodePageStateful()PyUnicode_FromOrdinal()PyUnicode_GetDefaultEncoding()PyUnicode_InternImmortal()PyUnicode_Partition()PyUnicode_RPartition()PyUnicode_RSplit()PyUnicode_Resize()PyVarObject.ob_basePyWeakReferencePyWrapperDescr_TypePyZip_TypePy_FileSystemDefaultEncodeErrorsPy_FileSystemDefaultEncodingPy_GetRecursionLimit()Py_HasFileSystemDefaultEncodingPy_MakePendingCalls()Py_SetRecursionLimit()Py_UTF8ModePy_intptr_tPy_uintptr_tgettersetterssizessizeargfuncssizessizeobjargprocsymtable