| # Copyright 2014 Gentoo Foundation |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| import portage |
| from portage.dep import Atom |
| from portage.exception import InvalidData |
| from portage.versions import _pkg_str |
| |
| class IndexedVardb(object): |
| """ |
| A vardbapi interface that sacrifices validation in order to |
| improve performance. It takes advantage of vardbdbapi._aux_cache, |
| which is backed by vdb_metadata.pickle. Since _aux_cache is |
| not updated for every single merge/unmerge (see |
| _aux_cache_threshold), the list of packages is obtained directly |
| from the real vardbapi instance. If a package is missing from |
| _aux_cache, then its metadata is obtained using the normal |
| (validated) vardbapi.aux_get method. |
| |
| For performance reasons, the match method only supports package |
| name and version constraints. |
| """ |
| |
| # Match returns unordered results. |
| match_unordered = True |
| |
| _copy_attrs = ('cpv_exists', |
| '_aux_cache_keys', '_cpv_sort_ascending') |
| |
| def __init__(self, vardb): |
| self._vardb = vardb |
| |
| for k in self._copy_attrs: |
| setattr(self, k, getattr(vardb, k)) |
| |
| self._cp_map = None |
| |
| def cp_all(self, sort=True): |
| """ |
| Returns an ordered iterator instead of a list, so that search |
| results can be displayed incrementally. |
| """ |
| if self._cp_map is not None: |
| return iter(sorted(self._cp_map)) if sort else iter(self._cp_map) |
| |
| delta_data = self._vardb._cache_delta.loadRace() |
| if delta_data is None: |
| return self._iter_cp_all() |
| |
| self._vardb._cache_delta.applyDelta(delta_data) |
| |
| self._cp_map = cp_map = {} |
| for cpv in self._vardb._aux_cache["packages"]: |
| try: |
| cpv = _pkg_str(cpv) |
| except InvalidData: |
| continue |
| |
| cp_list = cp_map.get(cpv.cp) |
| if cp_list is None: |
| cp_list = [] |
| cp_map[cpv.cp] = cp_list |
| cp_list.append(cpv) |
| |
| return iter(sorted(self._cp_map)) if sort else iter(self._cp_map) |
| |
| def _iter_cp_all(self): |
| self._cp_map = cp_map = {} |
| previous_cp = None |
| for cpv in self._vardb._iter_cpv_all(sort = True): |
| cp = portage.cpv_getkey(cpv) |
| if cp is not None: |
| cp_list = cp_map.get(cp) |
| if cp_list is None: |
| cp_list = [] |
| cp_map[cp] = cp_list |
| cp_list.append(cpv) |
| if previous_cp is not None and \ |
| previous_cp != cp: |
| yield previous_cp |
| previous_cp = cp |
| |
| if previous_cp is not None: |
| yield previous_cp |
| |
| def match(self, atom): |
| """ |
| For performance reasons, only package name and version |
| constraints are supported, and the returned list is |
| unordered. |
| """ |
| if not isinstance(atom, Atom): |
| atom = Atom(atom) |
| cp_list = self._cp_map.get(atom.cp) |
| if cp_list is None: |
| return [] |
| |
| if atom == atom.cp: |
| return cp_list[:] |
| else: |
| return portage.match_from_list(atom, cp_list) |
| |
| def aux_get(self, cpv, attrs, myrepo=None): |
| pkg_data = self._vardb._aux_cache["packages"].get(cpv) |
| if not isinstance(pkg_data, tuple) or \ |
| len(pkg_data) != 2 or \ |
| not isinstance(pkg_data[1], dict): |
| pkg_data = None |
| if pkg_data is None: |
| # It may be missing from _aux_cache due to |
| # _aux_cache_threshold. |
| return self._vardb.aux_get(cpv, attrs) |
| metadata = pkg_data[1] |
| return [metadata.get(k, "") for k in attrs] |