Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH 5/5] Add emerge --search-index option.
Date: Sat, 01 Nov 2014 22:47:01
Message-Id: 1414881983-19877-6-git-send-email-zmedico@gentoo.org
In Reply to: [gentoo-portage-dev] by Zac Medico
1 The new emerge --search-index option, which is enabled by default,
2 causes pkg_desc_index to be used for search optimization. The search
3 index needs to be regenerated by egencache after changes are made to
4 a repository (see the --update-pkg-desc-index action).
5
6 For users that would like to modify ebuilds in a repository without
7 running egencache afterwards, emerge --search-index=n can be used to
8 get non-indexed search. Alternatively, the user could simply remove
9 the stale index file, in order to disable the search index for a
10 particular repository.
11
12 In order to conserve memory, indices are read as streams, and
13 MultiIterGroupBy is used to group results from IndexedPortdb and
14 IndexedVardb. Stream-oriented search also makes it possible to
15 display search results incrementally (fixing bug #412471).
16
17 X-Gentoo-Bug: 525718
18 X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=525718
19 ---
20 man/emerge.1 | 8 ++++
21 pym/_emerge/actions.py | 3 +-
22 pym/_emerge/depgraph.py | 2 +-
23 pym/_emerge/main.py | 5 +++
24 pym/_emerge/search.py | 112 ++++++++++++++++++++++++++++++++++--------------
25 5 files changed, 95 insertions(+), 35 deletions(-)
26
27 diff --git a/man/emerge.1 b/man/emerge.1
28 index bbe71ac..7bcdd9a 100644
29 --- a/man/emerge.1
30 +++ b/man/emerge.1
31 @@ -796,6 +796,14 @@ If ebuilds using EAPIs which \fIdo not\fR support \fBHDEPEND\fR are built in
32 the same \fBemerge\fR run as those using EAPIs which \fIdo\fR support
33 \fBHDEPEND\fR, this option affects only the former.
34 .TP
35 +.BR "\-\-search\-index < y | n >"
36 +Enable or disable indexed search for search actions. This option is
37 +enabled by default. The search index needs to be regenerated by
38 +\fBegencache\fR(1) after changes are made to a repository (see the
39 +\fB\-\-update\-pkg\-desc\-index\fR action). This setting can be added
40 +to \fBEMERGE_DEFAULT_OPTS\fR (see \fBmake.conf\fR(5)) and later
41 +overridden via the command line.
42 +.TP
43 .BR "\-\-select [ y | n ] (\-w short option)"
44 Add specified packages to the world set (inverse of
45 \fB\-\-oneshot\fR). This is useful if you want to
46 diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py
47 index 48b0826..8a22ab5 100644
48 --- a/pym/_emerge/actions.py
49 +++ b/pym/_emerge/actions.py
50 @@ -2015,7 +2015,8 @@ def action_search(root_config, myopts, myfiles, spinner):
51 searchinstance = search(root_config,
52 spinner, "--searchdesc" in myopts,
53 "--quiet" not in myopts, "--usepkg" in myopts,
54 - "--usepkgonly" in myopts)
55 + "--usepkgonly" in myopts,
56 + search_index = myopts.get("--search-index", "y") != "n")
57 for mysearch in myfiles:
58 try:
59 searchinstance.execute(mysearch)
60 diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
61 index 78b9236..2fbb7ce 100644
62 --- a/pym/_emerge/depgraph.py
63 +++ b/pym/_emerge/depgraph.py
64 @@ -8596,7 +8596,7 @@ def ambiguous_package_name(arg, atoms, root_config, spinner, myopts):
65
66 s = search(root_config, spinner, "--searchdesc" in myopts,
67 "--quiet" not in myopts, "--usepkg" in myopts,
68 - "--usepkgonly" in myopts)
69 + "--usepkgonly" in myopts, search_index = False)
70 null_cp = portage.dep_getkey(insert_category_into_atom(
71 arg, "null"))
72 cat, atom_pn = portage.catsplit(null_cp)
73 diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py
74 index cf7966c..c08e12a 100644
75 --- a/pym/_emerge/main.py
76 +++ b/pym/_emerge/main.py
77 @@ -616,6 +616,11 @@ def parse_opts(tmpcmdline, silent=False):
78 "choices" :("True", "rdeps")
79 },
80
81 + "--search-index": {
82 + "help": "Enable or disable indexed search (enabled by default)",
83 + "choices": y_or_n
84 + },
85 +
86 "--select": {
87 "shortopt" : "-w",
88 "help" : "add specified packages to the world set " + \
89 diff --git a/pym/_emerge/search.py b/pym/_emerge/search.py
90 index 4b0fd9f..acde3bd 100644
91 --- a/pym/_emerge/search.py
92 +++ b/pym/_emerge/search.py
93 @@ -7,9 +7,12 @@ import re
94 import portage
95 from portage import os
96 from portage.dbapi.porttree import _parse_uri_map
97 +from portage.dbapi.IndexedPortdb import IndexedPortdb
98 +from portage.dbapi.IndexedVardb import IndexedVardb
99 from portage.localization import localized_size
100 from portage.output import bold, bold as white, darkgreen, green, red
101 from portage.util import writemsg_stdout
102 +from portage.util.iterators.MultiIterGroupBy import MultiIterGroupBy
103
104 from _emerge.Package import Package
105
106 @@ -25,15 +28,17 @@ class search(object):
107 # public interface
108 #
109 def __init__(self, root_config, spinner, searchdesc,
110 - verbose, usepkg, usepkgonly):
111 + verbose, usepkg, usepkgonly, search_index = True):
112 """Searches the available and installed packages for the supplied search key.
113 The list of available and installed packages is created at object instantiation.
114 This makes successive searches faster."""
115 self.settings = root_config.settings
116 - self.vartree = root_config.trees["vartree"]
117 - self.spinner = spinner
118 self.verbose = verbose
119 self.searchdesc = searchdesc
120 + self.searchkey = None
121 + # Disable the spinner since search results are displayed
122 + # incrementally.
123 + self.spinner = None
124 self.root_config = root_config
125 self.setconfig = root_config.setconfig
126 self.matches = {"pkg" : []}
127 @@ -45,6 +50,10 @@ class search(object):
128 bindb = root_config.trees["bintree"].dbapi
129 vardb = root_config.trees["vartree"].dbapi
130
131 + if search_index:
132 + portdb = IndexedPortdb(portdb)
133 + vardb = IndexedVardb(vardb)
134 +
135 if not usepkgonly and portdb._have_root_eclass_dir:
136 self._dbs.append(portdb)
137
138 @@ -53,16 +62,23 @@ class search(object):
139
140 self._dbs.append(vardb)
141 self._portdb = portdb
142 + self._vardb = vardb
143
144 def _spinner_update(self):
145 if self.spinner:
146 self.spinner.update()
147
148 def _cp_all(self):
149 - cp_all = set()
150 + iterators = []
151 for db in self._dbs:
152 - cp_all.update(db.cp_all())
153 - return list(sorted(cp_all))
154 + i = db.cp_all()
155 + try:
156 + i = iter(i)
157 + except TypeError:
158 + pass
159 + iterators.append(i)
160 + for group in MultiIterGroupBy(iterators):
161 + yield group[0]
162
163 def _aux_get(self, *args, **kwargs):
164 for db in self._dbs:
165 @@ -97,7 +113,7 @@ class search(object):
166 return {}
167
168 def _visible(self, db, cpv, metadata):
169 - installed = db is self.vartree.dbapi
170 + installed = db is self._vardb
171 built = installed or db is not self._portdb
172 pkg_type = "ebuild"
173 if installed:
174 @@ -171,8 +187,11 @@ class search(object):
175
176 def execute(self,searchkey):
177 """Performs the search for the supplied search key"""
178 + self.searchkey = searchkey
179 +
180 + def _iter_search(self):
181 +
182 match_category = 0
183 - self.searchkey=searchkey
184 self.packagematches = []
185 if self.searchdesc:
186 self.searchdesc=1
187 @@ -181,6 +200,7 @@ class search(object):
188 self.searchdesc=0
189 self.matches = {"pkg":[], "set":[]}
190 print("Searching... ", end=' ')
191 + print()
192
193 regexsearch = False
194 if self.searchkey.startswith('%'):
195 @@ -206,8 +226,24 @@ class search(object):
196 if self.searchre.search(match_string):
197 if not self._xmatch("match-visible", package):
198 masked=1
199 - self.matches["pkg"].append([package,masked])
200 + yield ("pkg", package, masked)
201 elif self.searchdesc: # DESCRIPTION searching
202 + # Check for DESCRIPTION match first, so that we can skip
203 + # the expensive visiblity check if it doesn't match.
204 + full_package = self._xmatch("match-all", package)
205 + if not full_package:
206 + continue
207 + full_package = full_package[-1]
208 + try:
209 + full_desc = self._aux_get(
210 + full_package, ["DESCRIPTION"])[0]
211 + except KeyError:
212 + portage.writemsg(
213 + "emerge: search: aux_get() failed, skipping\n",
214 + noiselevel=-1)
215 + continue
216 + if not self.searchre.search(full_desc):
217 + continue
218 full_package = self._xmatch("bestmatch-visible", package)
219 if not full_package:
220 #no match found; we don't want to query description
221 @@ -217,14 +253,8 @@ class search(object):
222 continue
223 else:
224 masked=1
225 - try:
226 - full_desc = self._aux_get(
227 - full_package, ["DESCRIPTION"])[0]
228 - except KeyError:
229 - print("emerge: search: aux_get() failed, skipping")
230 - continue
231 - if self.searchre.search(full_desc):
232 - self.matches["desc"].append([full_package,masked])
233 +
234 + yield ("desc", full_package, masked)
235
236 self.sdict = self.setconfig.getSets()
237 for setname in self.sdict:
238 @@ -235,16 +265,11 @@ class search(object):
239 match_string = setname.split("/")[-1]
240
241 if self.searchre.search(match_string):
242 - self.matches["set"].append([setname, False])
243 + yield ("set", setname, False)
244 elif self.searchdesc:
245 if self.searchre.search(
246 self.sdict[setname].getMetadata("DESCRIPTION")):
247 - self.matches["set"].append([setname, False])
248 -
249 - self.mlen=0
250 - for mtype in self.matches:
251 - self.matches[mtype].sort()
252 - self.mlen += len(self.matches[mtype])
253 + yield ("set", setname, False)
254
255 def addCP(self, cp):
256 if not self._xmatch("match-all", cp):
257 @@ -257,17 +282,32 @@ class search(object):
258
259 def output(self):
260 """Outputs the results of the search."""
261 - msg = []
262 +
263 + class msg(object):
264 + @staticmethod
265 + def append(msg):
266 + writemsg_stdout(msg, noiselevel=-1)
267 +
268 msg.append("\b\b \n[ Results for search key : " + \
269 bold(self.searchkey) + " ]\n")
270 - msg.append("[ Applications found : " + \
271 - bold(str(self.mlen)) + " ]\n\n")
272 - vardb = self.vartree.dbapi
273 + vardb = self._vardb
274 metadata_keys = set(Package.metadata_keys)
275 metadata_keys.update(["DESCRIPTION", "HOMEPAGE", "LICENSE", "SRC_URI"])
276 metadata_keys = tuple(metadata_keys)
277 - for mtype in self.matches:
278 - for match,masked in self.matches[mtype]:
279 +
280 + if self.searchkey is None:
281 + # Handle results added via addCP
282 + addCP_matches = []
283 + for mytype, (match, masked) in self.matches.items():
284 + addCP_matches.append(mytype, match, masked)
285 + iterator = iter(addCP_matches)
286 +
287 + else:
288 + # Do a normal search
289 + iterator = self._iter_search()
290 +
291 + for mtype, match, masked in iterator:
292 + self.mlen += 1
293 full_package = None
294 if mtype == "pkg":
295 full_package = self._xmatch(
296 @@ -367,12 +407,19 @@ class search(object):
297 + " " + desc + "\n")
298 msg.append(" " + darkgreen("License:") + \
299 " " + license + "\n\n")
300 - writemsg_stdout(''.join(msg), noiselevel=-1)
301 +
302 + msg.append("[ Applications found : " + \
303 + bold(str(self.mlen)) + " ]\n\n")
304 +
305 #
306 # private interface
307 #
308 def getInstallationStatus(self,package):
309 - installed_package = self.vartree.dep_bestmatch(package)
310 + installed_package = self._vardb.match(package)
311 + if installed_package:
312 + installed_package = installed_package[-1]
313 + else:
314 + installed_package = ""
315 result = ""
316 version = self.getVersion(installed_package,search.VERSION_RELEASE)
317 if len(version) > 0:
318 @@ -391,4 +438,3 @@ class search(object):
319 else:
320 result = ""
321 return result
322 -
323 --
324 2.0.4

Replies