跳转至内容

Python 包指南

来自 ArchWiki
Arch 软件包指南

32 位CLRCMake交叉编译DKMSEclipse 插件Electron字体Free PascalGNOMEGoHaskellJavaKDE内核模块LispMesonMinGWNode.js非免费应用OCamlPerlPHPPythonRRubyRust - 安全ShellVCSWeb 应用Wine

本文档涵盖了编写 PKGBUILD 以用于 Python 软件的标准和指南。

包命名

对于 Python 3 库模块,请使用 python-模块名。如果包提供一个与 Python 生态系统紧密相关的程序(例如 piptox),也请使用此前缀。对于其他应用程序,仅使用程序名。

注意 包名应全为小写。

Architecture

请参阅 PKGBUILD#arch

包含 C 扩展的 Python 包是架构相关的。否则,它极有可能是架构无关的。

使用 setuptools 构建的包在其 setup.py 文件中使用 ext_modules 关键字定义其 C 扩展。

Source(生效配置)

注意 根据 RFC0020,默认情况下使用上游提供的源码 tarball,而不是 PyPI 提供的 sdist tarball。

从 PyPI 网站链接的下载 URL 包含一个不可预测的哈希,每次更新包时都需要从 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)
请注意,发行名称可能包含连字符,而在 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 包通常使用特定语言的包管理器安装,例如 pip,它从在线存储库(通常是 PyPI,Python 包索引)获取包并跟踪相关文件。

然而,在 PKGBUILD 中管理 Python 包时,需要将 Python 包“安装”到临时位置 $pkgdir/usr/lib/python<Python 版本>/site-packages/$pkgname

对于使用 标准元数据pyproject.toml 中指定其构建后端 的 Python 包,最容易通过 python-buildpython-installer 来实现。旧包可能未能指定它们使用 setuptools,并且只提供一个需要手动调用的 setup.py

注意 包元数据中的依赖项必须在 depends 数组中定义,否则它们将不会被安装。

基于标准 (PEP 517)

提示 当从上游提供的源码 tarball 构建,并且上游依赖 git 来派生项目的版本字符串时,需要在构建 wheel 之前将工具特定的环境变量设置为 $pkgver

基于标准的工作流程很简单:使用 python-build 构建 wheel,并使用 python-installer 将其安装到 $pkgdir

注意 除了 python-buildpython-installer,您还需要将包使用的 构建后端 添加到 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" 可防止尝试直接安装到主机系统而不是打包到文件内,否则会导致权限错误。
  • 可以向 installer 传递 --compile-bytecode=...--no-compile-bytecode,但默认设置已经很合理,因此通常不需要。
注意 跳过 build 并将 .whl 文件放入 source 数组中是不推荐的,因为优先从源码构建。只有当后者不可行时才应使用(例如,包提供 wheel 源码,因此无法从源码构建)。
提示 如果您的包是 VCS 包 (python-…-git),请在您的 prepare 函数中包含命令 git -C "${srcdir}/${pkgname}" clean -dfx。这将删除过时的 wheel 文件以及其他构建产物,并有助于防止后续出现问题。另请参阅上游关于 setuptoolsPoetry 的问题。

setuptools 或 distutils

如果找不到 pyproject.toml 文件,或者它不包含 [build-system] 表,则意味着该项目使用的是旧的传统格式,其中项目提供一个 setup.py 文件,该文件会调用 setuptoolsdistutils.core 中的 setup 函数。

这些包通常也可以使用上述方法通过 python-buildpython-installer 进行构建和安装,这是首选方法。但它们还需要将 python-setuptools 添加到 makedepends

仍然可以继续使用旧的、已弃用的直接运行 setup.py 的方法来构建和安装这些包,下面对此进行了演示。之所以在此处介绍,是因为在 pep-517 兼容的方法因某种原因不起作用时,可以作为备用方案。

但请注意,使用此方法构建包时,在 package 步骤的输出中会收到以下警告:

SetuptoolsDeprecationWarning: setup.py install is deprecated.

另请注意,从 3.12 版开始的 Python 版本不再在其标准库中包含 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 来运行测试套件,因为它专门用于测试在 tox 运行时从 PyPI 下载的可重复配置,而测试将由包安装的版本。这违背了拥有 check 函数的初衷。
  • 避免将用于 linting、coverage 或 type checking 的 pytest 插件添加到 checkdepends(有关详细信息,请参阅 #禁用 pytest 选项 部分)。这会使引导过程变得困难,并且这些测试对于分发打包不是必需的,因为它们不测试功能。
  • 在设置自定义 PYTHONPATH 以进行测试时,避免使用相对路径,因为它可能导致不易察觉的错误情况。请改用绝对路径。

大多数提供测试套件的 Python 项目都使用 unittest 运行程序、nosetests 或 pytest(分别由 pythonpython-nosepython-pytest 提供),通过文件名或包含测试套件的目录名称中的 test 来运行测试。通常,只需运行 nosetestspytest 就足以运行测试套件。

check(){
    cd $_name-$pkgver
    
    # Builtin unittest
    python -m unittest discover -v

    # For nosetests
    nosetests

    # For pytest
    pytest
}

如果存在已编译的 C 扩展,则需要使用反映当前 Python 主次版本的 $PYTHONPATH 来运行测试,以便找到并加载它。

check(){
    cd $_name-$pkgver
    local python_version=$(python -c 'import sys; print("".join(map(str, sys.version_info[:2])))')
    
    # Builtin unittest
    PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-$python_version" python -m unittest discover -vs .
    
    # For nosetests
    PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-$python_version" nosetests

    # For pytest
    PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-$python_version" pytest
}

技巧与提示

使用 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 中的测试目录

确保不要将名为 tests/ 的目录直接安装在 site-packages/ 下(即 /usr/lib/pythonX.Y/site-packages/tests/)。这样做可能导致包之间的冲突。使用 setuptools 的 Python 包项目有时会被错误配置,将包含其测试的目录作为顶级 Python 包包含进去。如果遇到这种情况,可以通过向包项目提交问题来提供帮助,并要求他们修复此问题,例如 像这样

禁用 pytest 选项

运行 pytest 时,通常不希望使用额外的插件。特别是用于 linting 和 coverage 的插件在打包时会适得其反,因为行为的变化可能导致测试失败。为了禁用 addopts 等 pytest 选项,更倾向于在命令行中使用选项覆盖,而不是修补 pytest 使用的任何配置文件,以减少维护开销。

要取消设置所有附加选项,请使用:

pytest -o addopts=""

修复 meson-python 的可复现性问题

当使用 meson-python 作为 PEP 517 构建后端时,它会使用随机化的文件夹路径,这 会造成可复现性问题。可以通过使用 -Cbuild-dir 标志硬编码使用的文件夹来规避此问题。

python -m build --wheel --no-isolation -Cbuild-dir=build

运行已安装包的测试

某些包需要安装后才能成功运行测试。在这种情况下,例如 python-narwhals,您可以创建一个虚拟环境来安装构建好的包并在其中运行测试。

python -m venv --system-site-packages test-env
test-env/bin/python -m installer dist/*.whl
test-env/bin/python -P -m pytest

© . This site is unofficial and not affiliated with Arch Linux.

Content is available under GNU Free Documentation License 1.3 or later unless otherwise noted.