# Copyright 2016 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """User input handlers.""" class InputError(Exception): """An error with a user provided input.""" class _InputHandler(object): """An input handler base class.""" def get_choices_supplements(self): """Returns a pair of supplement strings representing input choices. @return: A pair consisting of a detailed representation of available choices, and a corresponding concise descriptor of available input choices with optional default. """ input_choices, default = self._get_input_choices_and_default() if input_choices: input_choices = '[%s]' % input_choices if default is not None: input_choices += ' (default: %s)' % default return self._get_choices_details(), input_choices def _get_input_choices_and_default(self): """Returns an input choices descriptor and a default (if any).""" raise NotImplementedError def _get_choices_details(self): """Returns a detailed description (string) of input choices.""" raise NotImplementedError def process(self, input_str): """Returns the result of processing the user input. @param input_str: The user input. @return: The result of processing the input. @raise InputError: Provided input is invalid. """ raise NotImplementedError class PauseInputHandler(_InputHandler): """A quiet input handler that just returns on any input.""" # Interface overrides. # def _get_input_choices_and_default(self): return None, None def _get_choices_details(self): return None def process(self, input_str): pass class YesNoInputHandler(_InputHandler): "A yes/no input handler with optional default.""" def __init__(self, default=None): """Initializes the input handler. @param default: The Boolean value to return by default. """ self._default = default self._input_choices = '%s/%s' % ('Y' if default is True else 'y', 'N' if default is False else 'n') # Interface overrides. # def _get_input_choices_and_default(self): # We highlight the default by uppercasing the corresponding choice # directly, so no need to return a default separately. return self._input_choices, None def _get_choices_details(self): return None def process(self, input_str): input_str = input_str.lower().strip() if input_str == 'y': return True if input_str == 'n': return False if not input_str and self._default is not None: return self._default raise InputError class MultipleChoiceInputHandler(_InputHandler): """A multiple choice input handler with optional default.""" def __init__(self, choices, default=None): """Initializes the input handler. @param choices: An iterable of input choices. @param default: Index of default choice (integer). """ max_idx = len(choices) if not (default is None or default in range(1, max_idx + 1)): raise ValueError('Default choice is not a valid index') self._choices = choices self._idx_range = '1-%d' % max_idx if max_idx > 1 else str(max_idx) self._default = None if default is None else str(default) # Interface overrides. # def _get_input_choices_and_default(self): return self._idx_range, self._default def _get_choices_details(self): return '\n'.join(['%d) %s' % (idx, choice) for idx, choice in enumerate(self._choices, 1)]) def process(self, input_str): """Returns the index (zero-based) and value of the chosen option.""" input_str = input_str or self._default if input_str: try: input_idx = int(input_str) - 1 if input_idx in range(len(self._choices)): return input_idx, self._choices[input_idx] except ValueError: pass raise InputError