Compare commits

...

No commits in common. "c9" and "c8" have entirely different histories.
c9 ... c8

8 changed files with 299 additions and 816 deletions

View File

View File

@ -1,27 +1,4 @@
%__python_provides() %{lua: %__python_provides %{_rpmconfigdir}/pythondistdeps.py --provides --majorver-provides-versions @MAJORVER-PROVIDES-VERSIONS@
-- Match buildroot/payload paths of the form %__python_requires %{_rpmconfigdir}/pythondeps.sh --requires
-- /PATH/OF/BUILDROOT/usr/bin/pythonMAJOR.MINOR %__python_path ^((/usr/lib(64)?/python[[:digit:]]+\\.[[:digit:]]+/site-packages/[^/]+\\.(dist-info|egg-info|egg-link))|(/usr/lib(64)?/python[[:digit:]]+\\.[[:digit:]]+/.*\\.(py[oc]?|so))|(%{_bindir}/python[[:digit:]]+\\.[[:digit:]]+))$
-- generating a line of the form %__python_magic [Pp]ython.*(executable|byte-compiled)
-- python(abi) = MAJOR.MINOR
-- (Don't match against -config tools e.g. /usr/bin/python2.6-config)
local path = rpm.expand('%1')
if path:match('/usr/bin/python%d+%.%d+$') then
local provides = path:gsub('.*/usr/bin/python(%d+%.%d+)', 'python(abi) = %1')
print(provides)
end
}
%__python_requires() %{lua:
-- Match buildroot paths of the form
-- /PATH/OF/BUILDROOT/usr/lib/pythonMAJOR.MINOR/ and
-- /PATH/OF/BUILDROOT/usr/lib64/pythonMAJOR.MINOR/
-- generating a line of the form:
-- python(abi) = MAJOR.MINOR
local path = rpm.expand('%1')
if path:match('/usr/lib%d*/python%d+%.%d+/.*') then
local requires = path:gsub('.*/usr/lib%d*/python(%d+%.%d+)/.*', 'python(abi) = %1')
print(requires)
end
}
%__python_path ^((%{_prefix}/lib(64)?/python[[:digit:]]+\\.[[:digit:]]+/.*\\.(py[oc]?|so))|(%{_bindir}/python[[:digit:]]+\\.[[:digit:]]+))$

View File

@ -1,85 +0,0 @@
#!/usr/bin/python3 -sB
# (imports pythondistdeps from /usr/lib/rpm, hence -B)
#
# This program is free software.
#
# It is placed in the public domain or under the CC0-1.0-Universal license,
# whichever is more permissive.
#
# Alternatively, it may be redistributed and/or modified under the terms of
# the LGPL version 2.1 (or later) or GPL version 2 (or later).
#
# Use this script to generate bundled provides, e.g.:
# ./pythonbundles.py setuptools-47.1.1/pkg_resources/_vendor/vendored.txt
import pathlib
import sys
import pythondistdeps
def generate_bundled_provides(path, 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}')
return provides
def compare(expected, given):
stripped = (l.strip() for l in given)
no_comments = set(l for l in stripped if not l.startswith('#'))
no_comments.discard('')
if expected == no_comments:
return True
extra_expected = expected - no_comments
extra_given = no_comments - expected
if extra_expected:
print('Missing expected provides:', file=sys.stderr)
for provide in sorted(extra_expected):
print(f' - {provide}', file=sys.stderr)
if extra_given:
print('Redundant unexpected provides:', file=sys.stderr)
for provide in sorted(extra_given):
print(f' + {provide}', file=sys.stderr)
return False
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(prog=sys.argv[0],
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('vendored', metavar='VENDORED.TXT',
help='Upstream information about vendored libraries')
parser.add_argument('-c', '--compare-with', action='store',
help='A string value to compare with and verify')
parser.add_argument('-n', '--namespace', action='store',
help='What namespace of provides will used', default='python3dist')
args = parser.parse_args()
provides = generate_bundled_provides(pathlib.Path(args.vendored), args.namespace)
if args.compare_with:
given = args.compare_with.splitlines()
same = compare(provides, given)
if not same:
sys.exit(1)
else:
for provide in sorted(provides):
print(provide)

32
SOURCES/pythondeps.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash
[ $# -ge 1 ] || {
cat > /dev/null
exit 0
}
case $1 in
-P|--provides)
shift
# Match buildroot/payload paths of the form
# /PATH/OF/BUILDROOT/usr/bin/pythonMAJOR.MINOR
# generating a line of the form
# python(abi) = MAJOR.MINOR
# (Don't match against -config tools e.g. /usr/bin/python2.6-config)
grep "/usr/bin/python.\..$" \
| sed -e "s|.*/usr/bin/python\(.\..\)|python(abi) = \1|"
;;
-R|--requires)
shift
# Match buildroot paths of the form
# /PATH/OF/BUILDROOT/usr/lib/pythonMAJOR.MINOR/ and
# /PATH/OF/BUILDROOT/usr/lib64/pythonMAJOR.MINOR/
# generating (uniqely) lines of the form:
# python(abi) = MAJOR.MINOR
grep -E "/usr/lib[^/]*/python[[:digit:]]+\.[[:digit:]]+/.*" \
| sed -Ee "s|.*/usr/lib[^/]*/python([[:digit:]]+\.[[:digit:]]+)/.*|python(abi) = \1|g" \
| sort | uniq
;;
esac
exit 0

