From feaaced48d407464b79cf067bc588334aeda31c7 Mon Sep 17 00:00:00 2001 From: Tomas Kopecek Date: Jun 16 2020 13:15:21 +0000 Subject: hub: allow glob matching for listTags server-side filtering would be better than CLI/client side filtering. Fixes: https://pagure.io/koji/issue/2086 --- diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py index 6577677..fea549b 100644 --- a/cli/koji_cli/commands.py +++ b/cli/koji_cli/commands.py @@ -4113,7 +4113,17 @@ def anon_handle_list_tags(goptions, session, args): if not buildinfo: parser.error(_("Invalid build %s" % options.build)) - tags = session.listTags(buildinfo.get('id', None), pkginfo.get('id', None)) + if not args: + # list everything if no pattern is supplied + args = [None] + + tags = [] + with session.multicall() as m: + for arg in args: + tags.append(m.listTags(build=buildinfo.get('id', None), + package=pkginfo.get('id', None), + pattern=arg)) + tags = list(itertools.chain(*[t.result for t in tags])) tags.sort(key=lambda x: x['name']) # if options.verbose: # fmt = "%(name)s [%(id)i] %(perm)s %(locked)s %(arches)s" @@ -4122,12 +4132,6 @@ def anon_handle_list_tags(goptions, session, args): else: fmt = "%(name)s" for tag in tags: - if args: - for pattern in args: - if fnmatch.fnmatch(tag['name'], pattern): - break - else: - continue if options.unlocked: if tag['locked'] or tag['perm']: continue diff --git a/hub/kojihub.py b/hub/kojihub.py index 9157f36..e33ef85 100644 --- a/hub/kojihub.py +++ b/hub/kojihub.py @@ -1232,27 +1232,26 @@ def readPackageList(tagID=None, userID=None, pkgID=None, event=None, inherit=Fal return packages -def list_tags(build=None, package=None, perms=True, queryOpts=None): - """List tags. If build is specified, only return tags associated with the - given build. If package is specified, only return tags associated with the - specified package. If neither is specified, return all tags. Build can be - either an integer ID or a string N-V-R. Package can be either an integer ID - or a string name. Only one of build and package may be specified. Returns - a list of maps. Each map contains keys: - - id - - name - - arches - - locked - - If package is specified, each map will also contain: - - owner_id - - owner_name - - blocked - - extra_arches - - If perms is True, each map will also contain: - - perm_id - - perm +def list_tags(build=None, package=None, perms=True, pattern=None, queryOpts=None): + """List tags according to filters + + :param int|str build: If build is specified, only return tags associated with + the given build. Build can be either an integer ID or + a string N-V-R. + :param int|str package: If package is specified, only return tags associated with the + specified package. Package can be either an integer ID or a + string name. + + In this case resulting map will have additional keys: + - owner_id + - owner_name + - blocked + - extra_arches + :param bool perms: If perms is True, perm_id and perm is added to resulting maps. + :param pattern: If glob pattern is specified, only return tags matching that pattern. + + :returns list of dicts: Each map contains id, name, arches and locked keys and + additional keys as specified via package or perms options. """ if build is not None and package is not None: raise koji.GenericError('only one of build and package may be specified') @@ -1293,6 +1292,11 @@ def list_tags(build=None, package=None, perms=True, queryOpts=None): " tag_package_owners.active IS TRUE") joins.append('users ON tag_package_owners.owner = users.id') packageID = packageinfo['id'] + if pattern is not None: + # copied from _prepareSearchTerms / glob + pattern = pattern.replace( + '\\', '\\\\').replace('_', r'\_').replace('?', '_').replace('*', '%') + clauses.append('tag.name ILIKE %(pattern)s') query = QueryProcessor(columns=fields, aliases=aliases, tables=tables, joins=joins, clauses=clauses, values=locals(),