1 |
The IndexedPortdb class uses pkg_desc_index to optimize searchs for |
2 |
package names and descriptions. If the package description index is |
3 |
missing from a particular repository, then all metadata for that |
4 |
repository is obtained using the normal pordbapi.aux_get method. |
5 |
|
6 |
This class only implements a subset of portdbapi functionality that is |
7 |
useful for searching pkg_desc_index incrementally. For this reason, |
8 |
the cp_all method returns an ordered iterator instead of a list, so |
9 |
that search results can be displayed incrementally. |
10 |
|
11 |
X-Gentoo-Bug: 525718 |
12 |
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=525718 |
13 |
--- |
14 |
pym/portage/dbapi/IndexedPortdb.py | 151 +++++++++++++++++++++++++++++++++++++ |
15 |
1 file changed, 151 insertions(+) |
16 |
create mode 100644 pym/portage/dbapi/IndexedPortdb.py |
17 |
|
18 |
diff --git a/pym/portage/dbapi/IndexedPortdb.py b/pym/portage/dbapi/IndexedPortdb.py |
19 |
new file mode 100644 |
20 |
index 0000000..4fb2cf1 |
21 |
--- /dev/null |
22 |
+++ b/pym/portage/dbapi/IndexedPortdb.py |
23 |
@@ -0,0 +1,151 @@ |
24 |
+# Copyright 2014 Gentoo Foundation |
25 |
+# Distributed under the terms of the GNU General Public License v2 |
26 |
+ |
27 |
+import errno |
28 |
+import io |
29 |
+import functools |
30 |
+import operator |
31 |
+import os |
32 |
+ |
33 |
+import portage |
34 |
+from portage import _encodings |
35 |
+from portage.dep import Atom |
36 |
+from portage.exception import FileNotFound |
37 |
+from portage.cache.index.IndexStreamIterator import IndexStreamIterator |
38 |
+from portage.cache.index.pkg_desc_index import pkg_desc_index_line_read |
39 |
+from portage.util.iterators.MultiIterGroupBy import MultiIterGroupBy |
40 |
+from portage.versions import _pkg_str |
41 |
+ |
42 |
+class IndexedPortdb(object): |
43 |
+ """ |
44 |
+ A portdbapi interface that uses a package description index to |
45 |
+ improve performance. If the description index is missing for a |
46 |
+ particular repository, then all metadata for that repository is |
47 |
+ obtained using the normal pordbapi.aux_get method. |
48 |
+ |
49 |
+ For performance reasons, the match method only supports package |
50 |
+ name and version constraints. For the same reason, the xmatch |
51 |
+ method is not implemented. |
52 |
+ """ |
53 |
+ |
54 |
+ _copy_attrs = ('cpv_exists', 'findname', 'getFetchMap', |
55 |
+ '_aux_cache_keys', '_cpv_sort_ascending', |
56 |
+ '_have_root_eclass_dir') |
57 |
+ |
58 |
+ def __init__(self, portdb): |
59 |
+ |
60 |
+ self._portdb = portdb |
61 |
+ |
62 |
+ for k in self._copy_attrs: |
63 |
+ setattr(self, k, getattr(portdb, k)) |
64 |
+ |
65 |
+ self._desc_cache = None |
66 |
+ self._cp_map = None |
67 |
+ |
68 |
+ def _init_index(self): |
69 |
+ |
70 |
+ cp_map = {} |
71 |
+ desc_cache = {} |
72 |
+ self._desc_cache = desc_cache |
73 |
+ self._cp_map = cp_map |
74 |
+ |
75 |
+ streams = [] |
76 |
+ for repo_path in self._portdb.porttrees: |
77 |
+ outside_repo = os.path.join(self._portdb.depcachedir, |
78 |
+ repo_path.lstrip(os.sep)) |
79 |
+ filenames = [] |
80 |
+ for parent_dir in (repo_path, outside_repo): |
81 |
+ filenames.append(os.path.join(parent_dir, |
82 |
+ "metadata", "pkg_desc_index")) |
83 |
+ |
84 |
+ repo_name = self._portdb.getRepositoryName(repo_path) |
85 |
+ |
86 |
+ try: |
87 |
+ f = None |
88 |
+ for filename in filenames: |
89 |
+ try: |
90 |
+ f = io.open(filename, |
91 |
+ encoding=_encodings["repo.content"]) |
92 |
+ except IOError as e: |
93 |
+ if e.errno not in (errno.ENOENT, errno.ESTALE): |
94 |
+ raise |
95 |
+ else: |
96 |
+ break |
97 |
+ |
98 |
+ if f is None: |
99 |
+ raise FileNotFound(filename) |
100 |
+ |
101 |
+ streams.append(iter(IndexStreamIterator(f, |
102 |
+ functools.partial(pkg_desc_index_line_read, |
103 |
+ repo = repo_name)))) |
104 |
+ except FileNotFound: |
105 |
+ |
106 |
+ # No descriptions index was found, so populate |
107 |
+ # cp_map the slow way. |
108 |
+ for cp in self._portdb.cp_all(trees=[repo_path]): |
109 |
+ |
110 |
+ cp_list = cp_map.get(cp) |
111 |
+ if cp_list is None: |
112 |
+ cp_list = [] |
113 |
+ cp_map[cp] = cp_list |
114 |
+ for cpv in self._portdb.cp_list( |
115 |
+ cp, mytree = repo_path): |
116 |
+ cp_list.append(_pkg_str(cpv, repo = repo_name)) |
117 |
+ |
118 |
+ # Create a sorted queue that will be merged with the |
119 |
+ # sorted/grouped results from MultiIterGroupBy as they |
120 |
+ # become available. |
121 |
+ yield_queue = sorted(cp_map, reverse = True) |
122 |
+ |
123 |
+ for cp_group in MultiIterGroupBy(streams, |
124 |
+ key = operator.attrgetter("cp")): |
125 |
+ |
126 |
+ new_cp = None |
127 |
+ cp_list = cp_map.get(cp_group[0].cp) |
128 |
+ if cp_list is None: |
129 |
+ new_cp = cp_group[0].cp |
130 |
+ cp_list = [] |
131 |
+ cp_map[cp_group[0].cp] = cp_list |
132 |
+ |
133 |
+ for entry in cp_group: |
134 |
+ cp_list.extend(entry.cpv_list) |
135 |
+ for cpv in entry.cpv_list: |
136 |
+ desc_cache[cpv] = entry.desc |
137 |
+ |
138 |
+ if new_cp is not None: |
139 |
+ while yield_queue and yield_queue[-1] < new_cp: |
140 |
+ yield yield_queue.pop() |
141 |
+ yield cp_group[0].cp |
142 |
+ |
143 |
+ while yield_queue: |
144 |
+ yield yield_queue.pop() |
145 |
+ |
146 |
+ def cp_all(self): |
147 |
+ """ |
148 |
+ Returns an ordered iterator instead of a list, so that search |
149 |
+ results can be displayed incrementally. |
150 |
+ """ |
151 |
+ if self._cp_map is None: |
152 |
+ return self._init_index() |
153 |
+ return iter(sorted(self._cp_map)) |
154 |
+ |
155 |
+ def match(self, atom): |
156 |
+ """ |
157 |
+ For performance reasons, only package name and version |
158 |
+ constraints are supported. |
159 |
+ """ |
160 |
+ if not isinstance(atom, Atom): |
161 |
+ atom = Atom(atom) |
162 |
+ cp_list = self._cp_map.get(atom.cp) |
163 |
+ if cp_list is None: |
164 |
+ return [] |
165 |
+ self._portdb._cpv_sort_ascending(cp_list) |
166 |
+ return portage.match_from_list(atom, cp_list) |
167 |
+ |
168 |
+ def aux_get(self, cpv, attrs, myrepo = None): |
169 |
+ if len(attrs) == 1 and attrs[0] == "DESCRIPTION": |
170 |
+ try: |
171 |
+ return [self._desc_cache[cpv]] |
172 |
+ except KeyError: |
173 |
+ pass |
174 |
+ return self._portdb.aux_get(cpv, attrs) |
175 |
-- |
176 |
2.0.4 |