summaryrefslogtreecommitdiff
path: root/eclass
diff options
context:
space:
mode:
Diffstat (limited to 'eclass')
-rw-r--r--eclass/Manifest.gzbin37343 -> 37513 bytes
-rw-r--r--eclass/pypi.eclass126
-rwxr-xr-xeclass/tests/pypi.sh96
-rw-r--r--eclass/verify-sig.eclass12
4 files changed, 208 insertions, 26 deletions
diff --git a/eclass/Manifest.gz b/eclass/Manifest.gz
index a33ff26008bd..1c25f73d923d 100644
--- a/eclass/Manifest.gz
+++ b/eclass/Manifest.gz
Binary files differ
diff --git a/eclass/pypi.eclass b/eclass/pypi.eclass
index e9d3eec1268b..182b0b6b8c3e 100644
--- a/eclass/pypi.eclass
+++ b/eclass/pypi.eclass
@@ -11,12 +11,21 @@
# @DESCRIPTION:
# The pypi.eclass can be used to easily obtain URLs for artifacts
# uploaded to PyPI.org. When inherited, the eclass defaults SRC_URI
-# to fetch ${P}.tar.gz sdist.
+# and S to fetch .tar.gz sdist. The project filename is normalized
+# by default (unless PYPI_NO_NORMALIZE is set prior to inheriting
+# the eclass), and the version is translated using
+# pypi_translate_version.
#
-# If necessary, SRC_URI can be overriden by the ebuild. Two helper
-# functions, pypi_sdist_url and pypi_wheel_url are provided to generate
-# URLs to artifacts of specified type, with customizable project name.
-# Additionally, pypi_wheel_name can be used to generate wheel filename.
+# If necessary, SRC_URI and S can be overriden by the ebuild. Two
+# helper functions, pypi_sdist_url and pypi_wheel_url are provided
+# to generate URLs to artifacts of specified type, with customizable
+# URL components. Additionally, pypi_wheel_name can be used to generate
+# wheel filename.
+#
+# pypi_normalize_name can be used to normalize an arbitrary project name
+# according to sdist/wheel normalization rules. pypi_translate_version
+# can be used to translate a Gentoo version string into its PEP 440
+# equivalent.
#
# @EXAMPLE:
# @CODE@
@@ -34,31 +43,91 @@ esac
if [[ ! ${_PYPI_ECLASS} ]]; then
_PYPI_ECLASS=1
-SRC_URI="
- https://files.pythonhosted.org/packages/source/${PN::1}/${PN}/${P}.tar.gz
-"
+# @ECLASS_VARIABLE: PYPI_NO_NORMALIZE
+# @PRE_INHERIT
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# When set to a non-empty value, disables project name normalization
+# for the default SRC_URI and S values.
+
+# @FUNCTION: pypi_normalize_name
+# @USAGE: <name>
+# @DESCRIPTION:
+# Normalize the project name according to sdist/wheel normalization
+# rules. That is, convert to lowercase and replace runs of [._-]
+# with a single underscore.
+#
+# Based on the spec, as of 2023-02-10:
+# https://packaging.python.org/en/latest/specifications/#package-distribution-file-formats
+pypi_normalize_name() {
+ [[ ${#} -ne 1 ]] && die "Usage: ${FUNCNAME} <name>"
+
+ local name=${1}
+ local shopt_save=$(shopt -p extglob)
+ shopt -s extglob
+ name=${name//+([._-])/_}
+ ${shopt_save}
+ echo "${name,,}"
+}
+
+# @FUNCTION: pypi_translate_version
+# @USAGE: <version>
+# @DESCRIPTION:
+# Translate the specified Gentoo version into the usual Python
+# counterpart. Assumes PEP 440 versions.
+#
+# Note that we do not have clear counterparts for the epoch segment,
+# nor for development release segment.
+pypi_translate_version() {
+ [[ ${#} -ne 1 ]] && die "Usage: ${FUNCNAME} <version>"
+
+ local version=${1}
+ version=${version/_alpha/a}
+ version=${version/_beta/b}
+ version=${version/_rc/rc}
+ version=${version/_p/.post}
+ echo "${version}"
+}
# @FUNCTION: pypi_sdist_url
-# @USAGE: [<project> [<version> [<suffix>]]]
+# @USAGE: [--no-normalize] [<project> [<version> [<suffix>]]]
# @DESCRIPTION:
# Output the URL to PyPI sdist for specified project/version tuple.
#
-# If <package> is unspecified, it defaults to ${PN}.
+# The `--no-normalize` option disables project name normalization
+# for sdist filename. This may be necessary when dealing with distfiles
+# generated using build systems that did not follow PEP 625
+# (i.e. the sdist name contains uppercase letters, hyphens or dots).
+#
+# If <package> is unspecified, it defaults to ${PN}. The package name
+# is normalized according to the specification unless `--no-normalize`
+# is passed.
#
-# If <version> is unspecified, it defaults to ${PV}.
+# If <version> is unspecified, it defaults to ${PV} translated
+# via pypi_translate_version. If it is specified, then it is used
+# verbatim (the function can be called explicitly to translate custom
+# version number).
#
# If <format> is unspecified, it defaults to ".tar.gz". Another valid
# value is ".zip" (please remember to add a BDEPEND on app-arch/unzip).
pypi_sdist_url() {
+ local normalize=1
+ if [[ ${1} == --no-normalize ]]; then
+ normalize=
+ shift
+ fi
+
if [[ ${#} -gt 3 ]]; then
- die "Usage: ${FUNCNAME} <project> [<version> [<suffix>]]"
+ die "Usage: ${FUNCNAME} [--no-normalize] <project> [<version> [<suffix>]]"
fi
local project=${1-"${PN}"}
- local version=${2-"${PV}"}
+ local version=${2-"$(pypi_translate_version "${PV}")"}
local suffix=${3-.tar.gz}
+ local fn_project=${project}
+ [[ ${normalize} ]] && fn_project=$(pypi_normalize_name "${project}")
printf "https://files.pythonhosted.org/packages/source/%s" \
- "${project::1}/${project}/${project}-${version}${suffix}"
+ "${project::1}/${project}/${fn_project}-${version}${suffix}"
}
# @FUNCTION: pypi_wheel_name
@@ -66,9 +135,13 @@ pypi_sdist_url() {
# @DESCRIPTION:
# Output the wheel filename for the specified project/version tuple.
#
-# If <package> is unspecified, it defaults to ${PN}.
+# If <package> is unspecified, it defaults to ${PN}. The package name
+# is normalized according to the wheel specification.
#
-# If <version> is unspecified, it defaults to ${PV}.
+# If <version> is unspecified, it defaults to ${PV} translated
+# via pypi_translate_version. If it is specified, then it is used
+# verbatim (the function can be called explicitly to translate custom
+# version number).
#
# If <python-tag> is unspecified, it defaults to "py3". It can also be
# "py2.py3", or a specific version in case of non-pure wheels.
@@ -81,8 +154,8 @@ pypi_wheel_name() {
die "Usage: ${FUNCNAME} <project> [<version> [<python-tag> [<abi-platform-tag>]]]"
fi
- local project=${1-"${PN}"}
- local version=${2-"${PV}"}
+ local project=$(pypi_normalize_name "${1-"${PN}"}")
+ local version=${2-"$(pypi_translate_version "${PV}")"}
local pytag=${3-py3}
local abitag=${4-none-any}
echo "${project}-${version}-${pytag}-${abitag}.whl"
@@ -101,7 +174,10 @@ pypi_wheel_name() {
#
# If <package> is unspecified, it defaults to ${PN}.
#
-# If <version> is unspecified, it defaults to ${PV}.
+# If <version> is unspecified, it defaults to ${PV} translated
+# via pypi_translate_version. If it is specified, then it is used
+# verbatim (the function can be called explicitly to translate custom
+# version number).
#
# If <python-tag> is unspecified, it defaults to "py3". It can also be
# "py2.py3", or a specific version in case of non-pure wheels.
@@ -117,12 +193,12 @@ pypi_wheel_url() {
fi
if [[ ${#} -gt 4 ]]; then
- die "Usage: ${FUNCNAME} <project> [<version> [<python-tag> [<abi-platform-tag>]]]"
+ die "Usage: ${FUNCNAME} [--unpack] <project> [<version> [<python-tag> [<abi-platform-tag>]]]"
fi
local filename=$(pypi_wheel_name "${@}")
local project=${1-"${PN}"}
- local version=${2-"${PV}"}
+ local version=${2-"$(pypi_translate_version "${PV}")"}
local pytag=${3-py3}
printf "https://files.pythonhosted.org/packages/%s" \
"${pytag}/${project::1}/${project}/${filename}"
@@ -132,4 +208,12 @@ pypi_wheel_url() {
fi
}
+if [[ ${PYPI_NO_NORMALIZE} ]]; then
+ SRC_URI="$(pypi_sdist_url --no-normalize)"
+ S="${WORKDIR}/${PN}-$(pypi_translate_version "${PV}")"
+else
+ SRC_URI="$(pypi_sdist_url)"
+ S="${WORKDIR}/$(pypi_normalize_name "${PN}")-$(pypi_translate_version "${PV}")"
+fi
+
fi
diff --git a/eclass/tests/pypi.sh b/eclass/tests/pypi.sh
new file mode 100755
index 000000000000..ebfcdb630856
--- /dev/null
+++ b/eclass/tests/pypi.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+# Copyright 2023 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=8
+source tests-common.sh || exit
+
+PN=Foo.Bar
+PV=1.2.3_beta2
+WORKDIR='<WORKDIR>'
+
+inherit pypi
+
+test-eq() {
+ local call=${1}
+ local exp=${2}
+
+ tbegin "${call} -> ${exp}"
+ local ret=0
+ local have=$(${call})
+ if [[ ${have} != ${exp} ]]; then
+ eindent
+ eerror "incorrect result: ${have}"
+ eoutdent
+ ret=1
+ fi
+ tend "${ret}"
+}
+
+test-eq "pypi_normalize_name foo" foo
+test-eq "pypi_normalize_name foo_bar" foo_bar
+test-eq "pypi_normalize_name foo___bar" foo_bar
+test-eq "pypi_normalize_name Flask-BabelEx" flask_babelex
+test-eq "pypi_normalize_name jaraco.context" jaraco_context
+
+test-eq "pypi_translate_version 1.2.3" 1.2.3
+test-eq "pypi_translate_version 1.2.3_p101" 1.2.3.post101
+test-eq "pypi_translate_version 1.2.3_alpha4" 1.2.3a4
+test-eq "pypi_translate_version 1.2.3_beta1" 1.2.3b1
+test-eq "pypi_translate_version 1.2.3_rc2" 1.2.3rc2
+test-eq "pypi_translate_version 1.2.3_rc2_p1" 1.2.3rc2.post1
+
+test-eq "pypi_wheel_name" foo_bar-1.2.3b2-py3-none-any.whl
+test-eq "pypi_wheel_name Flask-BabelEx" flask_babelex-1.2.3b2-py3-none-any.whl
+test-eq "pypi_wheel_name Flask-BabelEx 4" flask_babelex-4-py3-none-any.whl
+test-eq "pypi_wheel_name Flask-BabelEx 4 py2.py3" \
+ flask_babelex-4-py2.py3-none-any.whl
+test-eq "pypi_wheel_name cryptography 39.0.1 cp36 abi3-manylinux_2_28_x86_64" \
+ cryptography-39.0.1-cp36-abi3-manylinux_2_28_x86_64.whl
+
+test-eq "pypi_wheel_url" \
+ https://files.pythonhosted.org/packages/py3/F/Foo.Bar/foo_bar-1.2.3b2-py3-none-any.whl
+test-eq "pypi_wheel_url Flask-BabelEx" \
+ https://files.pythonhosted.org/packages/py3/F/Flask-BabelEx/flask_babelex-1.2.3b2-py3-none-any.whl
+test-eq "pypi_wheel_url Flask-BabelEx 4" \
+ https://files.pythonhosted.org/packages/py3/F/Flask-BabelEx/flask_babelex-4-py3-none-any.whl
+test-eq "pypi_wheel_url Flask-BabelEx 4 py2.py3" \
+ https://files.pythonhosted.org/packages/py2.py3/F/Flask-BabelEx/flask_babelex-4-py2.py3-none-any.whl
+test-eq "pypi_wheel_url cryptography 39.0.1 cp36 abi3-manylinux_2_28_x86_64" \
+ https://files.pythonhosted.org/packages/cp36/c/cryptography/cryptography-39.0.1-cp36-abi3-manylinux_2_28_x86_64.whl
+
+test-eq "pypi_wheel_url --unpack" \
+ "https://files.pythonhosted.org/packages/py3/F/Foo.Bar/foo_bar-1.2.3b2-py3-none-any.whl -> foo_bar-1.2.3b2-py3-none-any.whl.zip"
+test-eq "pypi_wheel_url --unpack Flask-BabelEx" \
+ "https://files.pythonhosted.org/packages/py3/F/Flask-BabelEx/flask_babelex-1.2.3b2-py3-none-any.whl -> flask_babelex-1.2.3b2-py3-none-any.whl.zip"
+test-eq "pypi_wheel_url --unpack Flask-BabelEx 4" \
+ "https://files.pythonhosted.org/packages/py3/F/Flask-BabelEx/flask_babelex-4-py3-none-any.whl -> flask_babelex-4-py3-none-any.whl.zip"
+test-eq "pypi_wheel_url --unpack Flask-BabelEx 4 py2.py3" \
+ "https://files.pythonhosted.org/packages/py2.py3/F/Flask-BabelEx/flask_babelex-4-py2.py3-none-any.whl -> flask_babelex-4-py2.py3-none-any.whl.zip"
+test-eq "pypi_wheel_url --unpack cryptography 39.0.1 cp36 abi3-manylinux_2_28_x86_64" \
+ "https://files.pythonhosted.org/packages/cp36/c/cryptography/cryptography-39.0.1-cp36-abi3-manylinux_2_28_x86_64.whl -> cryptography-39.0.1-cp36-abi3-manylinux_2_28_x86_64.whl.zip"
+
+test-eq "pypi_sdist_url" \
+ https://files.pythonhosted.org/packages/source/F/Foo.Bar/foo_bar-1.2.3b2.tar.gz
+test-eq "pypi_sdist_url Flask-BabelEx" \
+ https://files.pythonhosted.org/packages/source/F/Flask-BabelEx/flask_babelex-1.2.3b2.tar.gz
+test-eq "pypi_sdist_url Flask-BabelEx 4" \
+ https://files.pythonhosted.org/packages/source/F/Flask-BabelEx/flask_babelex-4.tar.gz
+test-eq "pypi_sdist_url Flask-BabelEx 4 .zip" \
+ https://files.pythonhosted.org/packages/source/F/Flask-BabelEx/flask_babelex-4.zip
+
+test-eq "pypi_sdist_url --no-normalize" \
+ https://files.pythonhosted.org/packages/source/F/Foo.Bar/Foo.Bar-1.2.3b2.tar.gz
+test-eq "pypi_sdist_url --no-normalize Flask-BabelEx" \
+ https://files.pythonhosted.org/packages/source/F/Flask-BabelEx/Flask-BabelEx-1.2.3b2.tar.gz
+test-eq "pypi_sdist_url --no-normalize Flask-BabelEx 4" \
+ https://files.pythonhosted.org/packages/source/F/Flask-BabelEx/Flask-BabelEx-4.tar.gz
+test-eq "pypi_sdist_url --no-normalize Flask-BabelEx 4 .zip" \
+ https://files.pythonhosted.org/packages/source/F/Flask-BabelEx/Flask-BabelEx-4.zip
+
+test-eq 'declare -p SRC_URI' \
+ 'declare -- SRC_URI="https://files.pythonhosted.org/packages/source/F/Foo.Bar/foo_bar-1.2.3b2.tar.gz"'
+test-eq 'declare -p S' \
+ 'declare -- S="<WORKDIR>/foo_bar-1.2.3b2"'
+
+texit
diff --git a/eclass/verify-sig.eclass b/eclass/verify-sig.eclass
index 394ce2e44427..a7d6d26ed432 100644
--- a/eclass/verify-sig.eclass
+++ b/eclass/verify-sig.eclass
@@ -1,4 +1,4 @@
-# Copyright 2020-2022 Gentoo Authors
+# Copyright 2020-2023 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
# @ECLASS: verify-sig.eclass
@@ -65,8 +65,9 @@ case ${VERIFY_SIG_METHOD} in
BDEPEND="
verify-sig? (
app-crypt/gnupg
- >=app-portage/gemato-16
- )"
+ >=app-portage/gemato-18.0
+ )
+ "
;;
signify)
BDEPEND="verify-sig? ( app-crypt/signify )"
@@ -144,8 +145,9 @@ verify-sig_verify_detached() {
# gpg can't handle very long TMPDIR
# https://bugs.gentoo.org/854492
local -x TMPDIR=/tmp
- gemato gpg-wrap -K "${key}" "${extra_args[@]}" -- \
- gpg --verify "${sig}" "${file}" ||
+ gemato openpgp-verify-detached -K "${key}" \
+ "${extra_args[@]}" --no-require-all-good \
+ "${sig}" "${file}" ||
die "PGP signature verification failed"
;;
signify)