Gentoo Archives: gentoo-portage-dev

From: Alexander Berntsen <bernalex@g.o>
To: gentoo-portage-dev@l.g.o
Subject: [gentoo-portage-dev] [PATCH] _emerge: Implement "--alert"
Date: Thu, 08 May 2014 15:57:12
Message-Id: 1399564582-9696-1-git-send-email-bernalex@gentoo.org
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