Compare commits

...

32 Commits
f33 ... rawhide

Author SHA1 Message Date
Fedora Release Engineering 7b3e3b30de Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
Signed-off-by: Fedora Release Engineering <releng@fedoraproject.org>
2022-07-22 22:07:02 +00:00
Miro Hrončok cf65060b7e https://fedoraproject.org/wiki/Changes/PythonDistPEP503ProvidesOnly 2022-06-02 12:43:00 +02:00
Miro Hrončok 0bd051d514 Don't include all requirements with True-evaluating markers in extras subpackages
The idea is that the extra subpackage only has requirements specific to that extra.
The logic however only excluded requirements without markers,
but requirements with *a* marker that was correct leaked to all extras subpackages.

E.g. with the following requirements:

    Requires-Dist: base-dependency
    Requires-Dist: base-dependency-with-matching-marker ; python_version < "3.15"
    Requires-Dist: base-dependency-with-unmatching-marker ; python_version < "3.8"
    Provides-Extra: an-extra
    Requires-Dist: extra-only-dependency-with-matching-marker ; extra == 'an-extra' and python_version < "3.15"
    Requires-Dist: extra-only-dependency-with-unmatching-marker ; extra == 'an-extra' and python_version < "3.8"

On Python 3.10, the base package generated the following requirements:

    python3.10dist(base-dependency)
    python3.10dist(base-dependency-with-matching-marker)

And for the [an-extra] extra:

    python3.10dist(base-dependency-with-matching-marker)  <--- REDUNDANT, WRONG
    python3.10dist(extra-only-dependency-with-matching-marker)

Now we no longer just check if the marker evaluates to True,
but we also check that the same marker evaluates to False when the extra is not given.

A real package with this issue is build[virtualenv] 0.8.0, which we use for tests. The package has:

    Requires-Dist: tomli (>=1.0.0) ; python_version < "3.11"

And on Python 3.10, it generated the following dependency for python3-build+virtualenv-0.8.0-2.fc37.noarch.rpm:

    python3.10dist(tomli) >= 1

Now it no longer does. This is asserted in tests.

Fixes https://bugzilla.redhat.com/show_bug.cgi?id=2090186

Upstream PR: https://github.com/rpm-software-management/python-rpm-packaging/pull/16
2022-06-02 12:05:11 +02:00
Sandro Mani 76e71def2c Add namespace option to pythodistdeps.py
Co-authored-by: Miro Hrončok <miro@hroncok.cz>
2022-02-10 11:54:50 +01:00
Charalampos Stratakis ecd2f8b3f8 Add rpminspect file 2022-02-02 15:24:19 +00:00
Tomas Orsava e18b8c952c Add tests for automatically not generating Obsoletes tags on Fedora 2022-02-02 13:58:09 +01:00
Tomas Orsava b1fa63bf02 From `python3-foo` packages automatically generate `python3.X-foo` Obsoletes tags on CentOS/RHEL 2022-01-26 17:07:05 +01:00
Tomas Orsava fbd2f87265 Fix typo in lua comment 2022-01-26 17:06:51 +01:00
Fedora Release Engineering 2ff265d8fd - Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
Signed-off-by: Fedora Release Engineering <releng@fedoraproject.org>
2022-01-21 15:19:07 +00:00
Gordon Messmer 2c2f8bd984 Handle legacy version specifiers that would previously raise exceptions. 2021-12-19 14:08:05 -08:00
Gordon Messmer a3ad67b505 Additional fix for dev releases. 2021-10-29 20:00:41 -07:00
Gordon Messmer 27f9733f0b Sync dependency conversion with upstream pyreq2rpm.
Improve handling of > operator, preventing post-release from satisfying most rpm requirements.
Improve handling of < operator, preventing pre-release from satisfying rpm requirement.
Improve handling of != operator with prefix matching, preventing pre-release from satisfying rpm requirements.
2021-10-28 21:50:58 -07:00
Fedora Release Engineering 98fa009fc8 - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
Signed-off-by: Fedora Release Engineering <releng@fedoraproject.org>
2021-07-23 09:18:27 +00:00
Miro Hrončok 9bd2a43a74 Support multiple vendor files in pythonbundles.py
Not bumping the release, will happily wait until it bubbles trough.
2021-06-22 18:28:43 +00:00
Miro Hrončok 04ae9b96f2 CI: Adapt pythondist.spec for Python 3.10 being the main Python version
Preserves comaptbility with Python 3.9.
2021-06-18 19:20:16 +02:00
Tomas Orsava cc489bde7a pythondistdeps.py: Catch all exceptions and terminate build if one is raised 2021-05-25 18:51:44 +02:00
Tomas Orsava 27d363833e pythondistdeps.py: Detect and error when metadata is corrupted 2021-05-25 18:11:00 +02:00
Miro Hrončok 20f8b2c775 Fix python(abi) generator (the one written in Python)
There were three problems:

 - sys.version was not imported
 - sys.version[:3] is not reliable on Python 3.10+
 - distutils is deprecated on Python 3.10+

We were not hit by the missing import in Fedora because we only run the script
on .dist-info/.egg-info/.egg and not on .py files, so this if-branch never runs.

But when the script was fed with a .py path, it errored:

    Traceback (most recent call last):
      File "/usr/lib/rpm/pythondistdeps.py", line 344, in <module>
        purelib = get_python_lib(standard_lib=0, plat_specific=0).split(version[:3])[0]
    NameError: name 'version' is not defined

The sys.version[:3] thing kinda works for Python 3.10+ because *in this
particular case* splitting on '3.1' and taking the prefix yields the same
results as splitting on '3.10', but I consider that mere coincidence.

Finally, since the distutils import happened at module-level,
we got the Deprecation warning in all Fedora's Python packages:

    /usr/lib/rpm/pythondistdeps.py:16: DeprecationWarning: The distutils package is deprecated and slated for removal in Python 3.12

Backported from https://github.com/rpm-software-management/python-rpm-packaging/commit/d12e039037
2021-04-19 22:59:10 +02:00
Miro Hrončok 0a12aa5a2f Do not generate setuptools requirement for console_scripts on Python 3.10+
See https://fedoraproject.org/wiki/Changes/Reduce_dependencies_on_python3-setuptools
2021-03-31 11:56:16 +02:00
Tomas Orsava a295a58559 Add __pycache__ into .gitignore 2021-03-11 13:41:54 +01:00
Tomas Orsava 3a4efade98 pythondistdeps.py: Always output extras names in lowercase 2021-03-11 13:41:54 +01:00
Tomas Orsava b44c808358 pythondistdeps.py: Compare extras as lowercase
- New test sources tarball with added test data
2021-03-11 13:41:25 +01:00
Tomas Orsava 103464475f pythondistdeps.py: Changing order in test-data 2021-03-11 12:46:23 +01:00
Tomas Orsava 48510eebae scripts/pythondistdeps: Fix for Python 3.10
self.name in PathDistribution is a property in Python 3.10+ and thus we
can't redefine it as an instance variable. Instead we explicitly define
it as a property, which works on all supported Python versions.
2021-02-24 14:07:24 +01:00
Tomas Orsava 438d8d3b70 scripts/pythondistdeps: Backport switch to importlib.metadata from upstream
Upstream change to importlib.metadata: https://github.com/rpm-software-management/rpm/pull/1317

Due to extras packages being hadled slightly differently by importlib,
one test case for this was added.  And due to changes in handling
requires.txt files, comments were removed from the pyreq2rpm.tests
testing package.

Also because of the switch, we removed the dependency on setuptools and
added a dependency on packaging.

Note: Some packages with egg-info files might provide a different name
due to this change if there is a conflict between the filename and the
name in the metadata. Previously, the filename was sometimes used to
parse the name, now it is always the content of that file, which is what
packaging does, and thus also pip and other Python tooling. Currently,
this is known to affect only 1 package in Fedora (ntpsec).

The resulting script is different from upstream because of not yet upstreamed changes in Fedora:
- scripts/pythondistdeps: Rework error messages
- scripts/pythondistdeps: Add parameter --package-name
- scripts/pythondistdeps: Implement provides/requires for extras packages
- pythondistdeps.py: When parsing extras name, take the rightmost +

These changes are proposed in this upstream PR: https://github.com/rpm-software-management/rpm/pull/1546
2021-02-18 16:08:27 +01:00
Miro Hrončok 2d631762c5 Remove unused 2.7 from --majorver-provides-versions
Fixup for 8c2a1c0ac9.
This makes no real difference, just a cleanup, hence not bumping.
2021-02-08 10:53:40 +01:00
Miro Hrončok 8c2a1c0ac9 Disable the dist generators for Python 2
https://fedoraproject.org/wiki/Changes/Disable_Python_2_Dist_RPM_Generators_and_Freeze_Python_2_Macros

The regex previously matched any Python version in a form of <single digit>.<at least one digit>.

Now it matches anything from 3.0 above: <single digit (3 or higher)>.<at least one digit>

It still does not match <multiple digits>.<at least one digit>, e.g. 11.0.

