Python 打包準則

出自 Arch Linux 中文维基

本文或本節需要翻譯。要貢獻翻譯,請訪問簡體中文翻譯團隊

附註: Many changes have happened on the English page since last translation.(在 Talk:Python 打包準則# 中討論)
Arch 打包準則

32 位CLRCMakeCrossDKMSEclipseElectronFontFree PascalGNOMEGoHaskellJavaKDEKernelLispMesonMinGWNode.jsNonfreeOCamlPerlPHPPythonRRubyRustVCSWebWine

本文檔描述了如何基於標準 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-}
注意: RFC0020默認使用上游提供的原始碼壓縮包,而不是PyPI提供的sdist壓縮包。

安裝方式[編輯 | 編輯原始碼]

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 metadatapyproject.toml 中指定其構建後端的Python包,這可以使用python-buildpython-installer最容易地實現。舊的包可能無法指定它們使用setuptools,並且只提供必須手動調用的 setup.py

基於標準(PEP 517)的安裝方式[編輯 | 編輯原始碼]

提示:

當從上游提供的源tarball構建時,上游依賴於git為項目派生版本字符串,在構建wheel之前,需要將工具特定的環境變量設置為 $pkgver

基於標準的工作流程很簡單:使用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原始碼的包,因此不能從原始碼構建)。
警告: 如果您的軟體包是一個VCS packagepython-…-git),請在您的 prepare 函數中包含命令 git -C "${srcdir}/${pkgname}" clean -dfx。這將沿著其他構建工件一起刪除舊的.whl文件,並有助於防止將來出現問題。另見setuptoolsPoetry的上游問題。

distutils 或 setuptools[編輯 | 編輯原始碼]

如果找不到 pyproject.toml 或者它沒有包含 [build-system] 表,這意味著項目使用的是舊的遺留格式,它使用了調用setuptoolsdistutilssetup.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-nosepython-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 入口點來運行測試。這對 pytestnosetests都有效。

check(){
    cd $_name-$pkgver

    # nosetests
    python setup.py nosetests

    # pytest - 需要安裝 python-pytest-runner
    python setup.py pytest
}

提示和技巧[編輯 | 編輯原始碼]

在PyPI上發現分離的PGP簽名[編輯 | 編輯原始碼]

警告:2023-05-23 PyPI I刪除了提供分離的 OpenPGP 簽名的功能

如果給定Python sdist tarball的分離PGP簽名存在,則應使用它們來驗證tarball。但是,簽名文件不會直接顯示在pypi.org上任何給定項目的文件下載部分。要發現sdist tarball及其潛在的簽名文件,可以使用此服務來獲得每個項目的概述: https://pypi.debian.net/

對於python-requests, 請見 https://pypi.debian.net/requests.

使用Python版本[編輯 | 編輯原始碼]

有時在準備、構建、測試或安裝過程中,需要參考系統的主要和次要Python版本(例如 3.93.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包。如果你遇到這種情況,你可以通過向軟體包項目提交一個問題來幫助他們解決這個問題,例如像 這樣