APKBUILD examples:Python: Difference between revisions

From Alpine Linux
mNo edit summary
(gpep517 only)
 
(29 intermediate revisions by 6 users not shown)
Line 1: Line 1:
A lot of Python packages use the ''setuptools'' or ''distutils'' framework. This mean that the build() and the package() section looks a bit different compared to an application which uses ''make''.
A lot of Python packages use the ''setuptools'' or ''distutils'' framework. This mean that the build() and the package() section looks a bit different compared to an application which uses ''make''.


== Considerations ==
== Considerations ==
=== Python 2 vs. Python 3 ===
Python 3 should be the default option.
If the project support both Python 2 and Python 3, then the Alpine package may provide both variants as ''py2-'' and ''py3-'' subpackages.
You may omit ''py2'' variant, but never omit ''py3'' variant if it's supported by the project.


=== pkgname ===
=== pkgname ===


Package name for a Python ''library'' must be prefixed with ''py-'', ''py2-'', or ''py3-''.
Package name for a Python ''library'' must be prefixed with ''py3-''.


; py- : Use if the project supports both Python 2 and Python 3, even if it does not support Python 3 yet, but may in future.
For an 'executable' (for example, <code>black</code>, <code>binwalk</code>), you generally don't need to prefix it.
; py2- : TODO
; py3- : Use if the project does not support Python 2.


There’s no exact rule if the prefix should be used for tools and applications written in Python, it varies.
There’s no exact rule if the prefix should be used for tools and applications written in Python, it varies.
Line 22: Line 13:
=== arch ===
=== arch ===


