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_Type
PyBool_Type
PyByteArrayIter_Type
PyBytesIter_Type
PyBytes_DecodeEscape()
PyBytes_Repr()
PyCFunction_Call()
PyCFunction_GetFlags()
PyCFunction_GetFunction()
PyCFunction_GetSelf()
PyCFunction_New()
PyCFunction_NewEx()
PyCFunction_Type
PyCMethod_New()
PyCapsule_Type
PyClassMethodDescr_Type
PyDictItems_Type
PyDictIterItem_Type
PyDictIterKey_Type
PyDictIterValue_Type
PyDictKeys_Type
PyDictProxy_Type
PyDictRevIterItem_Type
PyDictRevIterKey_Type
PyDictRevIterValue_Type
PyDictValues_Type
PyEllipsis_Type
PyEnum_Type
PyErr_Display()
PyErr_ProgramText()
PyEval_CallFunction()
PyEval_CallMethod()
PyEval_CallObjectWithKeywords()
PyExc_ArithmeticError
PyExc_AssertionError
PyExc_AttributeError
PyExc_BaseException
PyExc_BlockingIOError
PyExc_BrokenPipeError
PyExc_BufferError
PyExc_BytesWarning
PyExc_ChildProcessError
PyExc_ConnectionAbortedError
PyExc_ConnectionError
PyExc_ConnectionRefusedError
PyExc_ConnectionResetError
PyExc_DeprecationWarning
PyExc_EOFError
PyExc_EncodingWarning
PyExc_EnvironmentError
PyExc_Exception
PyExc_FileExistsError
PyExc_FileNotFoundError
PyExc_FloatingPointError
PyExc_FutureWarning
PyExc_GeneratorExit
PyExc_IOError
PyExc_ImportError
PyExc_ImportWarning
PyExc_IndentationError
PyExc_IndexError
PyExc_InterruptedError
PyExc_IsADirectoryError
PyExc_KeyError
PyExc_KeyboardInterrupt
PyExc_LookupError
PyExc_MemoryError
PyExc_ModuleNotFoundError
PyExc_NameError
PyExc_NotADirectoryError
PyExc_NotImplementedError
PyExc_OSError
PyExc_OverflowError
PyExc_PendingDeprecationWarning
PyExc_PermissionError
PyExc_ProcessLookupError
PyExc_RecursionError
PyExc_ReferenceError
PyExc_ResourceWarning
PyExc_RuntimeError
PyExc_RuntimeWarning
PyExc_StopAsyncIteration
PyExc_StopIteration
PyExc_SyntaxError
PyExc_SyntaxWarning
PyExc_SystemError
PyExc_SystemExit
PyExc_TabError
PyExc_TimeoutError
PyExc_TypeError
PyExc_UnboundLocalError
PyExc_UnicodeDecodeError
PyExc_UnicodeEncodeError
PyExc_UnicodeError
PyExc_UnicodeTranslateError
PyExc_UnicodeWarning
PyExc_UserWarning
PyExc_ValueError
PyExc_Warning
PyExc_WindowsError
PyExc_ZeroDivisionError
PyExceptionClass_Name()
PyFilter_Type
PyGILState_STATE
PyGetSetDescr_Type
PyListIter_Type
PyListRevIter_Type
PyLongRangeIter_Type
PyLong_GetInfo()
PyMap_Type
PyMemberDescr_Type
PyMemoryView_Type
PyMethodDescr_Type
PyModuleDef_Base
PyModuleDef_Type
PyOS_InterruptOccurred()
PyOS_mystricmp()
PyOS_mystrnicmp()
PyOS_sighandler_t
PyOS_strtol()
PyOS_strtoul()
PyObject_DelItemString()
PyObject_SelfIter()
PyRangeIter_Type
PyRange_Type
PyReversed_Type
PySequence_In()
PySetIter_Type
PySuper_Type
PySys_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_Type
PyTupleIter_Type
PyUnicodeIter_Type
PyUnicode_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_base
PyWeakReference
PyWrapperDescr_Type
PyZip_Type
Py_FileSystemDefaultEncodeErrors
Py_FileSystemDefaultEncoding
Py_GetRecursionLimit()
Py_HasFileSystemDefaultEncoding
Py_MakePendingCalls()
Py_SetRecursionLimit()
Py_UTF8Mode
Py_intptr_t
Py_uintptr_t
getter
setter
ssizessizeargfunc
ssizessizeobjargproc
symtable