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穩定 ABIPy_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 包括以下項目: