Python 打包準則
32 位 – CLR – CMake – DKMS – Eclipse – Electron – Free Pascal – GNOME – Go – Haskell – Java – 交叉編譯工具 – KDE – Lisp – Meson – MinGW – 內核模塊 – Node.js – Nonfree – OCaml – Perl – PHP – Python – R – Ruby – Rust – VCS – Web – Wine – 字體
本文檔描述了如何基於標準 PKGBUILD 來為 Python 程序進行打包。
包名[編輯 | 編輯原始碼]
對於 Python 3 的庫模塊包或與 Python 生態緊密耦合的應用程式(如 pip 和 tox),使用 python-模塊名或應用程式名
命名。對於其他應用程式,則僅使用程序原名。
架構[編輯 | 編輯原始碼]
參見 PKGBUILD#arch。
包含 C 擴展的 Python 軟件包與架構相關。反之則通常無關於架構。
使用 setuptools 構建的軟件包通過在 setup.py
中定義 ext_modules
關鍵字來聲明其使用的 C 擴展。
原始碼[編輯 | 編輯原始碼]
PyPI 網站提供的下載連結包含不可預測的哈希值,每次更新軟件包時需重新從 PyPI 獲取。這導致其不適合直接用於 PKGBUILD。PyPI 提供了以下穩定方案:PKGBUILD#source source=()
數組應使用以下 URL 模板:
- 原始碼包:
https://files.pythonhosted.org/packages/source/${_name::1}/${_name//-/_}/${_name//-/_}-$pkgver.tar.gz
- 純 Python wheel 包
https://files.pythonhosted.org/packages/py2.py3/${_name::1}/$_name/${_name//-/_}-$pkgver-py2.py3-none-any.whl
(同時兼容 Python 2 和 Python 3)https://files.pythonhosted.org/packages/py3/${_name::1}/$_name/${_name//-/_}-$pkgver-py3-none-any.whl
(僅兼容 Python 3)- 注意分發包名稱(distribution name,在 PyPI 註冊的名稱)可能包含連字符,但在 wheel 文件名中需轉換為下劃線。
- 架構相關的 wheel 包
- 可通過追加下劃線和架構名添加額外數組(如
source_x86_64=('...')
)。另可使用_py=cp310
避免重複 Python 版本: https://files.pythonhosted.org/packages/$_py/${_name::1}/$_name/${_name//-/_}-$pkgver-$_py-${_py}m-manylinux1_x86_64.whl
注意使用自定義變量 _name
代替 pkgname
,因 Python 軟件包通常帶有 python-
前綴。該變量可通過以下方式定義:
_name=${pkgname#python-}
安裝方式[編輯 | 編輯原始碼]
Python 中的包通常使用 Python 專用的包管理器(如 pip)進行安裝,這類工具會從在線倉庫(通常是 PyPI——Python 軟件包索引)獲取軟件包並追蹤相關文件。
然而,在使用 PKGBUILD
管理 Python 軟件包時,需要將 Python 包「安裝」到臨時位置 $pkgdir/usr/lib/python<Python 版本>/site-packages/$pkgname
。
對於使用標準元數據(standard metadata)在 pyproject.toml
中指定構建後端(build backend)的 Python 包,使用 python-build包 和 python-installer包 是完成上述「安裝」過程最容易的方式。
舊版本軟件包可能未聲明使用 setuptools,僅提供需手動調用的 setup.py
。
depends
數組中,否則相關依賴不會被安裝。基於標準(PEP 517)的安裝方式[編輯 | 編輯原始碼]
$pkgver
:
- python-flit-core包、python-hatch-vcs包 或 python-setuptools-scm包:
SETUPTOOLS_SCM_PRETEND_VERSION
- python-pbr包:
PBR_VERSION
- python-pdm-backend包:
PDM_BUILD_SCM_VERSION
基於標準的工作流程很簡單:使用 python-build包 構建一個 wheel,再使用 python-installer包 將其安裝到 $pkgdir
:
makedepends
。所有倉庫可用的構建後端均屬於 python-build-backend包組。請檢查項目的 pyproject.toml
文件中 build-system.build-backend
配置項的值,該值即為此項目實際使用的構建後端,若未配置則默認使用 python-setuptools包。makedepends=(python-build python-installer python-wheel) build() { cd $_name-$pkgver python -m build --wheel --no-isolation } package() { cd $_name-$pkgver python -m installer --destdir="$pkgdir" dist/*.whl }
其中:
--wheel
表示僅構建 wheel 文件,不生成原始碼分發包--no-isolation
表示使用系統已安裝的包(含depends
中聲明的依賴)進行構建,默認會創建隔離的虛擬環境執行構建--destdir="$pkgdir"
可防止直接嘗試安裝到宿主系統(而非軟件包目錄),避免權限錯誤--compile-bytecode=……
或--no-compile-bytecode
可傳遞給installer
,但默認選擇合理值,通常無需手動指定
build
階段而直接將 .whl
文件放入 source
數組,應優先採用原始碼構建。僅在無法通過源碼構建時(例如項目僅提供 wheel 源)才應使用此方式。python-……-git
),請在 prepare
函數中添加 git -C "${srcdir}/${pkgname}" clean -dfx
命令。此操作可清除舊版 wheel 及其他構建產物,避免後續問題。另請參閱 setuptools 與 Poetry 的上游 issue。使用 setuptools 或 distutils 的安裝方式[編輯 | 編輯原始碼]
若項目中不存在 pyproject.toml
文件,或該文件未包含 [build-system]
表,則說明項目使用遺留格式,即通過 setup.py 文件調用 setuptools 或 distutils.core 的 setup
函數。
此類軟件包通常仍可使用上述 python-build包 和 python-installer包 方法構建安裝(推薦方式),但需在 makedepends
中添加 python-setuptools包。
通過直接運行 setup.py 的舊方式(見下)仍可構建安裝,但此方法已棄用,僅建議在 PEP 517 兼容方式因故不可用時作為備選方案。
需注意,使用此方法構建時會在 package
階段輸出以下警告:
SetuptoolsDeprecationWarning: setup.py install is deprecated.
另請注意,Python 3.12 及後續版本的標準庫中已移除 distutils。對於仍使用 setup.py 的項目,必須在 makedepends
中添加 python-setuptools包(該軟件包提供了自帶的 distutils 實現)。
makedepends=('python-setuptools') build() { cd $_name-$pkgver python setup.py build } package() { cd $_name-$pkgver python setup.py install --root="$pkgdir" --optimize=1 }
其中:
--root="$pkgdir"
作用同前文--destdir
--optimize=1
預生成優化字節碼文件(「.opt-1.pyc」)而非在宿主系統按需生成,以便由 pacman 追蹤這些文件並保證權限一致--skip-build
跳過不必要的重複構建步驟(如build()
函數已執行過的構建過程)
若軟件包使用 python-setuptools-scm包,構建時可能報錯如下:
LookupError: setuptools-scm was unable to detect version for /build/python-jsonschema/src/jsonschema-3.2.0. Make sure you're either building from a fully intact git repository or PyPI tarballs. Most other sources (such as GitHub's tarballs, a git checkout without the .git folder) don't contain the necessary metadata and will not work.
需將 SETUPTOOLS_SCM_PRETEND_VERSION
環境變量設為 $pkgver
以完成構建:
export SETUPTOOLS_SCM_PRETEND_VERSION=$pkgver
測試[編輯 | 編輯原始碼]
- 避免使用
tox
運行測試套件,因其明確設計用於依據從 PyPI 下載的可復現的、固定的配置環境測試,而不會測試軟件包實際安裝後的版本,若使用則將使 check 函數失去意義 - 避免在 checkdepends 中添加用於代碼檢查(lint)、覆蓋率或類型檢查的 pytest 插件(詳見 #禁用 pytest 附加選項章節)。這些工具會增加引導難度且非發行版打包必需,因其不驗證功能正確性
大多數提供測試套件的 Python 項目使用 unittest 運行器、nosetests 或 pytest(分別由 python包、python-nose包 和 python-pytest包 提供),通過檢測特定格式的文件/目錄名和類/函數名(如含 test
的命名)自動發現測試套件。通常直接運行 nosetests
或 pytest
即可執行測試套件。
check(){ cd $_name-$pkgver # 使用內置的 unittest python -m unittest discover -vs . # 使用 nosetests nosetests # 使用 pytest pytest }
若存在已編譯的 C 擴展模塊,測試時需通過 $PYTHONPATH
指定與當前 Python 主次版本匹配的構建路徑以正確加載模塊。
check(){ cd $_name-$pkgver local python_version=$(python -c 'import sys; print("".join(map(str, sys.version_info[:2])))') # 使用內置的 unittest PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-$python_version" python -m unittest discover -vs . # 使用 nosetests PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-$python_version" nosetests # 使用 pytest PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-$python_version" pytest }
提示和技巧[編輯 | 編輯原始碼]
獲取 Python 版本號[編輯 | 編輯原始碼]
在準備、構建、測試或安裝過程中,如需引用系統 Python 的主次版本號(如 3.9
或 3.10
),請勿硬編碼版本號,而應通過 Python 解釋器動態獲取並存儲至局部變量:
check(){ local python_version=$(python -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') ... }
獲取 site-packages 路徑[編輯 | 編輯原始碼]
在構建、測試或安裝過程中,如需引用系統的 site-packages
目錄,請勿硬編碼路徑,而應通過 Python 解釋器動態獲取並存儲至局部變量:
check(){ local site_packages=$(python -c "import site; print(site.getsitepackages()[0])") ... }
避免在 site-package 中放置測試目錄[編輯 | 編輯原始碼]
避免在 site-packages/
下直接安裝名為 tests/
的目錄(即 /usr/lib/pythonX.Y/site-packages/tests/
),否則可能引發軟件包衝突。某些使用 setuptools 的 Python 項目存在錯誤配置,可能將其測試目錄作為頂層 Python 包包含。若發現此類問題,請向項目提交 issue 請求修復,例如此 issue。
禁用 pytest 附加選項[編輯 | 編輯原始碼]
運行 pytest 時,通常應禁用額外插件。特別是代碼檢查(lint)和覆蓋率(coverage)插件在打包場景中可能適得其反,因其行為變更可能導致測試失敗。
推薦通過命令行覆蓋配置選項(而非修改 pytest 配置文件)來禁用 addopts
等參數,以減少維護成本。要清空所有附加選項,可使用:
pytest -o addopts=""
修復 meson-python 的可復現性問題[編輯 | 編輯原始碼]
使用 meson-python包 作為 PEP 517 構建後端時,其隨機生成的構建目錄路徑會導致可復現性問題。可通過 -Cbuild-dir
參數硬編碼構建目錄來規避此問題:
python -m build --wheel --no-isolation -Cbuild-dir=build