1 |
Add --search-fuzzy option, with adjustable similarity ratio cutoff that |
2 |
defaults to 0.8 (80% similarity). |
3 |
|
4 |
X-Gentoo-bug: 65566 |
5 |
X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=65566 |
6 |
--- |
7 |
man/emerge.1 | 13 ++++++++++++- |
8 |
pym/_emerge/actions.py | 6 ++++-- |
9 |
pym/_emerge/main.py | 32 +++++++++++++++++++++++++++++++- |
10 |
pym/_emerge/search.py | 25 +++++++++++++++++++++++-- |
11 |
4 files changed, 70 insertions(+), 6 deletions(-) |
12 |
|
13 |
diff --git a/man/emerge.1 b/man/emerge.1 |
14 |
index bfa2f73..2727ccb 100644 |
15 |
--- a/man/emerge.1 |
16 |
+++ b/man/emerge.1 |
17 |
@@ -1,4 +1,4 @@ |
18 |
-.TH "EMERGE" "1" "Feb 2016" "Portage VERSION" "Portage" |
19 |
+.TH "EMERGE" "1" "Apr 2016" "Portage VERSION" "Portage" |
20 |
.SH "NAME" |
21 |
emerge \- Command\-line interface to the Portage system |
22 |
.SH "SYNOPSIS" |
23 |
@@ -854,6 +854,17 @@ If ebuilds using EAPIs which \fIdo not\fR support \fBHDEPEND\fR are built in |
24 |
the same \fBemerge\fR run as those using EAPIs which \fIdo\fR support |
25 |
\fBHDEPEND\fR, this option affects only the former. |
26 |
.TP |
27 |
+.BR "\-\-search\-fuzzy [ y | n ]" |
28 |
+Enable or disable fuzzy search for search actions. |
29 |
+.TP |
30 |
+.BR "\-\-search\-fuzzy\-cutoff CUTOFF" |
31 |
+Set similarity ratio cutoff (a floating-point number between 0 and 1). |
32 |
+Results with similarity ratios lower than the cutoff are discarded. |
33 |
+This option has no effect unless the \fB\-\-search\-fuzzy\fR option |
34 |
+is enabled. |
35 |
+.br |
36 |
+Defaults to 0.8 (80% similarity). |
37 |
+.TP |
38 |
.BR "\-\-search\-index < y | n >" |
39 |
Enable or disable indexed search for search actions. This option is |
40 |
enabled by default. The search index needs to be regenerated by |
41 |
diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py |
42 |
index 59626ad..caae79a 100644 |
43 |
--- a/pym/_emerge/actions.py |
44 |
+++ b/pym/_emerge/actions.py |
45 |
@@ -1,4 +1,4 @@ |
46 |
-# Copyright 1999-2015 Gentoo Foundation |
47 |
+# Copyright 1999-2016 Gentoo Foundation |
48 |
# Distributed under the terms of the GNU General Public License v2 |
49 |
|
50 |
from __future__ import division, print_function, unicode_literals |
51 |
@@ -1955,7 +1955,9 @@ def action_search(root_config, myopts, myfiles, spinner): |
52 |
spinner, "--searchdesc" in myopts, |
53 |
"--quiet" not in myopts, "--usepkg" in myopts, |
54 |
"--usepkgonly" in myopts, |
55 |
- search_index = myopts.get("--search-index", "y") != "n") |
56 |
+ search_index=myopts.get("--search-index", "y") != "n", |
57 |
+ fuzzy=myopts.get("--search-fuzzy", False), |
58 |
+ fuzzy_cutoff=myopts.get("--search-fuzzy-cutoff")) |
59 |
for mysearch in myfiles: |
60 |
try: |
61 |
searchinstance.execute(mysearch) |
62 |
diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py |
63 |
index 5dbafee..06c385e 100644 |
64 |
--- a/pym/_emerge/main.py |
65 |
+++ b/pym/_emerge/main.py |
66 |
@@ -1,4 +1,4 @@ |
67 |
-# Copyright 1999-2015 Gentoo Foundation |
68 |
+# Copyright 1999-2016 Gentoo Foundation |
69 |
# Distributed under the terms of the GNU General Public License v2 |
70 |
|
71 |
from __future__ import print_function |
72 |
@@ -156,6 +156,7 @@ def insert_optional_args(args): |
73 |
'--rebuild-if-unbuilt' : y_or_n, |
74 |
'--rebuilt-binaries' : y_or_n, |
75 |
'--root-deps' : ('rdeps',), |
76 |
+ '--search-fuzzy' : y_or_n, |
77 |
'--select' : y_or_n, |
78 |
'--selective' : y_or_n, |
79 |
"--use-ebuild-visibility": y_or_n, |
80 |
@@ -647,6 +648,16 @@ def parse_opts(tmpcmdline, silent=False): |
81 |
"choices" :("True", "rdeps") |
82 |
}, |
83 |
|
84 |
+ "--search-fuzzy": { |
85 |
+ "help": "Enable or disable fuzzy search", |
86 |
+ "choices": true_y_or_n |
87 |
+ }, |
88 |
+ |
89 |
+ "--search-fuzzy-cutoff": { |
90 |
+ "help": "Set similarity ratio cutoff (a floating-point number between 0 and 1)", |
91 |
+ "action": "store" |
92 |
+ }, |
93 |
+ |
94 |
"--search-index": { |
95 |
"help": "Enable or disable indexed search (enabled by default)", |
96 |
"choices": y_or_n |
97 |
@@ -908,6 +919,11 @@ def parse_opts(tmpcmdline, silent=False): |
98 |
if myoptions.root_deps in true_y: |
99 |
myoptions.root_deps = True |
100 |
|
101 |
+ if myoptions.search_fuzzy in true_y: |
102 |
+ myoptions.search_fuzzy = True |
103 |
+ else: |
104 |
+ myoptions.search_fuzzy = None |
105 |
+ |
106 |
if myoptions.select in true_y: |
107 |
myoptions.select = True |
108 |
myoptions.oneshot = False |
109 |
@@ -1000,6 +1016,20 @@ def parse_opts(tmpcmdline, silent=False): |
110 |
|
111 |
myoptions.rebuilt_binaries_timestamp = rebuilt_binaries_timestamp |
112 |
|
113 |
+ if myoptions.search_fuzzy_cutoff: |
114 |
+ try: |
115 |
+ fuzzy_cutoff = float(myoptions.search_fuzzy_cutoff) |
116 |
+ except ValueError: |
117 |
+ fuzzy_cutoff = 0.0 |
118 |
+ |
119 |
+ if fuzzy_cutoff <= 0.0: |
120 |
+ fuzzy_cutoff = None |
121 |
+ if not silent: |
122 |
+ parser.error("Invalid --search-fuzzy-cutoff parameter: '%s'\n" % \ |
123 |
+ (myoptions.search_fuzzy_cutoff,)) |
124 |
+ |
125 |
+ myoptions.search_fuzzy_cutoff = fuzzy_cutoff |
126 |
+ |
127 |
if myoptions.use_ebuild_visibility in true_y: |
128 |
myoptions.use_ebuild_visibility = True |
129 |
else: |
130 |
diff --git a/pym/_emerge/search.py b/pym/_emerge/search.py |
131 |
index 32d326e..3210854 100644 |
132 |
--- a/pym/_emerge/search.py |
133 |
+++ b/pym/_emerge/search.py |
134 |
@@ -1,8 +1,9 @@ |
135 |
-# Copyright 1999-2015 Gentoo Foundation |
136 |
+# Copyright 1999-2016 Gentoo Foundation |
137 |
# Distributed under the terms of the GNU General Public License v2 |
138 |
|
139 |
from __future__ import unicode_literals |
140 |
|
141 |
+import difflib |
142 |
import re |
143 |
import portage |
144 |
from portage import os |
145 |
@@ -28,7 +29,8 @@ class search(object): |
146 |
# public interface |
147 |
# |
148 |
def __init__(self, root_config, spinner, searchdesc, |
149 |
- verbose, usepkg, usepkgonly, search_index=True): |
150 |
+ verbose, usepkg, usepkgonly, search_index=True, |
151 |
+ fuzzy=False, fuzzy_cutoff=None): |
152 |
"""Searches the available and installed packages for the supplied search key. |
153 |
The list of available and installed packages is created at object instantiation. |
154 |
This makes successive searches faster.""" |
155 |
@@ -42,6 +44,8 @@ class search(object): |
156 |
self.spinner = None |
157 |
self.root_config = root_config |
158 |
self.setconfig = root_config.setconfig |
159 |
+ self.fuzzy = fuzzy |
160 |
+ self.fuzzy_cutoff = 0.8 if fuzzy_cutoff is None else fuzzy_cutoff |
161 |
self.matches = {"pkg" : []} |
162 |
self.mlen = 0 |
163 |
|
164 |
@@ -248,11 +252,26 @@ class search(object): |
165 |
if self.searchkey.startswith('@'): |
166 |
match_category = 1 |
167 |
self.searchkey = self.searchkey[1:] |
168 |
+ fuzzy = False |
169 |
if regexsearch: |
170 |
self.searchre=re.compile(self.searchkey,re.I) |
171 |
else: |
172 |
self.searchre=re.compile(re.escape(self.searchkey), re.I) |
173 |
|
174 |
+ # Fuzzy search does not support regular expressions, therefore |
175 |
+ # it is disabled for regular expression searches. |
176 |
+ if self.fuzzy: |
177 |
+ fuzzy = True |
178 |
+ cutoff = self.fuzzy_cutoff |
179 |
+ seq_match = difflib.SequenceMatcher() |
180 |
+ seq_match.set_seq2(self.searchkey.lower()) |
181 |
+ |
182 |
+ def fuzzy_search(match_string): |
183 |
+ seq_match.set_seq1(match_string.lower()) |
184 |
+ return (seq_match.real_quick_ratio() >= cutoff and |
185 |
+ seq_match.quick_ratio() >= cutoff and |
186 |
+ seq_match.ratio() >= cutoff) |
187 |
+ |
188 |
for package in self._cp_all(): |
189 |
self._spinner_update() |
190 |
|
191 |
@@ -280,6 +299,8 @@ class search(object): |
192 |
continue |
193 |
|
194 |
yield ("desc", package) |
195 |
+ elif fuzzy and fuzzy_search(match_string): |
196 |
+ yield ("pkg", package) |
197 |
|
198 |
self.sdict = self.setconfig.getSets() |
199 |
for setname in self.sdict: |
200 |
-- |
201 |
2.7.4 |