This is a breaking change, hence the version bump.
2021-02-03 14:09:45 +01:00
Fedora Release Engineering b65cf8549a - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
Signed-off-by: Fedora Release Engineering <releng@fedoraproject.org>
2021-01-27 13:13:21 +00:00
Tomas Orsava f328c9dd18 Add executable bit to pythonbundles.py
pythondistdeps.py is executable already
2020-10-19 12:56:51 +02:00
Tomas Orsava d77d134c10 Run scripts in an isolated environment (#1889080) 2020-10-19 12:56:43 +02:00
Miro Hrončok bfb7f70b99 Add a test for a requires with multiple underscores 2020-09-25 14:22:03 +02:00
Tomas Orsava cb3aaf6d26 Add a test for a requires with an underscore
We already have PyQt5_sip as a test of a provides with an underscore
2020-09-23 11:39:53 +02:00
24 changed files with 830 additions and 310 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
/test-sources-2020-04-29.tar.gz
/__pycache__/
/tests/__pycache__/
/tests/data/scripts_pythondistdeps/usr/
/test-sources-2021-03-11.tar.gz

View File

@ -1,7 +1,7 @@
Name: python-rpm-generators
Summary: Dependency generators for Python RPMs
Version: 11
Release: 11%{?dist}
Version: 13
Release: 2%{?dist}
# Originally all those files were part of RPM, so license is kept here
License: GPLv2+
@ -21,11 +21,11 @@ BuildArch: noarch
%package -n python3-rpm-generators
Summary: %{summary}
Requires: python3-setuptools
Requires: python3-packaging
# We have parametric macro generators, we need RPM 4.16 (4.15.90+ is 4.16 alpha)
Requires: rpm > 4.15.90-0
# This contains the Lua functions we use:
Requires: python-srpm-macros >= 3.8-5
Requires: python-srpm-macros >= 3.10-15
%description -n python3-rpm-generators
%{summary}.
@ -47,6 +47,74 @@ install -Dpm0755 -t %{buildroot}%{_rpmconfigdir} *.py
%{_rpmconfigdir}/pythonbundles.py
%changelog
* Fri Jul 22 2022 Fedora Release Engineering <releng@fedoraproject.org> - 13-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
* Thu Jun 02 2022 Miro Hrončok <mhroncok@redhat.com> - 13-1
- https://fedoraproject.org/wiki/Changes/PythonDistPEP503ProvidesOnly
* Fri May 27 2022 Miro Hrončok <mhroncok@redhat.com> - 12-15
- Don't include all requirements with True-evaluating markers in extras subpackages
- Fixes: rhbz#2090186
* Thu Feb 10 2022 Sandro Mani <manisandro@gmail.com> - 12-14
- Add namespace option to pythodistdeps.py to allow mingw-python generatros
* Wed Jan 26 2022 Tomas Orsava <torsava@redhat.com> - 12-13
- From `python3-foo` packages automatically generate `python3.X-foo` Obsoletes
tags on CentOS/RHEL
* Fri Jan 21 2022 Fedora Release Engineering <releng@fedoraproject.org> - 12-12
- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
* Sun Dec 19 2021 Gordon Messmer <gordon.messmer@gmail.com> - 12-11
- Handle legacy version specifiers that would previously raise exceptions.
* Fri Oct 29 2021 Gordon Messmer <gordon.messmer@gmail.com> - 12-10
- Additional fix for dev releases.
* Thu Oct 28 2021 Gordon Messmer <gordon.messmer@gmail.com> - 12-9
- Sync dependency conversion with upstream pyreq2rpm.
- Improve handling of > and < operators, and != operator with prefix matching
* Fri Jul 23 2021 Fedora Release Engineering <releng@fedoraproject.org> - 12-8
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
* Tue May 25 2021 Tomas Orsava <torsava@redhat.com> - 12-7
- pythondistdeps.py: Detect missing or corrupted metadata
- pythondistdeps.py: Catch all exceptions and terminate the build if one is raised
* Mon Apr 19 2021 Miro Hrončok <mhroncok@redhat.com> - 12-6
- Get rid of distutils deprecation warning (by not using it)
- The distutils module is deprecated in Python 3.10+
- https://www.python.org/dev/peps/pep-0632/
* Wed Mar 31 2021 Miro Hrončok <mhroncok@redhat.com> - 12-5
- Do not generate setuptools requirement for console_scripts on Python 3.10+
- See https://fedoraproject.org/wiki/Changes/Reduce_dependencies_on_python3-setuptools
* Thu Mar 11 2021 Tomas Orsava <torsava@redhat.com> - 12-4
- scripts/pythondistdeps: Treat extras names case-insensitively and always
output them in lower case (#1936875)
* Mon Feb 22 2021 Tomas Orsava <torsava@redhat.com> - 12-3
- scripts/pythondistdeps: Fix for Python 3.10
* Wed Feb 17 2021 Tomas Orsava <torsava@redhat.com> - 12-2
- scripts/pythondistdeps: Switch from using pkg_resources to importlib.metadata
for reading the egg/dist-info metadata
- The script no longer requires setuptools but instead requires packaging
* Wed Feb 03 2021 Miro Hrončok <mhroncok@redhat.com> - 12-1
- Disable the dist generators for Python 2
- https://fedoraproject.org/wiki/Changes/Disable_Python_2_Dist_RPM_Generators_and_Freeze_Python_2_Macros
* Wed Jan 27 2021 Fedora Release Engineering <releng@fedoraproject.org> - 11-13
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
* Mon Oct 19 2020 Tomas Orsava <torsava@redhat.com> - 11-12
- Run scripts in an isolated Python environment (#1889080)
* Wed Jul 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 11-11
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild

43
pythonbundles.py Normal file → Executable file
View File

@ -1,4 +1,4 @@
#!/usr/bin/python3 -B
#!/usr/bin/python3 -sB
# (imports pythondistdeps from /usr/lib/rpm, hence -B)
#
# This program is free software.
@ -22,26 +22,27 @@ import pythondistdeps
pythondistdeps.parse_version = parse_version
def generate_bundled_provides(path, namespace):
def generate_bundled_provides(paths, namespace):
provides = set()
for line in path.read_text().splitlines():
line, _, comment = line.partition('#')
if comment.startswith('egg='):
# not a real comment
# e.g. git+https://github.com/monty/spam.git@master#egg=spam&...
egg, *_ = comment.strip().partition(' ')
egg, *_ = egg.strip().partition('&')
name = pythondistdeps.normalize_name(egg[4:])
provides.add(f'Provides: bundled({namespace}({name}))')
continue
line = line.strip()
if line:
name, _, version = line.partition('==')
name = pythondistdeps.normalize_name(name)
bundled_name = f"bundled({namespace}({name}))"
python_provide = pythondistdeps.convert(bundled_name, '==', version)
provides.add(f'Provides: {python_provide}')
for path in paths:
for line in path.read_text().splitlines():
line, _, comment = line.partition('#')
if comment.startswith('egg='):
# not a real comment
# e.g. git+https://github.com/monty/spam.git@master#egg=spam&...
egg, *_ = comment.strip().partition(' ')
egg, *_ = egg.strip().partition('&')
name = pythondistdeps.normalize_name(egg[4:])
provides.add(f'Provides: bundled({namespace}({name}))')
continue
line = line.strip()
if line:
name, _, version = line.partition('==')
name = pythondistdeps.normalize_name(name)
bundled_name = f"bundled({namespace}({name}))"
python_provide = pythondistdeps.convert(bundled_name, '==', version)
provides.add(f'Provides: {python_provide}')
return provides
@ -70,7 +71,7 @@ if __name__ == '__main__':
parser = argparse.ArgumentParser(prog=sys.argv[0],
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('vendored', metavar='VENDORED.TXT',
parser.add_argument('vendored', metavar='VENDORED.TXT', nargs='+', type=pathlib.Path,
help='Upstream information about vendored libraries')
parser.add_argument('-c', '--compare-with', action='store',
help='A string value to compare with and verify')
@ -78,7 +79,7 @@ if __name__ == '__main__':
help='What namespace of provides will used', default='python3dist')
args = parser.parse_args()
provides = generate_bundled_provides(pathlib.Path(args.vendored), args.namespace)
provides = generate_bundled_provides(args.vendored, args.namespace)
if args.compare_with:
given = args.compare_with.splitlines()

View File

@ -1,3 +1,3 @@
%__pythondist_provides %{_rpmconfigdir}/pythondistdeps.py --provides --normalized-names-format pep503 --package-name %{name} --normalized-names-provide-both --majorver-provides-versions 2.7,%{__default_python3_version}
%__pythondist_requires %{_rpmconfigdir}/pythondistdeps.py --requires --normalized-names-format pep503 --package-name %{name} %{?!_python_no_extras_requires:--require-extras-subpackages}
%__pythondist_path ^/usr/lib(64)?/python[[:digit:]]\\.[[:digit:]]+/site-packages/[^/]+\\.(dist-info|egg-info|egg-link)$
%__pythondist_provides %{_rpmconfigdir}/pythondistdeps.py --provides --normalized-names-format pep503 --package-name %{name} --majorver-provides-versions %{__default_python3_version}
%__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 ^/usr/lib(64)?/python[3-9]\\.[[:digit:]]+/site-packages/[^/]+\\.(dist-info|egg-info|egg-link)$

View File

@ -1,4 +1,4 @@
#!/usr/bin/python3
#!/usr/bin/python3 -s
# -*- coding: utf-8 -*-
#
# Copyright 2010 Per Øyvind Karlsen <proyvind@moondrake.org>
@ -11,21 +11,128 @@
# RPM python dependency generator, using .egg-info/.egg-link/.dist-info data
#
# Please know:
# - Notes from an attempted rewrite from pkg_resources to importlib.metadata in
# 2020 can be found in the message of the commit that added this line.
from __future__ import print_function
import argparse
from os.path import basename, dirname, isdir, sep
from sys import argv, stdin, stderr, version
from distutils.sysconfig import get_python_lib
from os.path import dirname, sep
import re
from sys import argv, stdin, stderr, version_info
from sysconfig import get_path
from warnings import warn
from packaging.requirements import Requirement as Requirement_
from packaging.version import parse
import packaging.markers
# Monkey patching packaging.markers to handle extras names in a
# case-insensitive manner:
# pip considers dnspython[DNSSEC] and dnspython[dnssec] to be equal, but
# packaging markers treat extras in a case-sensitive manner. To solve this
# issue, we introduce a comparison operator that compares case-insensitively
# if both sides of the comparison are strings. And then we inject this
# operator into packaging.markers to be used when comparing names of extras.
# Fedora BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1936875
# Upstream issue: https://discuss.python.org/t/what-extras-names-are-treated-as-equal-and-why/7614
# - After it's established upstream what is the canonical form of an extras
# name, we plan to open an issue with packaging to hopefully solve this
# there without having to resort to monkeypatching.
def str_lower_eq(a, b):
if isinstance(a, str) and isinstance(b, str):
return a.lower() == b.lower()
else:
return a == b
packaging.markers._operators["=="] = str_lower_eq
try:
from importlib.metadata import PathDistribution
except ImportError:
from importlib_metadata import PathDistribution
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
def normalize_name(name):
"""https://www.python.org/dev/peps/pep-0503/#normalized-names"""
return re.sub(r'[-_.]+', '-', name).lower()
def legacy_normalize_name(name):
"""Like pkg_resources Distribution.key property"""
return re.sub(r'[-_]+', '-', name).lower()
class Requirement(Requirement_):
def __init__(self, requirement_string):
super(Requirement, self).__init__(requirement_string)
self.normalized_name = normalize_name(self.name)
self.legacy_normalized_name = legacy_normalize_name(self.name)
class Distribution(PathDistribution):
def __init__(self, path):
super(Distribution, self).__init__(Path(path))
# Check that the initialization went well and metadata are not missing or corrupted
# name is the most important attribute, if it doesn't exist, import failed
if not self.name or not isinstance(self.name, str):
print("*** PYTHON_METADATA_FAILED_TO_PARSE_ERROR___SEE_STDERR ***")
print('Error: Python metadata at `{}` are missing or corrupted.'.format(path), file=stderr)
exit(65) # os.EX_DATAERR
self.normalized_name = normalize_name(self.name)
self.legacy_normalized_name = legacy_normalize_name(self.name)
self.requirements = [Requirement(r) for r in self.requires or []]
self.extras = [
v.lower() for k, v in self.metadata.items() if k == 'Provides-Extra']
self.py_version = self._parse_py_version(path)
# `name` is defined as a property exactly like this in Python 3.10 in the
# PathDistribution class. Due to that we can't redefine `name` as a normal
# attribute. So we copied the Python 3.10 definition here into the code so
# that it works also on previous Python/importlib_metadata versions.
@property
def name(self):
"""Return the 'Name' metadata for the distribution package."""
return self.metadata['Name']
def _parse_py_version(self, path):
# Try to parse the Python version from the path the metadata
# resides at (e.g. /usr/lib/pythonX.Y/site-packages/...)
res = re.search(r"/python(?P<pyver>\d+\.\d+)/", path)
if res:
return res.group('pyver')
# If that hasn't worked, attempt to parse it from the metadata
# directory name
res = re.search(r"-py(?P<pyver>\d+.\d+)[.-]egg-info$", path)
if res:
return res.group('pyver')
return None
def requirements_for_extra(self, extra):
extra_deps = []
# we are only interested in dependencies with extra == 'our_extra' marker
for req in self.requirements:
# no marker at all, nothing to evaluate
if not req.marker:
continue
# does the marker include extra == 'our_extra'?
# we can only evaluate the marker as a whole,
# so we evaluate it twice (using 2 different marker_envs)
# and see if it only evaluates to True with our extra
if (req.marker.evaluate(get_marker_env(self, extra)) and
not req.marker.evaluate(get_marker_env(self, None))):
extra_deps.append(req)
return extra_deps
def __repr__(self):
return '{} from {}'.format(self.name, self._path)
class RpmVersion():
def __init__(self, version_id):
version = parse_version(version_id)
version = parse(version_id)
if isinstance(version._version, str):
self.version = version._version
else:
@ -34,6 +141,12 @@ class RpmVersion():
self.pre = version._version.pre
self.dev = version._version.dev
self.post = version._version.post
# version.local is ignored as it is not expected to appear
# in public releases
# https://www.python.org/dev/peps/pep-0440/#local-version-identifiers
def is_legacy(self):
return isinstance(self.version, str)
def increment(self):
self.version[-1] += 1
@ -43,7 +156,7 @@ class RpmVersion():
return self
def __str__(self):
if isinstance(self.version, str):
if self.is_legacy():
return self.version
if self.epoch:
rpm_epoch = str(self.epoch) + ':'
@ -69,6 +182,11 @@ def convert_compatible(name, operator, version_id):
print('Invalid requirement: {} {} {}'.format(name, operator, version_id), file=stderr)
exit(65) # os.EX_DATAERR
version = RpmVersion(version_id)
if version.is_legacy():
# LegacyVersions are not supported in this context
print("*** INVALID_REQUIREMENT_ERROR___SEE_STDERR ***")
print('Invalid requirement: {} {} {}'.format(name, operator, version_id), file=stderr)
exit(65) # os.EX_DATAERR
if len(version.version) == 1:
print("*** INVALID_REQUIREMENT_ERROR___SEE_STDERR ***")
print('Invalid requirement: {} {} {}'.format(name, operator, version_id), file=stderr)
@ -101,18 +219,32 @@ def convert_not_equal(name, operator, version_id):
if version_id.endswith('.*'):
version_id = version_id[:-2]
version = RpmVersion(version_id)
lower_version = RpmVersion(version_id).increment()
if version.is_legacy():
# LegacyVersions are not supported in this context
print("*** INVALID_REQUIREMENT_ERROR___SEE_STDERR ***")
print('Invalid requirement: {} {} {}'.format(name, operator, version_id), file=stderr)
exit(65) # os.EX_DATAERR
version_gt = RpmVersion(version_id).increment()
version_gt_operator = '>='
# Prevent dev and pre-releases from satisfying a < requirement
version = '{}~~'.format(version)
else:
version = RpmVersion(version_id)
lower_version = version
return '({} < {} or {} > {})'.format(
name, version, name, lower_version)
version_gt = version
version_gt_operator = '>'
return '({} < {} or {} {} {})'.format(
name, version, name, version_gt_operator, version_gt)
def convert_ordered(name, operator, version_id):
if version_id.endswith('.*'):
# PEP 440 does not define semantics for prefix matching
# with ordered comparisons
# see: https://github.com/pypa/packaging/issues/320
# and: https://github.com/pypa/packaging/issues/321
# This style of specifier is officially "unsupported",
# even though it is processed. Support may be removed
# in version 21.0.
version_id = version_id[:-2]
version = RpmVersion(version_id)
if operator == '>':
@ -123,6 +255,14 @@ def convert_ordered(name, operator, version_id):
operator = '<'
else:
version = RpmVersion(version_id)
# For backwards compatibility, fallback to previous behavior with LegacyVersions
if not version.is_legacy():
# Prevent dev and pre-releases from satisfying a < requirement
if operator == '<' and not version.pre and not version.dev and not version.post:
version = '{}~~'.format(version)
# Prevent post-releases from satisfying a > requirement
if operator == '>' and not version.pre and not version.dev and not version.post:
version = '{}.0'.format(version)
return '{} {} {}'.format(name, operator, version)
@ -144,16 +284,26 @@ def convert(name, operator, version_id):
format(version_id, name)) from exc
def normalize_name(name):
"""https://www.python.org/dev/peps/pep-0503/#normalized-names"""
import re
return re.sub(r'[-_.]+', '-', name).lower()
def get_marker_env(dist, extra):
# packaging uses a default environment using
# platform.python_version to evaluate if a dependency is relevant
# based on environment markers [1],
# e.g. requirement `argparse;python_version<"2.7"`
#
# Since we're running this script on one Python version while
# possibly evaluating packages for different versions, we
# set up an environment with the version we want to evaluate.
#
# [1] https://www.python.org/dev/peps/pep-0508/#environment-markers
return {"python_full_version": dist.py_version,
"python_version": dist.py_version,
"extra": extra}
if __name__ == "__main__":
def main():
"""To allow this script to be importable (and its classes/functions
reused), actions are performed only when run as a main script."""
reused), actions are defined in the main function and are performed only
when run as a main script."""
parser = argparse.ArgumentParser(prog=argv[0])
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-P', '--provides', action='store_true', help='Print Provides')
@ -174,9 +324,16 @@ if __name__ == "__main__":
help='Provide both `pep503` and `legacy-dots` format of normalized names (useful for a transition period)')
parser.add_argument('-L', '--legacy-provides', action='store_true', help='Print extra legacy pythonegg Provides')
parser.add_argument('-l', '--legacy', action='store_true', help='Print legacy pythonegg Provides/Requires instead')
parser.add_argument('--console-scripts-nodep-setuptools-since', action='store',
help='An optional Python version (X.Y), at least 3.8. '
'For that version and any newer version, '
'a dependency on "setuptools" WILL NOT be generated for packages with console_scripts/gui_scripts entry points. '
'By setting this flag, you guarantee that setuptools >= 47.2.0 is used '
'during the build of packages for this and any newer Python version.')
parser.add_argument('--require-extras-subpackages', action='store_true',
help="If there is a dependency on a package with extras functionality, require the extras subpackage")
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('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()
@ -203,6 +360,15 @@ if __name__ == "__main__":
# At least one type of normalization must be provided
assert normalized_names_provide_pep503 or normalized_names_provide_legacy
if args.console_scripts_nodep_setuptools_since:
nodep_setuptools_pyversion = parse(args.console_scripts_nodep_setuptools_since)
if nodep_setuptools_pyversion < parse("3.8"):
print("Only version 3.8+ is supported in --console-scripts-nodep-setuptools-since", file=stderr)
print("*** PYTHON_EXTRAS_ARGUMENT_ERROR___SEE_STDERR ***")
exit(65) # os.EX_DATAERR
else:
nodep_setuptools_pyversion = None
# Is this script being run for an extras subpackage?
extras_subpackage = None
if args.package_name and '+' in args.package_name:
@ -214,7 +380,9 @@ if __name__ == "__main__":
# and pluses in the middle can be easily replaced with dashes.
# Python extras names don't contain pluses according to PEP 508.
package_name_parts = args.package_name.rpartition('+')
extras_subpackage = package_name_parts[2] or None
extras_subpackage = package_name_parts[2].lower() or None
namespace = (args.namespace + "({})") if args.namespace else "{}"
for f in (args.files or stdin.readlines()):
f = f.strip()
@ -224,8 +392,9 @@ if __name__ == "__main__":
if py_abi and (lower.endswith('.py') or lower.endswith('.pyc') or lower.endswith('.pyo')):
if name not in py_deps:
py_deps[name] = []
purelib = get_python_lib(standard_lib=0, plat_specific=0).split(version[:3])[0]
platlib = get_python_lib(standard_lib=0, plat_specific=1).split(version[:3])[0]
running_python_version = '{}.{}'.format(*version_info[:2])
purelib = get_path('purelib').split(running_python_version)[0]
platlib = get_path('platlib').split(running_python_version)[0]
for lib in (purelib, platlib):
if lib in f:
spec = ('==', f.split(lib)[1].split(sep)[0])
@ -243,52 +412,21 @@ if __name__ == "__main__":
if lower.endswith('.egg') or \
lower.endswith('.egg-info') or \
lower.endswith('.dist-info'):
# This import is very slow, so only do it if needed
# - Notes from an attempted rewrite from pkg_resources to
# importlib.metadata in 2020 can be found in the message of
# the commit that added this line.
from pkg_resources import Distribution, FileMetadata, PathMetadata, Requirement, parse_version
dist_name = basename(f)
if isdir(f):
path_item = dirname(f)
metadata = PathMetadata(path_item, f)
else:
path_item = f
metadata = FileMetadata(f)
dist = Distribution.from_location(path_item, dist_name, metadata)
# Check if py_version is defined in the metadata file/directory name
dist = Distribution(f)
if not dist.py_version:
# Try to parse the Python version from the path the metadata
# resides at (e.g. /usr/lib/pythonX.Y/site-packages/...)
import re
res = re.search(r"/python(?P<pyver>\d+\.\d+)/", path_item)
if res:
dist.py_version = res.group('pyver')
else:
warn("Version for {!r} has not been found".format(dist), RuntimeWarning)
continue
warn("Version for {!r} has not been found".format(dist), RuntimeWarning)
continue
# pkg_resources use platform.python_version to evaluate if a
# dependency is relevant based on environment markers [1],
# e.g. requirement `argparse;python_version<"2.7"`
#
# Since we're running this script on one Python version while
# possibly evaluating packages for different versions, we mock the
# platform.python_version function. Discussed upstream [2].
#
# [1] https://www.python.org/dev/peps/pep-0508/#environment-markers
# [2] https://github.com/pypa/setuptools/pull/1275
import platform
platform.python_version = lambda: dist.py_version
platform.python_version_tuple = lambda: tuple(dist.py_version.split('.'))
# This is the PEP 503 normalized name.
# It does also convert dots to dashes, unlike dist.key.
# See https://bugzilla.redhat.com/show_bug.cgi?id=1791530
normalized_name = normalize_name(dist.project_name)
# If we're processing an extras subpackage, check that the extras exists
if extras_subpackage and extras_subpackage not in dist.extras:
# If processing an extras subpackage:
# Check that the extras name is declared in the metadata, or
# that there are some dependencies associated with the extras
# name in the requires.txt (this is an outdated way to declare
# extras packages).
# - If there is an extras package declared only in requires.txt
# without any dependencies, this check will fail. In that case
# make sure to use updated metadata and declare the extras
# package there.
if extras_subpackage and extras_subpackage not in dist.extras and not dist.requirements_for_extra(extras_subpackage):
print("*** PYTHON_EXTRAS_NOT_FOUND_ERROR___SEE_STDERR ***")
print(f"\nError: The package name contains an extras name `{extras_subpackage}` that was not found in the metadata.\n"
"Check if the extras were removed from the project. If so, consider removing the subpackage and obsoleting it from another.\n", file=stderr)
@ -301,32 +439,32 @@ if __name__ == "__main__":
if args.provides:
extras_suffix = f"[{extras_subpackage}]" if extras_subpackage else ""
# If egg/dist metadata says package name is python, we provide python(abi)
if dist.key == 'python':
name = 'python(abi)'
if dist.normalized_name == 'python':
name = namespace.format('python(abi)')
if name not in py_deps:
py_deps[name] = []
py_deps[name].append(('==', dist.py_version))
if not args.legacy or not args.majorver_only:
if normalized_names_provide_legacy:
name = 'python{}dist({}{})'.format(dist.py_version, dist.key, extras_suffix)
name = namespace.format('python{}dist({}{})').format(dist.py_version, dist.legacy_normalized_name, extras_suffix)
if name not in py_deps:
py_deps[name] = []
if normalized_names_provide_pep503:
name_ = 'python{}dist({}{})'.format(dist.py_version, normalized_name, extras_suffix)
name_ = namespace.format('python{}dist({}{})').format(dist.py_version, dist.normalized_name, extras_suffix)
if name_ not in py_deps:
py_deps[name_] = []
if args.majorver_provides or args.majorver_only or \
(args.majorver_provides_versions and dist.py_version in args.majorver_provides_versions):
if normalized_names_provide_legacy:
pymajor_name = 'python{}dist({}{})'.format(pyver_major, dist.key, extras_suffix)
pymajor_name = namespace.format('python{}dist({}{})').format(pyver_major, dist.legacy_normalized_name, extras_suffix)
if pymajor_name not in py_deps:
py_deps[pymajor_name] = []
if normalized_names_provide_pep503:
pymajor_name_ = 'python{}dist({}{})'.format(pyver_major, normalized_name, extras_suffix)
pymajor_name_ = namespace.format('python{}dist({}{})').format(pyver_major, dist.normalized_name, extras_suffix)
if pymajor_name_ not in py_deps:
py_deps[pymajor_name_] = []
if args.legacy or args.legacy_provides:
legacy_name = 'pythonegg({})({})'.format(pyver_major, dist.key)
legacy_name = namespace.format('pythonegg({})({})').format(pyver_major, dist.legacy_normalized_name)
if legacy_name not in py_deps:
py_deps[legacy_name] = []
if dist.version:
@ -349,9 +487,9 @@ if __name__ == "__main__":
if spec not in py_deps[legacy_name]:
py_deps[legacy_name].append(spec)
if args.requires or (args.recommends and dist.extras):
name = 'python(abi)'
name = namespace.format('python(abi)')
# If egg/dist metadata says package name is python, we don't add dependency on python(abi)
if dist.key == 'python':
if dist.normalized_name == 'python':
py_abi = False
if name in py_deps:
py_deps.pop(name)
@ -361,24 +499,23 @@ if __name__ == "__main__":
spec = ('==', dist.py_version)
if spec not in py_deps[name]:
py_deps[name].append(spec)
deps = dist.requires()
if args.recommends:
depsextras = dist.requires(extras=dist.extras)
if not args.requires:
for dep in reversed(depsextras):
if dep in deps:
depsextras.remove(dep)
deps = depsextras
elif extras_subpackage:
# Extras requires also contain the base requires included
deps = [d for d in dist.requires(extras=[extras_subpackage]) if d not in dist.requires()]
# console_scripts/gui_scripts entry points need pkg_resources from setuptools
if ((dist.get_entry_map('console_scripts') or
dist.get_entry_map('gui_scripts')) and
(lower.endswith('.egg') or
lower.endswith('.egg-info'))):
# stick them first so any more specific requirement overrides it
deps.insert(0, Requirement.parse('setuptools'))
if extras_subpackage:
deps = [d for d in dist.requirements_for_extra(extras_subpackage)]
else:
deps = dist.requirements
# console_scripts/gui_scripts entry points needed pkg_resources from setuptools
# on new Python/setuptools versions, this is no longer required
if nodep_setuptools_pyversion is None or parse(dist.py_version) < nodep_setuptools_pyversion:
if (dist.entry_points and
(lower.endswith('.egg') or
lower.endswith('.egg-info'))):
groups = {ep.group for ep in dist.entry_points}
if {"console_scripts", "gui_scripts"} & groups:
# stick them first so any more specific requirement
# overrides it
deps.insert(0, Requirement('setuptools'))
# add requires/recommends based on egg/dist metadata
for dep in deps:
# Even if we're requiring `foo[bar]`, also require `foo`
@ -388,68 +525,63 @@ if __name__ == "__main__":
if args.require_extras_subpackages and dep.extras:
# A dependency can have more than one extras,
# i.e. foo[bar,baz], so let's go through all of them
extras_suffixes += [f"[{e}]" for e in dep.extras]
extras_suffixes += [f"[{e.lower()}]" for e in dep.extras]
for extras_suffix in extras_suffixes:
if normalized_names_require_pep503:
dep_normalized_name = normalize_name(dep.project_name)
dep_normalized_name = dep.normalized_name
else:
dep_normalized_name = dep.key
dep_normalized_name = dep.legacy_normalized_name
if args.legacy:
name = 'pythonegg({})({})'.format(pyver_major, dep.key)
name = namespace.format('pythonegg({})({})').format(pyver_major, dep.legacy_normalized_name)
else:
if args.majorver_only:
name = 'python{}dist({}{})'.format(pyver_major, dep_normalized_name, extras_suffix)
name = namespace.format('python{}dist({}{})').format(pyver_major, dep_normalized_name, extras_suffix)
else:
name = 'python{}dist({}{})'.format(dist.py_version, dep_normalized_name, extras_suffix)
for spec in dep.specs:
if name not in py_deps:
py_deps[name] = []
if spec not in py_deps[name]:
py_deps[name].append(spec)
if not dep.specs:
name = namespace.format('python{}dist({}{})').format(dist.py_version, dep_normalized_name, extras_suffix)
if dep.marker and not args.recommends and not extras_subpackage:
if not dep.marker.evaluate(get_marker_env(dist, '')):
continue
if name not in py_deps:
py_deps[name] = []
for spec in dep.specifier:
if (spec.operator, spec.version) not in py_deps[name]:
py_deps[name].append((spec.operator, spec.version))
# Unused, for automatic sub-package generation based on 'extras' from egg/dist metadata
# TODO: implement in rpm later, or...?
if args.extras:
deps = dist.requires()
extras = dist.extras
print(extras)
for extra in extras:
print(dist.extras)
for extra in dist.extras:
print('%%package\textras-{}'.format(extra))
print('Summary:\t{} extra for {} python package'.format(extra, dist.key))
print('Summary:\t{} extra for {} python package'.format(extra, dist.legacy_normalized_name))
print('Group:\t\tDevelopment/Python')
depsextras = dist.requires(extras=[extra])
for dep in reversed(depsextras):
if dep in deps:
depsextras.remove(dep)
deps = depsextras
for dep in deps:
for spec in dep.specs:
if spec[0] == '!=':
print('Conflicts:\t{} {} {}'.format(dep.key, '==', spec[1]))
for dep in dist.requirements_for_extra(extra):
for spec in dep.specifier:
if spec.operator == '!=':
print('Conflicts:\t{} {} {}'.format(dep.legacy_normalized_name, '==', spec.version))
else:
print('Requires:\t{} {} {}'.format(dep.key, spec[0], spec[1]))
print('Requires:\t{} {} {}'.format(dep.legacy_normalized_name, spec.operator, spec.version))
print('%%description\t{}'.format(extra))
print('{} extra for {} python package'.format(extra, dist.key))
print('{} extra for {} python package'.format(extra, dist.legacy_normalized_name))
print('%%files\t\textras-{}\n'.format(extra))
if args.conflicts:
# Should we really add conflicts for extras?
# Creating a meta package per extra with recommends on, which has
# the requires/conflicts in stead might be a better solution...
for dep in dist.requires(extras=dist.extras):
name = dep.key
for spec in dep.specs:
if spec[0] == '!=':
if name not in py_deps:
py_deps[name] = []
spec = ('==', spec[1])
if spec not in py_deps[name]:
py_deps[name].append(spec)
for dep in dist.requirements:
for spec in dep.specifier:
if spec.operator == '!=':
if dep.legacy_normalized_name not in py_deps:
py_deps[dep.legacy_normalized_name] = []
spec = ('==', spec.version)
if spec not in py_deps[dep.legacy_normalized_name]:
py_deps[dep.legacy_normalized_name].append(spec)
names = list(py_deps.keys())
names.sort()
for name in names:
for name in sorted(py_deps):
if py_deps[name]:
# Print out versioned provides, requires, recommends, conflicts
spec_list = []
@ -463,3 +595,14 @@ if __name__ == "__main__":
else:
# Print out unversioned provides, requires, recommends, conflicts
print(name)
if __name__ == "__main__":
"""To allow this script to be importable (and its classes/functions
reused), actions are performed only when run as a main script."""
try:
main()
except Exception as exc:
print("*** PYTHONDISTDEPS_GENERATORS_FAILED ***", flush=True)
raise RuntimeError("Error: pythondistdeps.py generator encountered an unhandled exception and was terminated.") from exc

View File

@ -8,7 +8,7 @@
local provides = python.python_altprovides_once(name, evr)
-- provides is either an array/table or nil
-- nil means the function was already called with the same arguments:
-- either with another file in %1 or manually via %py_provide
-- either with another file in %1 or manually via %py_provides
if provides then
for i, provide in ipairs(provides) do
print(provide .. ' ')
@ -16,4 +16,27 @@
end
}
%__pythonname_obsoletes() %{?rhel:%{lua:
-- On CentOS/RHEL we automatically generate Obsoletes tags in the form:
-- package python3-foo -> Obsoletes: python3.XY-foo
-- This provides a clean upgrade path between major versions of CentOS/RHEL.
-- In Fedora this is not needed as we don't ship ecosystem packages
-- for alternative Python interpreters.
local python = require 'fedora.srpm.python'
-- this macro is called for each file in a package, the path being in %1
-- but we don't need to know the path, so we would get for each file: Macro %1 defined but not used within scope
-- in here, we expand %name conditionally on %1 to suppress the warning
local name = rpm.expand('%{?1:%{name}}')
local evr = rpm.expand('%{?epoch:%{epoch}:}%{version}-%{release}')
local obsoletes = python.python_altobsoletes_once(name, evr)
-- obsoletes is either an array/table or nil
-- nil means the function was already called with the same arguments:
-- either with another file in %1 or manually via %py_provides
if obsoletes then
for i, obsolete in ipairs(obsoletes) do
print(obsolete .. ' ')
end
end
}}
%__pythonname_path ^/

7
rpminspect.yaml Normal file
View File

@ -0,0 +1,7 @@
# completely disabled inspections:
inspections:
# there is no upstream and the files are changed from time to time
addedfiles: off
changedfiles: off
filesize: off
upstream: off

View File

@ -1 +1 @@
SHA512 (test-sources-2020-04-29.tar.gz) = a5539fbe05a4f7128b4f82e960c3f1392a55ad53086dfd7fbc436d2743feaf64784e08667237baed3a32f149db25bc63e4ab3efc2b0270f969c59550b75102b1
SHA512 (test-sources-2021-03-11.tar.gz) = 6f34c8151625be489a6a4d56d1fd3d39b7908bd31402c9703cb65918385320dfad35f35a32920c816fb85a829f44aaf04dc1d0654ccf512e26e87e95dbf87430

17
tests/console_script.sh Executable file
View File

@ -0,0 +1,17 @@
#!/usr/bin/bash -eux
RPMDIR=$(rpm --eval '%_topdir')/RPMS/noarch
RPMPKG="${RPMDIR}/isort-5.7.0-0.noarch.rpm"
mkdir -p $(rpm --eval '%_topdir')/SOURCES/
spectool -g -R isort.spec
for py_version in 3.6 3.7 3.8 3.9; do
rpmbuild -ba --define "python3_test_version ${py_version}" isort.spec
rpm -qp --requires ${RPMPKG} | grep "python${py_version}dist(setuptools)"
done
for py_version in 3.10 3.11; do
rpmbuild -ba --define "python3_test_version ${py_version}" isort.spec
rpm -qp --requires ${RPMPKG} | grep "python${py_version}dist(setuptools)" && exit 1 || true
done

View File

@ -0,0 +1,5 @@
Provides: bundled(python3dist(appdirs)) = 1.4.3
Provides: bundled(python3dist(ordered-set)) = 3.1.1
Provides: bundled(python3dist(packaging)) = 16.8
Provides: bundled(python3dist(pyparsing)) = 2.2.1
Provides: bundled(python3dist(six)) = 1.10

View File

@ -0,0 +1,3 @@
packaging==16.8
pyparsing==2.2.1
ordered-set==3.1.1

View File

@ -0,0 +1,3 @@
Provides: bundled(python3dist(ordered-set)) = 3.1.1
Provides: bundled(python3dist(packaging)) = 16.8
Provides: bundled(python3dist(pyparsing)) = 2.2.1

View File

@ -0,0 +1 @@
Corrupted dist-info metadata

View File

@ -1,4 +1,3 @@
# Taken from pyreq2rpm, removed tests that are expected to fail
foobar0~=2.4.8
foobar1~=2.4.8.0
foobar2~=2.4.8.1
@ -91,12 +90,15 @@ pyparsing0
pyparsing1>=2.0.1,!=2.0.4,!=2.1.2,!=2.1.6
babel>=1.3,!=2.0
# Tests for breakages in Fedora
fedora-python-nb2plots==0+unknown
# Other tests
hugo1==1.0.0.dev7
hugo2<=8a4
hugo3!=11.1.1b14
hugo4>11rc0
hugo5===11.1.0.post3
test___multiple__underscores==1
test_underscores==1
dnspython[DNSSEC]

View File

@ -1,101 +1,5 @@
--requires:
--provides:
pyreq2rpm.tests-2020.04.07.024dab0-py3.9.egg-info:
provides: python3.9dist(pyreq2rpm.tests) = 2020.04.07.024dab0
requires: |-
python(abi) = 3.9
((python3.9dist(babel) < 2 or python3.9dist(babel) > 2) with python3.9dist(babel) >= 1.3)
python3.9dist(fedora-python-nb2plots) = 0
(python3.9dist(foobar0) >= 2.4.8 with python3.9dist(foobar0) < 2.5)
(python3.9dist(foobar1) >= 2.4.8 with python3.9dist(foobar1) < 2.4.9)
(python3.9dist(foobar10) >= 2^post1 with python3.9dist(foobar10) < 3)
python3.9dist(foobar11) = 2.4.8
python3.9dist(foobar12) = 2.4.8
python3.9dist(foobar13) = 2.4.8.1
(python3.9dist(foobar14) >= 2.4.8 with python3.9dist(foobar14) < 2.4.9)
python3.9dist(foobar15) = 2
python3.9dist(foobar16) = 2
(python3.9dist(foobar17) >= 2 with python3.9dist(foobar17) < 3)
python3.9dist(foobar18) = 2.4.8~b5
python3.9dist(foobar19) = 2~b5
(python3.9dist(foobar2) >= 2.4.8.1 with python3.9dist(foobar2) < 2.4.9)
python3.9dist(foobar20) = 2.4.8^post1
python3.9dist(foobar21) = 2^post1
python3.9dist(foobar22) = 2.4.8
python3.9dist(foobar23) = 2.4.8
python3.9dist(foobar24) = 2.4.8.1
python3.9dist(foobar26) = 2
python3.9dist(foobar27) = 2
python3.9dist(foobar29) = 2.4.8~b5
python3.9dist(foobar30) = 2~b5
python3.9dist(foobar31) = 2.4.8^post1
python3.9dist(foobar32) = 2^post1
(python3.9dist(foobar33) < 2.4.8 or python3.9dist(foobar33) > 2.4.8)
(python3.9dist(foobar34) < 2.4.8 or python3.9dist(foobar34) > 2.4.8)
(python3.9dist(foobar35) < 2.4.8.1 or python3.9dist(foobar35) > 2.4.8.1)
(python3.9dist(foobar36) < 2.4.8 or python3.9dist(foobar36) > 2.4.9)
(python3.9dist(foobar37) < 2 or python3.9dist(foobar37) > 2)
(python3.9dist(foobar38) < 2 or python3.9dist(foobar38) > 2)
(python3.9dist(foobar39) < 2 or python3.9dist(foobar39) > 3)
(python3.9dist(foobar4) >= 2 with python3.9dist(foobar4) < 3)
(python3.9dist(foobar40) < 2.4.8~b5 or python3.9dist(foobar40) > 2.4.8~b5)
(python3.9dist(foobar41) < 2~b5 or python3.9dist(foobar41) > 2~b5)
(python3.9dist(foobar42) < 2.4.8^post1 or python3.9dist(foobar42) > 2.4.8^post1)
(python3.9dist(foobar43) < 2^post1 or python3.9dist(foobar43) > 2^post1)
python3.9dist(foobar44) <= 2.4.8
python3.9dist(foobar45) <= 2.4.8
python3.9dist(foobar46) <= 2.4.8.1
python3.9dist(foobar47) < 2.4.8
python3.9dist(foobar48) <= 2
python3.9dist(foobar49) <= 2
python3.9dist(foobar50) < 2
python3.9dist(foobar51) <= 2.4.8~b5
python3.9dist(foobar52) <= 2~b5
python3.9dist(foobar53) <= 2.4.8^post1
python3.9dist(foobar54) <= 2^post1
python3.9dist(foobar55) < 2.4.8
python3.9dist(foobar56) < 2.4.8
python3.9dist(foobar57) < 2.4.8.1
python3.9dist(foobar58) < 2.4.8
python3.9dist(foobar59) < 2
python3.9dist(foobar60) < 2
python3.9dist(foobar61) < 2
python3.9dist(foobar62) < 2.4.8~b5
python3.9dist(foobar63) < 2~b5
python3.9dist(foobar64) < 2.4.8^post1
python3.9dist(foobar65) < 2^post1
python3.9dist(foobar66) >= 2.4.8
python3.9dist(foobar67) >= 2.4.8
python3.9dist(foobar68) >= 2.4.8.1
python3.9dist(foobar69) >= 2.4.8
(python3.9dist(foobar7) >= 2.4.8~b5 with python3.9dist(foobar7) < 2.5)
python3.9dist(foobar70) >= 2
python3.9dist(foobar71) >= 2
python3.9dist(foobar72) >= 2
python3.9dist(foobar73) >= 2.4.8~b5
python3.9dist(foobar74) >= 2~b5
python3.9dist(foobar75) >= 2.4.8^post1
python3.9dist(foobar76) >= 2^post1
python3.9dist(foobar77) > 2.4.8
python3.9dist(foobar78) > 2.4.8
python3.9dist(foobar79) > 2.4.8.1
(python3.9dist(foobar8) >= 2~b5 with python3.9dist(foobar8) < 2.1)
python3.9dist(foobar80) >= 2.4.8
python3.9dist(foobar81) > 2
python3.9dist(foobar82) > 2
python3.9dist(foobar83) >= 2
python3.9dist(foobar84) > 2.4.8~b5
python3.9dist(foobar85) > 2~b5
python3.9dist(foobar86) > 2.4.8^post1
python3.9dist(foobar87) > 2^post1
(python3.9dist(foobar9) >= 2.4.8^post1 with python3.9dist(foobar9) < 2.5)
python3.9dist(hugo1) = 1~~dev7
python3.9dist(hugo2) <= 8~a4
(python3.9dist(hugo3) < 11.1.1~b14 or python3.9dist(hugo3) > 11.1.1~b14)
python3.9dist(hugo4) > 11~rc0
python3.9dist(hugo5) = 11.1^post3
python3.9dist(pyparsing0)
((python3.9dist(pyparsing1) < 2.0.4 or python3.9dist(pyparsing1) > 2.0.4) with (python3.9dist(pyparsing1) < 2.1.2 or python3.9dist(pyparsing1) > 2.1.2) with (python3.9dist(pyparsing1) < 2.1.6 or python3.9dist(pyparsing1) > 2.1.6) with python3.9dist(pyparsing1) >= 2.0.1)
usr/lib/python3.9/site-packages/taskotron_python_versions-0.1.dev6.dist-info:
provides: python3.9dist(taskotron-python-versions) = 0.1~~dev6
requires: |-
@ -124,7 +28,7 @@
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)
((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/mistune-0.8.4-py2.7.egg-info:
provides: |-
python2.7dist(mistune) = 0.8.4
@ -171,12 +75,12 @@
python2dist(tox) = 3.14
requires: |-
python(abi) = 2.7
(python2.7dist(filelock) < 4 with python2.7dist(filelock) >= 3)
(python2.7dist(importlib-metadata) < 1 with python2.7dist(importlib-metadata) >= 0.12)
(python2.7dist(filelock) < 4~~ with python2.7dist(filelock) >= 3)
(python2.7dist(importlib-metadata) < 1~~ with python2.7dist(importlib-metadata) >= 0.12)
python2.7dist(packaging) >= 14
(python2.7dist(pluggy) < 1 with python2.7dist(pluggy) >= 0.12)
(python2.7dist(py) < 2 with python2.7dist(py) >= 1.4.17)
(python2.7dist(six) < 2 with python2.7dist(six) >= 1)
(python2.7dist(pluggy) < 1~~ with python2.7dist(pluggy) >= 0.12)
(python2.7dist(py) < 2~~ with python2.7dist(py) >= 1.4.17)
(python2.7dist(six) < 2~~ with python2.7dist(six) >= 1)
python2.7dist(toml) >= 0.9.4
python2.7dist(virtualenv) >= 14
usr/lib/python2.7/site-packages/urllib3-1.25.7-py2.7.egg-info:
@ -201,7 +105,7 @@
python(abi) = 3.7
(python3.7dist(lazy-object-proxy) >= 1.4 with python3.7dist(lazy-object-proxy) < 1.5)
(python3.7dist(six) >= 1.12 with python3.7dist(six) < 2)
(python3.7dist(typed-ast) < 1.5 with python3.7dist(typed-ast) >= 1.4)
(python3.7dist(typed-ast) < 1.5~~ with python3.7dist(typed-ast) >= 1.4)
(python3.7dist(wrapt) >= 1.11 with python3.7dist(wrapt) < 1.12)
usr/lib/python3.7/site-packages/packaging-19.0.dist-info:
provides: |-
@ -244,12 +148,12 @@
python3dist(tox) = 3.14
requires: |-
python(abi) = 3.7
(python3.7dist(filelock) < 4 with python3.7dist(filelock) >= 3)
(python3.7dist(importlib-metadata) < 1 with python3.7dist(importlib-metadata) >= 0.12)
(python3.7dist(filelock) < 4~~ with python3.7dist(filelock) >= 3)
(python3.7dist(importlib-metadata) < 1~~ with python3.7dist(importlib-metadata) >= 0.12)
python3.7dist(packaging) >= 14
(python3.7dist(pluggy) < 1 with python3.7dist(pluggy) >= 0.12)
(python3.7dist(py) < 2 with python3.7dist(py) >= 1.4.17)
(python3.7dist(six) < 2 with python3.7dist(six) >= 1)
(python3.7dist(pluggy) < 1~~ with python3.7dist(pluggy) >= 0.12)
(python3.7dist(py) < 2~~ with python3.7dist(py) >= 1.4.17)
(python3.7dist(six) < 2~~ with python3.7dist(six) >= 1)
python3.7dist(toml) >= 0.9.4
python3.7dist(virtualenv) >= 14
usr/lib/python3.9/site-packages/astroid-2.3.3.dist-info:
@ -301,7 +205,7 @@
python3.9dist(setuptools) >= 21
python3.9dist(six) >= 1.9
python3.9dist(urllib3) >= 1.24.2
((python3.9dist(websocket-client) < 0.40 or python3.9dist(websocket-client) > 0.40) with (python3.9dist(websocket-client) < 0.41 or python3.9dist(websocket-client) > 0.42) with (python3.9dist(websocket-client) < 0.42 or python3.9dist(websocket-client) > 0.43) with python3.9dist(websocket-client) >= 0.32)
((python3.9dist(websocket-client) < 0.40 or python3.9dist(websocket-client) > 0.40) with (python3.9dist(websocket-client) < 0.41~~ or python3.9dist(websocket-client) >= 0.42) with (python3.9dist(websocket-client) < 0.42~~ or python3.9dist(websocket-client) >= 0.43) with python3.9dist(websocket-client) >= 0.32)
usr/lib/python3.9/site-packages/mistune-0.8.4-py3.9.egg-info:
provides: |-
python3.9dist(mistune) = 0.8.4
@ -356,11 +260,11 @@
python3dist(tox) = 3.14
requires: |-
python(abi) = 3.9
(python3.9dist(filelock) < 4 with python3.9dist(filelock) >= 3)
(python3.9dist(filelock) < 4~~ with python3.9dist(filelock) >= 3)
python3.9dist(packaging) >= 14
(python3.9dist(pluggy) < 1 with python3.9dist(pluggy) >= 0.12)
(python3.9dist(py) < 2 with python3.9dist(py) >= 1.4.17)
(python3.9dist(six) < 2 with python3.9dist(six) >= 1)
(python3.9dist(pluggy) < 1~~ with python3.9dist(pluggy) >= 0.12)
(python3.9dist(py) < 2~~ with python3.9dist(py) >= 1.4.17)
(python3.9dist(six) < 2~~ with python3.9dist(six) >= 1)
python3.9dist(toml) >= 0.9.4
python3.9dist(virtualenv) >= 14
usr/lib/python3.9/site-packages/urllib3-1.25.7-py3.9.egg-info:
@ -533,7 +437,7 @@
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)
((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/mistune-0.8.4-py2.7.egg-info:
provides: |-
python2.7dist(mistune) = 0.8.4
@ -585,7 +489,7 @@
python(abi) = 3.7
(python3.7dist(lazy-object-proxy) >= 1.4 with python3.7dist(lazy-object-proxy) < 1.5)
(python3.7dist(six) >= 1.12 with python3.7dist(six) < 2)
(python3.7dist(typed-ast) < 1.5 with python3.7dist(typed-ast) >= 1.4)
(python3.7dist(typed-ast) < 1.5~~ with python3.7dist(typed-ast) >= 1.4)
(python3.7dist(wrapt) >= 1.11 with python3.7dist(wrapt) < 1.12)
usr/lib/python3.7/site-packages/packaging-19.0.dist-info:
provides: python3.7dist(packaging) = 19
@ -650,11 +554,11 @@
python3dist(tox) = 3.14
requires: |-
python(abi) = 3.9
(python3.9dist(filelock) < 4 with python3.9dist(filelock) >= 3)
(python3.9dist(filelock) < 4~~ with python3.9dist(filelock) >= 3)
python3.9dist(packaging) >= 14
(python3.9dist(pluggy) < 1 with python3.9dist(pluggy) >= 0.12)
(python3.9dist(py) < 2 with python3.9dist(py) >= 1.4.17)
(python3.9dist(six) < 2 with python3.9dist(six) >= 1)
(python3.9dist(pluggy) < 1~~ with python3.9dist(pluggy) >= 0.12)
(python3.9dist(py) < 2~~ with python3.9dist(py) >= 1.4.17)
(python3.9dist(six) < 2~~ with python3.9dist(six) >= 1)
python3.9dist(toml) >= 0.9.4
python3.9dist(virtualenv) >= 14
usr/lib/python3.9/site-packages/urllib3-1.25.7-py3.9.egg-info:
@ -707,12 +611,12 @@
python2dist(tox) = 3.14
requires: |-
python(abi) = 2.7
(python2.7dist(filelock) < 4 with python2.7dist(filelock) >= 3)
(python2.7dist(importlib-metadata) < 1 with python2.7dist(importlib-metadata) >= 0.12)
(python2.7dist(filelock) < 4~~ with python2.7dist(filelock) >= 3)
(python2.7dist(importlib-metadata) < 1~~ with python2.7dist(importlib-metadata) >= 0.12)
python2.7dist(packaging) >= 14
(python2.7dist(pluggy) < 1 with python2.7dist(pluggy) >= 0.12)
(python2.7dist(py) < 2 with python2.7dist(py) >= 1.4.17)
(python2.7dist(six) < 2 with python2.7dist(six) >= 1)
(python2.7dist(pluggy) < 1~~ with python2.7dist(pluggy) >= 0.12)
(python2.7dist(py) < 2~~ with python2.7dist(py) >= 1.4.17)
(python2.7dist(six) < 2~~ with python2.7dist(six) >= 1)
python2.7dist(toml) >= 0.9.4
python2.7dist(virtualenv) >= 14
usr/lib/python2.7/site-packages/urllib3-1.25.7-py2.7.egg-info:
@ -749,12 +653,12 @@
provides: python3.7dist(tox) = 3.14
requires: |-
python(abi) = 3.7
(python3.7dist(filelock) < 4 with python3.7dist(filelock) >= 3)
(python3.7dist(importlib-metadata) < 1 with python3.7dist(importlib-metadata) >= 0.12)
(python3.7dist(filelock) < 4~~ with python3.7dist(filelock) >= 3)
(python3.7dist(importlib-metadata) < 1~~ with python3.7dist(importlib-metadata) >= 0.12)
python3.7dist(packaging) >= 14
(python3.7dist(pluggy) < 1 with python3.7dist(pluggy) >= 0.12)
(python3.7dist(py) < 2 with python3.7dist(py) >= 1.4.17)
(python3.7dist(six) < 2 with python3.7dist(six) >= 1)
(python3.7dist(pluggy) < 1~~ with python3.7dist(pluggy) >= 0.12)
(python3.7dist(py) < 2~~ with python3.7dist(py) >= 1.4.17)
(python3.7dist(six) < 2~~ with python3.7dist(six) >= 1)
python3.7dist(toml) >= 0.9.4
python3.7dist(virtualenv) >= 14
usr/lib64/python2.7/site-packages/scipy-1.2.1.dist-info:
@ -858,7 +762,24 @@
python3.9dist(simplejson) = 3.16
python3dist(simplejson) = 3.16
requires: python(abi) = 3.9
--requires --normalized-names-format legacy-dots:
--requires --namespace mingw64:
--provides --namespace mingw64:
usr/lib/python3.9/site-packages/taskotron_python_versions-0.1.dev6.dist-info:
provides: mingw64(python3.9dist(taskotron-python-versions)) = 0.1~~dev6
requires: |-
mingw64(python(abi)) = 3.9
mingw64(python3.9dist(libarchive-c))
mingw64(python3.9dist(python-bugzilla))
--requires --console-scripts-nodep-setuptools-since 3.7:
--provides --console-scripts-nodep-setuptools-since 3.6:
usr/lib/python3.9/site-packages/zope.component-4.3.0-py3.9.egg-info:
stderr:
provides: Only version 3.8+ is supported in --console-scripts-nodep-setuptools-since
requires: Only version 3.8+ is supported in --console-scripts-nodep-setuptools-since
stdout:
provides: '*** PYTHON_EXTRAS_ARGUMENT_ERROR___SEE_STDERR ***'
requires: '*** PYTHON_EXTRAS_ARGUMENT_ERROR___SEE_STDERR ***'
--requires --normalized-names-format legacy-dots --console-scripts-nodep-setuptools-since 3.10:
--provides --majorver-provides --normalized-names-format legacy-dots:
usr/lib/python2.7/site-packages/zope.component-4.3.0-py2.7.egg-info:
provides: |-
@ -896,16 +817,19 @@
provides: |-
python3.10dist(setuptools) = 41.6
python3dist(setuptools) = 41.6
requires: |-
python(abi) = 3.10
python3.10dist(setuptools)
requires: python(abi) = 3.10
usr/lib/python3.11/site-packages/pip-20.0.2-py3.11.egg-info:
provides: |-
python3.11dist(pip) = 20.0.2
python3dist(pip) = 20.0.2
requires: python(abi) = 3.11
usr/lib/python3.8/site-packages/pip-20.0.2-py3.8.egg-info:
provides: |-
python3.8dist(pip) = 20.0.2
python3dist(pip) = 20.0.2
requires: |-
python(abi) = 3.11
python3.11dist(setuptools)
python(abi) = 3.8
python3.8dist(setuptools)
usr/lib/python3.9/site-packages/zope.component-4.3.0-py3.9.egg-info:
provides: |-
python3.9dist(zope.component) = 4.3
@ -1214,6 +1138,49 @@
python3dist(backports-range) = 3.7.2
python3dist(backports.range) = 3.7.2
requires: python(abi) = 3.7
--requires --normalized-names-format pep503 --package-name python3-dns+DNSSEC:
--provides --majorver-provides --normalized-names-format pep503 --package-name python3-dns+DNSSEC:
usr/lib/python3.9/site-packages/dnspython-2.1.0-py3.9.egg-info:
provides: |-
python3.9dist(dnspython[dnssec]) = 2.1
python3dist(dnspython[dnssec]) = 2.1
requires: |-
python(abi) = 3.9
python3.9dist(cryptography) >= 2.6
--requires --normalized-names-format pep503 --package-name python3-dns+Dnssec:
--provides --majorver-provides --normalized-names-format pep503 --package-name python3-dns+Dnssec:
usr/lib/python3.9/site-packages/dnspython-2.1.0-py3.9.egg-info:
provides: |-
python3.9dist(dnspython[dnssec]) = 2.1
python3dist(dnspython[dnssec]) = 2.1
requires: |-
python(abi) = 3.9
python3.9dist(cryptography) >= 2.6
--requires --normalized-names-format pep503 --package-name python3-dns+dnssec:
--provides --majorver-provides --normalized-names-format pep503 --package-name python3-dns+dnssec:
usr/lib/python3.9/site-packages/dnspython-2.1.0-py3.9.egg-info:
provides: |-
python3.9dist(dnspython[dnssec]) = 2.1
python3dist(dnspython[dnssec]) = 2.1
requires: |-
python(abi) = 3.9
python3.9dist(cryptography) >= 2.6
usr/lib/python3.9/site-packages/dnspython-2.1.0.dist-info:
provides: |-
python3.9dist(dnspython[dnssec]) = 2.1
python3dist(dnspython[dnssec]) = 2.1
requires: |-
python(abi) = 3.9
python3.9dist(cryptography) >= 2.6
--requires --normalized-names-format pep503 --package-name python3-setuptools+certs:
--provides --majorver-provides --normalized-names-format pep503 --package-name python3-setuptools+certs:
usr/lib/python3.9/site-packages/setuptools-41.6.0.dist-info:
provides: |-
python3.9dist(setuptools[certs]) = 41.6
python3dist(setuptools[certs]) = 41.6
requires: |-
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:
usr/lib/python3.9/site-packages/zope.component-4.3.0-py3.9.egg-info:
@ -1246,6 +1213,117 @@
python3.9dist(coverage)
python3.9dist(nose)
python3.9dist(zope-testing)
--requires --normalized-names-format pep503 --require-extras-subpackages:
--provides --normalized-names-format pep503:
corrupted.dist-info:
stderr:
provides: |-
Error: Python metadata at `*/corrupted.dist-info` are missing or corrupted.
requires: |-
Error: Python metadata at `*/corrupted.dist-info` are missing or corrupted.
stdout:
provides: '*** PYTHON_METADATA_FAILED_TO_PARSE_ERROR___SEE_STDERR ***'
requires: '*** PYTHON_METADATA_FAILED_TO_PARSE_ERROR___SEE_STDERR ***'
pyreq2rpm.tests-2020.04.07.024dab0-py3.9.egg-info:
provides: python3.9dist(pyreq2rpm-tests) = 2020.04.07.024dab0
requires: |-
python(abi) = 3.9
((python3.9dist(babel) < 2 or python3.9dist(babel) > 2) with python3.9dist(babel) >= 1.3)
python3.9dist(dnspython)
python3.9dist(dnspython[dnssec])
python3.9dist(fedora-python-nb2plots) = 0
(python3.9dist(foobar0) >= 2.4.8 with python3.9dist(foobar0) < 2.5)
(python3.9dist(foobar1) >= 2.4.8 with python3.9dist(foobar1) < 2.4.9)
(python3.9dist(foobar10) >= 2^post1 with python3.9dist(foobar10) < 3)
python3.9dist(foobar11) = 2.4.8
python3.9dist(foobar12) = 2.4.8
python3.9dist(foobar13) = 2.4.8.1
(python3.9dist(foobar14) >= 2.4.8 with python3.9dist(foobar14) < 2.4.9)
python3.9dist(foobar15) = 2
python3.9dist(foobar16) = 2
(python3.9dist(foobar17) >= 2 with python3.9dist(foobar17) < 3)
python3.9dist(foobar18) = 2.4.8~b5
python3.9dist(foobar19) = 2~b5
(python3.9dist(foobar2) >= 2.4.8.1 with python3.9dist(foobar2) < 2.4.9)
python3.9dist(foobar20) = 2.4.8^post1
python3.9dist(foobar21) = 2^post1
python3.9dist(foobar22) = 2.4.8
python3.9dist(foobar23) = 2.4.8
python3.9dist(foobar24) = 2.4.8.1
python3.9dist(foobar26) = 2
python3.9dist(foobar27) = 2
python3.9dist(foobar29) = 2.4.8~b5
python3.9dist(foobar30) = 2~b5
python3.9dist(foobar31) = 2.4.8^post1
python3.9dist(foobar32) = 2^post1
(python3.9dist(foobar33) < 2.4.8 or python3.9dist(foobar33) > 2.4.8)
(python3.9dist(foobar34) < 2.4.8 or python3.9dist(foobar34) > 2.4.8)
(python3.9dist(foobar35) < 2.4.8.1 or python3.9dist(foobar35) > 2.4.8.1)
(python3.9dist(foobar36) < 2.4.8~~ or python3.9dist(foobar36) >= 2.4.9)
(python3.9dist(foobar37) < 2 or python3.9dist(foobar37) > 2)
(python3.9dist(foobar38) < 2 or python3.9dist(foobar38) > 2)
(python3.9dist(foobar39) < 2~~ or python3.9dist(foobar39) >= 3)
(python3.9dist(foobar4) >= 2 with python3.9dist(foobar4) < 3)
(python3.9dist(foobar40) < 2.4.8~b5 or python3.9dist(foobar40) > 2.4.8~b5)
(python3.9dist(foobar41) < 2~b5 or python3.9dist(foobar41) > 2~b5)
(python3.9dist(foobar42) < 2.4.8^post1 or python3.9dist(foobar42) > 2.4.8^post1)
(python3.9dist(foobar43) < 2^post1 or python3.9dist(foobar43) > 2^post1)
python3.9dist(foobar44) <= 2.4.8
python3.9dist(foobar45) <= 2.4.8
python3.9dist(foobar46) <= 2.4.8.1
python3.9dist(foobar47) < 2.4.8~~
python3.9dist(foobar48) <= 2
python3.9dist(foobar49) <= 2
python3.9dist(foobar50) < 2~~
python3.9dist(foobar51) <= 2.4.8~b5
python3.9dist(foobar52) <= 2~b5
python3.9dist(foobar53) <= 2.4.8^post1
python3.9dist(foobar54) <= 2^post1
python3.9dist(foobar55) < 2.4.8~~
python3.9dist(foobar56) < 2.4.8~~
python3.9dist(foobar57) < 2.4.8.1~~
python3.9dist(foobar58) < 2.4.8~~
python3.9dist(foobar59) < 2~~
python3.9dist(foobar60) < 2~~
python3.9dist(foobar61) < 2~~
python3.9dist(foobar62) < 2.4.8~b5
python3.9dist(foobar63) < 2~b5
python3.9dist(foobar64) < 2.4.8^post1
python3.9dist(foobar65) < 2^post1
python3.9dist(foobar66) >= 2.4.8
python3.9dist(foobar67) >= 2.4.8
python3.9dist(foobar68) >= 2.4.8.1
python3.9dist(foobar69) >= 2.4.8
(python3.9dist(foobar7) >= 2.4.8~b5 with python3.9dist(foobar7) < 2.5)
python3.9dist(foobar70) >= 2
python3.9dist(foobar71) >= 2
python3.9dist(foobar72) >= 2
python3.9dist(foobar73) >= 2.4.8~b5
python3.9dist(foobar74) >= 2~b5
python3.9dist(foobar75) >= 2.4.8^post1
python3.9dist(foobar76) >= 2^post1
python3.9dist(foobar77) > 2.4.8.0
python3.9dist(foobar78) > 2.4.8.0
python3.9dist(foobar79) > 2.4.8.1.0
(python3.9dist(foobar8) >= 2~b5 with python3.9dist(foobar8) < 2.1)
python3.9dist(foobar80) >= 2.4.8
python3.9dist(foobar81) > 2.0
python3.9dist(foobar82) > 2.0
python3.9dist(foobar83) >= 2
python3.9dist(foobar84) > 2.4.8~b5
python3.9dist(foobar85) > 2~b5
python3.9dist(foobar86) > 2.4.8^post1
python3.9dist(foobar87) > 2^post1
(python3.9dist(foobar9) >= 2.4.8^post1 with python3.9dist(foobar9) < 2.5)
python3.9dist(hugo1) = 1~~dev7
python3.9dist(hugo2) <= 8~a4
(python3.9dist(hugo3) < 11.1.1~b14 or python3.9dist(hugo3) > 11.1.1~b14)
python3.9dist(hugo4) > 11~rc0
python3.9dist(hugo5) = 11.1^post3
python3.9dist(pyparsing0)
((python3.9dist(pyparsing1) < 2.0.4 or python3.9dist(pyparsing1) > 2.0.4) with (python3.9dist(pyparsing1) < 2.1.2 or python3.9dist(pyparsing1) > 2.1.2) with (python3.9dist(pyparsing1) < 2.1.6 or python3.9dist(pyparsing1) > 2.1.6) with python3.9dist(pyparsing1) >= 2.0.1)
python3.9dist(test-multiple-underscores) = 1
python3.9dist(test-underscores) = 1
--requires --normalized-names-format pep503 --require-extras-subpackages --package-name python3-zope-component+missing:
--provides --majorver-provides --normalized-names-format pep503 --package-name python3-zope-component+missing:
usr/lib/python3.9/site-packages/zope.component-4.3.0-py3.9.egg-info:
@ -1275,4 +1353,13 @@
python3.9dist(zope-component[security])
python3.9dist(zope-component[zcml])
python3.9dist(zope-testing)
--requires --normalized-names-format pep503 --require-extras-subpackages --package-name python3-build+virtualenv:
--provides --majorver-provides --normalized-names-format pep503 --package-name python3-build+virtualenv:
usr/lib/python3.10/site-packages/build-0.8.0.dist-info:
provides: |-
python3.10dist(build[virtualenv]) = 0.8
python3dist(build[virtualenv]) = 0.8
requires: |-
python(abi) = 3.10
python3.10dist(virtualenv) >= 20.0.35

View File

@ -8,7 +8,7 @@ pip:
'19.1.1': ['2.7', '3.7']
'20.0.2': ['3.9']
sdist:
'20.0.2': ['3.11']
'20.0.2': ['3.8', '3.11']
packaging:
wheel:
'19.0': ['2.7', '3.7']
@ -95,3 +95,11 @@ fsleyes:
taskotron-python-versions:
wheel:
'0.1.dev6': ['3.9']
dnspython:
sdist:
'2.1.0': ['3.9']
wheel:
'2.1.0': ['3.9']
build:
wheel:
'0.8.0': ['3.10']

36
tests/isort.spec Normal file
View File

@ -0,0 +1,36 @@
Name: isort
Version: 5.7.0
Release: 0
Summary: A Python package with a console_scripts entrypoint
License: MIT
Source0: %{pypi_source}
BuildArch: noarch
BuildRequires: python3-devel
BuildRequires: python3-setuptools
# Turn off Python bytecode compilation because the build would fail without Python %%{python3_test_version}
%define __brp_python_bytecompile %{nil}
%description
...
%prep
%autosetup
%build
%py3_build
%install
%py3_install
# A fake installation by a different Python version:
%if "%{python3_version}" != "%{python3_test_version}"
mv %{buildroot}%{_prefix}/lib/python%{python3_version} \
%{buildroot}%{_prefix}/lib/python%{python3_test_version}
mv %{buildroot}%{_prefix}/lib/python%{python3_test_version}/site-packages/%{name}-%{version}-py%{python3_version}.egg-info \
%{buildroot}%{_prefix}/lib/python%{python3_test_version}/site-packages/%{name}-%{version}-py%{python3_test_version}.egg-info
%endif
%files
%{_bindir}/%{name}*
%{_prefix}/lib/python%{python3_test_version}/site-packages/%{name}*

View File

@ -8,9 +8,9 @@ spectool -g -R pythondist.spec
rpmbuild -ba pythondist.spec
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 '^python3dist(zope\.component)' && exit 1 || true
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)'
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 --requires ${RPMDIR}/python3-zope-component-4.3.0-0.noarch.rpm | grep '^python'$X_Y'dist(zope-event)'
@ -19,17 +19,28 @@ rpm -qp --requires ${RPMDIR}/python3-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 '^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)'
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 --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)'
if [ "$X_Y" != "3.9" ]; then
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 '^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 --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)'
fi
if [ "$X_Y" != "3.10" ]; then
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 '^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)'
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 --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)'
fi

View File

@ -24,10 +24,19 @@ Summary: ...
%description -n python3.7-zope-component
...
%if v"%{python3_version}" != v"3.9"
%package -n python3.9-zope-component
Summary: ...
%description -n python3.9-zope-component
...
%endif
%if v"%{python3_version}" != v"3.10"
%package -n python3.10-zope-component
Summary: ...
%description -n python3.10-zope-component
...
%endif
%prep
%autosetup -n zope.component-%{version}
@ -42,9 +51,17 @@ mkdir -p %{buildroot}/usr/lib/python3.7/site-packages
cp -a %{buildroot}%{python3_sitelib}/zope.component-%{version}-py%{python3_version}.egg-info \
%{buildroot}/usr/lib/python3.7/site-packages/zope.component-%{version}-py3.7.egg-info
%if v"%{python3_version}" != v"3.9"
mkdir -p %{buildroot}/usr/lib/python3.9/site-packages
cp -a %{buildroot}%{python3_sitelib}/zope.component-%{version}-py%{python3_version}.egg-info \
%{buildroot}/usr/lib/python3.9/site-packages/zope.component-%{version}-py3.9.egg-info
%endif
%if v"%{python3_version}" != v"3.10"
mkdir -p %{buildroot}/usr/lib/python3.10/site-packages
cp -a %{buildroot}%{python3_sitelib}/zope.component-%{version}-py%{python3_version}.egg-info \
%{buildroot}/usr/lib/python3.10/site-packages/zope.component-%{version}-py3.10.egg-info
%endif
%files -n python3-zope-component
%license LICENSE.txt
@ -54,6 +71,14 @@ cp -a %{buildroot}%{python3_sitelib}/zope.component-%{version}-py%{python3_versi
%license LICENSE.txt
/usr/lib/python3.7/site-packages/zope.component-%{version}-py3.7.egg-info/
%if v"%{python3_version}" != v"3.9"
%files -n python3.9-zope-component
%license LICENSE.txt
/usr/lib/python3.9/site-packages/zope.component-%{version}-py3.9.egg-info/
%endif
%if v"%{python3_version}" != v"3.10"
%files -n python3.10-zope-component
%license LICENSE.txt
/usr/lib/python3.10/site-packages/zope.component-%{version}-py3.10.egg-info/
%endif

View File

@ -34,3 +34,43 @@ test $(rpm -qp --provides ${RPMDIR}/python3-python_provide-0-0.noarch.rpm | grep
echo "Provides for python3-py_provides"
rpm -qp --provides ${RPMDIR}/python3-py_provides-0-0.noarch.rpm
test $(rpm -qp --provides ${RPMDIR}/python3-py_provides-0-0.noarch.rpm | grep python-py_provides | wc -l) -eq 1
echo "Obsoletes for python${X_Y}-foo"
rpm -qp --obsoletes ${RPMDIR}/python${X_Y}-foo-0-0.noarch.rpm
test $(rpm -qp --obsoletes ${RPMDIR}/python${X_Y}-foo-0-0.noarch.rpm | wc -l) -eq 0
echo "Obsoletes for python3-foo"
rpm -qp --obsoletes ${RPMDIR}/python3-foo-0-0.noarch.rpm
# In ELN/RHEL the pythonX.Y-* Obsoletes is generated, but not in Fedora, so we check for it explicitly.
rpm -qp --obsoletes ${RPMDIR}/python3-foo-0-0.noarch.rpm | grep -q '^python'${X_Y}'-py_provides < 0-0$' && exit 1 || true
test $(rpm -qp --obsoletes ${RPMDIR}/python3-foo-0-0.noarch.rpm | wc -l) -eq 0
echo "Obsoletes for python2-foo"
rpm -qp --obsoletes ${RPMDIR}/python2-foo-0-0.noarch.rpm
test $(rpm -qp --obsoletes ${RPMDIR}/python2-foo-0-0.noarch.rpm | wc -l) -eq 0
echo "Obsoletes for python-foo"
rpm -qp --obsoletes ${RPMDIR}/python-foo-0-0.noarch.rpm
test $(rpm -qp --obsoletes ${RPMDIR}/python-foo-0-0.noarch.rpm | wc -l) -eq 0
echo "Obsoletes for python3.5-foo"
rpm -qp --obsoletes ${RPMDIR}/python3.5-foo-0-0.noarch.rpm
test $(rpm -qp --obsoletes ${RPMDIR}/python3.5-foo-0-0.noarch.rpm | wc -l) -eq 0
echo "Obsoletes for python3-python_provide"
rpm -qp --obsoletes ${RPMDIR}/python3-python_provide-0-0.noarch.rpm
# The deprecated %python_provide macro always obsoletes python-foo
rpm -qp --obsoletes ${RPMDIR}/python3-python_provide-0-0.noarch.rpm | grep -q '^python-python_provide < 0-0$'
# In ELN/RHEL the pythonX.Y-* Obsoletes is generated, but not in Fedora, so we check for it explicitly.
rpm -qp --obsoletes ${RPMDIR}/python3-python_provide-0-0.noarch.rpm | grep -q '^python'${X_Y}'-python_provide < 0-0$' && exit 1 || true
test $(rpm -qp --obsoletes ${RPMDIR}/python3-python_provide-0-0.noarch.rpm | grep python-python_provide | wc -l) -eq 1
test $(rpm -qp --obsoletes ${RPMDIR}/python3-python_provide-0-0.noarch.rpm | wc -l) -eq 1
echo "Obsoletes for python3-py_provides"
rpm -qp --obsoletes ${RPMDIR}/python3-py_provides-0-0.noarch.rpm
rpm -qp --obsoletes ${RPMDIR}/python3-py_provides-0-0.noarch.rpm | grep -q '^python-py_provides < 0-0$' && exit 1 || true
# In ELN/RHEL the pythonX.Y-* Obsoletes is generated, but not in Fedora, so we check for it explicitly.
rpm -qp --obsoletes ${RPMDIR}/python3-py_provides-0-0.noarch.rpm | grep -q '^python'${X_Y}'-py_provides < 0-0$' && exit 1 || true
test $(rpm -qp --obsoletes ${RPMDIR}/python3-py_provides-0-0.noarch.rpm | wc -l) -eq 0

View File

@ -34,7 +34,7 @@ def run_pythonbundles(*args, success=True):
return cp
projects = pytest.mark.parametrize('project', ('pkg_resources', 'pip', 'pipenv'))
projects = pytest.mark.parametrize('project', ('pkg_resources', 'pip', 'pipenv', 'setuptools'))
@projects
@ -97,3 +97,23 @@ def test_compare_with_unexpected(project):
cp = run_pythonbundles(TEST_DATA / f'{project}.in', '--compare-with', longer, success=False)
assert cp.stdout == '', cp.stdout
assert cp.stderr == f'Redundant unexpected provides:\n + {unexpected}\n', cp.stderr
combo_order = pytest.mark.parametrize('projects', ['pkg_resources-setuptools', 'setuptools-pkg_resources'])
@combo_order
def test_multiple_vendor_files_output(projects):
cp = run_pythonbundles(*(TEST_DATA / f'{p}.in' for p in projects.split('-')))
expected = (TEST_DATA / 'pkg_resources_setuptools.out').read_text()
assert cp.stdout == expected, cp.stdout
assert cp.stderr == '', cp.stderr
@combo_order
def test_multiple_vendor_files_compare_with(projects):
expected = (TEST_DATA / 'pkg_resources_setuptools.out').read_text()
cp = run_pythonbundles(*(TEST_DATA / f'{p}.in' for p in projects.split('-')),
'--compare-with', expected)
assert cp.stdout == '', cp.stdout
assert cp.stderr == '', cp.stderr

View File

@ -29,6 +29,7 @@
from pathlib import Path
from fnmatch import fnmatch
import pytest
import shlex
import shutil
@ -224,8 +225,21 @@ def fixture_check_and_install_test_data():
def test_pythondistdeps(provides_params, requires_params, dist_egg_info_path, expected):
"""Runs pythondistdeps with the given parameters and dist-info/egg-info
path, compares the results with the expected results"""
expect_failure = "stderr" in expected
assert expected == run_pythondistdeps(provides_params, requires_params, dist_egg_info_path, expect_failure)
tested = run_pythondistdeps(provides_params, requires_params, dist_egg_info_path, expect_failure)
if expect_failure:
for k1, k2 in ((k1, k2) for k1 in expected.keys() for k2 in expected[k1].keys()):
if k1 == "stderr":
# Some stderr messages contain full file paths. To get around
# this, asterisk is used in the test-data and we compare with
# fnmatch that understands Unix-style wildcards.
assert fnmatch(tested[k1][k2], expected[k1][k2])
else:
assert expected[k1][k2] == tested[k1][k2]
else:
assert expected == tested
if __name__ == "__main__":

View File

@ -27,6 +27,9 @@
- pythondist:
dir: .
run: ./pythondist.sh
- console_script:
dir: .
run: ./console_script.sh
- prepare-test-data:
dir: .
run: tar -xvf test-sources-*.tar.gz -C ./tests/data/scripts_pythondistdeps/

View File

@ -3,6 +3,7 @@
#
# Requirements:
# - pip >= 20.0.1
# - poetry # Due to bug: https://github.com/pypa/pip/issues/9701
#
# First prune old test data