; noarch : Use for pure Python packages (i.e. without compiled code). Also add python2, or python3 to <tt>depends=</tt>.
; noarch : Use for pure Python packages (i.e. without compiled code).
; all (and others) :  Use for packages with native extensions (i.e. with compiled code). '''Do not''' add python2/python3 to <tt>depends=</tt> (it's auto-detected via dynamic linking to python library).
; all (and others) :  Use for packages with native extensions (i.e. with compiled code). '''Do not''' add python3 to <tt>depends=</tt> (it's auto-detected via dynamic linking to python library).


=== source ===
=== source ===


Basically all Python are published in [https://pypi.python.org/pypi PyPI] (the Python Package Index).
Most Python packages are published in [https://pypi.python.org/pypi PyPI](the Python Package Index).
If the package has a source tarball available in PyPI (that’s true for most packages), you should reference it in <tt>source=</tt> as:
If the package has a source tarball available in PyPI (that’s true for most packages), and it contains tests (some explicitly remove them from PyPI), you should reference it in <tt>source=</tt> as:


<pre>
<pre>
https://files.pythonhosted.org/packages/source/${_pkgname:0:1}/$_pkgname/$_pkgname-$pkgver.tar.gz
https://files.pythonhosted.org/packages/source/${_pyname%${_pyname#?}}/$_pyname/$_pyname-$pkgver.tar.gz
</pre>
</pre>


where <tt>_pkgname</tt> is a real name of the Python package.
where <tt>_pyname</tt> is the real name of the Python package.


Otherwise, use the normal upstream git tarballs.


== Examples ==
== Examples ==


Comments marked with <tt>##</tt> should not be copied into final APKBUILD!
=== pep517 invocation ===
 
 
=== Package for single Python version ===
 
If the project supports only Python 3 (or 2), then the APKBUILD is very simple:


<pre>
<pre>
pkgname="py3-foo"
pkgname="py3-foo"
_pkgname="foo"
...
...
depends="python3"
depends="py3-bar py3-baz"
makedepends="python3-dev"
makedepends="py3-gpep517 py3-setuptools py3-wheel python3-dev"
checkdepends="py3-pytest"
subpackages="$pkgname-pyc"
...
...


build() {
build() {
cd "$builddir"
gpep517 build-wheel \
python3 setup.py build
--wheel-dir dist \
}
--output-fd 3 3>&1 >&2
 
package() {
cd "$builddir"
python3 setup.py install --prefix=/usr --root="$pkgdir"
}
}
</pre>
=== Multiversion package without executables ===
Use this variant if the package does not install any files outside of <tt>/usr/lib/pythonX.Y/site-packages</tt>.
If the build modifies source code, e.g. using 2to3, read the last section!
<pre>
pkgname="py-foo"
_pkgname="PyFoo"
...
depends="py-bar py-baz"
makedepends="python2-dev python3-dev py-setuptools"
subpackages="py2-${pkgname#py-}:_py2 py3-${pkgname#py-}:_py3"
...
build() {
cd "$builddir"
python2 setup.py build || return 1
python3 setup.py build || return 1
}
package() {
mkdir -p "$pkgdir"
}
_py2() {
replaces="$pkgname"
depends="${depends//py-/py2-}"  ## remove if there are no common Python dependencies
_py python2
}
_py3() {
depends="${depends//py-/py3-}"  ## remove if there are no common Python dependencies
_py python3
}
_py() {
local python="$1"
pkgdesc="$pkgdesc (for $python)"
depends="$depends $python"  ## remove if arch isn't noarch
install_if="$pkgname=$pkgver-r$pkgrel $python"
cd "$builddir"
$python setup.py install --prefix=/usr --root="$subpkgdir"
}
</pre>
=== Multiversion package with executables ===
Use this variant if the packages installs some executables into <tt>/usr/bin</tt>.
If the build modifies source code, e.g. using 2to3, read the last section!
'''Note:''' This is just mere workaround that will not be needed in future once we add needed features to apk.
<pre>
pkgname="py-foo"
_pkgname="PyFoo"
...
depends="py-bar py-baz"
makedepends="python2-dev python3-dev py-setuptools"
subpackages="py2-${pkgname#py-}:_py2 py3-${pkgname#py-}:_py3"
...


build() {
check() {
cd "$builddir"
python3 -m venv --clear --system-site-packages testenv
python2 setup.py build || return 1
testenv/bin/python3 -m installer dist/*.whl
python3 setup.py build || return 1
testenv/bin/python3 -m pytest
}
}


package() {
package() {
mkdir -p "$pkgdir"/usr/bin
python3 -m installer -d "$pkgdir" \
 
dist/*.whl
# Create unsuffixed symlinks for executables from py3 subpackage.
local name; for name in foo bar baz; do
ln -s $name-3 "$pkgdir"/usr/bin/$name || return 1
done
}
 
_py2() {
replaces="$pkgname"
depends="${depends//py-/py2-}"  ## remove if there are no common Python dependencies
_py python2
}
 
_py3() {
depends="${depends//py-/py3-}"  ## remove if there are no common Python dependencies
_py python3
}
 
_py() {
local python="$1"
local pyver="${1:6:1}"
pkgdesc="$pkgdesc (for $python)"
depends="$depends $python"  ## remove if arch isn't noarch
install_if="$pkgname=$pkgver-r$pkgrel $python"
 
cd "$builddir"
$python setup.py install --prefix=/usr --root="$subpkgdir" || return 1
 
# Add version suffix to executable files.
local path; for path in "$subpkgdir"/usr/bin/*; do
mv "$path" "$path-$pyver" || return 1
done
}
}
</pre>
</pre>


 
Depending on the <code>build-backend</code> in the pyproject.toml, you might need py3-setuptools or py3-flit-core or py3-poetry-core or py3-hatchling at build time. If a project specifies literally <code>flit</code> or <code>poetry</code>, patch it to use the <code>-core</code> variant.
=== Multiversion package for project using 2to3 ===
 
Some Python projects use 2to3 (or similar tools) during build phase to make the code-base compatible with Python 3.
Since it modifies sources during build, you cannot run <tt>setup.py build</tt> with python2 and python3 one after another with the same source files.
You must duplicate the buildir and run them separately.
 
<pre>
prepare() {
default_prepare || return 1
 
# Soure files are modified during build with 2to3 tool, so we cannot
# build it for both Python versions in the same location.
local python; for python in python2 python3; do
cp -r "$builddir" "$builddir-$python" || return 1
done
}
 
build() {
local python; for python in python2 python3; do
cd "$builddir"-$python
$python setup.py build || return 1
done
}
 
_py() {
...
 
cd "$builddir"-$python
$python setup.py install --prefix=/usr --root="$subpkgdir" || return 1
}
</pre>


[[Category:Development]] [[Category:Python]]
[[Category:Development]] [[Category:Python]]

Latest revision as of 07:06, 11 May 2023

A lot of Python packages use the setuptools or distutils framework. This mean that the build() and the package() section looks a bit different compared to an application which uses make.

Considerations

pkgname

Package name for a Python library must be prefixed with py3-.

For an 'executable' (for example, black, binwalk), you generally don't need to prefix it.

There’s no exact rule if the prefix should be used for tools and applications written in Python, it varies.

arch

noarch
Use for pure Python packages (i.e. without compiled code).
all (and others)
Use for packages with native extensions (i.e. with compiled code). Do not add python3 to depends= (it's auto-detected via dynamic linking to python library).

source

Most Python packages are published in PyPI(the Python Package Index). If the package has a source tarball available in PyPI (that’s true for most packages), and it contains tests (some explicitly remove them from PyPI), you should reference it in source= as:

https://files.pythonhosted.org/packages/source/${_pyname%${_pyname#?}}/$_pyname/$_pyname-$pkgver.tar.gz

where _pyname is the real name of the Python package.

Otherwise, use the normal upstream git tarballs.

Examples

pep517 invocation

pkgname="py3-foo"
...
depends="py3-bar py3-baz"
makedepends="py3-gpep517 py3-setuptools py3-wheel python3-dev"
checkdepends="py3-pytest"
subpackages="$pkgname-pyc"
...

build() {
	gpep517 build-wheel \
		--wheel-dir dist \
		--output-fd 3 3>&1 >&2
}

check() {
	python3 -m venv --clear --system-site-packages testenv
	testenv/bin/python3 -m installer dist/*.whl
	testenv/bin/python3 -m pytest
}

package() {
	python3 -m installer -d "$pkgdir" \
		dist/*.whl
}

Depending on the build-backend in the pyproject.toml, you might need py3-setuptools or py3-flit-core or py3-poetry-core or py3-hatchling at build time. If a project specifies literally flit or poetry, patch it to use the -core variant.