Blame dnf/cli/commands/search.py

Packit 6f3914
# search.py
Packit 6f3914
# Search CLI command.
Packit 6f3914
#
Packit 6f3914
# Copyright (C) 2012-2016 Red Hat, Inc.
Packit 6f3914
#
Packit 6f3914
# This copyrighted material is made available to anyone wishing to use,
Packit 6f3914
# modify, copy, or redistribute it subject to the terms and conditions of
Packit 6f3914
# the GNU General Public License v.2, or (at your option) any later version.
Packit 6f3914
# This program is distributed in the hope that it will be useful, but WITHOUT
Packit 6f3914
# ANY WARRANTY expressed or implied, including the implied warranties of
Packit 6f3914
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
Packit 6f3914
# Public License for more details.  You should have received a copy of the
Packit 6f3914
# GNU General Public License along with this program; if not, write to the
Packit 6f3914
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit 6f3914
# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
Packit 6f3914
# source code or documentation are not subject to the GNU General Public
Packit 6f3914
# License and may only be used or replicated with the express permission of
Packit 6f3914
# Red Hat, Inc.
Packit 6f3914
#
Packit 6f3914
Packit 6f3914
from __future__ import absolute_import
Packit 6f3914
from __future__ import print_function
Packit 6f3914
from __future__ import unicode_literals
Packit 6f3914
Packit 6f3914
import collections
Packit 6f3914
Packit 6f3914
from dnf.cli import commands
Packit 6f3914
from dnf.cli.option_parser import OptionParser
Packit 6f3914
from dnf.i18n import ucd, _, C_
Packit 6f3914
Packit 6f3914
import dnf.i18n
Packit 6f3914
import dnf.match_counter
Packit 6f3914
import dnf.util
Packit 6f3914
import hawkey
Packit 6f3914
import logging
Packit 6f3914
Packit 6f3914
logger = logging.getLogger('dnf')
Packit 6f3914
Packit 6f3914
Packit 6f3914
class SearchCommand(commands.Command):
Packit 6f3914
    """A class containing methods needed by the cli to execute the
Packit 6f3914
    search command.
Packit 6f3914
    """
Packit 6f3914
Packit 6f3914
    aliases = ('search', 'se')
Packit 6f3914
    summary = _('search package details for the given string')
Packit 6f3914
Packit 6f3914
    @staticmethod
Packit 6f3914
    def set_argparser(parser):
Packit 6f3914
        parser.add_argument('--all', action='store_true',
Packit 6f3914
                            help=_("search also package description and URL"))
Packit 6f3914
        parser.add_argument('query_string', nargs='+', metavar=_('KEYWORD'),
Packit 6f3914
                            choices=['all'], default=None,
Packit 6f3914
                            action=OptionParser.PkgNarrowCallback,
Packit 6f3914
                            help=_("Keyword to search for"))
Packit 6f3914
Packit 6f3914
    def _search(self, args):
Packit 6f3914
        """Search for simple text tags in a package object."""
Packit 6f3914
Packit 6f3914
        TRANS_TBL = collections.OrderedDict((
Packit 6f3914
            ('name', C_('long', 'Name')),
Packit 6f3914
            ('summary', C_('long', 'Summary')),
Packit 6f3914
            ('description', C_('long', 'Description')),
Packit 6f3914
            ('url', _('URL')),
Packit 6f3914
        ))
Packit 6f3914
Packit 6f3914
        def _translate_attr(attr):
Packit 6f3914
            try:
Packit 6f3914
                return TRANS_TBL[attr]
Packit 6f3914
            except:
Packit 6f3914
                return attr
Packit 6f3914
Packit 6f3914
        def _print_section_header(exact_match, attrs, keys):
Packit 6f3914
            trans_attrs = map(_translate_attr, attrs)
Packit 6f3914
            # TRANSLATORS: separator used between package attributes (eg. Name & Summary & URL)
Packit 6f3914
            trans_attrs_str = _(' & ').join(trans_attrs)
Packit 6f3914
            if exact_match:
Packit 6f3914
                # TRANSLATORS: %s  - translated package attributes,
Packit 6f3914
                #              %%s - found keys (in listed attributes)
Packit 6f3914
                section_text = _('%s Exactly Matched: %%s') % trans_attrs_str
Packit 6f3914
            else:
Packit 6f3914
                # TRANSLATORS: %s  - translated package attributes,
Packit 6f3914
                #              %%s - found keys (in listed attributes)
Packit 6f3914
                section_text = _('%s Matched: %%s') % trans_attrs_str