View File

@ -1,3 +0,0 @@
%__pythondist_provides %{_rpmconfigdir}/pythondistdeps.py --provides --normalized-names-format pep503 --package-name %{name} --normalized-names-provide-both --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,9 +1,8 @@
#!/usr/bin/python3 -s #!/usr/libexec/platform-python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright 2010 Per Øyvind Karlsen <proyvind@moondrake.org> # Copyright 2010 Per Øyvind Karlsen <proyvind@moondrake.org>
# Copyright 2015 Neal Gompa <ngompa13@gmail.com> # Copyright 2015 Neal Gompa <ngompa13@gmail.com>
# Copyright 2020 SUSE LLC
# #
# This program is free software. It may be redistributed and/or modified under # This program is free software. It may be redistributed and/or modified under
# the terms of the LGPL version 2.1 (or later). # the terms of the LGPL version 2.1 (or later).
@ -12,535 +11,246 @@
# #
from __future__ import print_function from __future__ import print_function
import argparse from getopt import getopt
from os.path import dirname, sep from os.path import basename, dirname, isdir, sep
import re from sys import argv, stdin, version
from sys import argv, stdin, stderr, version_info from distutils.sysconfig import get_python_lib
from sysconfig import get_path
from warnings import warn 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 opts, args = getopt(
# case-insensitive manner: argv[1:], 'hPRrCEMmLl:',
# pip considers dnspython[DNSSEC] and dnspython[dnssec] to be equal, but ['help', 'provides', 'requires', 'recommends', 'conflicts', 'extras', 'majorver-provides', 'majorver-provides-versions=', 'majorver-only', 'legacy-provides' , 'legacy'])
# 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: Provides = False
from importlib.metadata import PathDistribution Requires = False
except ImportError: Recommends = False
from importlib_metadata import PathDistribution Conflicts = False
Extras = False
Provides_PyMajorVer_Variant = False
Provides_PyMajorVer_Versions = None
PyMajorVer_Deps = False
legacy_Provides = False
legacy = False
try: for o, a in opts:
from pathlib import Path if o in ('-h', '--help'):
except ImportError: print('-h, --help\tPrint help')
from pathlib2 import Path print('-P, --provides\tPrint Provides')
print('-R, --requires\tPrint Requires')
print('-r, --recommends\tPrint Recommends')
print('-C, --conflicts\tPrint Conflicts')
print('-E, --extras\tPrint Extras ')
print('-M, --majorver-provides\tPrint extra Provides with Python major version only for all Python versions')
print(' --majorver-provides-versions VERSIONS\n'
' \tPrint extra Provides with Python major version only for listed Python VERSIONS (comma separated, no spaces, e.g. 2.7,3.6)')
print('-m, --majorver-only\tPrint Provides/Requires with Python major version only')
print('-L, --legacy-provides\tPrint extra legacy pythonegg Provides')
print('-l, --legacy\tPrint legacy pythonegg Provides/Requires instead')
exit(1)
elif o in ('-P', '--provides'):
Provides = True
elif o in ('-R', '--requires'):
Requires = True
elif o in ('-r', '--recommends'):
Recommends = True
elif o in ('-C', '--conflicts'):
Conflicts = True
elif o in ('-E', '--extras'):
Extras = True
elif o in ('-M', '--majorver-provides'):
Provides_PyMajorVer_Variant = True
elif o in ('--majorver-provides-versions'):
Provides_PyMajorVer_Versions = a.split(",")
elif o in ('-m', '--majorver-only'):
PyMajorVer_Deps = True
elif o in ('-L', '--legacy-provides'):
legacy_Provides = True
elif o in ('-l', '--legacy'):
legacy = True
if Provides_PyMajorVer_Variant and Provides_PyMajorVer_Versions:
print("Error, options --majorver-provides and --majorver-provides-versions are mutually incompatible.")
exit(2)
def normalize_name(name): if Requires:
"""https://www.python.org/dev/peps/pep-0503/#normalized-names""" py_abi = True
return re.sub(r'[-_.]+', '-', name).lower() else:
py_abi = False
py_deps = {}
if args:
files = args
else:
files = stdin.readlines()
for f in files:
f = f.strip()
lower = f.lower()
name = 'python(abi)'
# add dependency based on path, versioned if within versioned python directory
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]
for lib in (purelib, platlib):
if lib in f:
spec = ('==', f.split(lib)[1].split(sep)[0])
if spec not in py_deps[name]:
py_deps[name].append(spec)
def legacy_normalize_name(name): # XXX: hack to workaround RPM internal dependency generator not passing directories
"""Like pkg_resources Distribution.key property""" lower_dir = dirname(lower)
return re.sub(r'[-_]+', '-', name).lower() if lower_dir.endswith('.egg') or \
lower_dir.endswith('.egg-info') or \
lower_dir.endswith('.dist-info'):
class Requirement(Requirement_): lower = lower_dir
def __init__(self, requirement_string): f = dirname(f)
super(Requirement, self).__init__(requirement_string) # Determine provide, requires, conflicts & recommends based on egg/dist metadata
self.normalized_name = normalize_name(self.name) if lower.endswith('.egg') or \
self.legacy_normalized_name = legacy_normalize_name(self.name) lower.endswith('.egg-info') or \
lower.endswith('.dist-info'):
# This import is very slow, so only do it if needed
class Distribution(PathDistribution): from pkg_resources import Distribution, FileMetadata, PathMetadata
def __init__(self, path): dist_name = basename(f)
super(Distribution, self).__init__(Path(path)) if isdir(f):
self.normalized_name = normalize_name(self.name) path_item = dirname(f)
self.legacy_normalized_name = legacy_normalize_name(self.name) metadata = PathMetadata(path_item, f)
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 = []
for req in self.requirements:
if not req.marker:
continue
if req.marker.evaluate(get_marker_env(self, extra)):
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_id)
if isinstance(version._version, str):
self.version = version._version
else: else:
self.epoch = version._version.epoch path_item = f
self.version = list(version._version.release) metadata = FileMetadata(f)
self.pre = version._version.pre dist = Distribution.from_location(path_item, dist_name, metadata)
self.dev = version._version.dev # Check if py_version is defined in the metadata file/directory name
self.post = version._version.post if not dist.py_version:
# Try to parse the Python version from the path the metadata
def increment(self): # resides at (e.g. /usr/lib/pythonX.Y/site-packages/...)
self.version[-1] += 1 import re
self.pre = None res = re.search(r"/python(?P<pyver>\d+\.\d+)/", path_item)
self.dev = None if res:
self.post = None dist.py_version = res.group('pyver')
return self else:
def __str__(self):
if isinstance(self.version, str):
return self.version
if self.epoch:
rpm_epoch = str(self.epoch) + ':'
else:
rpm_epoch = ''
while len(self.version) > 1 and self.version[-1] == 0:
self.version.pop()
rpm_version = '.'.join(str(x) for x in self.version)
if self.pre:
rpm_suffix = '~{}'.format(''.join(str(x) for x in self.pre))
elif self.dev:
rpm_suffix = '~~{}'.format(''.join(str(x) for x in self.dev))
elif self.post:
rpm_suffix = '^post{}'.format(self.post[1])
else:
rpm_suffix = ''
return '{}{}{}'.format(rpm_epoch, rpm_version, rpm_suffix)
def convert_compatible(name, operator, version_id):
if version_id.endswith('.*'):
print("*** INVALID_REQUIREMENT_ERROR___SEE_STDERR ***")
print('Invalid requirement: {} {} {}'.format(name, operator, version_id), file=stderr)
exit(65) # os.EX_DATAERR
version = RpmVersion(version_id)
if len(version.version) == 1:
print("*** INVALID_REQUIREMENT_ERROR___SEE_STDERR ***")
print('Invalid requirement: {} {} {}'.format(name, operator, version_id), file=stderr)
exit(65) # os.EX_DATAERR
upper_version = RpmVersion(version_id)
upper_version.version.pop()
upper_version.increment()
return '({} >= {} with {} < {})'.format(
name, version, name, upper_version)
def convert_equal(name, operator, version_id):
if version_id.endswith('.*'):
version_id = version_id[:-2] + '.0'
return convert_compatible(name, '~=', version_id)
version = RpmVersion(version_id)
return '{} = {}'.format(name, version)
def convert_arbitrary_equal(name, operator, version_id):
if version_id.endswith('.*'):
print("*** INVALID_REQUIREMENT_ERROR___SEE_STDERR ***")
print('Invalid requirement: {} {} {}'.format(name, operator, version_id), file=stderr)
exit(65) # os.EX_DATAERR
version = RpmVersion(version_id)
return '{} = {}'.format(name, version)
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()
else:
version = RpmVersion(version_id)
lower_version = version
return '({} < {} or {} > {})'.format(
name, version, name, lower_version)
def convert_ordered(name, operator, version_id):
if version_id.endswith('.*'):
# PEP 440 does not define semantics for prefix matching
# with ordered comparisons
version_id = version_id[:-2]
version = RpmVersion(version_id)
if operator == '>':
# distutils will allow a prefix match with '>'
operator = '>='
if operator == '<=':
# distutils will not allow a prefix match with '<='
operator = '<'
else:
version = RpmVersion(version_id)
return '{} {} {}'.format(name, operator, version)
OPERATORS = {'~=': convert_compatible,
'==': convert_equal,
'===': convert_arbitrary_equal,
'!=': convert_not_equal,
'<=': convert_ordered,
'<': convert_ordered,
'>=': convert_ordered,
'>': convert_ordered}
def convert(name, operator, version_id):
try:
return OPERATORS[operator](name, operator, version_id)
except Exception as exc:
raise RuntimeError("Cannot process Python package version `{}` for name `{}`".
format(version_id, name)) from exc
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__":
"""To allow this script to be importable (and its classes/functions
reused), actions 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')
group.add_argument('-R', '--requires', action='store_true', help='Print Requires')
group.add_argument('-r', '--recommends', action='store_true', help='Print Recommends')
group.add_argument('-C', '--conflicts', action='store_true', help='Print Conflicts')
group.add_argument('-E', '--extras', action='store_true', help='[Unused] Generate spec file snippets for extras subpackages')
group_majorver = parser.add_mutually_exclusive_group()
group_majorver.add_argument('-M', '--majorver-provides', action='store_true', help='Print extra Provides with Python major version only')
group_majorver.add_argument('--majorver-provides-versions', action='append',
help='Print extra Provides with Python major version only for listed '
'Python VERSIONS (appended or comma separated without spaces, e.g. 2.7,3.9)')
parser.add_argument('-m', '--majorver-only', action='store_true', help='Print Provides/Requires with Python major version only')
parser.add_argument('-n', '--normalized-names-format', action='store',
default="legacy-dots", choices=["pep503", "legacy-dots"],
help='Format of normalized names according to pep503 or legacy format that allows dots [default]')
parser.add_argument('--normalized-names-provide-both', action='store_true',
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('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()
py_abi = args.requires
py_deps = {}
if args.majorver_provides_versions:
# Go through the arguments (can be specified multiple times),
# and parse individual versions (can be comma-separated)
args.majorver_provides_versions = [v for vstring in args.majorver_provides_versions
for v in vstring.split(",")]
# If normalized_names_require_pep503 is True we require the pep503
# normalized name, if it is False we provide the legacy normalized name
normalized_names_require_pep503 = args.normalized_names_format == "pep503"
# If normalized_names_provide_pep503/legacy is True we provide the
# pep503/legacy normalized name, if it is False we don't
normalized_names_provide_pep503 = \
args.normalized_names_format == "pep503" or args.normalized_names_provide_both
normalized_names_provide_legacy = \
args.normalized_names_format == "legacy-dots" or args.normalized_names_provide_both
# 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:
# The extras names are encoded in the package names after the + sign.
# We take the part after the rightmost +, ignoring when empty,
# this allows packages like nicotine+ or c++ to work fine.
# While packages with names like +spam or foo+bar would break,
# names started with the plus sign are not very common
# 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].lower() or None
for f in (args.files or stdin.readlines()):
f = f.strip()
lower = f.lower()
name = 'python(abi)'
# add dependency based on path, versioned if within versioned python directory
if py_abi and (lower.endswith('.py') or lower.endswith('.pyc') or lower.endswith('.pyo')):
if name not in py_deps:
py_deps[name] = []
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])
if spec not in py_deps[name]:
py_deps[name].append(spec)
# XXX: hack to workaround RPM internal dependency generator not passing directories
lower_dir = dirname(lower)
if lower_dir.endswith('.egg') or \
lower_dir.endswith('.egg-info') or \
lower_dir.endswith('.dist-info'):
lower = lower_dir
f = dirname(f)
# Determine provide, requires, conflicts & recommends based on egg/dist metadata
if lower.endswith('.egg') or \
lower.endswith('.egg-info') or \
lower.endswith('.dist-info'):
dist = Distribution(f)
if not dist.py_version:
warn("Version for {!r} has not been found".format(dist), RuntimeWarning) warn("Version for {!r} has not been found".format(dist), RuntimeWarning)
continue continue
# If processing an extras subpackage: # XXX: https://github.com/pypa/setuptools/pull/1275
# Check that the extras name is declared in the metadata, or import platform
# that there are some dependencies associated with the extras platform.python_version = lambda: dist.py_version
# 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)
exit(65) # os.EX_DATAERR
if args.majorver_provides or args.majorver_provides_versions or \ if Provides_PyMajorVer_Variant or PyMajorVer_Deps or legacy_Provides or legacy or Provides_PyMajorVer_Versions:
args.majorver_only or args.legacy_provides or args.legacy: # Get the Python major version
# Get the Python major version pyver_major = dist.py_version.split('.')[0]
pyver_major = dist.py_version.split('.')[0] if Provides:
if args.provides: # If egg/dist metadata says package name is python, we provide python(abi)
extras_suffix = f"[{extras_subpackage}]" if extras_subpackage else "" if dist.key == 'python':
# If egg/dist metadata says package name is python, we provide python(abi) name = 'python(abi)'
if dist.normalized_name == 'python': if name not in py_deps:
name = 'python(abi)' py_deps[name] = []
if name not in py_deps: py_deps[name].append(('==', dist.py_version))
py_deps[name] = [] if not legacy or not PyMajorVer_Deps:
py_deps[name].append(('==', dist.py_version)) name = 'python{}dist({})'.format(dist.py_version, dist.key)
if not args.legacy or not args.majorver_only: if name not in py_deps:
if normalized_names_provide_legacy: py_deps[name] = []
name = 'python{}dist({}{})'.format(dist.py_version, dist.legacy_normalized_name, extras_suffix) if Provides_PyMajorVer_Variant or PyMajorVer_Deps or \
(Provides_PyMajorVer_Versions and dist.py_version in Provides_PyMajorVer_Versions):
pymajor_name = 'python{}dist({})'.format(pyver_major, dist.key)
if pymajor_name not in py_deps:
py_deps[pymajor_name] = []
if legacy or legacy_Provides:
legacy_name = 'pythonegg({})({})'.format(pyver_major, dist.key)
if legacy_name not in py_deps:
py_deps[legacy_name] = []
if dist.version:
spec = ('==', dist.version)
if spec not in py_deps[name]:
if not legacy:
py_deps[name].append(spec)
if Provides_PyMajorVer_Variant or \
(Provides_PyMajorVer_Versions and dist.py_version in Provides_PyMajorVer_Versions):
py_deps[pymajor_name].append(spec)
if legacy or legacy_Provides:
py_deps[legacy_name].append(spec)
if Requires or (Recommends and dist.extras):
name = 'python(abi)'
# If egg/dist metadata says package name is python, we don't add dependency on python(abi)
if dist.key == 'python':
py_abi = False
if name in py_deps:
py_deps.pop(name)
elif py_abi and dist.py_version:
if name not in py_deps:
py_deps[name] = []
spec = ('==', dist.py_version)
if spec not in py_deps[name]:
py_deps[name].append(spec)
deps = dist.requires()
if Recommends:
depsextras = dist.requires(extras=dist.extras)
if not Requires:
for dep in reversed(depsextras):
if dep in deps:
depsextras.remove(dep)
deps = depsextras
# add requires/recommends based on egg/dist metadata
for dep in deps:
if legacy:
name = 'pythonegg({})({})'.format(pyver_major, dep.key)
else:
if PyMajorVer_Deps:
name = 'python{}dist({})'.format(pyver_major, dep.key)
else:
name = 'python{}dist({})'.format(dist.py_version, dep.key)
for spec in dep.specs:
if spec[0] != '!=':
if name not in py_deps: if name not in py_deps:
py_deps[name] = [] py_deps[name] = []
if normalized_names_provide_pep503:
name_ = '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.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, 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.legacy_normalized_name)
if legacy_name not in py_deps:
py_deps[legacy_name] = []
if dist.version:
version = dist.version
spec = ('==', version)
if normalized_names_provide_legacy:
if spec not in py_deps[name]: if spec not in py_deps[name]:
py_deps[name].append(spec) py_deps[name].append(spec)
if args.majorver_provides or \ if not dep.specs:
(args.majorver_provides_versions and dist.py_version in args.majorver_provides_versions): py_deps[name] = []
py_deps[pymajor_name].append(spec) # Unused, for automatic sub-package generation based on 'extras' from egg/dist metadata
if normalized_names_provide_pep503: # TODO: implement in rpm later, or...?
if spec not in py_deps[name_]: if Extras:
py_deps[name_].append(spec) deps = dist.requires()
if args.majorver_provides or \ extras = dist.extras
(args.majorver_provides_versions and dist.py_version in args.majorver_provides_versions): print(extras)
py_deps[pymajor_name_].append(spec) for extra in extras:
if args.legacy or args.legacy_provides: print('%%package\textras-{}'.format(extra))
if spec not in py_deps[legacy_name]: print('Summary:\t{} extra for {} python package'.format(extra, dist.key))
py_deps[legacy_name].append(spec) print('Group:\t\tDevelopment/Python')
if args.requires or (args.recommends and dist.extras): depsextras = dist.requires(extras=[extra])
name = 'python(abi)' for dep in reversed(depsextras):
# If egg/dist metadata says package name is python, we don't add dependency on python(abi) if dep in deps:
if dist.normalized_name == 'python': depsextras.remove(dep)
py_abi = False deps = depsextras
if name in py_deps:
py_deps.pop(name)
elif py_abi and dist.py_version:
if name not in py_deps:
py_deps[name] = []
spec = ('==', dist.py_version)
if spec not in py_deps[name]:
py_deps[name].append(spec)
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: for dep in deps:
# Even if we're requiring `foo[bar]`, also require `foo` for spec in dep.specs:
# to be safe, and to make it discoverable through if spec[0] == '!=':
# `repoquery --whatrequires` print('Conflicts:\t{} {} {}'.format(dep.key, '==', spec[1]))
extras_suffixes = [""]
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.lower()}]" for e in dep.extras]
for extras_suffix in extras_suffixes:
if normalized_names_require_pep503:
dep_normalized_name = dep.normalized_name
else: else:
dep_normalized_name = dep.legacy_normalized_name print('Requires:\t{} {} {}'.format(dep.key, spec[0], spec[1]))
print('%%description\t{}'.format(extra))
if args.legacy: print('{} extra for {} python package'.format(extra, dist.key))
name = 'pythonegg({})({})'.format(pyver_major, dep.legacy_normalized_name) print('%%files\t\textras-{}\n'.format(extra))
else: if Conflicts:
if args.majorver_only: # Should we really add conflicts for extras?
name = 'python{}dist({}{})'.format(pyver_major, dep_normalized_name, extras_suffix) # Creating a meta package per extra with recommends on, which has
else: # the requires/conflicts in stead might be a better solution...
name = 'python{}dist({}{})'.format(dist.py_version, dep_normalized_name, extras_suffix) for dep in dist.requires(extras=dist.extras):
name = dep.key
if dep.marker and not args.recommends and not extras_subpackage: for spec in dep.specs:
if not dep.marker.evaluate(get_marker_env(dist, '')): if spec[0] == '!=':
continue
if name not in py_deps: if name not in py_deps:
py_deps[name] = [] py_deps[name] = []
for spec in dep.specifier: spec = ('==', spec[1])
if (spec.operator, spec.version) not in py_deps[name]: if spec not in py_deps[name]:
py_deps[name].append((spec.operator, spec.version)) py_deps[name].append(spec)
names = list(py_deps.keys())
# Unused, for automatic sub-package generation based on 'extras' from egg/dist metadata names.sort()
# TODO: implement in rpm later, or...? for name in names:
if args.extras: if py_deps[name]:
print(dist.extras) # Print out versioned provides, requires, recommends, conflicts
for extra in dist.extras: for spec in py_deps[name]:
print('%%package\textras-{}'.format(extra)) print('{} {} {}'.format(name, spec[0], spec[1]))
print('Summary:\t{} extra for {} python package'.format(extra, dist.legacy_normalized_name)) else:
print('Group:\t\tDevelopment/Python') # Print out unversioned provides, requires, recommends, conflicts
for dep in dist.requirements_for_extra(extra): print(name)
for spec in dep.specifier:
if spec.operator == '!=':
print('Conflicts:\t{} {} {}'.format(dep.legacy_normalized_name, '==', spec.version))
else:
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.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.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)
for name in sorted(py_deps):
if py_deps[name]:
# Print out versioned provides, requires, recommends, conflicts
spec_list = []
for spec in py_deps[name]:
spec_list.append(convert(name, spec[0], spec[1]))
if len(spec_list) == 1:
print(spec_list[0])
else:
# Sort spec_list so that the results can be tested easily
print('({})'.format(' with '.join(sorted(spec_list))))
else:
# Print out unversioned provides, requires, recommends, conflicts
print(name)

View File

@ -1,42 +0,0 @@
%__pythonname_provides() %{lua:
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 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_provides
if provides then
for i, provide in ipairs(provides) do
print(provide .. ' ')
end
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 ^/

View File

@ -1,7 +1,11 @@
# Disable automatic bytecompilation. We install only one script and we will
# never "import" it.
%undefine py_auto_byte_compile
Name: python-rpm-generators Name: python-rpm-generators
Summary: Dependency generators for Python RPMs Summary: Dependency generators for Python RPMs
Version: 12 Version: 5
Release: 9%{?dist} Release: 8%{?dist}
# Originally all those files were part of RPM, so license is kept here # Originally all those files were part of RPM, so license is kept here
License: GPLv2+ License: GPLv2+
@ -9,10 +13,8 @@ Url: https://src.fedoraproject.org/python-rpm-generators
# Commit is the last change in following files # Commit is the last change in following files
Source0: https://raw.githubusercontent.com/rpm-software-management/rpm/102eab50b3d0d6546dfe082eac0ade21e6b3dbf1/COPYING Source0: https://raw.githubusercontent.com/rpm-software-management/rpm/102eab50b3d0d6546dfe082eac0ade21e6b3dbf1/COPYING
Source1: python.attr Source1: python.attr
Source2: pythondist.attr Source2: pythondeps.sh
Source3: pythonname.attr Source3: pythondistdeps.py
Source4: pythondistdeps.py
Source5: pythonbundles.py
BuildArch: noarch BuildArch: noarch
@ -21,11 +23,13 @@ BuildArch: noarch
%package -n python3-rpm-generators %package -n python3-rpm-generators
Summary: %{summary} Summary: %{summary}
Requires: python3-packaging %if 0%{?rhel} && 0%{?rhel} >= 8
# We have parametric macro generators, we need RPM 4.16 (4.15.90+ is 4.16 alpha) Requires: platform-python-setuptools
Requires: rpm > 4.15.90-0 %else
# This contains the Lua functions we use: Requires: python3-setuptools
Requires: python-srpm-macros >= 3.9-49 %endif
# The point of split
Conflicts: rpm-build < 4.13.0.1-2
%description -n python3-rpm-generators %description -n python3-rpm-generators
%{summary}. %{summary}.
@ -34,157 +38,47 @@ Requires: python-srpm-macros >= 3.9-49
%autosetup -c -T %autosetup -c -T
cp -a %{sources} . cp -a %{sources} .
# Set which Python versions should have the major-version provides
# (pythonXdist...) generated
sed -i 's/@MAJORVER-PROVIDES-VERSIONS@/2.7,3.6/' python.attr
%install %install
install -Dpm0644 -t %{buildroot}%{_fileattrsdir} *.attr install -Dpm0644 -t %{buildroot}%{_fileattrsdir} python.attr
install -Dpm0755 -t %{buildroot}%{_rpmconfigdir} *.py install -Dpm0755 -t %{buildroot}%{_rpmconfigdir} pythondeps.sh pythondistdeps.py
%files -n python3-rpm-generators %files -n python3-rpm-generators
%license COPYING %license COPYING
%{_fileattrsdir}/python.attr %{_fileattrsdir}/python.attr
%{_fileattrsdir}/pythondist.attr %{_rpmconfigdir}/pythondeps.sh
%{_fileattrsdir}/pythonname.attr
%{_rpmconfigdir}/pythondistdeps.py %{_rpmconfigdir}/pythondistdeps.py
%{_rpmconfigdir}/pythonbundles.py
%changelog %changelog
* Fri Sep 22 2023 Tomáš Hrnčiar <thrnciar@redhat.com> - 12-9 * Mon Nov 14 2022 Charalampos Stratakis <cstratak@redhat.com> - 5-8
- Avoid needless pkg_resources import in pythonbundles.py - Fix the pythondeps.sh and pythondistdeps.py scripts for multiple digits python versions
- Resolves: RHEL-6110 - Resolves: rhbz#2143990
* Wed Jan 26 2022 Tomas Orsava <torsava@redhat.com> - 12-8 * Tue Jun 15 2021 Tomas Orsava <torsava@redhat.com> - 5-7
- From `python3-foo` packages automatically generate `python3.X-foo` Obsoletes - Do not parse nested dist/egg-info metadata
tags on CentOS/RHEL - Resolves: rhbz#1916172
- Resolves: rhbz#1990421
* Tue Aug 10 2021 Mohan Boddu <mboddu@redhat.com> - 12-7 * Thu Dec 12 2019 Tomas Orsava <torsava@redhat.com> - 5-6
- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags - Enabled gating
Related: rhbz#1991688 - Related: rhbz#1776941
* Mon Apr 19 2021 Miro Hrončok <mhroncok@redhat.com> - 12-6 * Wed Nov 27 2019 Tomas Orsava <torsava@redhat.com> - 5-5
- Get rid of distutils deprecation warning (by not using it) - Create major-version provides only on major Python versions (2.7, 3.6)
- The distutils module is deprecated in Python 3.10+ - Fix an extra parenthesis in python.attr
- https://www.python.org/dev/peps/pep-0632/ - Resolves: rhbz#1776941
* Fri Apr 16 2021 Miro Hrončok <mhroncok@redhat.com> - 12-5.1 * Fri Nov 16 2018 Lumír Balhar <lbalhar@redhat.com> - 5-4
- Do not generate setuptools requirement for console_scripts on Python 3.10+ - Require platform-python-setuptools instead of python3-setuptools
- See https://fedoraproject.org/wiki/Changes/Reduce_dependencies_on_python3-setuptools - Resolves: rhbz#1650544
* Fri Apr 16 2021 Mohan Boddu <mboddu@redhat.com> - 12-5 * Sat Jul 28 2018 Miro Hrončok <mhroncok@redhat.com> - 5-3
- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
* 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
* Tue Jul 21 2020 Miro Hrončok <mhroncok@redhat.com> - 11-10
- pythondistdeps: Split Python Extras names after the rightmost plus sign
- pythondistdeps: Handle edge cases of version comparisons more closely to
upstream, despite irrationality
See: https://github.com/pypa/packaging/issues/320
* Fri Jul 10 2020 Tomas Orsava <torsava@redhat.com> - 11-9
- pythondistdeps: Implement provides/requires for extras packages
- Enable --require-extras-subpackages
- Adapt Python version marker workaround for setuptools 42+
* Fri Jun 26 2020 Miro Hrončok <mhroncok@redhat.com> - 11-8
- Fix python(abi) requires generator, it picked files from almost good directories
- Add a script to generate Python bundled provides
* Thu May 21 2020 Miro Hrončok <mhroncok@redhat.com> - 11-7
- Use PEP 503 names for requires
* Tue May 05 2020 Miro Hrončok <mhroncok@redhat.com> - 11-6
- Deduplicate automatically provided names trough Python RPM Lua macros
* Wed Apr 29 2020 Tomas Orsava <torsava@redhat.com> - 11-5
- Backporting proposed upstream changes
https://github.com/rpm-software-management/rpm/pull/1195
- Only provide python3dist(..) for the main Python versions (BZ#1812083)
- Preparation for the proper handling of normalized names (BZ#1791530)
- Add a test suite (and enable it in Fedora CI)
- Better error messages for unsupported package versions
- Fix sorting of dev versions
* Tue Apr 28 2020 Miro Hrončok <mhroncok@redhat.com> - 11-4
- Don't define global Lua variables from Python generator
* Mon Apr 20 2020 Gordon Messmer <gordon.messmer@gmail.com> - 11-3
- Handle all-zero versions without crashing
* Tue Apr 07 2020 Miro Hrončok <mhroncok@redhat.com> - 11-2
- Use dynamic %%_prefix value when matching files for python(abi) provides
- Sync with upstream RPM dist generator
* Wed Apr 01 2020 Miro Hrončok <mhroncok@redhat.com> - 11-1
- Rewrite python(abi) generators to Lua to make them faster
- RPM 4.16+ is needed
- Automatically call %%python_provide
* Thu Jan 30 2020 Fedora Release Engineering <releng@fedoraproject.org> - 10-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
* Fri Jan 17 2020 Miro Hrončok <mhroncok@redhat.com> - 10-3
- Also provide pythonXdist() with PEP 503 normalized names (#1791530)
* Fri Jan 03 2020 Miro Hrončok <mhroncok@redhat.com> - 10-2
- Fix more complicated requirement expressions by adding parenthesis
* Wed Jan 01 2020 Miro Hrončok <mhroncok@redhat.com> - 10-1
- Handle version ending with ".*" (#1758141)
- Handle compatible-release operator "~=" (#1758141)
- Use rich deps for semantically versioned dependencies
- Match Python version if minor has multiple digits (e.g. 3.10, #1777382)
- Only add setuptools requirement for egg-info packages
* Fri Jul 26 2019 Fedora Release Engineering <releng@fedoraproject.org> - 9-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
* Mon Jun 24 2019 Tomas Orsava <torsava@redhat.com> - 9-1
- Canonicalize Python versions and properly handle != spec
* Wed Apr 17 2019 Miro Hrončok <mhroncok@redhat.com> - 8-1
- console_scripts entry points to require setuptools
https://github.com/rpm-software-management/rpm/pull/666
* Sat Feb 02 2019 Fedora Release Engineering <releng@fedoraproject.org> - 7-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
* Thu Dec 20 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 7-1
- Enable requires generator
* Wed Oct 03 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 6-1
- Tighten regex for depgen
* Sat Jul 28 2018 Miro Hrončok <mhroncok@redhat.com> - 5-4
- Use nonstandardlib for purelib definition (#1609492) - Use nonstandardlib for purelib definition (#1609492)
* Sat Jul 28 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 5-3 * Tue Jun 05 2018 Tomas Orsava <torsava@redhat.com> - 5-2
- Add pythondist generator - Switch the pythondistdeps.py script to /usr/libexec/platform-python
* Sat Jul 14 2018 Fedora Release Engineering <releng@fedoraproject.org> - 5-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Sun Feb 11 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 5-1 * Sun Feb 11 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 5-1
- Fork upstream generators - Fork upstream generators