Lines 3-15
Link Here
|
3 |
|
3 |
|
4 |
from __future__ import print_function |
4 |
from __future__ import print_function |
5 |
|
5 |
|
|
|
6 |
import io |
6 |
import re |
7 |
import re |
7 |
import portage |
8 |
import portage |
8 |
from portage import os |
9 |
from portage import os, _encodings |
9 |
from portage.dbapi.porttree import _parse_uri_map |
10 |
from portage.dbapi.porttree import _parse_uri_map |
|
|
11 |
from portage.dep import Atom |
12 |
from portage.exception import InvalidAtom, InvalidData |
10 |
from portage.localization import localized_size |
13 |
from portage.localization import localized_size |
11 |
from portage.output import bold, bold as white, darkgreen, green, red |
14 |
from portage.output import bold, bold as white, darkgreen, green, red |
12 |
from portage.util import writemsg_stdout |
15 |
from portage.util import writemsg_stdout |
|
|
16 |
from portage.versions import _pkg_str |
13 |
|
17 |
|
14 |
from _emerge.Package import Package |
18 |
from _emerge.Package import Package |
15 |
|
19 |
|
Lines 25-36
class search(object):
Link Here
|
25 |
# public interface |
29 |
# public interface |
26 |
# |
30 |
# |
27 |
def __init__(self, root_config, spinner, searchdesc, |
31 |
def __init__(self, root_config, spinner, searchdesc, |
28 |
verbose, usepkg, usepkgonly): |
32 |
verbose, usepkg, usepkgonly, search_index = True): |
29 |
"""Searches the available and installed packages for the supplied search key. |
33 |
"""Searches the available and installed packages for the supplied search key. |
30 |
The list of available and installed packages is created at object instantiation. |
34 |
The list of available and installed packages is created at object instantiation. |
31 |
This makes successive searches faster.""" |
35 |
This makes successive searches faster.""" |
32 |
self.settings = root_config.settings |
36 |
self.settings = root_config.settings |
33 |
self.vartree = root_config.trees["vartree"] |
|
|
34 |
self.spinner = spinner |
37 |
self.spinner = spinner |
35 |
self.verbose = verbose |
38 |
self.verbose = verbose |
36 |
self.searchdesc = searchdesc |
39 |
self.searchdesc = searchdesc |
Lines 45-50
class search(object):
Link Here
|
45 |
bindb = root_config.trees["bintree"].dbapi |
48 |
bindb = root_config.trees["bintree"].dbapi |
46 |
vardb = root_config.trees["vartree"].dbapi |
49 |
vardb = root_config.trees["vartree"].dbapi |
47 |
|
50 |
|
|
|
51 |
if search_index: |
52 |
portdb = IndexedPortdb(portdb) |
53 |
vardb = IndexedVardb(vardb) |
54 |
|
48 |
if not usepkgonly and portdb._have_root_eclass_dir: |
55 |
if not usepkgonly and portdb._have_root_eclass_dir: |
49 |
self._dbs.append(portdb) |
56 |
self._dbs.append(portdb) |
50 |
|
57 |
|
Lines 53-58
class search(object):
Link Here
|
53 |
|
60 |
|
54 |
self._dbs.append(vardb) |
61 |
self._dbs.append(vardb) |
55 |
self._portdb = portdb |
62 |
self._portdb = portdb |
|
|
63 |
self._vardb = vardb |
56 |
|
64 |
|
57 |
def _spinner_update(self): |
65 |
def _spinner_update(self): |
58 |
if self.spinner: |
66 |
if self.spinner: |
Lines 97-103
class search(object):
Link Here
|
97 |
return {} |
105 |
return {} |
98 |
|
106 |
|
99 |
def _visible(self, db, cpv, metadata): |
107 |
def _visible(self, db, cpv, metadata): |
100 |
installed = db is self.vartree.dbapi |
108 |
installed = db is self._vardb |
101 |
built = installed or db is not self._portdb |
109 |
built = installed or db is not self._portdb |
102 |
pkg_type = "ebuild" |
110 |
pkg_type = "ebuild" |
103 |
if installed: |
111 |
if installed: |
Lines 208-213
class search(object):
Link Here
|
208 |
masked=1 |
216 |
masked=1 |
209 |
self.matches["pkg"].append([package,masked]) |
217 |
self.matches["pkg"].append([package,masked]) |
210 |
elif self.searchdesc: # DESCRIPTION searching |
218 |
elif self.searchdesc: # DESCRIPTION searching |
|
|
219 |
# Check for DESCRIPTION match first, so that we can skip |
220 |
# the expensive visiblity check if it doesn't match. |
221 |
full_package = self._xmatch("match-all", package) |
222 |
if not full_package: |
223 |
continue |
224 |
full_package = full_package[-1] |
225 |
try: |
226 |
full_desc = self._aux_get( |
227 |
full_package, ["DESCRIPTION"])[0] |
228 |
except KeyError: |
229 |
portage.writemsg( |
230 |
"emerge: search: aux_get() failed, skipping\n", |
231 |
noiselevel=-1) |
232 |
continue |
233 |
if not self.searchre.search(full_desc): |
234 |
continue |
211 |
full_package = self._xmatch("bestmatch-visible", package) |
235 |
full_package = self._xmatch("bestmatch-visible", package) |
212 |
if not full_package: |
236 |
if not full_package: |
213 |
#no match found; we don't want to query description |
237 |
#no match found; we don't want to query description |
Lines 217-230
class search(object):
Link Here
|
217 |
continue |
241 |
continue |
218 |
else: |
242 |
else: |
219 |
masked=1 |
243 |
masked=1 |
220 |
try: |
244 |
|
221 |
full_desc = self._aux_get( |
245 |
self.matches["desc"].append((full_package, masked)) |
222 |
full_package, ["DESCRIPTION"])[0] |
|
|
223 |
except KeyError: |
224 |
print("emerge: search: aux_get() failed, skipping") |
225 |
continue |
226 |
if self.searchre.search(full_desc): |
227 |
self.matches["desc"].append([full_package,masked]) |
228 |
|
246 |
|
229 |
self.sdict = self.setconfig.getSets() |
247 |
self.sdict = self.setconfig.getSets() |
230 |
for setname in self.sdict: |
248 |
for setname in self.sdict: |
Lines 262-268
class search(object):
Link Here
|
262 |
bold(self.searchkey) + " ]\n") |
280 |
bold(self.searchkey) + " ]\n") |
263 |
msg.append("[ Applications found : " + \ |
281 |
msg.append("[ Applications found : " + \ |
264 |
bold(str(self.mlen)) + " ]\n\n") |
282 |
bold(str(self.mlen)) + " ]\n\n") |
265 |
vardb = self.vartree.dbapi |
283 |
vardb = self._vardb |
266 |
metadata_keys = set(Package.metadata_keys) |
284 |
metadata_keys = set(Package.metadata_keys) |
267 |
metadata_keys.update(["DESCRIPTION", "HOMEPAGE", "LICENSE", "SRC_URI"]) |
285 |
metadata_keys.update(["DESCRIPTION", "HOMEPAGE", "LICENSE", "SRC_URI"]) |
268 |
metadata_keys = tuple(metadata_keys) |
286 |
metadata_keys = tuple(metadata_keys) |
Lines 372-378
class search(object):
Link Here
|
372 |
# private interface |
390 |
# private interface |
373 |
# |
391 |
# |
374 |
def getInstallationStatus(self,package): |
392 |
def getInstallationStatus(self,package): |
375 |
installed_package = self.vartree.dep_bestmatch(package) |
393 |
installed_package = self._vardb.match(package) |
|
|
394 |
if installed_package: |
395 |
installed_package = installed_package[-1] |
396 |
else: |
397 |
installed_package = "" |
376 |
result = "" |
398 |
result = "" |
377 |
version = self.getVersion(installed_package,search.VERSION_RELEASE) |
399 |
version = self.getVersion(installed_package,search.VERSION_RELEASE) |
378 |
if len(version) > 0: |
400 |
if len(version) > 0: |
Lines 392-394
class search(object):
Link Here
|
392 |
result = "" |
414 |
result = "" |
393 |
return result |
415 |
return result |
394 |
|
416 |
|
395 |
- |
417 |
|
|
|
418 |
class IndexedPortdb(object): |
419 |
""" |
420 |
A portdbapi interface that uses a package description index to |
421 |
improve performance. If the description index is missing for a |
422 |
particular repository, then all metadata for that repository is |
423 |
obtained using the normal pordbapi.aux_get method. |
424 |
""" |
425 |
def __init__(self, portdb): |
426 |
self._portdb = portdb |
427 |
self.cpv_exists = portdb.cpv_exists |
428 |
self.getFetchMap = portdb.getFetchMap |
429 |
self.findname = portdb.findname |
430 |
self._aux_cache_keys = portdb._aux_cache_keys |
431 |
self._have_root_eclass_dir = portdb._have_root_eclass_dir |
432 |
self._cpv_sort_ascending = portdb._cpv_sort_ascending |
433 |
self._desc_cache = None |
434 |
self._cp_map = None |
435 |
|
436 |
def _init_index(self): |
437 |
cp_map = {} |
438 |
desc_cache = {} |
439 |
for repo_path in self._portdb.porttrees: |
440 |
outside_repo = os.path.join(self._portdb.depcachedir, |
441 |
repo_path.lstrip(os.sep)) |
442 |
for parent_dir in (repo_path, outside_repo): |
443 |
file_path = os.path.join(parent_dir, |
444 |
"metadata", "pkg_desc_index") |
445 |
|
446 |
try: |
447 |
with io.open(file_path, |
448 |
encoding=_encodings["repo.content"]) as f: |
449 |
for line in f: |
450 |
try: |
451 |
pkgs, desc = line.split(":", 1) |
452 |
except ValueError: |
453 |
continue |
454 |
desc = desc.strip() |
455 |
try: |
456 |
cp, pkgs = pkgs.split(" ", 1) |
457 |
except ValueError: |
458 |
continue |
459 |
if not cp: |
460 |
continue |
461 |
try: |
462 |
atom = Atom(cp) |
463 |
except InvalidAtom: |
464 |
continue |
465 |
if cp != atom.cp: |
466 |
continue |
467 |
cp_list = cp_map.get(cp) |
468 |
if cp_list is None: |
469 |
cp_list = [] |
470 |
cp_map[cp] = cp_list |
471 |
for ver in pkgs.split(): |
472 |
try: |
473 |
cpv = _pkg_str(cp + "-" + ver) |
474 |
except InvalidData: |
475 |
pass |
476 |
else: |
477 |
cp_list.append(cpv) |
478 |
desc_cache[cpv] = desc |
479 |
except IOError: |
480 |
pass |
481 |
else: |
482 |
break |
483 |
else: |
484 |
# No descriptions index was found, so populate |
485 |
# cp_map the slow way. |
486 |
for cp in self._portdb.cp_all(trees=[repo_path]): |
487 |
cp_list = cp_map.get(cp) |
488 |
if cp_list is None: |
489 |
cp_list = [] |
490 |
cp_map[cp] = cp_list |
491 |
for cpv in self._portdb.cp_list(cp, mytree=repo_path): |
492 |
if cpv not in cp_list: |
493 |
cp_list.append(_pkg_str(cpv)) |
494 |
|
495 |
self._desc_cache = desc_cache |
496 |
self._cp_map = cp_map |
497 |
|
498 |
def cp_all(self): |
499 |
if self._cp_map is None: |
500 |
self._init_index() |
501 |
return list(self._cp_map) |
502 |
|
503 |
def match(self, atom): |
504 |
if not isinstance(atom, Atom): |
505 |
atom = Atom(atom) |
506 |
cp_list = self._cp_map.get(atom.cp) |
507 |
if cp_list is None: |
508 |
return [] |
509 |
self._portdb._cpv_sort_ascending(cp_list) |
510 |
return portage.match_from_list(atom, cp_list) |
511 |
|
512 |
def aux_get(self, cpv, attrs, myrepo = None): |
513 |
if len(attrs) == 1 and attrs[0] == "DESCRIPTION": |
514 |
try: |
515 |
return [self._desc_cache[cpv]] |
516 |
except KeyError: |
517 |
pass |
518 |
return self._portdb.aux_get(cpv, attrs) |
519 |
|
520 |
|
521 |
class IndexedVardb(object): |
522 |
""" |
523 |
A vardbapi interface that sacrifices validation in order to |
524 |
improve performance. It takes advantage of vardbdbapi._aux_cache, |
525 |
which is backed by vdb_metadata.pickle. Since _aux_cache is |
526 |
not updated for every single merge/unmerge (see |
527 |
_aux_cache_threshold), the list of packages is obtained directly |
528 |
from the real vardbapi instance. If a package is missing from |
529 |
_aux_cache, then its metadata is obtained using the normal |
530 |
(validated) vardbapi.aux_get method. |
531 |
""" |
532 |
def __init__(self, vardb): |
533 |
self._vardb = vardb |
534 |
self._aux_cache_keys = vardb._aux_cache_keys |
535 |
self._cpv_sort_ascending = vardb._cpv_sort_ascending |
536 |
self._cp_map = {} |
537 |
self.cpv_exists = vardb.cpv_exists |
538 |
|
539 |
def cp_all(self): |
540 |
if self._cp_map: |
541 |
return list(self._cp_map) |
542 |
cp_map = self._cp_map |
543 |
for cpv in self._vardb.cpv_all(): |
544 |
cp = portage.cpv_getkey(cpv) |
545 |
if cp is not None: |
546 |
cp_list = cp_map.get(cp) |
547 |
if cp_list is None: |
548 |
cp_list = [] |
549 |
cp_map[cp] = cp_list |
550 |
cp_list.append(_pkg_str(cpv)) |
551 |
return list(cp_map) |
552 |
|
553 |
def match(self, atom): |
554 |
if not isinstance(atom, Atom): |
555 |
atom = Atom(atom) |
556 |
cp_list = self._cp_map.get(atom.cp) |
557 |
if cp_list is None: |
558 |
return [] |
559 |
self._vardb._cpv_sort_ascending(cp_list) |
560 |
return portage.match_from_list(atom, cp_list) |
561 |
|
562 |
def aux_get(self, cpv, attrs, myrepo = None): |
563 |
pkg_data = self._vardb._aux_cache["packages"].get(cpv) |
564 |
if not isinstance(pkg_data, tuple) or \ |
565 |
len(pkg_data) != 2 or \ |
566 |
not isinstance(pkg_data[1], dict): |
567 |
pkg_data = None |
568 |
if pkg_data is None: |
569 |
# It may be missing from _aux_cache due to |
570 |
# _aux_cache_threshold. |
571 |
return self._vardb.aux_get(cpv, attrs) |
572 |
metadata = pkg_data[1] |
573 |
return [metadata.get(k, "") for k in attrs] |