https://wiki.archlinux.org/api.php?action=feedcontributions&user=Dusansimic&feedformat=atomArchWiki - User contributions [en]2024-03-29T08:29:32ZUser contributionsMediaWiki 1.41.0https://wiki.archlinux.org/index.php?title=Python_package_guidelines&diff=750160Python package guidelines2022-10-01T09:47:17Z<p>Dusansimic: Add Python 2 unmaintained and eol warning</p>
<hr />
<div>[[Category:Arch package guidelines]]<br />
[[ja:Python パッケージガイドライン]]<br />
[[pt:Python package guidelines]]<br />
[[zh-hans:Python package guidelines]]<br />
{{Package guidelines}}<br />
This document covers standards and guidelines on writing [[PKGBUILD]]s for [[Python]] software.<br />
<br />
== Package naming ==<br />
<br />
For [[Python#Installation|Python 3]] library modules, use {{ic|python-''modulename''}}. Also use the prefix if the package provides a program that is strongly coupled to the Python ecosystem (e.g. ''pip'' or ''tox''). For other applications, use only the program name.<br />
<br />
The same applies to Python 2 except that the prefix (if needed) is {{Ic|python2-}}.<br />
<br />
{{Note|The package name should be entirely lowercase.}}<br />
{{Warning|Python 2 has reached end-of-life and is no longer maintained or used by Arch ([https://archlinux.org/news/removing-python2-from-the-repositories/ link to Arch news]).}}<br />
<br />
== Architecture ==<br />
<br />
See [[PKGBUILD#arch]].<br />
<br />
A Python package that contains C extensions is architecture-dependent. Otherwise it is most likely architecture-independent.<br />
<br />
Packages built using [https://setuptools.pypa.io/ setuptools] define their C extensions using the {{ic|ext_modules}} keyword in {{ic|setup.py}}.<br />
<br />
== Source ==<br />
<br />
Download URLs linked from the PyPI website include an unpredictable hash that needs to be fetched from the PyPI website each time a package must be updated. This makes them unsuitable for use in a PKGBUILD. PyPI [https://github.com/pypa/pypi-legacy/issues/438#issuecomment-226940730 provides] an alternative stable scheme: [[PKGBUILD#source]] {{ic|1=source=()}} array should use the following URL templates:<br />
<br />
;Source package:<br />
:{{ic|<nowiki>https://files.pythonhosted.org/packages/source/${_name::1}/$_name/$_name-$pkgver.tar.gz</nowiki>}}<br />
;Pure Python wheel package<br />
:{{ic|<nowiki>https://files.pythonhosted.org/packages/py2.py3/${_name::1}/$_name/${_name//-/_}-$pkgver-py2.py3-none-any.whl</nowiki>}} (Bilingual – Python 2 and Python 3 compatible)<br />
:{{ic|<nowiki>https://files.pythonhosted.org/packages/py3/${_name::1}/$_name/${_name//-/_}-$pkgver-py3-none-any.whl</nowiki>}} (Python 3 only)<br />
:Note that the distribution name can contain dashes, while its representation in a wheel filename cannot (they are converted to underscores).<br />
;Architecture specific wheel package<br />
:Additional architecture-specific arrays can be added by appending an underscore and the architecture name, e.g. {{ic|1=source_x86_64=('...')}}. Also {{ic|1=_py=cp310}} can be used to not repeat the Python version:<br />
:{{ic|<nowiki>https://files.pythonhosted.org/packages/$_py/${_name::1}/$_name/${_name//-/_}-$pkgver-$_py-${_py}m-manylinux1_x86_64.whl</nowiki>}}<br />
<br />
Note that a custom {{ic|'''_name'''}} variable is used instead of {{ic|pkgname}} since Python packages are generally prefixed with {{ic|python-}}. This variable can generically be defined as follows:<br />
<br />
_name=${pkgname#python-}<br />
<br />
== Installation methods ==<br />
<br />
Python packages are generally installed using language-specific package manager such as [https://pip.pypa.io/ pip], which fetches packages from an online repository (usually [https://pypi.org/ PyPI], the Python Package Index) and tracks the relevant files.<br />
<br />
However, for managing Python packages from within {{ic|PKGBUILD}}s, one needs to "install" the Python package to the temporary location {{ic|''$pkgdir''/usr/lib/python''<Python version>''/site-packages/''$pkgname''}}.<br />
<br />
For Python packages using [https://www.python.org/dev/peps/pep-0517/ standard metadata] to specify their build backend in {{ic|pyproject.toml}}, this can most easily achieved using {{pkg|python-build}} and {{pkg|python-installer}}.<br />
Old packages might fail to specify that they use setuptools, and only offer a {{ic|setup.py}} that has to be invoked manually.<br />
<br />
{{Note|Dependencies from the package's metadata must be defined in the {{ic|depends}} array otherwise they will not be installed.}}<br />
<br />
=== Standards based (PEP 517) ===<br />
<br />
A standards based workflow is straightforward: Build a wheel using {{pkg|python-build}} and install it to {{ic|$pkgdir}} using {{pkg|python-installer}}:<br />
<br />
{{bc|1=<br />
makedepends=(python-build python-installer python-wheel)<br />
<br />
build() {<br />
cd "$_name-$pkgver"<br />
python -m build --wheel --no-isolation<br />
}<br />
<br />
package() {<br />
cd "$_name-$pkgver"<br />
python -m installer --destdir="$pkgdir" dist/*.whl<br />
}<br />
}}<br />
<br />
where<br />
<br />
* {{ic|1=--wheel}} results in only a wheel file to be built, no source distribution.<br />
* {{ic|1=--no-isolation}} means that the package is built using what is installed on your system (which includes packages you specified in {{ic|depends}}), by default the tool creates an isolated virtual environment and performs the build there.<br />
* {{ic|1=--destdir="$pkgdir"}} prevents trying to directly install in the host system instead of inside the package file, which would result in a permission error<br />
* {{ic|1=--compile-bytecode=...}} or {{ic|1=--no-compile-bytecode}} can be passed to {{ic|installer}}, but the default is sensibly picked, so this should not be necessary.<br />
<br />
{{Warning|Skipping {{ic|build}} and putting the {{ic|.whl}} file in your {{ic|source}} array is discouraged in favor of building from source, and should only be used when the latter is not a viable option (for example, packages which '''only''' come with wheel sources, and therefore cannot be built from source).}}<br />
<br />
=== setuptools or distutils ===<br />
<br />
If no {{ic|pyproject.toml}} can be found or it fails to contain a {{ic|[build-system]}} table, it means the project is using the old legacy format, which uses a ''setup.py'' file which invokes ''setuptools'' or ''distutils''. Note that while ''distutils'' is included in Python's standardlib, having ''setuptools'' installed means that you use a patched version of ''distutils''.<br />
<br />
{{bc|1=<br />
makedepends=('python-setuptools') # unless it only requires distutils<br />
<br />
build() {<br />
cd "$_name-$pkgver"<br />
python setup.py build<br />
}<br />
<br />
package() {<br />
cd "$_name-$pkgver"<br />
python setup.py install --root="$pkgdir" --optimize=1<br />
}<br />
}}<br />
<br />
where:<br />
<br />
* {{ic|1=--root="$pkgdir"}} works like {{ic|1=--destdir}} above<br />
* {{ic|1=--optimize=1}} compiles optimized bytecode files (''.opt-1.pyc'') so they can be tracked by [[pacman]] instead of being created on the host system on demand.<br />
* Adding {{ic|1=--skip-build}} optimizes away the unnecessary attempt to re-run the build steps already run in the {{ic|build()}} function, if that is the case.<br />
<br />
If the resulting package includes executables which [https://setuptools.readthedocs.io/en/latest/setuptools.html#automatic-script-creation import the deprecated pkg_resources module], then ''setuptools'' must be additionally specified as a {{ic|depends}} in the split {{ic|package_*()}} functions; alternatively, if the PKGBUILD only installs the Python package for a single version of Python, ''setuptools'' should be moved from {{ic|makedepends}} to {{ic|depends}}.<br />
<br />
Some packages try to use ''setuptools'' and fall back to ''distutils'' if ''setuptools'' could not be imported. In this case, setuptools should be added as a {{ic|makedepends}}, so that the resulting Python metadata is better.<br />
<br />
If a package needs ''setuptools'' to be built due to including executables (which is not supported by ''distutils''), but only imports ''distutils'', then building will raise a warning, and the resulting package will be broken (it will not contain the executables):<br />
<br />
{{bc|1=<br />
/usr/lib/python3.8/distutils/dist.py:274: UserWarning: Unknown distribution option: 'entry_points'<br />
warnings.warn(msg)<br />
}}<br />
<br />
An upstream bug should be reported. To work around the problem, an undocumented setuptools feature can be used:<br />
<br />
{{bc|1=<br />
# fails because of distutils<br />
python setup.py build<br />
<br />
# works by using a setuptools shim<br />
python -m setuptools.launch setup.py build<br />
}}<br />
<br />
If a package uses {{Pkg|python-setuptools-scm}}, the package most likely will not build with an error such as:<br />
<br />
{{bc|1=<br />
LookupError: setuptools-scm was unable to detect version for /build/python-jsonschema/src/jsonschema-3.2.0.<br />
<br />
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.<br />
}}<br />
<br />
To get it building {{ic|SETUPTOOLS_SCM_PRETEND_VERSION}} has to be exported as an environment variable with {{ic|$pkgver}} as the value:<br />
<br />
export SETUPTOOLS_SCM_PRETEND_VERSION=$pkgver<br />
<br />
== Check ==<br />
<br />
{{Warning|Avoid using {{ic|tox}} to run testsuites as it is explicitly designed to test repeatable configurations downloaded from PyPI while {{ic|tox}} is running, and does '''not''' test the version that will be installed by the package. This defeats the purpose of having a ''check'' function at all.}}<br />
<br />
Most Python projects providing a testsuite use nosetests or pytest to run tests with {{ic|test}} in the name of the file or directory containing the testsuite. In general, simply running {{ic|nosetests}} or {{ic|pytest}} is enough to run the testsuite.<br />
<br />
{{bc|<br />
check(){<br />
cd "$srcdir/foo-$pkgver"<br />
<br />
# For nosetests<br />
nosetests<br />
<br />
# For pytest<br />
pytest<br />
}<br />
}}<br />
<br />
If there is a compiled C extension, the tests need to be run using a {{ic|$PYTHONPATH}}, that reflects the current major and minor version of Python in order to find and load it.<br />
<br />
{{bc|1=<br />
check(){<br />
cd "$pkgname-$pkgver"<br />
local python_version=$(python -c 'import sys; print("".join(map(str, sys.version_info[:2])))')<br />
# For nosetests<br />
PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-${python_version}" nosetests<br />
<br />
# For pytest<br />
PYTHONPATH="$PWD/build/lib.linux-$CARCH-cpython-${python_version}" pytest<br />
}<br />
}}<br />
<br />
Some projects provide {{ic|setup.py}} entry points for running the test. This works for both {{ic|pytest}} and {{ic|nosetests}}.<br />
<br />
{{bc|<br />
check(){<br />
cd "$srcdir/foo-$pkgver"<br />
<br />
# For nosetests<br />
python setup.py nosetests<br />
<br />
# For pytest - needs python-pytest-runner<br />
python setup.py pytest<br />
}<br />
}}<br />
<br />
== Tips and tricks ==<br />
<br />
=== Discovering detached PGP signatures on PyPI ===<br />
<br />
If detached PGP signatures for a given Python sdist tarball exist, they should be used to verify the tarball. However, the signature files do not show up directly in the files download section of any given project on pypi.org. To discover the sdist tarballs and their potential signature files, it is possible to use this service to get an overview per project: https://pypi.debian.net/<br />
<br />
For {{Pkg|python-requests}}, this would be https://pypi.debian.net/requests.<br />
<br />
=== Using site-packages ===<br />
<br />
Sometimes during building, testing or installation it is required to refer to the system's {{ic|site-packages}} directory. To not hardcode the directory, use a call to the system Python version to retrieve the path and store it in a local variable:<br />
<br />
{{bc|1=<br />
check(){<br />
cd "$pkgname-$pkgver"<br />
local site_packages=$(python -c "import site; print(site.getsitepackages()[0])")<br />
...<br />
}<br />
}}<br />
<br />
=== Test directory in site-package ===<br />
<br />
Make sure to not install a directory named just {{ic|tests}} into {{ic|site-packages}} (i.e. {{ic|/usr/lib/pythonX.Y/site-packages/tests/}}). Python package projects using setuptools are sometimes misconfigured to include the directory containing its tests as a top level Python package. If you encounter this, you can help by filing an issue with the package project and ask them to fix this, e.g. [https://github.com/Lightning-AI/lightning/issues/10335 like this].</div>Dusansimic