1 |
Implement "--alert" (short option "-A"), which adds a terminal bell |
2 |
character ('\a') to all prompts, when it is set to True or "y". It may |
3 |
be used without an option, like e.g. --ask, to mean True. |
4 |
|
5 |
The patch refactors userquery to UserQuery to accommodate --alert. It |
6 |
also adds the option to the emerge man page. |
7 |
--- |
8 |
man/emerge.1 | 5 ++++ |
9 |
pym/_emerge/UserQuery.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ |
10 |
pym/_emerge/actions.py | 19 ++++++++----- |
11 |
pym/_emerge/depgraph.py | 9 ++++-- |
12 |
pym/_emerge/main.py | 13 +++++++++ |
13 |
pym/_emerge/unmerge.py | 5 ++-- |
14 |
pym/_emerge/userquery.py | 55 ------------------------------------- |
15 |
7 files changed, 110 insertions(+), 67 deletions(-) |
16 |
create mode 100644 pym/_emerge/UserQuery.py |
17 |
delete mode 100644 pym/_emerge/userquery.py |
18 |
|
19 |
diff --git a/man/emerge.1 b/man/emerge.1 |
20 |
index abb0ed8..bbcf569 100644 |
21 |
--- a/man/emerge.1 |
22 |
+++ b/man/emerge.1 |
23 |
@@ -297,6 +297,11 @@ re\-distributable. With default |
24 |
configuration, this would result in an effective |
25 |
\fBACCEPT_RESTRICT\fR value of "* -bindist". |
26 |
.TP |
27 |
+.BR "\-\-alert " |
28 |
+Add a terminal bell character ('\\a') to all interactive prompts. This is |
29 |
+especially useful if dependency resolution is taking a long time, and |
30 |
+you want emerge to alert you when it is finished. |
31 |
+.TP |
32 |
.BR "\-\-alphabetical " |
33 |
When displaying USE and other flag output, combines the enabled and |
34 |
disabled lists into one list and sorts the whole list alphabetically. |
35 |
diff --git a/pym/_emerge/UserQuery.py b/pym/_emerge/UserQuery.py |
36 |
new file mode 100644 |
37 |
index 0000000..c866a0d |
38 |
--- /dev/null |
39 |
+++ b/pym/_emerge/UserQuery.py |
40 |
@@ -0,0 +1,71 @@ |
41 |
+# Copyright 1999-2014 Gentoo Foundation |
42 |
+# Distributed under the terms of the GNU General Public License v2 |
43 |
+ |
44 |
+from __future__ import print_function |
45 |
+ |
46 |
+import signal |
47 |
+import sys |
48 |
+ |
49 |
+from portage.output import bold, create_color_func |
50 |
+ |
51 |
+ |
52 |
+class UserQuery(object): |
53 |
+ """The UserQuery class is used to prompt the user with a set of responses, |
54 |
+ as well as accepting and handling the responses.""" |
55 |
+ |
56 |
+ def __init__(self, myopts): |
57 |
+ self.myopts = myopts |
58 |
+ |
59 |
+ def query(self, prompt, enter_invalid, responses=None, colours=None): |
60 |
+ """Display a prompt and a set of responses, then waits for user input |
61 |
+ and check it against the responses. The first match is returned. |
62 |
+ |
63 |
+ An empty response will match the first value in the list of responses, |
64 |
+ unless enter_invalid is True. The input buffer is *not* cleared prior |
65 |
+ to the prompt! |
66 |
+ |
67 |
+ prompt: The String to display as a prompt. |
68 |
+ responses: a List of Strings with the acceptable responses. |
69 |
+ colours: a List of Functions taking and returning a String, used to |
70 |
+ process the responses for display. Typically these will be functions |
71 |
+ like red() but could be e.g. lambda x: "DisplayString". |
72 |
+ |
73 |
+ If responses is omitted, it defaults to ["Yes", "No"], [green, red]. |
74 |
+ If only colours is omitted, it defaults to [bold, ...]. |
75 |
+ |
76 |
+ Returns a member of the List responses. (If called without optional |
77 |
+ arguments, it returns "Yes" or "No".) |
78 |
+ |
79 |
+ KeyboardInterrupt is converted to SystemExit to avoid tracebacks being |
80 |
+ printed.""" |
81 |
+ if responses is None: |
82 |
+ responses = ["Yes", "No"] |
83 |
+ colours = [ |
84 |
+ create_color_func("PROMPT_CHOICE_DEFAULT"), |
85 |
+ create_color_func("PROMPT_CHOICE_OTHER") |
86 |
+ ] |
87 |
+ elif colours is None: |
88 |
+ colours=[bold] |
89 |
+ colours=(colours*len(responses))[:len(responses)] |
90 |
+ if "--alert" in self.myopts: |
91 |
+ prompt = '\a' + prompt |
92 |
+ print(bold(prompt), end=' ') |
93 |
+ try: |
94 |
+ while True: |
95 |
+ if sys.hexversion >= 0x3000000: |
96 |
+ response=input("["+"/".join([colours[i](responses[i]) |
97 |
+ for i in range(len(responses))])+"] ") |
98 |
+ else: |
99 |
+ response=raw_input("["+"/".join([colours[i](responses[i]) |
100 |
+ for i in range(len(responses))])+"] ") |
101 |
+ if response or not enter_invalid: |
102 |
+ for key in responses: |
103 |
+ # An empty response will match the |
104 |
+ # first value in responses. |
105 |
+ if response.upper()==key[:len(response)].upper(): |
106 |
+ return key |
107 |
+ print("Sorry, response '%s' not understood." % response, |
108 |
+ end=' ') |
109 |
+ except (EOFError, KeyboardInterrupt): |
110 |
+ print("Interrupted.") |
111 |
+ sys.exit(128 + signal.SIGINT) |
112 |
diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py |
113 |
index ff72d70..9fc967b 100644 |
114 |
--- a/pym/_emerge/actions.py |
115 |
+++ b/pym/_emerge/actions.py |
116 |
@@ -84,7 +84,7 @@ from _emerge.sync.old_tree_timestamp import old_tree_timestamp_warn |
117 |
from _emerge.unmerge import unmerge |
118 |
from _emerge.UnmergeDepPriority import UnmergeDepPriority |
119 |
from _emerge.UseFlagDisplay import pkg_use_display |
120 |
-from _emerge.userquery import userquery |
121 |
+from _emerge.UserQuery import UserQuery |
122 |
|
123 |
if sys.hexversion >= 0x3000000: |
124 |
long = int |
125 |
@@ -387,8 +387,9 @@ def action_build(settings, trees, mtimedb, |
126 |
else: |
127 |
prompt="Would you like to merge these packages?" |
128 |
print() |
129 |
+ uq = UserQuery(myopts) |
130 |
if prompt is not None and "--ask" in myopts and \ |
131 |
- userquery(prompt, enter_invalid) == "No": |
132 |
+ uq.query(prompt, enter_invalid) == "No": |
133 |
print() |
134 |
print("Quitting.") |
135 |
print() |
136 |
@@ -468,6 +469,7 @@ def action_build(settings, trees, mtimedb, |
137 |
|
138 |
def action_config(settings, trees, myopts, myfiles): |
139 |
enter_invalid = '--ask-enter-invalid' in myopts |
140 |
+ uq = UserQuery(myopts) |
141 |
if len(myfiles) != 1: |
142 |
print(red("!!! config can only take a single package atom at this time\n")) |
143 |
sys.exit(1) |
144 |
@@ -497,7 +499,7 @@ def action_config(settings, trees, myopts, myfiles): |
145 |
print(options[-1]+") "+pkg) |
146 |
print("X) Cancel") |
147 |
options.append("X") |
148 |
- idx = userquery("Selection?", enter_invalid, responses=options) |
149 |
+ idx = uq.query("Selection?", enter_invalid, responses=options) |
150 |
if idx == "X": |
151 |
sys.exit(128 + signal.SIGINT) |
152 |
pkg = pkgs[int(idx)-1] |
153 |
@@ -512,7 +514,7 @@ def action_config(settings, trees, myopts, myfiles): |
154 |
|
155 |
print() |
156 |
if "--ask" in myopts: |
157 |
- if userquery("Ready to configure %s?" % pkg, enter_invalid) == "No": |
158 |
+ if uq.query("Ready to configure %s?" % pkg, enter_invalid) == "No": |
159 |
sys.exit(128 + signal.SIGINT) |
160 |
else: |
161 |
print("Configuring pkg...") |
162 |
@@ -1365,7 +1367,8 @@ def action_deselect(settings, trees, opts, atoms): |
163 |
if '--ask' in opts: |
164 |
prompt = "Would you like to remove these " + \ |
165 |
"packages from your world favorites?" |
166 |
- if userquery(prompt, enter_invalid) == 'No': |
167 |
+ uq = UserQuery(opts) |
168 |
+ if uq.query(prompt, enter_invalid) == 'No': |
169 |
return 128 + signal.SIGINT |
170 |
|
171 |
remaining = set(world_set) |
172 |
@@ -2403,7 +2406,8 @@ def _sync_repo(emerge_config, repo): |
173 |
|
174 |
if (retries==0): |
175 |
if "--ask" in myopts: |
176 |
- if userquery("Do you want to sync your Portage tree " + \ |
177 |
+ uq = UserQuery(myopts) |
178 |
+ if uq.query("Do you want to sync your Portage tree " + \ |
179 |
"with the mirror at\n" + blue(dosyncuri) + bold("?"), |
180 |
enter_invalid) == "No": |
181 |
print() |
182 |
@@ -3842,7 +3846,8 @@ def run_action(emerge_config): |
183 |
(access_desc,), noiselevel=-1) |
184 |
if portage.data.secpass < 1 and not need_superuser: |
185 |
portage.data.portage_group_warning() |
186 |
- if userquery("Would you like to add --pretend to options?", |
187 |
+ uq = UserQuery(emerge_config.opts) |
188 |
+ if uq.query("Would you like to add --pretend to options?", |
189 |
"--ask-enter-invalid" in emerge_config.opts) == "No": |
190 |
return 128 + signal.SIGINT |
191 |
emerge_config.opts["--pretend"] = True |
192 |
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py |
193 |
index 28d4026..b20af77 100644 |
194 |
--- a/pym/_emerge/depgraph.py |
195 |
+++ b/pym/_emerge/depgraph.py |
196 |
@@ -73,7 +73,7 @@ from _emerge.SetArg import SetArg |
197 |
from _emerge.show_invalid_depstring_notice import show_invalid_depstring_notice |
198 |
from _emerge.UnmergeDepPriority import UnmergeDepPriority |
199 |
from _emerge.UseFlagDisplay import pkg_use_display |
200 |
-from _emerge.userquery import userquery |
201 |
+from _emerge.UserQuery import UserQuery |
202 |
|
203 |
from _emerge.resolver.backtracking import Backtracker, BacktrackParameter |
204 |
from _emerge.resolver.package_tracker import PackageTracker, PackageTrackerDbapiWrapper |
205 |
@@ -520,6 +520,9 @@ class depgraph(object): |
206 |
self._event_loop = (portage._internal_caller and |
207 |
global_event_loop() or EventLoop(main=False)) |
208 |
|
209 |
+ self.uq = UserQuery(myopts) |
210 |
+ self.query = UserQuery.query |
211 |
+ |
212 |
def _load_vdb(self): |
213 |
""" |
214 |
Load installed package metadata if appropriate. This used to be called |
215 |
@@ -7624,7 +7627,7 @@ class depgraph(object): |
216 |
if ask and write_to_file and file_to_write_to: |
217 |
prompt = "\nWould you like to add these " + \ |
218 |
"changes to your config files?" |
219 |
- if userquery(prompt, enter_invalid) == 'No': |
220 |
+ if self.query(prompt, enter_invalid) == 'No': |
221 |
write_to_file = False |
222 |
|
223 |
if write_to_file and file_to_write_to: |
224 |
@@ -7891,7 +7894,7 @@ class depgraph(object): |
225 |
"favorites?" |
226 |
enter_invalid = '--ask-enter-invalid' in \ |
227 |
self._frozen_config.myopts |
228 |
- if userquery(prompt, enter_invalid) == "No": |
229 |
+ if self.query(prompt, enter_invalid) == "No": |
230 |
skip = True |
231 |
|
232 |
if not skip: |
233 |
diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py |
234 |
index eddb16c..1a920f7 100644 |
235 |
--- a/pym/_emerge/main.py |
236 |
+++ b/pym/_emerge/main.py |
237 |
@@ -124,6 +124,7 @@ def insert_optional_args(args): |
238 |
new_args = [] |
239 |
|
240 |
default_arg_opts = { |
241 |
+ '--alert' : y_or_n, |
242 |
'--ask' : y_or_n, |
243 |
'--autounmask' : y_or_n, |
244 |
'--autounmask-keep-masks': y_or_n, |
245 |
@@ -169,6 +170,7 @@ def insert_optional_args(args): |
246 |
# since existence of -n makes it too ambiguous. |
247 |
short_arg_opts_n = { |
248 |
'a' : y_or_n, |
249 |
+ 'A' : y_or_n, |
250 |
'b' : y_or_n, |
251 |
'g' : y_or_n, |
252 |
'G' : y_or_n, |
253 |
@@ -300,6 +302,12 @@ def parse_opts(tmpcmdline, silent=False): |
254 |
true_y = ("True", "y") |
255 |
argument_options = { |
256 |
|
257 |
+ "--alert": { |
258 |
+ "shortopt" : "-A", |
259 |
+ "help" : "alert (terminal bell) on prompts", |
260 |
+ "choices" : true_y_or_n |
261 |
+ }, |
262 |
+ |
263 |
"--ask": { |
264 |
"shortopt" : "-a", |
265 |
"help" : "prompt before performing any actions", |
266 |
@@ -675,6 +683,11 @@ def parse_opts(tmpcmdline, silent=False): |
267 |
|
268 |
myoptions, myargs = parser.parse_known_args(args=tmpcmdline) |
269 |
|
270 |
+ if myoptions.alert in true_y: |
271 |
+ myoptions.alert = True |
272 |
+ else: |
273 |
+ myoptions.alert = None |
274 |
+ |
275 |
if myoptions.ask in true_y: |
276 |
myoptions.ask = True |
277 |
else: |
278 |
diff --git a/pym/_emerge/unmerge.py b/pym/_emerge/unmerge.py |
279 |
index b04f8f3..8f98563 100644 |
280 |
--- a/pym/_emerge/unmerge.py |
281 |
+++ b/pym/_emerge/unmerge.py |
282 |
@@ -17,8 +17,8 @@ from portage.versions import cpv_sort_key, _pkg_str |
283 |
|
284 |
from _emerge.emergelog import emergelog |
285 |
from _emerge.Package import Package |
286 |
+from _emerge.UserQuery import UserQuery |
287 |
from _emerge.UninstallFailure import UninstallFailure |
288 |
-from _emerge.userquery import userquery |
289 |
from _emerge.countdown import countdown |
290 |
|
291 |
def _unmerge_display(root_config, myopts, unmerge_action, |
292 |
@@ -529,7 +529,8 @@ def unmerge(root_config, myopts, unmerge_action, |
293 |
#we're done... return |
294 |
return os.EX_OK |
295 |
if "--ask" in myopts: |
296 |
- if userquery("Would you like to unmerge these packages?", |
297 |
+ uq = UserQuery(myopts) |
298 |
+ if uq.query("Would you like to unmerge these packages?", |
299 |
enter_invalid) == "No": |
300 |
# enter pretend mode for correct formatting of results |
301 |
myopts["--pretend"] = True |
302 |
diff --git a/pym/_emerge/userquery.py b/pym/_emerge/userquery.py |
303 |
deleted file mode 100644 |
304 |
index efae80a..0000000 |
305 |
--- a/pym/_emerge/userquery.py |
306 |
+++ /dev/null |
307 |
@@ -1,55 +0,0 @@ |
308 |
-# Copyright 1999-2012 Gentoo Foundation |
309 |
-# Distributed under the terms of the GNU General Public License v2 |
310 |
- |
311 |
-from __future__ import print_function |
312 |
- |
313 |
-import signal |
314 |
-import sys |
315 |
- |
316 |
-from portage.output import bold, create_color_func |
317 |
- |
318 |
-def userquery(prompt, enter_invalid, responses=None, colours=None): |
319 |
- """Displays a prompt and a set of responses, then waits for a response |
320 |
- which is checked against the responses and the first to match is |
321 |
- returned. An empty response will match the first value in responses, |
322 |
- unless enter_invalid is True. The input buffer is *not* cleared prior |
323 |
- to the prompt! |
324 |
- |
325 |
- prompt: a String. |
326 |
- responses: a List of Strings. |
327 |
- colours: a List of Functions taking and returning a String, used to |
328 |
- process the responses for display. Typically these will be functions |
329 |
- like red() but could be e.g. lambda x: "DisplayString". |
330 |
- If responses is omitted, defaults to ["Yes", "No"], [green, red]. |
331 |
- If only colours is omitted, defaults to [bold, ...]. |
332 |
- |
333 |
- Returns a member of the List responses. (If called without optional |
334 |
- arguments, returns "Yes" or "No".) |
335 |
- KeyboardInterrupt is converted to SystemExit to avoid tracebacks being |
336 |
- printed.""" |
337 |
- if responses is None: |
338 |
- responses = ["Yes", "No"] |
339 |
- colours = [ |
340 |
- create_color_func("PROMPT_CHOICE_DEFAULT"), |
341 |
- create_color_func("PROMPT_CHOICE_OTHER") |
342 |
- ] |
343 |
- elif colours is None: |
344 |
- colours=[bold] |
345 |
- colours=(colours*len(responses))[:len(responses)] |
346 |
- print(bold(prompt), end=' ') |
347 |
- try: |
348 |
- while True: |
349 |
- if sys.hexversion >= 0x3000000: |
350 |
- response=input("["+"/".join([colours[i](responses[i]) for i in range(len(responses))])+"] ") |
351 |
- else: |
352 |
- response=raw_input("["+"/".join([colours[i](responses[i]) for i in range(len(responses))])+"] ") |
353 |
- if response or not enter_invalid: |
354 |
- for key in responses: |
355 |
- # An empty response will match the |
356 |
- # first value in responses. |
357 |
- if response.upper()==key[:len(response)].upper(): |
358 |
- return key |
359 |
- print("Sorry, response '%s' not understood." % response, end=' ') |
360 |
- except (EOFError, KeyboardInterrupt): |
361 |
- print("Interrupted.") |
362 |
- sys.exit(128 + signal.SIGINT) |
363 |
-- |
364 |
1.8.3.2 |