commit: 1a00ddf5fb089724a127c8dc72d63f10ae72abbf Author: Alexander Berntsen <bernalex <AT> gentoo <DOT> org> AuthorDate: Thu May 8 15:47:58 2014 +0000 Commit: Alexander Berntsen <bernalex <AT> gentoo <DOT> org> CommitDate: Sat Jun 14 20:49:14 2014 +0000 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=1a00ddf5
emerge: Implement "--alert" Implement "--alert" (short option "-A"), which adds a terminal bell character ('\a') to all prompts, when it is set to True or "y". It may be used without an option, like e.g. --ask, to mean True. The patch refactors userquery to a class, UserQuery, to accommodate --alert. It also adds the option to the emerge man page. A quick example is to do 'emerge -PAc portage'. If you have your terminal emulator set up to make '\a' into a window manager urgency hint, move your cursor to a different window to get the effect. 'sleep 2 && emerge' is another way to test this. The --alert option is especially useful if you, like me, tend to fire up a big world update when you get to the office in the morning (or night), and then immediately change your focus to something else. If you now do 'emerge -auAD world', emerge will courteously point out when it has finished calculating the graph, and again when it has finished emerging. Signed-off-by: Alexander Berntsen <bernalex <AT> gentoo.org> --- man/emerge.1 | 5 ++++ pym/_emerge/UserQuery.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ pym/_emerge/actions.py | 19 ++++++++----- pym/_emerge/depgraph.py | 9 ++++-- pym/_emerge/main.py | 13 +++++++++ pym/_emerge/unmerge.py | 5 ++-- pym/_emerge/userquery.py | 55 ------------------------------------- 7 files changed, 110 insertions(+), 67 deletions(-) diff --git a/man/emerge.1 b/man/emerge.1 index abb0ed8..bbcf569 100644 --- a/man/emerge.1 +++ b/man/emerge.1 @@ -297,6 +297,11 @@ re\-distributable. With default configuration, this would result in an effective \fBACCEPT_RESTRICT\fR value of "* -bindist". .TP +.BR "\-\-alert " +Add a terminal bell character ('\\a') to all interactive prompts. This is +especially useful if dependency resolution is taking a long time, and +you want emerge to alert you when it is finished. +.TP .BR "\-\-alphabetical " When displaying USE and other flag output, combines the enabled and disabled lists into one list and sorts the whole list alphabetically. diff --git a/pym/_emerge/UserQuery.py b/pym/_emerge/UserQuery.py new file mode 100644 index 0000000..c866a0d --- /dev/null +++ b/pym/_emerge/UserQuery.py @@ -0,0 +1,71 @@ +# Copyright 1999-2014 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +from __future__ import print_function + +import signal +import sys + +from portage.output import bold, create_color_func + + +class UserQuery(object): + """The UserQuery class is used to prompt the user with a set of responses, + as well as accepting and handling the responses.""" + + def __init__(self, myopts): + self.myopts = myopts + + def query(self, prompt, enter_invalid, responses=None, colours=None): + """Display a prompt and a set of responses, then waits for user input + and check it against the responses. The first match is returned. + + An empty response will match the first value in the list of responses, + unless enter_invalid is True. The input buffer is *not* cleared prior + to the prompt! + + prompt: The String to display as a prompt. + responses: a List of Strings with the acceptable responses. + colours: a List of Functions taking and returning a String, used to + process the responses for display. Typically these will be functions + like red() but could be e.g. lambda x: "DisplayString". + + If responses is omitted, it defaults to ["Yes", "No"], [green, red]. + If only colours is omitted, it defaults to [bold, ...]. + + Returns a member of the List responses. (If called without optional + arguments, it returns "Yes" or "No".) + + KeyboardInterrupt is converted to SystemExit to avoid tracebacks being + printed.""" + if responses is None: + responses = ["Yes", "No"] + colours = [ + create_color_func("PROMPT_CHOICE_DEFAULT"), + create_color_func("PROMPT_CHOICE_OTHER") + ] + elif colours is None: + colours=[bold] + colours=(colours*len(responses))[:len(responses)] + if "--alert" in self.myopts: + prompt = '\a' + prompt + print(bold(prompt), end=' ') + try: + while True: + if sys.hexversion >= 0x3000000: + response=input("["+"/".join([colours[i](responses[i]) + for i in range(len(responses))])+"] ") + else: + response=raw_input("["+"/".join([colours[i](responses[i]) + for i in range(len(responses))])+"] ") + if response or not enter_invalid: + for key in responses: + # An empty response will match the + # first value in responses. + if response.upper()==key[:len(response)].upper(): + return key + print("Sorry, response '%s' not understood." % response, + end=' ') + except (EOFError, KeyboardInterrupt): + print("Interrupted.") + sys.exit(128 + signal.SIGINT) diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py index 4ca2f1c..b935139 100644 --- a/pym/_emerge/actions.py +++ b/pym/_emerge/actions.py @@ -84,7 +84,7 @@ from _emerge.sync.old_tree_timestamp import old_tree_timestamp_warn from _emerge.unmerge import unmerge from _emerge.UnmergeDepPriority import UnmergeDepPriority from _emerge.UseFlagDisplay import pkg_use_display -from _emerge.userquery import userquery +from _emerge.UserQuery import UserQuery if sys.hexversion >= 0x3000000: long = int @@ -387,8 +387,9 @@ def action_build(settings, trees, mtimedb, else: prompt="Would you like to merge these packages?" print() + uq = UserQuery(myopts) if prompt is not None and "--ask" in myopts and \ - userquery(prompt, enter_invalid) == "No": + uq.query(prompt, enter_invalid) == "No": print() print("Quitting.") print() @@ -468,6 +469,7 @@ def action_build(settings, trees, mtimedb, def action_config(settings, trees, myopts, myfiles): enter_invalid = '--ask-enter-invalid' in myopts + uq = UserQuery(myopts) if len(myfiles) != 1: print(red("!!! config can only take a single package atom at this time\n")) sys.exit(1) @@ -497,7 +499,7 @@ def action_config(settings, trees, myopts, myfiles): print(options[-1]+") "+pkg) print("X) Cancel") options.append("X") - idx = userquery("Selection?", enter_invalid, responses=options) + idx = uq.query("Selection?", enter_invalid, responses=options) if idx == "X": sys.exit(128 + signal.SIGINT) pkg = pkgs[int(idx)-1] @@ -512,7 +514,7 @@ def action_config(settings, trees, myopts, myfiles): print() if "--ask" in myopts: - if userquery("Ready to configure %s?" % pkg, enter_invalid) == "No": + if uq.query("Ready to configure %s?" % pkg, enter_invalid) == "No": sys.exit(128 + signal.SIGINT) else: print("Configuring pkg...") @@ -1364,7 +1366,8 @@ def action_deselect(settings, trees, opts, atoms): if '--ask' in opts: prompt = "Would you like to remove these " + \ "packages from your world favorites?" - if userquery(prompt, enter_invalid) == 'No': + uq = UserQuery(opts) + if uq.query(prompt, enter_invalid) == 'No': return 128 + signal.SIGINT remaining = set(world_set) @@ -2402,7 +2405,8 @@ def _sync_repo(emerge_config, repo): if (retries==0): if "--ask" in myopts: - if userquery("Do you want to sync your Portage tree " + \ + uq = UserQuery(myopts) + if uq.query("Do you want to sync your Portage tree " + \ "with the mirror at\n" + blue(dosyncuri) + bold("?"), enter_invalid) == "No": print() @@ -3841,7 +3845,8 @@ def run_action(emerge_config): (access_desc,), noiselevel=-1) if portage.data.secpass < 1 and not need_superuser: portage.data.portage_group_warning() - if userquery("Would you like to add --pretend to options?", + uq = UserQuery(emerge_config.opts) + if uq.query("Would you like to add --pretend to options?", "--ask-enter-invalid" in emerge_config.opts) == "No": return 128 + signal.SIGINT emerge_config.opts["--pretend"] = True diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index bede7c6..8177b74 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -73,7 +73,7 @@ from _emerge.SetArg import SetArg from _emerge.show_invalid_depstring_notice import show_invalid_depstring_notice from _emerge.UnmergeDepPriority import UnmergeDepPriority from _emerge.UseFlagDisplay import pkg_use_display -from _emerge.userquery import userquery +from _emerge.UserQuery import UserQuery from _emerge.resolver.backtracking import Backtracker, BacktrackParameter from _emerge.resolver.package_tracker import PackageTracker, PackageTrackerDbapiWrapper @@ -520,6 +520,9 @@ class depgraph(object): self._event_loop = (portage._internal_caller and global_event_loop() or EventLoop(main=False)) + self.uq = UserQuery(myopts) + self.query = UserQuery.query + def _load_vdb(self): """ Load installed package metadata if appropriate. This used to be called @@ -7655,7 +7658,7 @@ class depgraph(object): if ask and write_to_file and file_to_write_to: prompt = "\nWould you like to add these " + \ "changes to your config files?" - if userquery(prompt, enter_invalid) == 'No': + if self.query(prompt, enter_invalid) == 'No': write_to_file = False if write_to_file and file_to_write_to: @@ -7922,7 +7925,7 @@ class depgraph(object): "favorites?" enter_invalid = '--ask-enter-invalid' in \ self._frozen_config.myopts - if userquery(prompt, enter_invalid) == "No": + if self.query(prompt, enter_invalid) == "No": skip = True if not skip: diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py index eddb16c..1a920f7 100644 --- a/pym/_emerge/main.py +++ b/pym/_emerge/main.py @@ -124,6 +124,7 @@ def insert_optional_args(args): new_args = [] default_arg_opts = { + '--alert' : y_or_n, '--ask' : y_or_n, '--autounmask' : y_or_n, '--autounmask-keep-masks': y_or_n, @@ -169,6 +170,7 @@ def insert_optional_args(args): # since existence of -n makes it too ambiguous. short_arg_opts_n = { 'a' : y_or_n, + 'A' : y_or_n, 'b' : y_or_n, 'g' : y_or_n, 'G' : y_or_n, @@ -300,6 +302,12 @@ def parse_opts(tmpcmdline, silent=False): true_y = ("True", "y") argument_options = { + "--alert": { + "shortopt" : "-A", + "help" : "alert (terminal bell) on prompts", + "choices" : true_y_or_n + }, + "--ask": { "shortopt" : "-a", "help" : "prompt before performing any actions", @@ -675,6 +683,11 @@ def parse_opts(tmpcmdline, silent=False): myoptions, myargs = parser.parse_known_args(args=tmpcmdline) + if myoptions.alert in true_y: + myoptions.alert = True + else: + myoptions.alert = None + if myoptions.ask in true_y: myoptions.ask = True else: diff --git a/pym/_emerge/unmerge.py b/pym/_emerge/unmerge.py index b04f8f3..8f98563 100644 --- a/pym/_emerge/unmerge.py +++ b/pym/_emerge/unmerge.py @@ -17,8 +17,8 @@ from portage.versions import cpv_sort_key, _pkg_str from _emerge.emergelog import emergelog from _emerge.Package import Package +from _emerge.UserQuery import UserQuery from _emerge.UninstallFailure import UninstallFailure -from _emerge.userquery import userquery from _emerge.countdown import countdown def _unmerge_display(root_config, myopts, unmerge_action, @@ -529,7 +529,8 @@ def unmerge(root_config, myopts, unmerge_action, #we're done... return return os.EX_OK if "--ask" in myopts: - if userquery("Would you like to unmerge these packages?", + uq = UserQuery(myopts) + if uq.query("Would you like to unmerge these packages?", enter_invalid) == "No": # enter pretend mode for correct formatting of results myopts["--pretend"] = True diff --git a/pym/_emerge/userquery.py b/pym/_emerge/userquery.py deleted file mode 100644 index efae80a..0000000 --- a/pym/_emerge/userquery.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 1999-2012 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -from __future__ import print_function - -import signal -import sys - -from portage.output import bold, create_color_func - -def userquery(prompt, enter_invalid, responses=None, colours=None): - """Displays a prompt and a set of responses, then waits for a response - which is checked against the responses and the first to match is - returned. An empty response will match the first value in responses, - unless enter_invalid is True. The input buffer is *not* cleared prior - to the prompt! - - prompt: a String. - responses: a List of Strings. - colours: a List of Functions taking and returning a String, used to - process the responses for display. Typically these will be functions - like red() but could be e.g. lambda x: "DisplayString". - If responses is omitted, defaults to ["Yes", "No"], [green, red]. - If only colours is omitted, defaults to [bold, ...]. - - Returns a member of the List responses. (If called without optional - arguments, returns "Yes" or "No".) - KeyboardInterrupt is converted to SystemExit to avoid tracebacks being - printed.""" - if responses is None: - responses = ["Yes", "No"] - colours = [ - create_color_func("PROMPT_CHOICE_DEFAULT"), - create_color_func("PROMPT_CHOICE_OTHER") - ] - elif colours is None: - colours=[bold] - colours=(colours*len(responses))[:len(responses)] - print(bold(prompt), end=' ') - try: - while True: - if sys.hexversion >= 0x3000000: - response=input("["+"/".join([colours[i](responses[i]) for i in range(len(responses))])+"] ") - else: - response=raw_input("["+"/".join([colours[i](responses[i]) for i in range(len(responses))])+"] ") - if response or not enter_invalid: - for key in responses: - # An empty response will match the - # first value in responses. - if response.upper()==key[:len(response)].upper(): - return key - print("Sorry, response '%s' not understood." % response, end=' ') - except (EOFError, KeyboardInterrupt): - print("Interrupted.") - sys.exit(128 + signal.SIGINT)