#
# Copyright (C) 2014 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# the GNU General Public License v.2, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY expressed or implied, including the implied warranties of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details. You should have received a copy of the
# GNU General Public License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
# source code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission of
# Red Hat, Inc.
#
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
from dnf.i18n import _
from dnf.cli import commands
from dnf.cli.option_parser import OptionParser
import argparse
import datetime
import logging
import re
import sys
import dnf
import dnf.cli
import dnf.exceptions
import dnf.subject
import dnf.util
import hawkey
logger = logging.getLogger('dnf')
QFORMAT_DEFAULT = '%{name}-%{epoch}:%{version}-%{release}.%{arch}'
# matches %[-][dd]{attr}
QFORMAT_MATCH = re.compile(r'%(-?\d*?){([:.\w]+?)}')
QUERY_TAGS = """\
name, arch, epoch, version, release, reponame (repoid), from_repo, evr,
debug_name, source_name, source_debug_name,
installtime, buildtime, size, downloadsize, installsize,
provides, requires, obsoletes, conflicts, sourcerpm,
description, summary, license, url, reason"""
OPTS_MAPPING = {
'conflicts': 'conflicts',
'enhances': 'enhances',
'obsoletes': 'obsoletes',
'provides': 'provides',
'recommends': 'recommends',
'requires': 'requires',
'requires-pre': 'requires_pre',
'suggests': 'suggests',
'supplements': 'supplements'
}
def rpm2py_format(queryformat):
"""Convert a rpm like QUERYFMT to an python .format() string."""
def fmt_repl(matchobj):
fill = matchobj.groups()[0]
key = matchobj.groups()[1]
if fill:
if fill[0] == '-':
fill = '>' + fill[1:]
else:
fill = '<' + fill
fill = ':' + fill
return '{0.' + key.lower() + fill + "}"
def brackets(txt):
return txt.replace('{', '{{').replace('}', '}}')
queryformat = queryformat.replace("\\n", "\n").replace("\\t", "\t")
for key, value in OPTS_MAPPING.items():
queryformat = queryformat.replace(key, value)
fmt = ""
spos = 0
for item in QFORMAT_MATCH.finditer(queryformat):
fmt += brackets(queryformat[spos:item.start()])
fmt += fmt_repl(item)
spos = item.end()
fmt += brackets(queryformat[spos:])
return fmt
class _CommaSplitCallback(OptionParser._SplitCallback):
SPLITTER = r'\s*,\s*'
class RepoQueryCommand(commands.Command):
"""A class containing methods needed by the cli to execute the repoquery command.
"""
nevra_forms = {'repoquery-n': hawkey.FORM_NAME,
'repoquery-na': hawkey.FORM_NA,
'repoquery-nevra': hawkey.FORM_NEVRA}
aliases = ('repoquery', 'rq') + tuple(nevra_forms.keys())
summary = _('search for packages matching keyword')
@staticmethod
def filter_repo_arch(opts, query):
"""Filter query by repoid and arch options"""
if opts.repo:
query.filterm(reponame=opts.repo)
if opts.arches:
query.filterm(arch=opts.arches)
return query
@staticmethod
def set_argparser(parser):
parser.add_argument('-a', '--all', dest='queryall', action='store_true',
help=_("Query all packages (shorthand for repoquery '*' "
"or repoquery without argument)"))
parser.add_argument('--show-duplicates', action='store_true',
help=_("Query all versions of packages (default)"))
parser.add_argument('--arch', '--archlist', dest='arches', default=[],
action=_CommaSplitCallback, metavar='ARCH',
help=_('show only results from this ARCH'))
parser.add_argument('-f', '--file', metavar='FILE', nargs='+',
help=_('show only results that owns FILE'))
parser.add_argument('--whatconflicts', default=[], action=_CommaSplitCallback,
metavar='REQ',
help=_('show only results that conflict REQ'))
parser.add_argument('--whatdepends', default=[], action=_CommaSplitCallback,
metavar='REQ',
help=_('shows results that requires, suggests, supplements, enhances, '
'or recommends package provides and files REQ'))
parser.add_argument('--whatobsoletes', default=[], action=_CommaSplitCallback,
metavar='REQ',
help=_('show only results that obsolete REQ'))
parser.add_argument('--whatprovides', default=[], action=_CommaSplitCallback,
metavar='REQ',
help=_('show only results that provide REQ'))
parser.add_argument('--whatrequires', default=[], action=_CommaSplitCallback,
metavar='REQ',
help=_('shows results that requires package provides and files REQ'))
parser.add_argument('--whatrecommends', default=[], action=_CommaSplitCallback,
metavar='REQ',
help=_('show only results that recommend REQ'))
parser.add_argument('--whatenhances', default=[], action=_CommaSplitCallback,
metavar='REQ',
help=_('show only results that enhance REQ'))
parser.add_argument('--whatsuggests', default=[], action=_CommaSplitCallback,
metavar='REQ',
help=_('show only results that suggest REQ'))
parser.add_argument('--whatsupplements', default=[], action=_CommaSplitCallback,
metavar='REQ',
help=_('show only results that supplement REQ'))
whatrequiresform = parser.add_mutually_exclusive_group()
whatrequiresform.add_argument("--alldeps", action="store_true",
help=_("check non-explicit dependencies (files and Provides); default"))
whatrequiresform.add_argument("--exactdeps", action="store_true",
help=_('check dependencies exactly as given, opposite of --alldeps'))
parser.add_argument("--recursive", action="store_true", help=_(
'used with --whatrequires, and --requires --resolve, query packages recursively.'))
parser.add_argument('--deplist', action='store_true', help=_(
"show a list of all dependencies and what packages provide them"))
parser.add_argument('--resolve', action='store_true',
help=_('resolve capabilities to originating package(s)'))
parser.add_argument("--tree", action="store_true",
help=_('show recursive tree for package(s)'))
parser.add_argument('--srpm', action='store_true',
help=_('operate on corresponding source RPM'))
parser.add_argument("--latest-limit", dest='latest_limit', type=int,
help=_('show N latest packages for a given name.arch'
' (or latest but N if N is negative)'))
parser.add_argument("--disable-modular-filtering", action="store_true",
help=_("list also packages of inactive module streams"))
outform = parser.add_mutually_exclusive_group()
outform.add_argument('-i', "--info", dest='queryinfo',
default=False, action='store_true',
help=_('show detailed information about the package'))
outform.add_argument('-l', "--list", dest='queryfilelist',
default=False, action='store_true',
help=_('show list of files in the package'))
outform.add_argument('-s', "--source", dest='querysourcerpm',
default=False, action='store_true',
help=_('show package source RPM name'))
outform.add_argument('--changelogs', dest='querychangelogs',
default=False, action='store_true',
help=_('show changelogs of the package'))
outform.add_argument('--qf', "--queryformat", dest='queryformat',
default=QFORMAT_DEFAULT,
help=_('display format for listing packages: '
'"%%{name} %%{version} ...", '
'use --querytags to view full tag list'))
parser.add_argument('--querytags', action='store_true',
help=_('show available tags to use with '
'--queryformat'))
outform.add_argument("--nevra", dest='queryformat', const=QFORMAT_DEFAULT,
action='store_const',
help=_('use name-epoch:version-release.architecture format for '
'displaying found packages (default)'))
outform.add_argument("--nvr", dest='queryformat', const='%{name}-%{version}-%{release}',
action='store_const', help=_('use name-version-release format for '
'displaying found packages '
'(rpm query default)'))
outform.add_argument("--envra", dest='queryformat',
const='%{epoch}:%{name}-%{version}-%{release}.%{arch}',
action='store_const',
help=_('use epoch:name-version-release.architecture format for '
'displaying found packages'))
outform.add_argument('--groupmember', action="store_true", help=_(
'Display in which comps groups are presented selected packages'))
pkgfilter = parser.add_mutually_exclusive_group()
pkgfilter.add_argument("--duplicates", dest='pkgfilter',
const='duplicated', action='store_const',
help=_('limit the query to installed duplicate '
'packages'))
pkgfilter.add_argument("--duplicated", dest='pkgfilter',
const='duplicated', action='store_const',
help=argparse.SUPPRESS)
pkgfilter.add_argument("--installonly", dest='pkgfilter',
const='installonly', action='store_const',
help=_('limit the query to installed installonly packages'))
pkgfilter.add_argument("--unsatisfied", dest='pkgfilter',
const='unsatisfied', action='store_const',
help=_('limit the query to installed packages with unsatisfied dependencies'))
parser.add_argument('--location', action='store_true',
help=_('show a location from where packages can be downloaded'))
package_attribute = parser.add_mutually_exclusive_group()
help_msgs = {
'conflicts': _('Display capabilities that the package conflicts with.'),
'depends': _('Display capabilities that the package can depend on, enhance, recommend,'
' suggest, and supplement.'),
'enhances': _('Display capabilities that the package can enhance.'),
'provides': _('Display capabilities provided by the package.'),
'recommends': _('Display capabilities that the package recommends.'),
'requires': _('Display capabilities that the package depends on.'),
'requires-pre': _('If the package is not installed display capabilities that it depends on for '
'running %%pre and %%post scriptlets. If the package is installed display '
'capabilities that is depends for %%pre, %%post, %%preun and %%postun.'),
'suggests': _('Display capabilities that the package suggests.'),
'supplements': _('Display capabilities that the package can supplement.')
}
for arg, help_msg in help_msgs.items():
name = '--%s' % arg
package_attribute.add_argument(name, dest='packageatr', action='store_const',
const=arg, help=help_msg)
parser.add_argument('--available', action="store_true", help=_('Display only available packages.'))
help_list = {
'installed': _('Display only installed packages.'),
'extras': _('Display only packages that are not present in any of available repositories.'),
'upgrades': _('Display only packages that provide an upgrade for some already installed package.'),
'unneeded': _('Display only packages that can be removed by "{prog} autoremove" '
'command.').format(prog=dnf.util.MAIN_PROG),
'userinstalled': _('Display only packages that were installed by user.')
}
list_group = parser.add_mutually_exclusive_group()
for list_arg, help_arg in help_list.items():
switch = '--%s' % list_arg
list_group.add_argument(switch, dest='list', action='store_const',
const=list_arg, help=help_arg)
# make --autoremove hidden compatibility alias for --unneeded
list_group.add_argument(
'--autoremove', dest='list', action='store_const',
const="unneeded", help=argparse.SUPPRESS)
parser.add_argument('--recent', action="store_true", help=_('Display only recently edited packages'))
parser.add_argument('key', nargs='*', metavar="KEY",
help=_('the key to search for'))
def pre_configure(self):
if not self.opts.quiet:
self.cli.redirect_logger(stdout=logging.WARNING, stderr=logging.INFO)
def configure(self):
if not self.opts.quiet:
self.cli.redirect_repo_progress()
demands = self.cli.demands
if self.opts.obsoletes:
if self.opts.packageatr:
self.cli._option_conflict("--obsoletes", "--" + self.opts.packageatr)
else:
self.opts.packageatr = "obsoletes"
if self.opts.querytags:
return
if self.opts.resolve and not self.opts.packageatr:
raise dnf.cli.CliError(
_("Option '--resolve' has to be used together with one of the "
"'--conflicts', '--depends', '--enhances', '--provides', '--recommends', "
"'--requires', '--requires-pre', '--suggests' or '--supplements' options"))
if self.opts.recursive:
if self.opts.exactdeps:
self.cli._option_conflict("--recursive", "--exactdeps")
if not any([self.opts.whatrequires,
(self.opts.packageatr == "requires" and self.opts.resolve)]):
raise dnf.cli.CliError(
_("Option '--recursive' has to be used with '--whatrequires