針對下游套件作者#
本文件旨在說明一些關於撰寫依賴於 NumPy 的套件的最佳實務做法。
了解 NumPy 的版本控制和 API/ABI 穩定性#
NumPy 使用標準、PEP 440 相容的版本控制方案:major.minor.bugfix
。主版本發行極為罕見,如果發生,則很可能表示 ABI 變更。NumPy 1.xx 版本從 2006 年發行到 2023 年;2024 年初的 NumPy 2.0 是第一個變更 ABI 的版本(在次要版本中,針對邊角案例可能發生過幾次較小的 ABI 變更)。次要版本定期發行,通常每 6 個月一次。次要版本包含新功能、棄用和移除先前已棄用的程式碼。錯誤修正版本發行頻率更高;它們不包含任何新功能或棄用。
重要的是要知道,NumPy 像 Python 本身和大多數其他著名的科學 Python 專案一樣,不使用語意化版本控制。相反地,向後不相容的 API 變更需要至少在兩個版本中發出棄用警告。如需更多詳細資訊,請參閱 NEP 23 — 向後相容性和棄用政策。
NumPy 具有 Python API 和 C API。C API 可以直接使用,或透過 Cython、f2py 或其他此類工具使用。如果您的套件使用 C API,則 NumPy 的 ABI(應用程式二進位介面)穩定性非常重要。NumPy 的 ABI 是向前相容但不向後相容的。這表示:針對給定目標版本的 NumPy C API 編譯的二進位檔,仍然可以在較新的 NumPy 版本上正確執行,但不能在較舊的版本上執行。
針對 NumPy 主要分支或預發行版本進行測試#
對於依賴於 NumPy 的大型、積極維護的套件,我們建議在 CI 中針對 NumPy 的開發版本進行測試。為了簡化此操作,每夜建置版本以 wheel 形式在 https://anaconda.org/scientific-python-nightly-wheels/ 提供。範例安裝命令
pip install -U --pre --only-binary :all: -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple numpy
這有助於偵測 NumPy 中需要在下一個 NumPy 版本發行之前修復的迴歸錯誤。此外,我們建議在此 CI 作業中,針對警告(全部警告或至少 DeprecationWarning
和 FutureWarning
)引發錯誤。這會讓您及早警告 NumPy 中的變更,以便調整您的程式碼。
如果您想針對最新的 NumPy 每夜建置版本測試您自己的 wheel 建置,並且您正在使用 cibuildwheel
,您可能需要在您的 CI 設定檔中加入類似這樣的內容
CIBW_ENVIRONMENT: "PIP_PRE=1 PIP_EXTRA_INDEX_URL=https://pypi.anaconda.org/scientific-python-nightly-wheels/simple"
新增對 NumPy 的依賴#
建置時期依賴#
注意
在 NumPy 1.25 之前,NumPy C-API 預設情況下並未以向後相容的方式公開。這表示當使用早於 1.25 的 NumPy 版本進行編譯時,您必須使用您希望支援的最舊版本進行編譯。這可以使用 oldest-supported-numpy 來完成。請參閱 NumPy 1.24 文件。
如果套件直接使用 NumPy C API,或者它使用其他依賴於它的工具(如 Cython 或 Pythran),則 NumPy 是該套件的建置時期依賴。
預設情況下,NumPy 將公開一個 API,該 API 向後相容於支援當前最舊相容 Python 版本的 NumPy 最舊版本。NumPy 1.25.0 支援 Python 3.9 及更高版本,而 NumPy 1.19 是第一個支援 Python 3.9 的版本。因此,我們保證,當使用預設值時,NumPy 1.25 將公開一個與 NumPy 1.19 相容的 C-API。(確切版本在 NumPy 內部標頭檔中設定)。
NumPy 也向前相容於所有次要版本,但主版本將需要重新編譯(請參閱稍後關於 NumPy 2.0 的特定建議)。
預設行為可以自訂,例如透過新增
#define NPY_TARGET_VERSION NPY_1_22_API_VERSION
在每個需要 NumPy C-API 的擴充模組中,在包含任何 NumPy 標頭(或等效的 -D
編譯器旗標)之前。如果您需要使用新加入的 API,但會犧牲與舊版本的相容性,這主要很有用。
如果由於某些原因,您希望預設情況下針對當前安裝的 NumPy 版本進行編譯,您可以新增
#ifndef NPY_TARGET_VERSION
#define NPY_TARGET_VERSION NPY_API_VERSION
#endif
這允許使用者透過 -DNPY_TARGET_VERSION
覆寫預設值。此定義對於每個擴充模組(使用 import_array()
)必須一致,並且也適用於 umath 模組。
當您針對 NumPy 進行編譯時,您應該將適當的版本限制新增到您的 pyproject.toml
中(請參閱 PEP 517)。因為您的擴充功能將不相容於 NumPy 的新主版本,並且可能不相容於非常舊的版本。
關於 conda-forge 套件,請參閱此處。
目前,通常就像包含以下內容一樣容易
host:
- numpy
run:
- {{ pin_compatible('numpy') }}
執行時期依賴和版本範圍#
NumPy 本身和許多核心科學 Python 套件已就放棄支援舊版 Python 和 NumPy 版本的時間表達成共識:NEP29。我們建議所有依賴於 NumPy 的套件都遵循 NEP 29 中的建議。
對於執行時期依賴,請在使用 setup.py
中的 install_requires
指定版本界限(假設您使用 numpy.distutils
或 setuptools
進行建置)。
大多數依賴於 NumPy 的函式庫都不需要設定版本上限:NumPy 非常謹慎地維護向後相容性。
話雖如此,如果您是 (a) 保證頻繁發行的專案,(b) 使用 NumPy API 表面的很大一部分,以及 (c) 擔心 NumPy 中的變更可能會破壞您的程式碼,您可以設定 <MAJOR.MINOR + N
的版本上限,其中 N 不小於 3,而 MAJOR.MINOR
是 NumPy 的當前版本 [*]。如果您使用 NumPy C API(直接或透過 Cython),您也可以釘選目前的主版本以防止 ABI 損壞。請注意,設定 NumPy 的版本上限可能會 影響您的函式庫與其他較新套件一同安裝的能力。
NumPy 2.0 的特定建議#
NumPy 2.0 是一個 ABI 破壞性版本,但它確實包含對建置可在 2.0 和 1.xx 版本上運作的 wheel 的支援。重要的是要了解
當您在建置時期使用 NumPy 1.xx 版本為您的套件建置 wheel 時,這些 wheel 將無法與 NumPy 2.0 搭配使用。
當您在建置時期使用 NumPy 2.x 版本為您的套件建置 wheel 時,這些 wheel 將可與 NumPy 1.xx 搭配使用。
首次保證 NumPy 2.0 的 ABI 穩定之時,將是發行 2.0 的第一個候選版本(即 2.0.0rc1)之時。我們關於處理您對 NumPy 的依賴的建議如下
在您套件的主要(開發)分支中,請勿新增任何限制。
如果您依賴 NumPy C API(例如透過在 C/C++ 中直接使用,或透過使用 NumPy 的 Cython 程式碼),請在您套件的發行版本/發行分支的依賴中繼資料中新增
numpy<2.0
要求。在 numpy2.0.0rc1
發行且您可以針對該版本之前,請執行此操作。理由:NumPy C ABI 將在 2.0 中變更,因此任何依賴於 NumPy 的已編譯擴充模組都將損壞;它們需要重新編譯。如果您依賴 NumPy Python API 的大型 API 表面,也請考慮在您的中繼資料中新增相同的
numpy<2.0
要求,直到您確定您的程式碼已針對 2.0 中的變更進行更新(即,當您已測試過程式碼可針對2.0.0rc1
運作時)。理由:我們將進行重大的 API 清理,許多別名和已棄用/不建議使用的物件將被移除(例如,請參閱 NumPy 2.0 遷移指南 和 NEP 52 — NumPy 2.0 的 Python API 清理),因此,除非您僅使用現代/建議的功能和物件,否則您的程式碼可能至少需要進行一些調整。計劃在第一個 NumPy 2.0 候選版本發行後不久(可能在 2024 年 2 月 1 日左右)發行您自己的依賴於
numpy
的套件。理由:在那個時間點,您可以發行將與 2.0 和 1.X 版本一起運作的套件,因此您自己的最終使用者將不會看到太多/任何中斷(您希望pip install mypackage
在 NumPy 2.0 發行的當天繼續運作)。一旦
2.0.0rc1
可用,您就可以按照下面概述的方式調整您在pyproject.toml
中的中繼資料。
有兩種情況:您需要保持與 numpy 1.xx 的相容性,同時也支援 2.0,或者您可以為您套件的新版本放棄 numpy 1.xx 支援,並僅支援 >=2.0。後者比較簡單,但對您的使用者來說可能更具限制性。在這種情況下,只需將 numpy>=2.0
(或 numpy>=2.0.0rc1
)新增到您的建置和執行時期需求中,您就可以開始了。我們現在將重點關注「保持與 1.xx 和 2.x 的相容性」,這稍微複雜一些。
想要支援 NumPy 1.23.5 及更高版本的套件使用 NumPy C API(透過 C/Cython/等等)的範例:
[build-system]
build-backend = ...
requires = [
# Note for packagers: this constraint is specific to wheels
# for PyPI; it is also supported to build against 1.xx still.
# If you do so, please ensure to include a `numpy<2.0`
# runtime requirement for those binary packages.
"numpy>=2.0.0rc1",
...
]
[project]
dependencies = [
"numpy>=1.23.5",
]
我們建議您至少有一個 CI 作業,該作業透過 wheel 進行建置/安裝,然後針對套件支援的最舊 numpy 版本執行測試。例如
- name: Build wheel via wheel, then install it
run: |
python -m build # This will pull in numpy 2.0 in an isolated env
python -m pip install dist/*.whl
- name: Test against oldest supported numpy version
run: |
python -m pip install numpy==1.23.5
# now run test suite
上述方法僅在 NumPy 2.0 在 PyPI 上可用時才有效。如果您想針對 NumPy 2.0-dev wheel 進行測試,您必須使用 numpy 每夜建置版本(請參閱上方本節)或從原始碼建置 numpy。