Python 打包準則
32 位 – CLR – CMake – Cross – DKMS – Eclipse – Electron – Font – 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-modulename
進行命名。如果軟件包與 Python 某個生態系統(例如 pip 或 tox) 關係密切,請添加相應前綴。對於其他 Python 程序來説, 使用程序名就足夠了。無論如何, 包的名字應該完全使用小寫。
這同樣適用於 Python 2,只需要修改前綴為 python2-
(如果必須)。
帶版本的軟件包[編輯 | 編輯原始碼]
如果你需要添加一個帶版本的軟件包,請使用這種格式: python-模塊名-版本
, 比如 python-colorama-0.2.5
。這樣,Python 的依賴colorama==0.2.5
就會成為名為 python-colorama-0.2.5
的 Arch 軟件包。
運行架構[編輯 | 編輯原始碼]
參見 PKGBUILD#arch。
包含C擴展的Python包依賴於體系結構。否則,它很可能是與架構無關的。
使用setuptools構建的包使用 setup.py 中的 ext_modules 關鍵字定義其C擴展。
原始碼[編輯 | 編輯原始碼]
PyPI 中像是這種格式 https://pypi.python.org/packages/source/${_name:0:1}/${_name}/${_name}-${pkgver}.tar.gz
<footnote> 的 URL 已經在 2016 年靜默失效了, 而新的格式需要一種只有從 PyPI 網站才能取得的不可預測的哈希碼[1][失效連結 2021-05-17 ⓘ]。
在下游維護者向 PyPI 維護者抱怨了這個問題 [2][失效連結 2021-05-17 ⓘ] 之後, 一種新的穩定的格式出現了 [3][失效連結 2021-05-17 ⓘ]: PKGBUILD#source source=()
數值現在需要使用如下 URL 模板:
請注意,我們使用了自定義的 $_name
而不是 $pkgname
,因為 Python 包通常都命名成這樣: python-$_name
。
- 源碼包:
https://files.pythonhosted.org/packages/source/${_name::1}/$_name/$_name-$pkgver.tar.gz
- 雙版本 wheel 包 (Python 2 和 Python 3 都兼容)
https://files.pythonhosted.org/packages/py2.py3/${_name::1}/$_name/${_name//-/_}-$pkgver-py2.py3-none-any.whl
(Python 2 and Python 3 都兼容)
- python3 wheel 包 (僅兼容Python 3)
https://files.pythonhosted.org/packages/py3/${_name::1}/$_name/${_name//-/_}-$pkgver-py3-none-any.whl
(僅兼容Python 3)
請注意,發行版名稱可以包含破折號,而其在wheel文件名中的表示形式則不能(它們被轉換為下劃線)。
- 特定架構的 wheel 包
- 對於特定架構的 wheel 包,可以使用
source_x86_64=('...')
來表示 x86_64 架構的下載地址。與此同時可以使用_py=py36
來避免重複書寫 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 或者 easy_install[失效連結 2021-05-17 ⓘ], 這些工具就像是能從在線源 (通常是 PyPI, Python 包索引 (Python Package Index) ) 獲取源文件的軟件包管理器一樣,而且還能跟蹤相關的文件狀態。 (如需這兩個工具的詳細比較,請參見 pip vs easy_install).
然而,為了從 PKGBUILD
中管理Python包,需要將Python包「安裝」到臨時位置
$pkgdir/usr/lib/python<Python version>/site-packages/$pkgname
對於使用standard metadata在 pyproject.toml
中指定其構建後端的Python包,這可以使用python-build包和python-installer包最容易地實現。舊的包可能無法指定它們使用setuptools,並且只提供必須手動調用的 setup.py
。
基於標準(PEP 517)的安裝方式[編輯 | 編輯原始碼]
當從上游提供的源tarball構建時,上游依賴於git為項目派生版本字符串,在構建wheel之前,需要將工具特定的環境變量設置為 $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 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
數組中是不鼓勵的,因為跳過build
不有利於從原始碼構建,並且只應該在從源碼構建不是可行的選擇時使用(例如,僅帶有wheel原始碼的包,因此不能從原始碼構建)。python-…-git
),請在您的 prepare
函數中包含命令 git -C "${srcdir}/${pkgname}" clean -dfx
。這將沿着其他構建工件一起刪除舊的.whl
文件,並有助於防止將來出現問題。另見setuptools和Poetry的上游問題。distutils 或 setuptools[編輯 | 編輯原始碼]
如果找不到 pyproject.toml
或者它沒有包含 [build-system]
表,這意味着項目使用的是舊的遺留格式,它使用了調用setuptools或distutils的 setup.py文件。請注意,雖然 distutils 包含在Python的standardlib中,但安裝setuptools 意味着您使用的是distutils 的補丁版本。
makedepends=('python-setuptools') # unless it only requires distutils 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()
函數中運行的構建步驟的不必要嘗試(如果是這種情況)
如果setuptools未安裝,有些軟件包會嘗試使用setuptools失敗,然後使用distutils進行安裝。在這種情況下,setuptools應該作為 makedepends
添加,這樣得到的Python元數據會更好。
如果一個包需要 setuptools 來構建,因為它包含了可執行文件(distutils不支持),但只導入了distutils,那麼構建時會發出如下警吿,表示生成的包會被破壞(它將不包含可執行文件):
/usr/lib/python3.8/distutils/dist.py:274: UserWarning: Unknown distribution option: 'entry_points' warnings.warn(msg)
應報吿上游錯誤。要解決這個問題,可以使用未記錄的setuptools功能:
# 使用 distutils 失敗 python setup.py build # 使用 setuptools 有效 python -m setuptools.launch setup.py 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
來運行測試套件,因為它被明確設計為在 tox
運行時測試從PyPI下載的可重複配置,並且不測試包將安裝的版本。這完全違背了具有檢查功能的目的。大多數提供測試套件的Python項目都使用nosetests或pytest (分別由python-nose包 和 python-pytest包提供)來運行測試,並在包含測試套件的文件或目錄的名稱中使用 test
。一般來説,簡單地運行 nosetests
或者 pytest
就足以運行測試套件。
check(){ cd $_name-$pkgver # 使用 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])))') # nosetests PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-$python_version" nosetests # pytest PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-$python_version" pytest }
有些項目提供了 setup.py
入口點來運行測試。這對 pytest
和 nosetests
都有效。
check(){ cd $_name-$pkgver # nosetests python setup.py nosetests # pytest - 需要安裝 python-pytest-runner python setup.py pytest }
提示和技巧[編輯 | 編輯原始碼]
在PyPI上發現分離的PGP簽名[編輯 | 編輯原始碼]
如果給定Python sdist tarball的分離PGP簽名存在,則應使用它們來驗證tarball。但是,簽名文件不會直接顯示在pypi.org上任何給定項目的文件下載部分。要發現sdist tarball及其潛在的簽名文件,可以使用此服務來獲得每個項目的概述: https://pypi.debian.net/
對於python-requests包, 請見 https://pypi.debian.net/requests.
使用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包。如果你遇到這種情況,你可以通過向軟件包項目提交一個問題來幫助他們解決這個問題,例如像這樣。