Packit 6f3914
            formatted = self.base.output.fmtSection(section_text % ", ".join(keys))
Packit 6f3914
            print(ucd(formatted))
Packit 6f3914
Packit 6f3914
        counter = dnf.match_counter.MatchCounter()
Packit 6f3914
        for arg in args:
Packit 6f3914
            self._search_counted(counter, 'name', arg)
Packit 6f3914
            self._search_counted(counter, 'summary', arg)
Packit 6f3914
Packit 6f3914
        if self.opts.all:
Packit 6f3914
            for arg in args:
Packit 6f3914
                self._search_counted(counter, 'description', arg)
Packit 6f3914
                self._search_counted(counter, 'url', arg)
Packit 6f3914
        else:
Packit 6f3914
            needles = len(args)
Packit 6f3914
            pkgs = list(counter.keys())
Packit 6f3914
            for pkg in pkgs:
Packit 6f3914
                if len(counter.matched_needles(pkg)) != needles:
Packit 6f3914
                    del counter[pkg]
Packit 6f3914
Packit 6f3914
        used_attrs = None
Packit 6f3914
        matched_needles = None
Packit 6f3914
        exact_match = False
Packit 6f3914
        print_section_header = False
Packit 6f3914
        limit = None
Packit 6f3914
        if not self.base.conf.showdupesfromrepos:
Packit 6f3914
            limit = self.base.sack.query().filterm(pkg=counter.keys()).latest()
Packit 6f3914
Packit 6f3914
        seen = set()
Packit 6f3914
        for pkg in counter.sorted(reverse=True, limit_to=limit):
Packit 6f3914
            if not self.base.conf.showdupesfromrepos:
Packit 6f3914
                if pkg.name + pkg.arch in seen:
Packit 6f3914
                    continue
Packit 6f3914
                seen.add(pkg.name + pkg.arch)
Packit 6f3914
Packit 6f3914
            if used_attrs != counter.matched_keys(pkg):
Packit 6f3914
                used_attrs = counter.matched_keys(pkg)
Packit 6f3914
                print_section_header = True
Packit 6f3914
            if matched_needles != counter.matched_needles(pkg):
Packit 6f3914
                matched_needles = counter.matched_needles(pkg)
Packit 6f3914
                print_section_header = True
Packit 6f3914
            if exact_match != (counter.matched_haystacks(pkg) == matched_needles):
Packit 6f3914
                exact_match = counter.matched_haystacks(pkg) == matched_needles
Packit 6f3914
                print_section_header = True
Packit 6f3914
            if print_section_header:
Packit 6f3914
                _print_section_header(exact_match, used_attrs, matched_needles)
Packit 6f3914
                print_section_header = False
Packit 6f3914
            self.base.output.matchcallback(pkg, counter.matched_haystacks(pkg), args)
Packit 6f3914
Packit 6f3914
        if len(counter) == 0:
Packit 6f3914
            logger.info(_('No matches found.'))
Packit 6f3914
Packit 6f3914
    def _search_counted(self, counter, attr, needle):
Packit 6f3914
        fdict = {'%s__substr' % attr : needle}
Packit 6f3914
        if dnf.util.is_glob_pattern(needle):
Packit 6f3914
            fdict = {'%s__glob' % attr : needle}
Packit 6f3914
        q = self.base.sack.query().filterm(hawkey.ICASE, **fdict)
Packit 6f3914
        for pkg in q.run():
Packit 6f3914
            counter.add(pkg, attr, needle)
Packit 6f3914
        return counter
Packit 6f3914
Packit 6f3914
    def pre_configure(self):
Packit Service 8b25b4
        if not self.opts.quiet:
Packit 6f3914
            self.cli.redirect_logger(stdout=logging.WARNING, stderr=logging.INFO)
Packit 6f3914
Packit 6f3914
    def configure(self):
Packit Service 8b25b4
        if not self.opts.quiet:
Packit 6f3914
            self.cli.redirect_repo_progress()
Packit 6f3914
        demands = self.cli.demands
Packit 6f3914
        demands.available_repos = True
Packit 6f3914
        demands.fresh_metadata = False
Packit 6f3914
        demands.sack_activation = True
Packit 6f3914
        self.opts.all = self.opts.all or self.opts.query_string_action
Packit 6f3914
Packit 6f3914
    def run(self):
Packit 6f3914
        logger.debug(_('Searching Packages: '))
Packit 6f3914
        return self._search(self.opts.query_string)