Python 包准则
32 位 – CLR – CMake – 交叉编译 – DKMS – Eclipse – Electron – 字体 – Free Pascal – GNOME – Go – Haskell – Java – KDE – 内核模块 – Lisp – Meson – MinGW – Node.js – 非自由软件 – OCaml – Perl – PHP – Python – R – Ruby – Rust - 安全 – Shell – VCS – Web – Wine
本文档涵盖了为 Python 软件编写 PKGBUILDs 的标准和准则。
包命名
对于 Python 3 库模块,请使用 python-模块名
。如果软件包提供的程序与 Python 生态系统紧密耦合(例如 pip 或 tox),也请使用此前缀。对于其他应用程序,仅使用程序名称。
架构
参见 PKGBUILD#arch。
包含 C 扩展的 Python 软件包是架构相关的。否则,它很可能是架构无关的。
使用 setuptools 构建的软件包使用 setup.py
中的 ext_modules
关键字定义其 C 扩展。
来源
从 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)安装,pip 从在线仓库(通常是 PyPI,Python 软件包索引)获取软件包并跟踪相关文件。
但是,为了从 PKGBUILD
中管理 Python 软件包,需要将 Python 软件包“安装”到临时位置 $pkgdir/usr/lib/python<Python 版本>/site-packages/$pkgname
。
对于使用 标准元数据 在 pyproject.toml
中指定其构建后端的 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 的上游问题。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 跟踪,而不是在主机系统上按需创建。- 如果软件包使用了 python-setuptools-scm,则软件包很可能无法构建,并出现如下错误
添加 --skip-build
可以优化掉不必要的尝试,以重新运行已在 build()
函数中运行的构建步骤(如果情况如此)。
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 下载的可重复配置,而tox
正在运行,并且不测试软件包将安装的版本。这完全失去了设置 check 函数的目的。 - 避免将用于 linting、覆盖率或类型检查的 pytest 插件添加到 checkdepends(有关详细信息,请参阅 #禁用 pytest 选项 部分)。这使得引导启动更加困难,并且这些测试不是发行版打包所必需的,因为它们不测试功能。
大多数提供测试套件的 Python 项目都使用 unittest runner 或 nosetests 或 pytest(分别由 python 和 python-nose 和 python-pytest 提供)来运行测试,文件名或目录名中包含 test
。通常,只需运行 nosetests
或 pytest
就足以运行测试套件。
check(){ cd $_name-$pkgver # Builtin unittest python -m unittest discover -vs . # For nosetests nosetests # For pytest pytest }
如果存在编译后的 C 扩展,则需要使用 $PYTHONPATH
运行测试,这反映了当前 Python 的主要版本和次要版本,以便查找并加载它。
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.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 中的测试目录
确保不要将名为 tests/
的目录直接安装在 site-packages/
下(即 /usr/lib/pythonX.Y/site-packages/tests/
)。这样做可能会导致软件包之间发生冲突。使用 setuptools 的 Python 软件包项目有时配置错误,会将包含其测试的目录作为顶级 Python 软件包包含在内。如果遇到这种情况,你可以通过向软件包项目提交 issue 并要求他们修复此问题来提供帮助,例如 像这样。
禁用 pytest 选项
运行 pytest 时,通常不希望使用额外的插件运行。特别是用于 linting 和覆盖率的插件在打包中会适得其反,因为行为的更改可能会导致测试失败。为了禁用 pytest 选项(例如 addopts
),首选在命令行上使用选项覆盖,而不是修补 pytest 使用的任何配置文件,因为维护开销。
要取消设置所有其他选项,请使用
pytest -o addopts=""
修复 meson-python 的可重现性问题
当使用 meson-python 作为 PEP 517 构建后端时,它使用随机化的文件夹路径 导致可重现性问题。这可以通过使用 -Cbuild-dir
标志硬编码使用的文件夹来规避
python -m build --wheel --no-isolation -Cbuild-dir=build