@@ -7,28 +7,37 @@
def pypi_default_package(d):
"""
Return a reasonable guess for the PyPI package name by
- stripping any python- prefix from PN.
+ stripping any python- prefix from PN and normalising it.
"""
bpn = d.getVar('BPN')
if bpn.startswith('python-'):
- return bpn[7:]
+ return pypi_package_normalise(bpn[7:])
elif bpn.startswith('python3-'):
- return bpn[8:]
- return bpn
+ return pypi_package_normalise(bpn[8:])
+ return pypi_package_normalise(bpn)
-# The PyPi package name (defaults to PN without the python3- prefix)
+# The PyPI package name, normalised as per
+# https://packaging.python.org/en/latest/specifications/name-normalization/.
+# Defaults to BPN without a python3- prefix.
PYPI_PACKAGE ?= "${@pypi_default_package(d)}"
+
+# The name of the sdist. This defaults to the normalised name with underscores as per
+# https://packaging.python.org/en/latest/specifications/source-distribution-format/,
+# but can be overridden if needed.
+PYPI_PACKAGE_SDIST ?= "${@d.getVar("PYPI_PACKAGE").replace("-", "_")}"
+
# The file extension of the source archive
PYPI_PACKAGE_EXT ?= "tar.gz"
+
# An optional prefix for the download file in the case of name collisions
PYPI_ARCHIVE_NAME_PREFIX ?= ""
def pypi_src_uri(d):
"""
- Construct a source URL as per https://warehouse.pypa.io/api-reference/integration-guide.html#predictable-urls.
+ Construct a source URL as per https://docs.pypi.org/api/#predictable-urls.
"""
- package = d.getVar('PYPI_PACKAGE')
- archive_name = d.expand('${PYPI_PACKAGE}-${PV}.${PYPI_PACKAGE_EXT}')
+ package = d.getVar('PYPI_PACKAGE_SDIST')
+ archive_name = d.expand('${PYPI_PACKAGE_SDIST}-${PV}.${PYPI_PACKAGE_EXT}')
url = 'https://files.pythonhosted.org/packages/source/%s/%s/%s' % (package[0], package, archive_name)
download_prefix = d.getVar("PYPI_ARCHIVE_NAME_PREFIX")
@@ -37,34 +46,30 @@ def pypi_src_uri(d):
return url
-def pypi_normalize(d):
- """"
- Normalize the package names to match PEP625 (https://peps.python.org/pep-0625/).
- """
- import re
- return re.sub(r"[-_.]+", "-", d.getVar('PYPI_PACKAGE')).lower()
-
PYPI_SRC_URI ?= "${@pypi_src_uri(d)}"
HOMEPAGE ?= "https://pypi.python.org/pypi/${PYPI_PACKAGE}/"
SECTION = "devel/python"
SRC_URI:prepend = "${PYPI_SRC_URI} "
-S = "${UNPACKDIR}/${PYPI_PACKAGE}-${PV}"
+S = "${UNPACKDIR}/${PYPI_PACKAGE_SDIST}-${PV}"
-def pypi_normalize_regex(d):
- # Use a regex wildcard instead of hyphen as the filenames
- # may or may not have been normalised properly.
- return pypi_normalize(d).replace("-", "[_-]")
-
-# Use the simple repository API rather than the potentially unstable project URL
-# More information on the pypi API specification is avaialble here:
+# More information on the PyPI API specification is available here:
# https://packaging.python.org/en/latest/specifications/simple-repository-api/
#
-# NOTE: All URLs for the simple API MUST request canonical normalized URLs per the spec
-UPSTREAM_CHECK_URI ?= "https://pypi.org/simple/${@pypi_normalize(d)}/"
-UPSTREAM_CHECK_REGEX ?= "(?i)${@pypi_normalize_regex(d)}-(?P<pver>(\d+(\.[\d\-]+)*(\.post\d+)?))\.(tar\.gz|tgz|zip|tar\.bz2)"
+# Use a case-insensitive regex wildcard instead of hyphen as the filenames may
+# or may not have been normalised.
+UPSTREAM_CHECK_URI ?= "https://pypi.org/simple/${PYPI_PACKAGE}/"
+UPSTREAM_CHECK_REGEX ?= "(?i)${@d.getVar("PYPI_PACKAGE").replace("-", "[_-]")}-(?P<pver>(\d+(\.[\d\-]+)*(\.post\d+)?))\.(tar\.gz|tgz|zip|tar\.bz2)"
CVE_PRODUCT ?= "python:${PYPI_PACKAGE}"
# Generate ecosystem-specific Package URL for SPDX
-SPDX_PACKAGE_URLS =+ "pkg:pypi/${@pypi_normalize(d)}@${PV} "
+SPDX_PACKAGE_URLS =+ "pkg:pypi/${PYPI_PACKAGE}@${PV} "
+
+def pypi_package_normalise(s):
+ """
+ Normalise the passed package name as per
+ https://packaging.python.org/en/latest/specifications/name-normalization/
+ """
+ import re
+ return re.sub(r"[-_.]+", "-", s).lower()
Currently the semantics of the PYPI_PACKAGE variable are slightly unexpected, as it is the name of the source distribution (tarball), not the logical package itself. This meant many recipes had to set PYPI_PACKAGE and there were problems when we wanted to use PyPI APIs that expect the normalised package name, not whatever the sdist was called. This commit changes the definition of PYPI_PACKAGE to be the normalised package name[1], so PySocks would be pysocks and boolean.py would be boolean-py. This means we can use the correct package name in any PyPI API calls, which all expect a normalised name. Some uploaded tarballs do not have normalised names, so add a new variable PYPI_PACKAGE_SDIST for the name of the sdist tarball. This defaults to the source distribution normalised[2] PYPI_PACKAGE, but can be overriden if needed. Whilst this causes some churn in recipes, it mostly lets recipes remove lines. [1] https://packaging.python.org/en/latest/specifications/name-normalization/ [2] https://packaging.python.org/en/latest/specifications/source-distribution-format/, Signed-off-by: Ross Burton <ross.burton@arm.com> --- meta/classes-recipe/pypi.bbclass | 59 +++++++++++++++++--------------- 1 file changed, 32 insertions(+), 27 deletions(-)