diff options
Diffstat (limited to 'pym/_emerge/search.py')
-rw-r--r-- | pym/_emerge/search.py | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/pym/_emerge/search.py b/pym/_emerge/search.py new file mode 100644 index 000000000..69e13233a --- /dev/null +++ b/pym/_emerge/search.py @@ -0,0 +1,378 @@ +import os +import re +from itertools import izip + +try: + import portage +except ImportError: + from os import path as osp + import sys + sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) + import portage + +from portage.output import bold as white, darkgreen, green, red + +from _emerge.Package import Package +from _emerge.visible import visible + +class search(object): + + # + # class constants + # + VERSION_SHORT=1 + VERSION_RELEASE=2 + + # + # public interface + # + def __init__(self, root_config, spinner, searchdesc, + verbose, usepkg, usepkgonly): + """Searches the available and installed packages for the supplied search key. + 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.root_config = root_config + self.setconfig = root_config.setconfig + self.matches = {"pkg" : []} + self.mlen = 0 + + def fake_portdb(): + pass + self.portdb = fake_portdb + for attrib in ("aux_get", "cp_all", + "xmatch", "findname", "getFetchMap"): + setattr(fake_portdb, attrib, getattr(self, "_"+attrib)) + + self._dbs = [] + + portdb = root_config.trees["porttree"].dbapi + bindb = root_config.trees["bintree"].dbapi + vardb = root_config.trees["vartree"].dbapi + + if not usepkgonly and portdb._have_root_eclass_dir: + self._dbs.append(portdb) + + if (usepkg or usepkgonly) and bindb.cp_all(): + self._dbs.append(bindb) + + self._dbs.append(vardb) + self._portdb = portdb + + def _cp_all(self): + cp_all = set() + for db in self._dbs: + cp_all.update(db.cp_all()) + return list(sorted(cp_all)) + + def _aux_get(self, *args, **kwargs): + for db in self._dbs: + try: + return db.aux_get(*args, **kwargs) + except KeyError: + pass + raise + + def _findname(self, *args, **kwargs): + for db in self._dbs: + if db is not self._portdb: + # We don't want findname to return anything + # unless it's an ebuild in a portage tree. + # Otherwise, it's already built and we don't + # care about it. + continue + func = getattr(db, "findname", None) + if func: + value = func(*args, **kwargs) + if value: + return value + return None + + def _getFetchMap(self, *args, **kwargs): + for db in self._dbs: + func = getattr(db, "getFetchMap", None) + if func: + value = func(*args, **kwargs) + if value: + return value + return {} + + def _visible(self, db, cpv, metadata): + installed = db is self.vartree.dbapi + built = installed or db is not self._portdb + pkg_type = "ebuild" + if installed: + pkg_type = "installed" + elif built: + pkg_type = "binary" + return visible(self.settings, + Package(type_name=pkg_type, root_config=self.root_config, + cpv=cpv, built=built, installed=installed, metadata=metadata)) + + def _xmatch(self, level, atom): + """ + This method does not expand old-style virtuals because it + is restricted to returning matches for a single ${CATEGORY}/${PN} + and old-style virual matches unreliable for that when querying + multiple package databases. If necessary, old-style virtuals + can be performed on atoms prior to calling this method. + """ + cp = portage.dep_getkey(atom) + if level == "match-all": + matches = set() + for db in self._dbs: + if hasattr(db, "xmatch"): + matches.update(db.xmatch(level, atom)) + else: + matches.update(db.match(atom)) + result = list(x for x in matches if portage.cpv_getkey(x) == cp) + db._cpv_sort_ascending(result) + elif level == "match-visible": + matches = set() + for db in self._dbs: + if hasattr(db, "xmatch"): + matches.update(db.xmatch(level, atom)) + else: + db_keys = list(db._aux_cache_keys) + for cpv in db.match(atom): + metadata = izip(db_keys, + db.aux_get(cpv, db_keys)) + if not self._visible(db, cpv, metadata): + continue + matches.add(cpv) + result = list(x for x in matches if portage.cpv_getkey(x) == cp) + db._cpv_sort_ascending(result) + elif level == "bestmatch-visible": + result = None + for db in self._dbs: + if hasattr(db, "xmatch"): + cpv = db.xmatch("bestmatch-visible", atom) + if not cpv or portage.cpv_getkey(cpv) != cp: + continue + if not result or cpv == portage.best([cpv, result]): + result = cpv + else: + db_keys = Package.metadata_keys + # break out of this loop with highest visible + # match, checked in descending order + for cpv in reversed(db.match(atom)): + if portage.cpv_getkey(cpv) != cp: + continue + metadata = izip(db_keys, + db.aux_get(cpv, db_keys)) + if not self._visible(db, cpv, metadata): + continue + if not result or cpv == portage.best([cpv, result]): + result = cpv + break + else: + raise NotImplementedError(level) + return result + + def execute(self,searchkey): + """Performs the search for the supplied search key""" + match_category = 0 + self.searchkey=searchkey + self.packagematches = [] + if self.searchdesc: + self.searchdesc=1 + self.matches = {"pkg":[], "desc":[], "set":[]} + else: + self.searchdesc=0 + self.matches = {"pkg":[], "set":[]} + print "Searching... ", + + regexsearch = False + if self.searchkey.startswith('%'): + regexsearch = True + self.searchkey = self.searchkey[1:] + if self.searchkey.startswith('@'): + match_category = 1 + self.searchkey = self.searchkey[1:] + if regexsearch: + self.searchre=re.compile(self.searchkey,re.I) + else: + self.searchre=re.compile(re.escape(self.searchkey), re.I) + for package in self.portdb.cp_all(): + self.spinner.update() + + if match_category: + match_string = package[:] + else: + match_string = package.split("/")[-1] + + masked=0 + if self.searchre.search(match_string): + if not self.portdb.xmatch("match-visible", package): + masked=1 + self.matches["pkg"].append([package,masked]) + elif self.searchdesc: # DESCRIPTION searching + full_package = self.portdb.xmatch("bestmatch-visible", package) + if not full_package: + #no match found; we don't want to query description + full_package = portage.best( + self.portdb.xmatch("match-all", package)) + if not full_package: + continue + else: + masked=1 + try: + full_desc = self.portdb.aux_get( + full_package, ["DESCRIPTION"])[0] + except KeyError: + print "emerge: search: aux_get() failed, skipping" + continue + if self.searchre.search(full_desc): + self.matches["desc"].append([full_package,masked]) + + self.sdict = self.setconfig.getSets() + for setname in self.sdict: + self.spinner.update() + if match_category: + match_string = setname + else: + match_string = setname.split("/")[-1] + + if self.searchre.search(match_string): + self.matches["set"].append([setname, False]) + 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]) + + def addCP(self, cp): + if not self.portdb.xmatch("match-all", cp): + return + masked = 0 + if not self.portdb.xmatch("bestmatch-visible", cp): + masked = 1 + self.matches["pkg"].append([cp, masked]) + self.mlen += 1 + + def output(self): + """Outputs the results of the search.""" + print "\b\b \n[ Results for search key : "+white(self.searchkey)+" ]" + print "[ Applications found : "+white(str(self.mlen))+" ]" + print " " + vardb = self.vartree.dbapi + for mtype in self.matches: + for match,masked in self.matches[mtype]: + full_package = None + if mtype == "pkg": + catpack = match + full_package = self.portdb.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.portdb.xmatch("match-all",match)) + elif mtype == "desc": + full_package = match + match = portage.cpv_getkey(match) + elif mtype == "set": + print green("*")+" "+white(match) + print " ", darkgreen("Description:")+" ", self.sdict[match].getMetadata("DESCRIPTION") + print + if full_package: + try: + desc, homepage, license = self.portdb.aux_get( + full_package, ["DESCRIPTION","HOMEPAGE","LICENSE"]) + except KeyError: + print "emerge: search: aux_get() failed, skipping" + continue + if masked: + print green("*")+" "+white(match)+" "+red("[ Masked ]") + else: + print green("*")+" "+white(match) + myversion = self.getVersion(full_package, search.VERSION_RELEASE) + + mysum = [0,0] + file_size_str = None + mycat = match.split("/")[0] + mypkg = match.split("/")[1] + mycpv = match + "-" + myversion + myebuild = self.portdb.findname(mycpv) + if myebuild: + pkgdir = os.path.dirname(myebuild) + from portage import manifest + mf = manifest.Manifest( + pkgdir, self.settings["DISTDIR"]) + try: + uri_map = self.portdb.getFetchMap(mycpv) + except portage.exception.InvalidDependString, e: + file_size_str = "Unknown (%s)" % (e,) + del e + else: + try: + mysum[0] = mf.getDistfilesSize(uri_map) + except KeyError, e: + file_size_str = "Unknown (missing " + \ + "digest for %s)" % (e,) + del e + + available = False + for db in self._dbs: + if db is not vardb and \ + db.cpv_exists(mycpv): + available = True + if not myebuild and hasattr(db, "bintree"): + myebuild = db.bintree.getname(mycpv) + try: + mysum[0] = os.stat(myebuild).st_size + except OSError: + myebuild = None + break + + if myebuild and file_size_str is None: + mystr = str(mysum[0] / 1024) + mycount = len(mystr) + while (mycount > 3): + mycount -= 3 + mystr = mystr[:mycount] + "," + mystr[mycount:] + file_size_str = mystr + " kB" + + if self.verbose: + if available: + print " ", darkgreen("Latest version available:"),myversion + print " ", self.getInstallationStatus(mycat+'/'+mypkg) + if myebuild: + print " %s %s" % \ + (darkgreen("Size of files:"), file_size_str) + print " ", darkgreen("Homepage:")+" ",homepage + print " ", darkgreen("Description:")+" ",desc + print " ", darkgreen("License:")+" ",license + print + # + # private interface + # + def getInstallationStatus(self,package): + installed_package = self.vartree.dep_bestmatch(package) + result = "" + version = self.getVersion(installed_package,search.VERSION_RELEASE) + if len(version) > 0: + result = darkgreen("Latest version installed:")+" "+version + else: + result = darkgreen("Latest version installed:")+" [ Not Installed ]" + return result + + def getVersion(self,full_package,detail): + if len(full_package) > 1: + package_parts = portage.catpkgsplit(full_package) + if detail == search.VERSION_RELEASE and package_parts[3] != 'r0': + result = package_parts[2]+ "-" + package_parts[3] + else: + result = package_parts[2] + else: + result = "" + return result + |