commit: 4938b8a8a72e719b394a5c5b0c5030c160091d57 Author: Zac Medico <zmedico <AT> gentoo <DOT> org> AuthorDate: Sat Nov 1 15:06:16 2014 +0000 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> CommitDate: Sun Dec 7 23:10:47 2014 +0000 URL: http://sources.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=4938b8a8
Display emerge search results incrementally (412471) This makes emerge --search / --searchdesc display individual search results as soon as they become available. A search._iter_search method is split out from the search.execute method, and the search.output method operates on that. The spinner is now disabled, but the spinner code remains, in case we later decide to enable it optionally. X-Gentoo-Bug: 412471 X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=412471 --- pym/_emerge/search.py | 120 +++++++++++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 49 deletions(-) diff --git a/pym/_emerge/search.py b/pym/_emerge/search.py index 4b0fd9f..e51d206 100644 --- a/pym/_emerge/search.py +++ b/pym/_emerge/search.py @@ -1,8 +1,6 @@ # Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -from __future__ import print_function - import re import portage from portage import os @@ -30,10 +28,12 @@ class search(object): The list of available and installed packages is created at object instantiation. This makes successive searches faster.""" self.settings = root_config.settings - self.vartree = root_config.trees["vartree"] - self.spinner = spinner self.verbose = verbose self.searchdesc = searchdesc + self.searchkey = None + # Disable the spinner since search results are displayed + # incrementally. + self.spinner = None self.root_config = root_config self.setconfig = root_config.setconfig self.matches = {"pkg" : []} @@ -53,6 +53,7 @@ class search(object): self._dbs.append(vardb) self._portdb = portdb + self._vardb = vardb def _spinner_update(self): if self.spinner: @@ -97,7 +98,7 @@ class search(object): return {} def _visible(self, db, cpv, metadata): - installed = db is self.vartree.dbapi + installed = db is self._vardb built = installed or db is not self._portdb pkg_type = "ebuild" if installed: @@ -171,8 +172,11 @@ class search(object): def execute(self,searchkey): """Performs the search for the supplied search key""" + self.searchkey = searchkey + + def _iter_search(self): + match_category = 0 - self.searchkey=searchkey self.packagematches = [] if self.searchdesc: self.searchdesc=1 @@ -180,7 +184,7 @@ class search(object): else: self.searchdesc=0 self.matches = {"pkg":[], "set":[]} - print("Searching... ", end=' ') + writemsg_stdout("Searching...\n\n", noiselevel=-1) regexsearch = False if self.searchkey.startswith('%'): @@ -202,29 +206,28 @@ class search(object): else: match_string = package.split("/")[-1] - masked=0 if self.searchre.search(match_string): - if not self._xmatch("match-visible", package): - masked=1 - self.matches["pkg"].append([package,masked]) + yield ("pkg", package) elif self.searchdesc: # DESCRIPTION searching - full_package = self._xmatch("bestmatch-visible", package) + # Use match-all to avoid an expensive visibility check, + # since the visibility check can be avoided entirely + # when the DESCRIPTION does not match. + full_package = self._xmatch("match-all", package) if not full_package: - #no match found; we don't want to query description - full_package = portage.best( - self._xmatch("match-all", package)) - if not full_package: - continue - else: - masked=1 + continue + full_package = full_package[-1] try: full_desc = self._aux_get( full_package, ["DESCRIPTION"])[0] except KeyError: - print("emerge: search: aux_get() failed, skipping") + portage.writemsg( + "emerge: search: aux_get() failed, skipping\n", + noiselevel=-1) continue - if self.searchre.search(full_desc): - self.matches["desc"].append([full_package,masked]) + if not self.searchre.search(full_desc): + continue + + yield ("desc", package) self.sdict = self.setconfig.getSets() for setname in self.sdict: @@ -235,51 +238,56 @@ class search(object): match_string = setname.split("/")[-1] if self.searchre.search(match_string): - self.matches["set"].append([setname, False]) + yield ("set", setname) elif self.searchdesc: if self.searchre.search( self.sdict[setname].getMetadata("DESCRIPTION")): - self.matches["set"].append([setname, False]) - - self.mlen=0 - for mtype in self.matches: - self.matches[mtype].sort() - self.mlen += len(self.matches[mtype]) + yield ("set", setname) def addCP(self, cp): if not self._xmatch("match-all", cp): return - masked = 0 - if not self._xmatch("bestmatch-visible", cp): - masked = 1 - self.matches["pkg"].append([cp, masked]) + self.matches["pkg"].append(cp) self.mlen += 1 def output(self): """Outputs the results of the search.""" - msg = [] + + class msg(object): + @staticmethod + def append(msg): + writemsg_stdout(msg, noiselevel=-1) + msg.append("\b\b \n[ Results for search key : " + \ bold(self.searchkey) + " ]\n") - msg.append("[ Applications found : " + \ - bold(str(self.mlen)) + " ]\n\n") - vardb = self.vartree.dbapi + vardb = self._vardb metadata_keys = set(Package.metadata_keys) metadata_keys.update(["DESCRIPTION", "HOMEPAGE", "LICENSE", "SRC_URI"]) metadata_keys = tuple(metadata_keys) - for mtype in self.matches: - for match,masked in self.matches[mtype]: + + if self.searchkey is None: + # Handle results added via addCP + addCP_matches = [] + for mytype, match in self.matches.items(): + addCP_matches.append(mytype, match) + iterator = iter(addCP_matches) + + else: + # Do a normal search + iterator = self._iter_search() + + for mtype, match in iterator: + self.mlen += 1 + masked = False full_package = None - if mtype == "pkg": + if mtype in ("pkg", "desc"): full_package = self._xmatch( "bestmatch-visible", match) if not full_package: - #no match found; we don't want to query description - masked=1 - full_package = portage.best( - self._xmatch("match-all",match)) - elif mtype == "desc": - full_package = match - match = portage.cpv_getkey(match) + masked = True + full_package = self._xmatch("match-all", match) + if full_package: + full_package = full_package[-1] elif mtype == "set": msg.append(green("*") + " " + bold(match) + "\n") if self.verbose: @@ -367,12 +375,26 @@ class search(object): + " " + desc + "\n") msg.append(" " + darkgreen("License:") + \ " " + license + "\n\n") - writemsg_stdout(''.join(msg), noiselevel=-1) + + msg.append("[ Applications found : " + \ + bold(str(self.mlen)) + " ]\n\n") + + # This method can be called multiple times, so + # reset the match count for the next call. Don't + # reset it at the beginning of this method, since + # that would lose modfications from the addCP + # method. + self.mlen = 0 + # # private interface # def getInstallationStatus(self,package): - installed_package = self.vartree.dep_bestmatch(package) + installed_package = self._vardb.match(package) + if installed_package: + installed_package = installed_package[-1] + else: + installed_package = "" result = "" version = self.getVersion(installed_package,search.VERSION_RELEASE) if len(version) > 0: