I think the different cli flags are not orthogonal enough. Here is the latest version from the PR plus what I think should be done to get a clean behavior.
Anyone missing anything? Florian -- Red Hat GmbH, http://www.de.redhat.com/ Registered seat: Grasbrunn, Commercial register: Amtsgericht Muenchen, HRB 153243, Managing Directors: Charles Cachera, Michael Cunningham, Michael O'Neill, Charles Peters
>From 3c2c063694b5afe6f5333fda8db721747c952421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Neal=20Gompa=20=28=E3=83=8B=E3=83=BC=E3=83=AB=E3=83=BB?= =?UTF-8?q?=E3=82=B3=E3=82=99=E3=83=B3=E3=83=8F=E3=82=9A=29?= <ngomp...@gmail.com> Date: Sun, 29 Nov 2015 08:32:03 -0500 Subject: [PATCH 1/2] Rename to pythonX.Ydist, read .dist-info, support legacy pythoneggs()() Per the recommendation of Nick Coghlan and Toshio Kuratomi, pythonXegg(M) is being renamed to pythonX.Ydist(M). An option has also been added to add a pythonXdist(M) Provides for distributions that may prefer to have it. The option '--majorver-provides' is intended for use if only one Python stack per major version will be available at a given time, as unexpected results may occur if there are multiple independent Python stacks per major version available. Consequently, it will not be on by default when using the generator for generating Provides. Additionally, .egg-info data is being replaced with .dist-info data, so we need to handle that case, too. See for more details: https://lists.fedoraproject.org/archives/list/python-devel%40lists.fedoraproject.org/thread/SQBSAS4T25HK5YJBNBSFDD7KDQWDELL6/ Also, Thierry Vignaud brought up on rpm-maint that Mageia currently uses "pythonegg(X)(M)" (e.g. "pythonegg(3)(rpm)" for python3 rpm bindings package) in their Python packages to pull in Python dependencies and requested a way to not break Mageia. After discussing with Florian Festi about it, Mageia's pythonegg(X)(M) will be supported by adding '--legacy' as a switch to generate legacy Provides/Requires to maintain compatibility with Mageia's existing usage and to give them a path to transition from this usage over time. This switch will also enable pythonXdist(M) Provides format to allow for a transition, as the conditions for using pythonXdist(M) are the same as pythonegg(X)(M). Additionally, the switch '--majorver-prov-with-legacy' uses new-style dependencies while additionally adding old-style "pythonegg(X)(M)" Provides for compatibility. --- scripts/pythondistdeps.py | 223 ++++++++++++++++++++++++++++++++++++++++++++++ scripts/pythoneggs.py | 191 --------------------------------------- 2 files changed, 223 insertions(+), 191 deletions(-) create mode 100755 scripts/pythondistdeps.py delete mode 100755 scripts/pythoneggs.py diff --git a/scripts/pythondistdeps.py b/scripts/pythondistdeps.py new file mode 100755 index 0000000..0b94dbd --- /dev/null +++ b/scripts/pythondistdeps.py @@ -0,0 +1,223 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright 2010 Per Ãyvind Karlsen <proyv...@moondrake.org> +# Copyright 2015 Neal Gompa <ngomp...@gmail.com> +# +# This program is free software. It may be redistributed and/or modified under +# the terms of the LGPL version 2.1 (or later). +# +# RPM python dependency generator, using .egg-info/.egg-link/.dist-info data +# + +from __future__ import print_function +from getopt import getopt +from os.path import basename, dirname, isdir, sep +from sys import argv, stdin, version +from distutils.sysconfig import get_python_lib + + +opts, args = getopt( + argv[1:], 'hPRrCOEMml:', + ['help', 'provides', 'requires', 'recommends', 'conflicts', 'extras', 'majorver-provides', 'majorver-prov-with-legacy' , 'legacy']) + +Provides = False +Requires = False +Recommends = False +Conflicts = False +Extras = False +Provides_PyMajorVer_Variant = False +legacy_Provides = False +legacy = False + +for o, a in opts: + if o in ('-h', '--help'): + print('-h, --help\tPrint help') + 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') + print('-m, --majorver-prov-with-legacy\tPrint extra Provides with Python major version only and extra legacy Provides') + print('-l, --legacy\tPrint extra legacy pythonegg Provides/Requires instead (also enables --majorver-provides)') + 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 ('-m', '--majorver-prov-with-legacy'): + Provides_PyMajorVer_Variant = True + legacy_Provides = True + elif o in ('-l', '--legacy'): + legacy = True + Provides_PyMajorVer_Variant = True + +if Requires: + py_abi = True +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=1, plat_specific=0).split(version[:3])[0] + platlib = get_python_lib(standard_lib=1, 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) + + # 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('.egg-link') 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('.egg-link') or \ + lower.endswith('.dist-info'): + # This import is very slow, so only do it if needed + from pkg_resources import Distribution, FileMetadata, PathMetadata + 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) + if Provides_PyMajorVer_Variant and Provides: + # Get the Python major version + pyver_major = dist.py_version.split('.')[0] + if Provides: + # If egg/dist metadata says package name is python, we provide python(abi) + if dist.key == 'python': + name = 'python(abi)' + if name not in py_deps: + py_deps[name] = [] + py_deps[name].append(('==', dist.py_version)) + name = 'python{}dist({})'.format(dist.py_version, dist.key) + if name not in py_deps: + py_deps[name] = [] + if Provides_PyMajorVer_Variant: + 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]: + py_deps[name].append(spec) + if Provides_PyMajorVer_Variant: + 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: + name = 'python{}dist({})'.format(dist.py_version, dep.key) + for spec in dep.specs: + if spec[0] != '!=': + 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: + py_deps[name] = [] + # Unused, for automatic sub-package generation based on 'extras' from egg/dist metadata + # TODO: implement in rpm later, or...? + if Extras: + deps = dist.requires() + extras = dist.extras + print(extras) + for extra in extras: + print('%%package\textras-{}'.format(extra)) + print('Summary:\t{} extra for {} python package'.format(extra, dist.key)) + 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])) + else: + print('Requires:\t{} {} {}'.format(dep.key, spec[0], spec[1])) + print('%%description\t{}'.format(extra)) + print('{} extra for {} python package'.format(extra, dist.key)) + print('%%files\t\textras-{}\n'.format(extra)) + if 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) +names = list(py_deps.keys()) +names.sort() +for name in names: + if py_deps[name]: + # Print out versioned provides, requires, recommends, conflicts + for spec in py_deps[name]: + print('{} {} {}'.format(name, spec[0], spec[1])) + else: + # Print out unversioned provides, requires, recommends, conflicts + print(name) diff --git a/scripts/pythoneggs.py b/scripts/pythoneggs.py deleted file mode 100755 index f0ec38d..0000000 --- a/scripts/pythoneggs.py +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright 2010 Per Ãyvind Karlsen <proyv...@moondrake.org> -# Copyright 2015 Neal Gompa <ngomp...@gmail.com> -# -# This program is free software. It may be redistributed and/or modified under -# the terms of the LGPL version 2.1 (or later). -# -# RPM python (egg) dependency generator. -# - -from __future__ import print_function -from getopt import getopt -from os.path import basename, dirname, isdir, sep -from sys import argv, stdin, version -from distutils.sysconfig import get_python_lib - - -opts, args = getopt( - argv[1:], 'hPRrCOE:', - ['help', 'provides', 'requires', 'recommends', 'conflicts', 'extras']) - -Provides = False -Requires = False -Recommends = False -Conflicts = False -Extras = False - -for o, a in opts: - if o in ('-h', '--help'): - print('-h, --help\tPrint help') - 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 ') - 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 - -if Requires: - py_abi = True -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=1, plat_specific=0).split(version[:3])[0] - platlib = get_python_lib(standard_lib=1, 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) - - # 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('.egg-link'): - lower = lower_dir - f = dirname(f) - # Determine provide, requires, conflicts & recommends based on egg metadata - if lower.endswith('.egg') or \ - lower.endswith('.egg-info') or \ - lower.endswith('.egg-link'): - # This import is very slow, so only do it if needed - from pkg_resources import Distribution, FileMetadata, PathMetadata - 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) - # Get the Python major version - pyver_major = dist.py_version.split('.')[0] - if Provides: - # If egg metadata says package name is python, we provide python(abi) - if dist.key == 'python': - name = 'python(abi)' - if name not in py_deps: - py_deps[name] = [] - py_deps[name].append(('==', dist.py_version)) - name = 'python{}egg({})'.format(pyver_major, dist.key) - if name not in py_deps: - py_deps[name] = [] - if dist.version: - spec = ('==', dist.version) - if spec not in py_deps[name]: - py_deps[name].append(spec) - if Requires or (Recommends and dist.extras): - name = 'python(abi)' - # If egg 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 metadata - for dep in deps: - name = 'python{}egg({})'.format(pyver_major, dep.key) - for spec in dep.specs: - if spec[0] != '!=': - 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: - py_deps[name] = [] - # Unused, for automatic sub-package generation based on 'extras' from egg metadata - # TODO: implement in rpm later, or...? - if Extras: - deps = dist.requires() - extras = dist.extras - print(extras) - for extra in extras: - print('%%package\textras-{}'.format(extra)) - print('Summary:\t{} extra for {} python egg'.format(extra, dist.key)) - 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])) - else: - print('Requires:\t{} {} {}'.format(dep.key, spec[0], spec[1])) - print('%%description\t{}'.format(extra)) - print('{} extra for {} python egg'.format(extra, dist.key)) - print('%%files\t\textras-{}\n'.format(extra)) - if 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) -names = list(py_deps.keys()) -names.sort() -for name in names: - if py_deps[name]: - # Print out versioned provides, requires, recommends, conflicts - for spec in py_deps[name]: - print('{} {} {}'.format(name, spec[0], spec[1])) - else: - # Print out unversioned provides, requires, recommends, conflicts - print(name) -- 2.1.0
>From 7e6d4d94f90d2ef0997f8da0f86398988837a4c3 Mon Sep 17 00:00:00 2001 From: Florian Festi <ffe...@redhat.com> Date: Thu, 11 Feb 2016 17:25:54 +0100 Subject: [PATCH] Disentangle --majorver-provides from the legacy behaviour Make --legacy behave as before thet switch to the dist syntax Add -L, --legacy-provides to add old style Provides to the new dist deps --- scripts/pythondistdeps.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/scripts/pythondistdeps.py b/scripts/pythondistdeps.py index 0b94dbd..10cbe9d 100755 --- a/scripts/pythondistdeps.py +++ b/scripts/pythondistdeps.py @@ -38,9 +38,9 @@ for o, a in opts: 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') - print('-m, --majorver-prov-with-legacy\tPrint extra Provides with Python major version only and extra legacy Provides') - print('-l, --legacy\tPrint extra legacy pythonegg Provides/Requires instead (also enables --majorver-provides)') + print('-m, --majorver-provides\tPrint extra Provides 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 @@ -52,14 +52,12 @@ for o, a in opts: Conflicts = True elif o in ('-E', '--extras'): Extras = True - elif o in ('-M', '--majorver-provides'): - Provides_PyMajorVer_Variant = True - elif o in ('-m', '--majorver-prov-with-legacy'): + elif o in ('-m', '--majorver-provides'): Provides_PyMajorVer_Variant = True + elif o in ('-L', '--legacy-provides'): legacy_Provides = True elif o in ('-l', '--legacy'): legacy = True - Provides_PyMajorVer_Variant = True if Requires: py_abi = True @@ -120,9 +118,10 @@ for f in files: if name not in py_deps: py_deps[name] = [] py_deps[name].append(('==', dist.py_version)) - name = 'python{}dist({})'.format(dist.py_version, dist.key) - if name not in py_deps: - py_deps[name] = [] + if not legacy: + name = 'python{}dist({})'.format(dist.py_version, dist.key) + if name not in py_deps: + py_deps[name] = [] if Provides_PyMajorVer_Variant: pymajor_name = 'python{}dist({})'.format(pyver_major, dist.key) if pymajor_name not in py_deps: @@ -134,7 +133,8 @@ for f in files: if dist.version: spec = ('==', dist.version) if spec not in py_deps[name]: - py_deps[name].append(spec) + if not legacy: + py_deps[name].append(spec) if Provides_PyMajorVer_Variant: py_deps[pymajor_name].append(spec) if legacy or legacy_Provides: -- 2.1.0
_______________________________________________ Rpm-maint mailing list Rpm-maint@lists.rpm.org http://lists.rpm.org/mailman/listinfo/rpm-maint