Generate purl(pkg:pypi/...) Provides
- Fixes: rhbz#2514458 - https://fedoraproject.org/wiki/Changes/Adopt_PURL_Metadata Alongside the existing python*dist() Provides, generate Package URL (purl) Provides like purl(pkg:pypi/setuptools@41.6.0) for identifying Python packages in a ecosystem-independent way. The purl name is normalized per the purl spec for PyPI (lowercase, underscores replaced with dashes, dots preserved, unlike PEP 503). The version is canonicalized per PEP 440 with trailing zeros preserved. Enabled by default via the %__purl_provides_pypi macro. Packagers can %undefine it for packages not actually on PyPI. Extras subpackages do not get purl Provides. Assisted-By: Claude Opus 4.6
This commit is contained in:
parent
6afe8d5271
commit
43b2eea5d4
6 changed files with 76 additions and 4 deletions
6
macros.python-rpm-generators
Normal file
6
macros.python-rpm-generators
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Enable purl(pkg:pypi/...) Provides generation by default.
|
||||
# To disable for packages not actually on PyPI, add to the spec file:
|
||||
# %%undefine _purl_provides_pypi
|
||||
# or:
|
||||
# %%global _purl_provides_pypi 0
|
||||
%_purl_provides_pypi 1
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
Name: python-rpm-generators
|
||||
Summary: Dependency generators for Python RPMs
|
||||
Version: 14
|
||||
Version: 15
|
||||
Release: %autorelease
|
||||
|
||||
Url: https://src.fedoraproject.org/rpms/python-rpm-generators
|
||||
|
|
@ -18,6 +18,11 @@ Source4: pythondistdeps.py
|
|||
# LicenseRef-Fedora-Public-Domain OR CC0-1.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later
|
||||
# Note that CC0-1.0 is not allowed for code in Fedora, so we skip it in the package License tag
|
||||
Source5: pythonbundles.py
|
||||
# Macros to enable/disable opt-in/opt-out generator features.
|
||||
# Older features have their macros in python-srpm-macros,
|
||||
# but shipping them here is simpler to maintain.
|
||||
# This is essentially data, so no explicit license.
|
||||
Source6: macros.python-rpm-generators
|
||||
|
||||
# See individual licenses above Source declarations
|
||||
# Originally, this was simplified to GPL-2.0-or-later, but "effective license" analysis is no longer allowed
|
||||
|
|
@ -46,6 +51,7 @@ cp -a %{sources} .
|
|||
%install
|
||||
install -Dpm0644 -t %{buildroot}%{_fileattrsdir} *.attr
|
||||
install -Dpm0755 -t %{buildroot}%{_rpmconfigdir} *.py
|
||||
install -Dpm0644 -t %{buildroot}%{_rpmmacrodir} macros.*
|
||||
|
||||
%files -n python3-rpm-generators
|
||||
%license COPYING
|
||||
|
|
@ -54,6 +60,7 @@ install -Dpm0755 -t %{buildroot}%{_rpmconfigdir} *.py
|
|||
%{_fileattrsdir}/pythonname.attr
|
||||
%{_rpmconfigdir}/pythondistdeps.py
|
||||
%{_rpmconfigdir}/pythonbundles.py
|
||||
%{_rpmmacrodir}/macros.python-rpm-generators
|
||||
|
||||
%changelog
|
||||
%autochangelog
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
%__pythondist_provides %{_rpmconfigdir}/pythondistdeps.py --provides --normalized-names-format pep503 --package-name %{name} --majorver-provides-versions %{__default_python3_version} %{?!_python_dist_allow_version_zero:--fail-if-zero}
|
||||
%__pythondist_provides %{_rpmconfigdir}/pythondistdeps.py --provides --normalized-names-format pep503 --package-name %{name} --majorver-provides-versions %{__default_python3_version} %{?!_python_dist_allow_version_zero:--fail-if-zero} %[0%{?_purl_provides_pypi} ? "--purl-provides" : ""]
|
||||
%__pythondist_requires %{_rpmconfigdir}/pythondistdeps.py --requires --normalized-names-format pep503 --package-name %{name} %{?!_python_no_extras_requires:--require-extras-subpackages} --console-scripts-nodep-setuptools-since 3.10
|
||||
%__pythondist_path ^%{?!flatpak:/usr}%{?flatpak:/app}/lib(64)?/python[3-9]\\.[[:digit:]]+/site-packages/[^/]+\\.(dist-info|egg-info|egg-link)$
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ from sysconfig import get_path
|
|||
from warnings import warn
|
||||
|
||||
from packaging.requirements import Requirement as Requirement_
|
||||
from packaging.utils import canonicalize_version
|
||||
from packaging.version import parse
|
||||
import packaging.markers
|
||||
|
||||
|
|
@ -63,6 +64,13 @@ def legacy_normalize_name(name):
|
|||
return re.sub(r'[-_]+', '-', name).lower()
|
||||
|
||||
|
||||
def purl_normalize_name(name):
|
||||
"""https://packageurl.org/docs/purl-spec/types/definitions/pypi-definition#name-definition
|
||||
Lowercase and replace underscore _ with dash -.
|
||||
Unlike PEP 503, dots and sequences of dashes are preserved."""
|
||||
return name.lower().replace('_', '-')
|
||||
|
||||
|
||||
class Requirement(Requirement_):
|
||||
def __init__(self, requirement_string):
|
||||
super(Requirement, self).__init__(requirement_string)
|
||||
|
|
@ -343,12 +351,17 @@ def main():
|
|||
parser.add_argument('--package-name', action='store', help="Name of the RPM package that's being inspected. Required for extras requires/provides to work.")
|
||||
parser.add_argument('--namespace', action='store', help="Namespace for the printed Requires, Provides, Recommends and Conflicts")
|
||||
parser.add_argument('--fail-if-zero', action='store_true', help='Fail the script if the automatically generated Provides version was 0, which usually indicates a packaging error.')
|
||||
parser.add_argument('--purl-provides', action='store_true',
|
||||
help='Generate purl(pkg:pypi/...) Provides for Package URL identification')
|
||||
parser.add_argument('files', nargs=argparse.REMAINDER, help="Files from the RPM package that are to be inspected, can also be supplied on stdin")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.fail_if_zero and not args.provides:
|
||||
raise parser.error('--fail-if-zero only works with --provides')
|
||||
|
||||
if args.purl_provides and not args.provides:
|
||||
raise parser.error('--purl-provides only works with --provides')
|
||||
|
||||
py_abi = args.requires
|
||||
py_deps = {}
|
||||
|
||||
|
|
@ -479,6 +492,20 @@ def main():
|
|||
legacy_name = namespace.format('pythonegg({})({})').format(pyver_major, dist.legacy_normalized_name)
|
||||
if legacy_name not in py_deps:
|
||||
py_deps[legacy_name] = []
|
||||
if args.purl_provides and not extras_subpackage and dist.name and dist.version:
|
||||
# The purl is a universal identifier, not namespace-wrapped.
|
||||
# The version is canonicalized per PEP 440 but trailing
|
||||
# zeros are preserved to match how versions appear on PyPI.
|
||||
# The purl spec does not specify PEP 440 canonicalization,
|
||||
# but without it, non-canonical spellings like "1.0beta2"
|
||||
# and "1.0b2" would produce different purls for the same
|
||||
# package version.
|
||||
purl_name = purl_normalize_name(dist.name)
|
||||
purl_version = canonicalize_version(dist.version, strip_trailing_zero=False)
|
||||
purl_provide = 'purl(pkg:pypi/{}@{})'.format(purl_name, purl_version)
|
||||
if purl_provide not in py_deps:
|
||||
# The Provide is unversioned, version is part of name
|
||||
py_deps[purl_provide] = []
|
||||
if dist.version:
|
||||
version = dist.version
|
||||
spec = ('==', version)
|
||||
|
|
|
|||
|
|
@ -968,9 +968,28 @@
|
|||
python3dist(backports.range) = 3.7.2
|
||||
requires: python(abi) = 3.7
|
||||
--requires --normalized-names-format pep503:
|
||||
--provides --majorver-provides --normalized-names-format pep503:
|
||||
--provides --majorver-provides --normalized-names-format pep503 --purl-provides:
|
||||
usr/lib/python2.7/site-packages/kubernetes-11.0.0b2.dist-info:
|
||||
provides: |-
|
||||
purl(pkg:pypi/kubernetes@11.0.0b2)
|
||||
python2.7dist(kubernetes) = 11~b2
|
||||
python2dist(kubernetes) = 11~b2
|
||||
requires: |-
|
||||
python(abi) = 2.7
|
||||
python2.7dist(certifi) >= 14.5.14
|
||||
python2.7dist(google-auth) >= 1.0.1
|
||||
python2.7dist(ipaddress) >= 1.0.17
|
||||
python2.7dist(python-dateutil) >= 2.5.3
|
||||
python2.7dist(pyyaml) >= 3.12
|
||||
python2.7dist(requests)
|
||||
python2.7dist(requests-oauthlib)
|
||||
python2.7dist(setuptools) >= 21
|
||||
python2.7dist(six) >= 1.9
|
||||
python2.7dist(urllib3) >= 1.24.2
|
||||
((python2.7dist(websocket-client) < 0.40 or python2.7dist(websocket-client) > 0.40) with (python2.7dist(websocket-client) < 0.41~~ or python2.7dist(websocket-client) >= 0.42) with (python2.7dist(websocket-client) < 0.42~~ or python2.7dist(websocket-client) >= 0.43) with python2.7dist(websocket-client) >= 0.32)
|
||||
usr/lib/python2.7/site-packages/zope.component-4.3.0-py2.7.egg-info:
|
||||
provides: |-
|
||||
purl(pkg:pypi/zope.component@4.3.0)
|
||||
python2.7dist(zope-component) = 4.3
|
||||
python2dist(zope-component) = 4.3
|
||||
requires: |-
|
||||
|
|
@ -980,6 +999,7 @@
|
|||
python2.7dist(zope-interface) >= 4.1
|
||||
usr/lib/python2.7/site-packages/zope.event-4.2.0.dist-info:
|
||||
provides: |-
|
||||
purl(pkg:pypi/zope.event@4.2.0)
|
||||
python2.7dist(zope-event) = 4.2
|
||||
python2dist(zope-event) = 4.2
|
||||
requires: |-
|
||||
|
|
@ -987,6 +1007,7 @@
|
|||
python2.7dist(setuptools)
|
||||
usr/lib/python2.7/site-packages/zope.interface-4.6.0.dist-info:
|
||||
provides: |-
|
||||
purl(pkg:pypi/zope.interface@4.6.0)
|
||||
python2.7dist(zope-interface) = 4.6
|
||||
python2dist(zope-interface) = 4.6
|
||||
requires: |-
|
||||
|
|
@ -994,6 +1015,7 @@
|
|||
python2.7dist(setuptools)
|
||||
usr/lib/python2.7/site-packages/zope.schema-4.4.2-py2.7.egg-info:
|
||||
provides: |-
|
||||
purl(pkg:pypi/zope.schema@4.4.2)
|
||||
python2.7dist(zope-schema) = 4.4.2
|
||||
python2dist(zope-schema) = 4.4.2
|
||||
requires: |-
|
||||
|
|
@ -1003,6 +1025,7 @@
|
|||
python2.7dist(zope-interface) >= 3.6
|
||||
usr/lib/python3.9/site-packages/zope.component-4.3.0-py3.9.egg-info:
|
||||
provides: |-
|
||||
purl(pkg:pypi/zope.component@4.3.0)
|
||||
python3.9dist(zope-component) = 4.3
|
||||
python3dist(zope-component) = 4.3
|
||||
requires: |-
|
||||
|
|
@ -1012,6 +1035,7 @@
|
|||
python3.9dist(zope-interface) >= 4.1
|
||||
usr/lib/python3.9/site-packages/zope.event-4.2.0.dist-info:
|
||||
provides: |-
|
||||
purl(pkg:pypi/zope.event@4.2.0)
|
||||
python3.9dist(zope-event) = 4.2
|
||||
python3dist(zope-event) = 4.2
|
||||
requires: |-
|
||||
|
|
@ -1019,6 +1043,7 @@
|
|||
python3.9dist(setuptools)
|
||||
usr/lib/python3.9/site-packages/zope.interface-5.1.0-py3.9.egg-info:
|
||||
provides: |-
|
||||
purl(pkg:pypi/zope.interface@5.1.0)
|
||||
python3.9dist(zope-interface) = 5.1
|
||||
python3dist(zope-interface) = 5.1
|
||||
requires: |-
|
||||
|
|
@ -1026,6 +1051,7 @@
|
|||
python3.9dist(setuptools)
|
||||
usr/lib/python3.9/site-packages/zope.schema-4.4.2-py3.9.egg-info:
|
||||
provides: |-
|
||||
purl(pkg:pypi/zope.schema@4.4.2)
|
||||
python3.9dist(zope-schema) = 4.4.2
|
||||
python3dist(zope-schema) = 4.4.2
|
||||
requires: |-
|
||||
|
|
@ -1035,11 +1061,13 @@
|
|||
python3.9dist(zope-interface) >= 3.6
|
||||
usr/lib64/python2.7/site-packages/backports.range-3.7.2-py2.7.egg-info:
|
||||
provides: |-
|
||||
purl(pkg:pypi/backports.range@3.7.2)
|
||||
python2.7dist(backports-range) = 3.7.2
|
||||
python2dist(backports-range) = 3.7.2
|
||||
requires: python(abi) = 2.7
|
||||
usr/lib64/python3.7/site-packages/backports.range-3.7.2-py3.7.egg-info:
|
||||
provides: |-
|
||||
purl(pkg:pypi/backports.range@3.7.2)
|
||||
python3.7dist(backports-range) = 3.7.2
|
||||
python3dist(backports-range) = 3.7.2
|
||||
requires: python(abi) = 3.7
|
||||
|
|
@ -1182,7 +1210,7 @@
|
|||
python(abi) = 3.9
|
||||
python3.9dist(certifi) = 2016.9.26
|
||||
--requires --normalized-names-format pep503 --package-name python3-zope-component+testing:
|
||||
--provides --majorver-provides --normalized-names-format pep503 --package-name python3-zope-component+testing:
|
||||
--provides --majorver-provides --normalized-names-format pep503 --purl-provides --package-name python3-zope-component+testing:
|
||||
usr/lib/python3.9/site-packages/zope.component-4.3.0-py3.9.egg-info:
|
||||
provides: |-
|
||||
python3.9dist(zope-component[testing]) = 4.3
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ rpm -qp --provides ${RPMDIR}/python3-zope-component-4.3.0-0.noarch.rpm | grep '^
|
|||
rpm -qp --provides ${RPMDIR}/python3-zope-component-4.3.0-0.noarch.rpm | grep '^python3dist(zope-component)'
|
||||
rpm -qp --provides ${RPMDIR}/python3-zope-component-4.3.0-0.noarch.rpm | grep '^python'$X_Y'dist(zope\.component)' && exit 1 || true
|
||||
rpm -qp --provides ${RPMDIR}/python3-zope-component-4.3.0-0.noarch.rpm | grep '^python'$X_Y'dist(zope-component)'
|
||||
rpm -qp --provides ${RPMDIR}/python3-zope-component-4.3.0-0.noarch.rpm | grep '^purl(pkg:pypi/zope\.component@4\.3\.0)$'
|
||||
|
||||
rpm -qp --requires ${RPMDIR}/python3-zope-component-4.3.0-0.noarch.rpm | grep '^python'$X_Y'dist(zope-event)'
|
||||
rpm -qp --requires ${RPMDIR}/python3-zope-component-4.3.0-0.noarch.rpm | grep '^python'$X_Y'dist(zope-interface)'
|
||||
|
|
@ -21,6 +22,7 @@ rpm -qp --provides ${RPMDIR}/python3.7-zope-component-4.3.0-0.noarch.rpm | grep
|
|||
rpm -qp --provides ${RPMDIR}/python3.7-zope-component-4.3.0-0.noarch.rpm | grep '^python3dist(zope-component)' && exit 1 || true
|
||||
rpm -qp --provides ${RPMDIR}/python3.7-zope-component-4.3.0-0.noarch.rpm | grep '^python3\.7dist(zope\.component)' && exit 1 || true
|
||||
rpm -qp --provides ${RPMDIR}/python3.7-zope-component-4.3.0-0.noarch.rpm | grep '^python3\.7dist(zope-component)'
|
||||
rpm -qp --provides ${RPMDIR}/python3.7-zope-component-4.3.0-0.noarch.rpm | grep '^purl(pkg:pypi/zope\.component@4\.3\.0)$'
|
||||
|
||||
rpm -qp --requires ${RPMDIR}/python3.7-zope-component-4.3.0-0.noarch.rpm | grep '^python3\.7dist(zope-event)'
|
||||
rpm -qp --requires ${RPMDIR}/python3.7-zope-component-4.3.0-0.noarch.rpm | grep '^python3\.7dist(zope-interface)'
|
||||
|
|
@ -30,6 +32,7 @@ rpm -qp --provides ${RPMDIR}/python3.9-zope-component-4.3.0-0.noarch.rpm | grep
|
|||
rpm -qp --provides ${RPMDIR}/python3.9-zope-component-4.3.0-0.noarch.rpm | grep '^python3dist(zope-component)' && exit 1 || true
|
||||
rpm -qp --provides ${RPMDIR}/python3.9-zope-component-4.3.0-0.noarch.rpm | grep '^python3\.9dist(zope\.component)' && exit 1 || true
|
||||
rpm -qp --provides ${RPMDIR}/python3.9-zope-component-4.3.0-0.noarch.rpm | grep '^python3\.9dist(zope-component)'
|
||||
rpm -qp --provides ${RPMDIR}/python3.9-zope-component-4.3.0-0.noarch.rpm | grep '^purl(pkg:pypi/zope\.component@4\.3\.0)$'
|
||||
|
||||
rpm -qp --requires ${RPMDIR}/python3.9-zope-component-4.3.0-0.noarch.rpm | grep '^python3\.9dist(zope-event)'
|
||||
rpm -qp --requires ${RPMDIR}/python3.9-zope-component-4.3.0-0.noarch.rpm | grep '^python3\.9dist(zope-interface)'
|
||||
|
|
@ -40,6 +43,7 @@ rpm -qp --provides ${RPMDIR}/python3.10-zope-component-4.3.0-0.noarch.rpm | grep
|
|||
rpm -qp --provides ${RPMDIR}/python3.10-zope-component-4.3.0-0.noarch.rpm | grep '^python3dist(zope-component)' && exit 1 || true
|
||||
rpm -qp --provides ${RPMDIR}/python3.10-zope-component-4.3.0-0.noarch.rpm | grep '^python3\.10dist(zope\.component)' && exit 1 || true
|
||||
rpm -qp --provides ${RPMDIR}/python3.10-zope-component-4.3.0-0.noarch.rpm | grep '^python3\.10dist(zope-component)'
|
||||
rpm -qp --provides ${RPMDIR}/python3.10-zope-component-4.3.0-0.noarch.rpm | grep '^purl(pkg:pypi/zope\.component@4\.3\.0)$'
|
||||
|
||||
rpm -qp --requires ${RPMDIR}/python3.10-zope-component-4.3.0-0.noarch.rpm | grep '^python3\.10dist(zope-event)'
|
||||
rpm -qp --requires ${RPMDIR}/python3.10-zope-component-4.3.0-0.noarch.rpm | grep '^python3\.10dist(zope-interface)'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue