1# Copyright 2016 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""User input handlers.""" 6 7 8class InputError(Exception): 9 """An error with a user provided input.""" 10 11 12class _InputHandler(object): 13 """An input handler base class.""" 14 15 def get_choices_supplements(self): 16 """Returns a pair of supplement strings representing input choices. 17 18 @return: A pair consisting of a detailed representation of available 19 choices, and a corresponding concise descriptor of available 20 input choices with optional default. 21 """ 22 input_choices, default = self._get_input_choices_and_default() 23 if input_choices: 24 input_choices = '[%s]' % input_choices 25 if default is not None: 26 input_choices += ' (default: %s)' % default 27 28 return self._get_choices_details(), input_choices 29 30 31 def _get_input_choices_and_default(self): 32 """Returns an input choices descriptor and a default (if any).""" 33 raise NotImplementedError 34 35 36 def _get_choices_details(self): 37 """Returns a detailed description (string) of input choices.""" 38 raise NotImplementedError 39 40 41 def process(self, input_str): 42 """Returns the result of processing the user input. 43 44 @param input_str: The user input. 45 46 @return: The result of processing the input. 47 48 @raise InputError: Provided input is invalid. 49 """ 50 raise NotImplementedError 51 52 53class PauseInputHandler(_InputHandler): 54 """A quiet input handler that just returns on any input.""" 55 56 # Interface overrides. 57 # 58 def _get_input_choices_and_default(self): 59 return None, None 60 61 62 def _get_choices_details(self): 63 return None 64 65 66 def process(self, input_str): 67 pass 68 69 70class YesNoInputHandler(_InputHandler): 71 "A yes/no input handler with optional default.""" 72 73 def __init__(self, default=None): 74 """Initializes the input handler. 75 76 @param default: The Boolean value to return by default. 77 """ 78 self._default = default 79 self._input_choices = '%s/%s' % ('Y' if default is True else 'y', 80 'N' if default is False else 'n') 81 82 83 # Interface overrides. 84 # 85 def _get_input_choices_and_default(self): 86 # We highlight the default by uppercasing the corresponding choice 87 # directly, so no need to return a default separately. 88 return self._input_choices, None 89 90 91 def _get_choices_details(self): 92 return None 93 94 95 def process(self, input_str): 96 input_str = input_str.lower().strip() 97 if input_str == 'y': 98 return True 99 if input_str == 'n': 100 return False 101 if not input_str and self._default is not None: 102 return self._default 103 raise InputError 104 105 106class MultipleChoiceInputHandler(_InputHandler): 107 """A multiple choice input handler with optional default.""" 108 109 def __init__(self, choices, default=None): 110 """Initializes the input handler. 111 112 @param choices: An iterable of input choices. 113 @param default: Index of default choice (integer). 114 """ 115 max_idx = len(choices) 116 if not (default is None or default in range(1, max_idx + 1)): 117 raise ValueError('Default choice is not a valid index') 118 self._choices = choices 119 self._idx_range = '1-%d' % max_idx if max_idx > 1 else str(max_idx) 120 self._default = None if default is None else str(default) 121 122 123 # Interface overrides. 124 # 125 def _get_input_choices_and_default(self): 126 return self._idx_range, self._default 127 128 129 def _get_choices_details(self): 130 return '\n'.join(['%d) %s' % (idx, choice) 131 for idx, choice in enumerate(self._choices, 1)]) 132 133 134 def process(self, input_str): 135 """Returns the index (zero-based) and value of the chosen option.""" 136 input_str = input_str or self._default 137 if input_str: 138 try: 139 input_idx = int(input_str) - 1 140 if input_idx in range(len(self._choices)): 141 return input_idx, self._choices[input_idx] 142 except ValueError: 143 pass 144 145 raise InputError 146