diff options
Diffstat (limited to 'eclass')
-rw-r--r-- | eclass/Manifest.gz | bin | 37343 -> 37513 bytes | |||
-rw-r--r-- | eclass/pypi.eclass | 126 | ||||
-rwxr-xr-x | eclass/tests/pypi.sh | 96 | ||||
-rw-r--r-- | eclass/verify-sig.eclass | 12 |
4 files changed, 208 insertions, 26 deletions
diff --git a/eclass/Manifest.gz b/eclass/Manifest.gz Binary files differindex a33ff26008bd..1c25f73d923d 100644 --- a/eclass/Manifest.gz +++ b/eclass/Manifest.gz 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) |