C API 穩定性¶
除非有另外記錄於文件,Python 的 C API 被包含在向後相容性策略 PEP 387 中。大多數改動都是相容於原始碼的(通常只會增加新的 API)。更改現有 API 或刪除 API 僅在棄用期後或修復嚴重問題時進行。
CPython 的應用程式二進位介面 (Application Binary Interface, ABI) 在次要版本中是向前和向後相容的(如果它們以相同的方式編譯;請參閱下面的平台注意事項)。因此,為 Python 3.10.0 編譯的程式碼將能夠在 3.10.8 上運行,反之亦然,但 3.9.x 和 3.11.x 就需要分別編譯。
C API 有兩層級,有不同的穩定性期望:
不穩定 API,可能會在次要版本中發生變化,而沒有棄用階段。會在名稱中以
PyUnstable
前綴來標記。受限 API,在多個次要版本之間相容。當有定義
Py_LIMITED_API
時,只有這個子集會從Python.h
公開。
下面將更詳細地討論這些內容。
帶有底線前綴的名稱是私有 API (private API),像是 _Py_InternalState
,即使在補丁版本 (patch release) 中也可能被更改,不會另行通知。如果你需要使用這個 API,可以聯繫 CPython 開發者 並針對你的使用方法來討論是否新增公開的 API。
不穩定的 C API¶
任何以 PyUnstable
前綴命名的 API 都會公開 CPython 實作細節,並可能在每個次要版本中進行更改(例如從 3.9 到 3.10),而不會出現任何棄用警告。但是它不會在錯誤修復發布版本中發生變化(例如從 3.10.0 到 3.10.1)。
它通常用於專門的低階工具,例如偵錯器。
使用此 API 的專案應該要遵循 CPython 開發細節,並花費額外的力氣來針對這些變動來做調整。
穩定的應用程式二進位介面¶
為簡單起見,本文件討論擴充 (extension),但受限 API 和穩定 ABI 在所有 API 使用方式中都以相同的方式運作 -- 例如在嵌入式 Python (embedding Python) 中。
受限 C API¶
Python 3.2 引入了受限 API (Limited API),它是 Python C API 的一個子集。僅使用受限 API 的擴充可以只編譯一次就使用於多個版本的 Python。受限 API 的內容列在下方。
-
Py_LIMITED_API¶
在包含
Python.h
之前定義此巨集以選擇只使用受限 API,並挑選受限 API 版本。將
Py_LIMITED_API
定義為對應於你的擴充有支援的最低 Python 版本的PY_VERSION_HEX
值。該擴充無需重新編譯即可與從指定版本開始的所有 Python 3 版本一起使用,並且可以使用過去版本有引入的受限 API。與其直接使用
PY_VERSION_HEX
巨集,不如寫死 (hardcode) 最小次要版本(例如代表 Python 3.10 的0x030A0000
),以便在使用未來的 Python 版本進行編譯時仍保持穩定性。你還可以將
Py_LIMITED_API
定義為3
,這與0x03020000
(Python 3.2,引入了受限 API 的版本)相同。
穩定 ABI¶
為了實現它,Python 提供了一個穩定 ABI (Stable ABI):一組將在各個 Python 3.x 版本之間保持相容的符號。
穩定 ABI 被包含在受限 API 中開放的符號,但也包含其他符號 - 例如,支援舊版受限 API 所必需的函式。
在 Windows 上,使用穩定 ABI 的擴充應該連接到 python3.dll
而不是特定版本的函式庫,例如 python39.dll
。
在某些平台上,Python 將查找並加載以 abi3
標籤命名的共享函式庫檔案(例如 mymodule.abi3.so
)。它不檢查此類擴充是否符合穩定的 ABI。確保的責任在使用者(或者打包工具)身上,例如使用 3.10+ 受限 API 建置的擴充不會為較低版本的 Python 所安裝。
穩定 ABI 中的所有函式都作為函式存在於 Python 的共享函式庫中,而不僅是作為巨集。這使得它們可被用於不使用 C 預處理器 (preprocessor) 的語言。
受限 API 範圍和性能¶
受限 API 的目標是允許使用完整的 C API 進行所有可能的操作,但可能會降低性能。
例如,雖然 PyList_GetItem()
可用,但它的「不安全」巨集變體 PyList_GET_ITEM()
為不可用。巨集運行可以更快,因為它可以依賴 list 物件的特定版本實作細節。
如果沒有定義 Py_LIMITED_API
,一些 C API 函式將被嵌入或被替換為巨集。定義 Py_LIMITED_API
會禁用嵌入,從而隨著 Python 資料結構的改進而提高穩定性,但可能會降低性能。
通過省略 Py_LIMITED_API
定義,可以使用特定版本的 ABI 編譯受限 API 擴充。這可以提高該 Python 版本的性能,但會限制相容性。使用 Py_LIMITED_API
編譯將產生一個擴充,可以在特定版本的擴充不可用的地方發布 — 例如,用於即將發布的 Python 版本的預發布版本 (prerelease)。
受限 API 注意事項¶
請注意,使用 Py_LIMITED_API
進行編譯不完全保證程式碼符合受限 API 或穩定 ABI。Py_LIMITED_API
僅涵蓋定義,但 API 還包括其他議題,例如預期的語義 (semantic)。
Py_LIMITED_API
無法防範的一個問題是使用在較低 Python 版本中無效的引數來呼叫函式。例如一個開始接受 NULL
作為引數的函式。在 Python 3.9 中,NULL
現在代表選擇預設行為,但在 Python 3.8 中,引數將被直接使用,導致 NULL
取消參照 (dereference) 且崩潰 (crash)。類似的引數適用於結構 (struct) 的欄位。
另一個問題是,當有定義 Py_LIMITED_API
時,一些結構欄位目前不會被隱藏,即使它們是受限 API 的一部分。
出於這些原因,我們建議要以它支援的所有次要 Python 版本來測試擴充,並且最好使用最低版本進行建置。
我們也建議要查看所有使用過的 API 的文件,檢查它是否明確屬於受限 API。即使有定義 Py_LIMITED_API
,一些私有聲明也會因為技術原因(或者甚至是無意地,例如臭蟲)而被公開出來。
另請注意,受限 API 不一定是穩定的:在 Python 3.8 中使用 Py_LIMITED_API
進行編譯意味著擴充將能以 Python 3.12 運行,但不一定能以 Python 3.12 編譯。特別是如果穩定 ABI 保持穩定,部分受限 API 可能會被棄用和刪除。
平台注意事項¶
ABI 穩定性不僅取決於 Python,還取決於使用的編譯器、低階函式庫和編譯器選項。出於穩定 ABI 的目的,這些細節定義了一個「平台」。它們通常取決於作業系統種類和處理器架構
每個特定的 Python 發布者都有責任確保特定平台上的所有 Python 版本都以不破壞穩定 ABI 的方式建置。python.org
和許多第三方發布者發布的 Windows 和 macOS 版本就是這種情況。
受限 API 的內容¶
目前,受限 API 包括以下項目:
PyBaseObject_Type
PyByteArrayIter_Type
PyBytesIter_Type
PyBytes_DecodeEscape()
PyBytes_Repr()
PyCFunction_Call()
PyCFunction_GetFlags()
PyCFunction_GetFunction()
PyCFunction_GetSelf()
PyCFunction_Type
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_BaseExceptionGroup
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
PyMap_Type
PyMemberDescr_Type
PyMemoryView_Type
PyMethodDescr_Type
PyModuleDef_Base
PyModuleDef_Type
PyOS_InterruptOccurred()
PyOS_mystricmp()
PyOS_mystrnicmp()
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_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
ssizessizeargfunc
ssizessizeobjargproc
symtable