From 0c9665427c3dd51ee94a5757f1a49db77a8e53b5 Mon Sep 17 00:00:00 2001 From: Tomas Orsava Date: Tue, 2 Jun 2020 21:12:11 +0200 Subject: [PATCH] scripts/pythondistdeps: Implement provides/requires for extras packages --- pythondist.attr | 4 +-- pythondistdeps.py | 73 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/pythondist.attr b/pythondist.attr index 7aefb3c..a94ab70 100644 --- a/pythondist.attr +++ b/pythondist.attr @@ -1,3 +1,3 @@ -%__pythondist_provides %{_rpmconfigdir}/pythondistdeps.py --provides --normalized-names-format pep503 --normalized-names-provide-both --majorver-provides-versions 2.7,%{__default_python3_version} -%__pythondist_requires %{_rpmconfigdir}/pythondistdeps.py --requires --normalized-names-format pep503 +%__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} %__pythondist_path ^/usr/lib(64)?/python[[:digit:]]\\.[[:digit:]]+/site-packages/[^/]+\\.(dist-info|egg-info|egg-link)$ diff --git a/pythondistdeps.py b/pythondistdeps.py index 93a9fd3..e9fe7da 100755 --- a/pythondistdeps.py +++ b/pythondistdeps.py @@ -157,7 +157,7 @@ if __name__ == "__main__": 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='Print Extras') + 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', @@ -171,7 +171,9 @@ 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('--package-name', action='store', help="Name of the RPM package that's being inspected") + 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() @@ -198,6 +200,12 @@ if __name__ == "__main__": # At least one type of normalization must be provided assert normalized_names_provide_pep503 or normalized_names_provide_legacy + # Is this script being run for an extras subpackage? + extras_subpackage = None + if args.package_name: + package_name_parts = args.package_name.partition('+') + extras_subpackage = package_name_parts[2] or None + for f in (args.files or stdin.readlines()): f = f.strip() lower = f.lower() @@ -268,11 +276,18 @@ if __name__ == "__main__": # 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: + 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.") + exit(65) # os.EX_DATAERR + if args.majorver_provides or args.majorver_provides_versions or \ args.majorver_only or args.legacy_provides or args.legacy: # Get the Python major version pyver_major = dist.py_version.split('.')[0] 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)' @@ -281,21 +296,21 @@ if __name__ == "__main__": 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) + name = 'python{}dist({}{})'.format(dist.py_version, dist.key, 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) + name_ = 'python{}dist({}{})'.format(dist.py_version, 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) + pymajor_name = 'python{}dist({}{})'.format(pyver_major, dist.key, 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) + pymajor_name_ = 'python{}dist({}{})'.format(pyver_major, normalized_name, extras_suffix) if pymajor_name_ not in py_deps: py_deps[pymajor_name_] = [] if args.legacy or args.legacy_provides: @@ -342,6 +357,9 @@ if __name__ == "__main__": 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 @@ -351,25 +369,34 @@ if __name__ == "__main__": deps.insert(0, Requirement.parse('setuptools')) # add requires/recommends based on egg/dist metadata for dep in deps: - if normalized_names_require_pep503: - dep_normalized_name = normalize_name(dep.project_name) - else: - dep_normalized_name = dep.key - - if args.legacy: - name = 'pythonegg({})({})'.format(pyver_major, dep.key) - else: - if args.majorver_only: - name = 'python{}dist({})'.format(pyver_major, dep_normalized_name) + # Even if we're requiring `foo[bar]`, also require `foo` + # to be safe, and to make it discoverable through + # `repoquery --whatrequires` + 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}]" for e in dep.extras] + for extras_suffix in extras_suffixes: + if normalized_names_require_pep503: + dep_normalized_name = normalize_name(dep.project_name) else: - name = 'python{}dist({})'.format(dist.py_version, dep_normalized_name) - for spec in dep.specs: - if name not in py_deps: + dep_normalized_name = dep.key + + if args.legacy: + name = 'pythonegg({})({})'.format(pyver_major, dep.key) + else: + if args.majorver_only: + name = '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: 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 args.extras: