azure-devops/azext_devops/dev/common/prompting.py (62 lines of code) (raw):

# -------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- import sys from knack.log import get_logger from knack.prompting import NoTTYException, verify_is_a_tty, prompt logger = get_logger(__name__) def delete_last_line(): "Use this function to delete the last line in the STDOUT" from colorama import init, deinit init() # cursor up one line sys.stdout.write('\x1b[1A') # delete last line sys.stdout.write('\x1b[2K') deinit() def prompt_user_friendly_choice_list(msg, a_list, default=1, help_string=None, error_msg=None): """Prompt user to select from a list of possible choices. :param msg: A message displayed to the user before the choice list :type msg: str :param a_list: The list of choices (list of strings or list of dicts with 'name' & 'desc') "type a_list: list :param default: The default option that should be chosen if user doesn't enter a choice :type default: int :param help_string: Help message to be displayed on the input terminal :type help_string: str :param error_msg: Error message to display if the terminal is non interactive :type error_msg: str :returns: The list index of the item chosen. """ verify_is_a_tty_or_raise_error(error_msg=error_msg) options = '\n'.join([' [{}] {}{}' .format(i + 1, x['name'] if isinstance(x, dict) and 'name' in x else x, ' - ' + x['desc'] if isinstance(x, dict) and 'desc' in x else '') for i, x in enumerate(a_list)]) allowed_vals = list(range(1, len(a_list) + 1)) linesToDelete = len(a_list) + 1 while True: val = prompt('{}\n{}\nPlease enter a choice [Default choice({})]: '.format(msg, options, default)) if val == '?' and help_string is not None: for x in range(0, linesToDelete): delete_last_line() print('Please enter a choice [Default choice({})]: {}'.format(default, '?')) print(help_string) continue if not val: val = '{}'.format(default) try: ans = int(val) if ans in allowed_vals: for x in range(0, linesToDelete): delete_last_line() print('Please enter a choice [Default choice({})]: {}'.format(default, a_list[ans - 1])) print('') # array index is 0-based, user input is 1-based return ans - 1 raise ValueError except ValueError: for x in range(0, linesToDelete): delete_last_line() print('Please enter a choice [Default choice({})]: {}'.format(default, val)) logger.warning('Valid values are %s', allowed_vals) def prompt_not_empty(msg, help_string=None): """ Wrapper on knacks prompt function which does not return until non none value is recieved from user input. """ if not help_string: help_string = 'This field cannot be left blank.' user_input = None while not user_input: user_input = prompt(msg=msg, help_string=help_string) return user_input def verify_is_a_tty_or_raise_error(error_msg=None): """ Verifies interactive environment raises NoTTYException with the error_msg string if the environment is non-interative """ try: verify_is_a_tty() except NoTTYException: raise NoTTYException(error_msg) def try_verify_is_a_tty(): """ Return True for TTY and False for non-TTY terminals """ try: verify_is_a_tty() return True except NoTTYException: return False