Home | History | Annotate | Download | only in tester_feedback
      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 
      8 class InputError(Exception):
      9     """An error with a user provided input."""
     10 
     11 
     12 class _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 
     53 class 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 
     70 class 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 
    106 class 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