def form(**kwargs: Question) -> "Form": """Create a form with multiple questions. The parameter name of a question will be the key for the answer in the returned dict. Args: kwargs: Questions to ask in the form. """ return Form(*(FormField(k, q) for k, q in kwargs.items()))
def text(message: Text, default: Text = "", validate: Union[Type[Validator], Callable[[Text], bool], None] = None, # noqa qmark: Text = DEFAULT_QUESTION_PREFIX, style: Optional[Style] = None, **kwargs: Any) -> Question: """Prompt the user to enter a free text message. This question type can be used to prompt the user for some text input. Args: message: Question text default: Default value will be returned if the user just hits enter. validate: Require the entered value to pass a validation. The value can not be submited until the validator accepts it (e.g. to check minimum password length). This can either be a function accepting the input and returning a boolean, or an class reference to a subclass of the prompt toolkit Validator class. qmark: Question prefix displayed in front of the question. By default this is a `?` style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. Returns: Question: Question instance, ready to be prompted (using `.ask()`). """ merged_style = merge_styles([DEFAULT_STYLE, style]) validator = build_validator(validate) def get_prompt_tokens(): return [("class:qmark", qmark), ("class:question", ' {} '.format(message))] p = PromptSession(get_prompt_tokens, style=merged_style, validator=validator, **kwargs) p.default_buffer.reset(Document(default)) return Question(p.app)
def checkbox(message: Text, choices: List[Union[Text, Choice, Dict[Text, Any]]], default: Optional[Text] = None, qmark: Text = DEFAULT_QUESTION_PREFIX, style: Optional[Style] = None, use_pointer: bool = True, **kwargs: Any) -> Question: """Ask the user to select from a list of items. This is a multiselect, the user can choose one, none or many of the items. Args: message: Question text choices: Items shown in the selection, this can contain `Choice` or or `Separator` objects or simple items as strings. Passing `Choice` objects, allows you to configure the item more (e.g. preselecting it or disabeling it). default: Default return value (single value). If you want to preselect multiple items, use `Choice("foo", checked=True)` instead. qmark: Question prefix displayed in front of the question. By default this is a `?` style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. use_pointer: Flag to enable the pointer in front of the currently highlighted element. Returns: Question: Question instance, ready to be prompted (using `.ask()`). """ merged_style = merge_styles([DEFAULT_STYLE, style]) ic = InquirerControl(choices, default, use_pointer=use_pointer) def get_prompt_tokens(): tokens = [] tokens.append(("class:qmark", qmark)) tokens.append(("class:question", ' {} '.format(message))) if ic.is_answered: nbr_selected = len(ic.selected_options) if nbr_selected == 0: tokens.append(("class:answer", ' hecho')) elif nbr_selected == 1: if isinstance(ic.get_selected_values()[0].title, list): tokens.append(("class:answer", "".join([ token[1] for token in ic.get_selected_values()[0].title ]))) else: tokens.append( ("class:answer", ' [{}]'.format(ic.get_selected_values()[0].title))) else: tokens.append(("class:answer", ' hecho ({} selecciones)'.format(nbr_selected))) else: tokens.append(("class:instruction", ' \n (Usa las teclas de flechas para mover, ' '<space> seleccionar, ' '<a> alternar, ' '<i> invertir)')) return tokens layout = common.create_inquirer_layout(ic, get_prompt_tokens, **kwargs) bindings = KeyBindings() @bindings.add(Keys.ControlQ, eager=True) @bindings.add(Keys.ControlC, eager=True) def _(event): event.app.exit(exception=KeyboardInterrupt, style='class:aborting') @bindings.add(' ', eager=True) def toggle(event): pointed_choice = ic.get_pointed_at().value if pointed_choice in ic.selected_options: ic.selected_options.remove(pointed_choice) else: ic.selected_options.append(pointed_choice) @bindings.add('i', eager=True) def invert(event): inverted_selection = [ c.value for c in ic.choices if not isinstance(c, Separator) and c.value not in ic.selected_options and not c.disabled ] ic.selected_options = inverted_selection @bindings.add('a', eager=True) def all(event): all_selected = True # all choices have been selected for c in ic.choices: if (not isinstance(c, Separator) and c.value not in ic.selected_options and not c.disabled): # add missing ones ic.selected_options.append(c.value) all_selected = False if all_selected: ic.selected_options = [] @bindings.add(Keys.Down, eager=True) @bindings.add("j", eager=True) def move_cursor_down(event): ic.select_next() while not ic.is_selection_valid(): ic.select_next() @bindings.add(Keys.Up, eager=True) @bindings.add("k", eager=True) def move_cursor_up(event): ic.select_previous() while not ic.is_selection_valid(): ic.select_previous() @bindings.add(Keys.ControlM, eager=True) def set_answer(event): ic.is_answered = True event.app.exit(result=[c.value for c in ic.get_selected_values()]) @bindings.add(Keys.Any) def other(event): """Disallow inserting other text. """ pass return Question( Application(layout=layout, key_bindings=bindings, style=merged_style, **kwargs))
def select(message: Text, choices: List[Union[Text, Choice, Dict[Text, Any]]], default: Optional[Text] = None, qmark: Text = DEFAULT_QUESTION_PREFIX, style: Optional[Style] = None, use_shortcuts: bool = False, use_indicator: bool = False, **kwargs: Any) -> Question: """Prompt the user to select one item from the list of choices. The user can only select one option. Args: message: Question text choices: Items shown in the selection, this can contain `Choice` or or `Separator` objects or simple items as strings. Passing `Choice` objects, allows you to configure the item more (e.g. preselecting it or disabeling it). default: Default return value (single value). qmark: Question prefix displayed in front of the question. By default this is a `?` style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. use_indicator: Flag to enable the small indicator in front of the list highlighting the current location of the selection cursor. use_shortcuts: Allow the user to select items from the list using shortcuts. The shortcuts will be displayed in front of the list items. Returns: Question: Question instance, ready to be prompted (using `.ask()`). """ if choices is None or len(choices) == 0: raise ValueError('A list of choices needs to be provided.') if use_shortcuts and len(choices) > len(InquirerControl.SHORTCUT_KEYS): raise ValueError('A list with shortcuts supports a maximum of {} ' 'choices as this is the maximum number ' 'of keyboard shortcuts that are available. You' 'provided {} choices!' ''.format(len(InquirerControl.SHORTCUT_KEYS), len(choices))) merged_style = merge_styles([DEFAULT_STYLE, style]) ic = InquirerControl(choices, default, use_indicator=use_indicator, use_shortcuts=use_shortcuts) def get_prompt_tokens(): # noinspection PyListCreation tokens = [("class:qmark", qmark), ("class:question", ' {} '.format(message))] if ic.is_answered: tokens.append(("class:answer", ' ' + ic.get_pointed_at().title)) else: if use_shortcuts: tokens.append(("class:instruction", ' (Use shortcuts)')) else: tokens.append(("class:instruction", ' (Use arrow keys)')) return tokens layout = common.create_inquirer_layout(ic, get_prompt_tokens, **kwargs) bindings = KeyBindings() @bindings.add(Keys.ControlQ, eager=True) @bindings.add(Keys.ControlC, eager=True) def _(event): event.app.exit(exception=KeyboardInterrupt, style='class:aborting') if use_shortcuts: # add key bindings for choices for i, c in enumerate(ic.choices): if isinstance(c, Separator): continue # noinspection PyShadowingNames def _reg_binding(i, keys): # trick out late evaluation with a "function factory": # https://stackoverflow.com/a/3431699 @bindings.add(keys, eager=True) def select_choice(event): ic.pointed_at = i _reg_binding(i, c.shortcut_key) else: @bindings.add(Keys.Down, eager=True) @bindings.add("j", eager=True) def move_cursor_down(event): ic.select_next() while not ic.is_selection_valid(): ic.select_next() @bindings.add(Keys.Up, eager=True) @bindings.add("k", eager=True) def move_cursor_up(event): ic.select_previous() while not ic.is_selection_valid(): ic.select_previous() @bindings.add(Keys.ControlM, eager=True) def set_answer(event): ic.is_answered = True event.app.exit(result=ic.get_pointed_at().value) @bindings.add(Keys.Any) def other(event): """Disallow inserting other text. """ pass return Question( Application(layout=layout, key_bindings=bindings, style=merged_style, **kwargs))
def path( message: str, default: str = "", qmark: str = DEFAULT_QUESTION_PREFIX, validate: Any = None, style: Optional[Style] = None, only_directories: bool = False, file_filter: Optional[Callable[[str], bool]] = None, complete_style: CompleteStyle = CompleteStyle.MULTI_COLUMN, **kwargs: Any, ) -> Question: """A text input for a file or directory path with autocompletion enabled. Example: >>> import questionary >>> questionary.path("What's the path to the projects version file?").ask() ? What's the path to the projects version file? ./pyproject.toml './pyproject.toml' .. image:: ../images/path.gif This is just a really basic example, the prompt can be customised using the parameters. Args: message: Question text. default: Default return value (single value). qmark: Question prefix displayed in front of the question. By default this is a ``?``. complete_style: How autocomplete menu would be shown, it could be ``COLUMN`` ``MULTI_COLUMN`` or ``READLINE_LIKE`` from :class:`prompt_toolkit.shortcuts.CompleteStyle`. validate: Require the entered value to pass a validation. The value can not be submitted until the validator accepts it (e.g. to check minimum password length). This can either be a function accepting the input and returning a boolean, or an class reference to a subclass of the prompt toolkit Validator class. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. only_directories: Only show directories in auto completion file_filter: Optional callable to filter suggested paths. Only paths where the passed callable evaluates to ``True`` will show up in the suggested paths. This does not validate the typed path, e.g. it is still possible for the user to enter a path manually, even though this filter evaluates to ``False``. If in addition to filtering suggestions you also want to validate the result, use ``validate`` in combination with the ``file_filter``. Returns: :class:`Question`: Question instance, ready to be prompted (using ``.ask()``). """ merged_style = merge_styles([DEFAULT_STYLE, style]) def get_prompt_tokens() -> List[Tuple[str, str]]: return [("class:qmark", qmark), ("class:question", " {} ".format(message))] validator = build_validator(validate) bindings = KeyBindings() @bindings.add(Keys.ControlM, eager=True) def set_answer(event: KeyPressEvent): if event.current_buffer.complete_state is not None: event.current_buffer.complete_state = None elif event.app.current_buffer.validate(set_cursor=True): # When the validation succeeded, accept the input. result_path = event.app.current_buffer.document.text if result_path.endswith(os.path.sep): result_path = result_path[:-1] event.app.exit(result=result_path) event.app.current_buffer.append_to_history() @bindings.add(os.path.sep, eager=True) def next_segment(event: KeyPressEvent): b = event.app.current_buffer if b.complete_state: b.complete_state = None current_path = b.document.text if not current_path.endswith(os.path.sep): b.insert_text(os.path.sep) b.start_completion(select_first=False) p = PromptSession( get_prompt_tokens, lexer=SimpleLexer("class:answer"), style=merged_style, completer=GreatUXPathCompleter(only_directories=only_directories, file_filter=file_filter, expanduser=True), validator=validator, complete_style=complete_style, key_bindings=bindings, **kwargs, ) p.default_buffer.reset(Document(default)) return Question(p.app)
def confirm(message: Text, default: bool = True, qmark: Text = DEFAULT_QUESTION_PREFIX, style: Optional[Style] = None, **kwargs: Any) -> Question: """Prompt the user to confirm or reject. This question type can be used to prompt the user for a confirmation of a yes-or-no question. If the user just hits enter, the default value will be returned. Args: message: Question text default: Default value will be returned if the user just hits enter. qmark: Question prefix displayed in front of the question. By default this is a `?` style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. Returns: Question: Question instance, ready to be prompted (using `.ask()`). """ merged_style = merge_styles([DEFAULT_STYLE, style]) status = {'answer': None} def get_prompt_tokens(): tokens = [] tokens.append(("class:qmark", qmark)) tokens.append(("class:question", ' {} '.format(message))) if status['answer'] is not None: answer = ' {}'.format(YES if status['answer'] else NO) tokens.append(("class:answer", answer)) else: instruction = ' {}'.format(YES_OR_NO if default else NO_OR_YES) tokens.append(("class:instruction", instruction)) return to_formatted_text(tokens) bindings = KeyBindings() @bindings.add(Keys.ControlQ, eager=True) @bindings.add(Keys.ControlC, eager=True) def _(event): event.app.exit(exception=KeyboardInterrupt, style='class:aborting') @bindings.add('n') @bindings.add('N') def key_n(event): status['answer'] = False event.app.exit(result=False) @bindings.add('y') @bindings.add('Y') @bindings.add('s') @bindings.add('S') def key_y(event): status['answer'] = True event.app.exit(result=True) @bindings.add(Keys.ControlM, eager=True) def set_answer(event): status['answer'] = default event.app.exit(result=default) @bindings.add(Keys.Any) def other(event): """Disallow inserting other text.""" pass return Question( PromptSession(get_prompt_tokens, key_bindings=bindings, style=merged_style, **kwargs).app)
def autocomplete( message: str, choices: List[str], default: str = "", qmark: str = DEFAULT_QUESTION_PREFIX, completer: Optional[Completer] = None, meta_information: Optional[Dict[str, Any]] = None, ignore_case: bool = True, match_middle: bool = True, complete_style: CompleteStyle = CompleteStyle.COLUMN, validate: Any = None, style: Optional[Style] = None, **kwargs: Any, ) -> Question: """Prompt the user to enter a message with autocomplete help. Example: >>> import questionary >>> questionary.autocomplete( ... 'Choose ant specie', ... choices=[ ... 'Camponotus pennsylvanicus', ... 'Linepithema humile', ... 'Eciton burchellii', ... "Atta colombica", ... 'Polyergus lucidus', ... 'Polyergus rufescens', ... ]).ask() ? Choose ant specie Atta colombica 'Atta colombica' .. image:: ../images/autocomplete.gif This is just a realy basic example, the prompt can be customised using the parameters. Args: message: Question text choices: Items shown in the selection, this contains items as strings default: Default return value (single value). qmark: Question prefix displayed in front of the question. By default this is a ``?`` completer: A prompt_toolkit :class:`prompt_toolkit.completion.Completion` implementation. If not set, a questionary completer implementation will be used. meta_information: A dictionary with information/anything about choices. ignore_case: If true autocomplete would ignore case. match_middle: If true autocomplete would search in every string position not only in string begin. complete_style: How autocomplete menu would be shown, it could be ``COLUMN`` ``MULTI_COLUMN`` or ``READLINE_LIKE`` from :class:`prompt_toolkit.shortcuts.CompleteStyle`. validate: Require the entered value to pass a validation. The value can not be submitted until the validator accepts it (e.g. to check minimum password length). This can either be a function accepting the input and returning a boolean, or an class reference to a subclass of the prompt toolkit Validator class. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. Returns: :class:`Question`: Question instance, ready to be prompted (using ``.ask()``). """ merged_style = merge_styles([DEFAULT_STYLE, style]) def get_prompt_tokens() -> List[Tuple[str, str]]: return [("class:qmark", qmark), ("class:question", " {} ".format(message))] def get_meta_style(meta: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]: if meta: for key in meta: meta[key] = HTML("<text>{}</text>").format(meta[key]) return meta validator = build_validator(validate) if completer is None: if not choices: raise ValueError("No choices is given, you should use Text question.") # use the default completer completer = WordCompleter( choices, ignore_case=ignore_case, meta_information=get_meta_style(meta_information), match_middle=match_middle, ) p = PromptSession( get_prompt_tokens, lexer=SimpleLexer("class:answer"), style=merged_style, completer=completer, validator=validator, complete_style=complete_style, **kwargs, ) p.default_buffer.reset(Document(default)) return Question(p.app)
def text( message: Text, default: Text = "", validate: Any = None, qmark: Text = DEFAULT_QUESTION_PREFIX, style: Optional[Style] = None, multiline: bool = False, instruction: Optional[Text] = None, **kwargs: Any, ) -> Question: """Prompt the user to enter a free text message. This question type can be used to prompt the user for some text input. Args: message: Question text default: Default value will be returned if the user just hits enter. validate: Require the entered value to pass a validation. The value can not be submited until the validator accepts it (e.g. to check minimum password length). This can either be a function accepting the input and returning a boolean, or an class reference to a subclass of the prompt toolkit Validator class. qmark: Question prefix displayed in front of the question. By default this is a `?` style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. multiline: If `True`, multiline input will be enabled. instruction: Write instructions for the user if needed. If `None` and `multiline=True`, some instructions will appear. Returns: Question: Question instance, ready to be prompted (using `.ask()`). """ merged_style = merge_styles([DEFAULT_STYLE, style]) validator = build_validator(validate) if instruction is None and multiline: instruction = INSTRUCTION_MULTILINE def get_prompt_tokens() -> List[Tuple[Text, Text]]: result = [("class:qmark", qmark), ("class:question", " {} ".format(message))] if instruction: result.append(("class:instruction", " {} ".format(instruction))) return result p = PromptSession( get_prompt_tokens, style=merged_style, validator=validator, lexer=SimpleLexer("class:answer"), multiline=multiline, **kwargs, ) p.default_buffer.reset(Document(default)) return Question(p.app)
def select( message: str, choices: Sequence[Union[str, Choice, Dict[str, Any]]], default: Optional[Union[str, Choice, Dict[str, Any]]] = None, qmark: str = DEFAULT_QUESTION_PREFIX, style: Optional[Style] = None, use_shortcuts: bool = False, use_arrow_keys: bool = True, use_indicator: bool = False, use_pointer: bool = True, instruction: Optional[str] = None, **kwargs: Any, ) -> Question: """Prompt the user to select one item from the list of choices. The user can only select one option. Args: message: Question text choices: Items shown in the selection, this can contain `Choice` or or `Separator` objects or simple items as strings. Passing `Choice` objects, allows you to configure the item more (e.g. preselecting it or disabeling it). default: A value corresponding to a selectable item in the choices, to initially set the pointer position to. qmark: Question prefix displayed in front of the question. By default this is a `?` instruction: A hint on how to navigate the menu. It's `(Use shortcuts)` if only `use_shortcuts` is set to True, `(Use arrow keys or shortcuts)` if `use_arrow_keys` & `use_shortcuts` are set and `(Use arrow keys)` by default. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. use_indicator: Flag to enable the small indicator in front of the list highlighting the current location of the selection cursor. use_shortcuts: Allow the user to select items from the list using shortcuts. The shortcuts will be displayed in front of the list items. use_arrow_keys: Allow usage of arrow keys to select item. use_pointer: Flag to enable the pointer in front of the currently highlighted element. Returns: Question: Question instance, ready to be prompted (using `.ask()`). """ if choices is None or len(choices) == 0: raise ValueError("A list of choices needs to be provided.") if use_shortcuts and len(choices) > len(InquirerControl.SHORTCUT_KEYS): raise ValueError("A list with shortcuts supports a maximum of {} " "choices as this is the maximum number " "of keyboard shortcuts that are available. You" "provided {} choices!" "".format(len(InquirerControl.SHORTCUT_KEYS), len(choices))) merged_style = merge_styles([DEFAULT_STYLE, style]) ic = InquirerControl( choices, default, use_indicator=use_indicator, use_shortcuts=use_shortcuts, use_arrow_keys=use_arrow_keys, use_pointer=use_pointer, initial_choice=default, ) def get_prompt_tokens(): # noinspection PyListCreation tokens = [("class:qmark", qmark), ("class:question", " {} ".format(message))] if ic.is_answered: if isinstance(ic.get_pointed_at().title, list): tokens.append(( "class:answer", "".join([token[1] for token in ic.get_pointed_at().title]), )) else: tokens.append( ("class:answer", " " + ic.get_pointed_at().title)) else: if instruction: tokens.append(("class:instruction", instruction)) else: if use_shortcuts and use_arrow_keys: instruction_msg = " (Use shortcuts or arrow keys)" elif use_shortcuts and not use_arrow_keys: instruction_msg = " (Use shortcuts)" else: instruction_msg = " (Use arrow keys)" tokens.append(("class:instruction", instruction_msg)) return tokens layout = common.create_inquirer_layout(ic, get_prompt_tokens, **kwargs) bindings = KeyBindings() @bindings.add(Keys.ControlQ, eager=True) @bindings.add(Keys.ControlC, eager=True) def _(event): event.app.exit(exception=KeyboardInterrupt, style="class:aborting") if use_shortcuts: # add key bindings for choices for i, c in enumerate(ic.choices): if isinstance(c, Separator): continue # noinspection PyShadowingNames def _reg_binding(i, keys): # trick out late evaluation with a "function factory": # https://stackoverflow.com/a/3431699 @bindings.add(keys, eager=True) def select_choice(event): ic.pointed_at = i _reg_binding(i, c.shortcut_key) if use_arrow_keys or use_shortcuts is False: @bindings.add(Keys.Down, eager=True) @bindings.add("j", eager=True) def move_cursor_down(event): ic.select_next() while not ic.is_selection_valid(): ic.select_next() @bindings.add(Keys.Up, eager=True) @bindings.add("k", eager=True) def move_cursor_up(event): ic.select_previous() while not ic.is_selection_valid(): ic.select_previous() @bindings.add(Keys.ControlM, eager=True) def set_answer(event): ic.is_answered = True event.app.exit(result=ic.get_pointed_at().value) @bindings.add(Keys.Any) def other(event): """Disallow inserting other text. """ pass return Question( Application( layout=layout, key_bindings=bindings, style=merged_style, **utils.used_kwargs(kwargs, Application.__init__), ))
def select( message: str, choices: Sequence[Union[str, Choice, Dict[str, Any]]], default: Optional[Union[str, Choice, Dict[str, Any]]] = None, qmark: str = DEFAULT_QUESTION_PREFIX, pointer: Optional[str] = DEFAULT_SELECTED_POINTER, style: Optional[Style] = None, use_shortcuts: bool = False, use_arrow_keys: bool = True, use_indicator: bool = False, use_jk_keys: bool = True, show_selected: bool = False, instruction: Optional[str] = None, **kwargs: Any, ) -> Question: """A list of items to select **one** option from. The user can pick one option and confirm it (if you want to allow the user to select multiple options, use :meth:`questionary.checkbox` instead). Example: >>> import questionary >>> questionary.select( ... "What do you want to do?", ... choices=[ ... "Order a pizza", ... "Make a reservation", ... "Ask for opening hours" ... ]).ask() ? What do you want to do? Order a pizza 'Order a pizza' .. image:: ../images/select.gif This is just a really basic example, the prompt can be customised using the parameters. Args: message: Question text choices: Items shown in the selection, this can contain :class:`Choice` or or :class:`Separator` objects or simple items as strings. Passing :class:`Choice` objects, allows you to configure the item more (e.g. preselecting it or disabling it). default: A value corresponding to a selectable item in the choices, to initially set the pointer position to. qmark: Question prefix displayed in front of the question. By default this is a ``?``. pointer: Pointer symbol in front of the currently highlighted element. By default this is a ``ยป``. Use ``None`` to disable it. instruction: A hint on how to navigate the menu. It's ``(Use shortcuts)`` if only ``use_shortcuts`` is set to True, ``(Use arrow keys or shortcuts)`` if ``use_arrow_keys`` & ``use_shortcuts`` are set and ``(Use arrow keys)`` by default. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. use_indicator: Flag to enable the small indicator in front of the list highlighting the current location of the selection cursor. use_shortcuts: Allow the user to select items from the list using shortcuts. The shortcuts will be displayed in front of the list items. Arrow keys, j/k keys and shortcuts are not mutually exclusive. use_arrow_keys: Allow the user to select items from the list using arrow keys. Arrow keys, j/k keys and shortcuts are not mutually exclusive. use_jk_keys: Allow the user to select items from the list using `j` (down) and `k` (up) keys. Arrow keys, j/k keys and shortcuts are not mutually exclusive. show_selected: Display current selection choice at the bottom of list. Returns: :class:`Question`: Question instance, ready to be prompted (using ``.ask()``). """ if not (use_arrow_keys or use_shortcuts or use_jk_keys): raise ValueError( "Some option to move the selection is required. Arrow keys, j/k keys or shortcuts." ) if use_shortcuts and use_jk_keys: if any(getattr(c, "shortcut_key", "") in ["j", "k"] for c in choices): raise ValueError( "A choice is trying to register j/k as a " "shortcut key when they are in use as arrow keys " "disable one or the other." ) if choices is None or len(choices) == 0: raise ValueError("A list of choices needs to be provided.") if use_shortcuts and len(choices) > len(InquirerControl.SHORTCUT_KEYS): raise ValueError( "A list with shortcuts supports a maximum of {} " "choices as this is the maximum number " "of keyboard shortcuts that are available. You" "provided {} choices!" "".format(len(InquirerControl.SHORTCUT_KEYS), len(choices)) ) merged_style = merge_styles([DEFAULT_STYLE, style]) ic = InquirerControl( choices, default, pointer=pointer, use_indicator=use_indicator, use_shortcuts=use_shortcuts, show_selected=show_selected, use_arrow_keys=use_arrow_keys, initial_choice=default, ) def get_prompt_tokens(): # noinspection PyListCreation tokens = [("class:qmark", qmark), ("class:question", " {} ".format(message))] if ic.is_answered: if isinstance(ic.get_pointed_at().title, list): tokens.append( ( "class:answer", "".join([token[1] for token in ic.get_pointed_at().title]), ) ) else: tokens.append(("class:answer", ic.get_pointed_at().title)) else: if instruction: tokens.append(("class:instruction", instruction)) else: if use_shortcuts and use_arrow_keys: instruction_msg = "(Use shortcuts or arrow keys)" elif use_shortcuts and not use_arrow_keys: instruction_msg = "(Use shortcuts)" else: instruction_msg = "(Use arrow keys)" tokens.append(("class:instruction", instruction_msg)) return tokens layout = common.create_inquirer_layout(ic, get_prompt_tokens, **kwargs) bindings = KeyBindings() @bindings.add(Keys.ControlQ, eager=True) @bindings.add(Keys.ControlC, eager=True) def _(event): event.app.exit(exception=KeyboardInterrupt, style="class:aborting") if use_shortcuts: # add key bindings for choices for i, c in enumerate(ic.choices): if c.shortcut_key is None and not c.disabled and not use_arrow_keys: raise RuntimeError( "{} does not have a shortcut and arrow keys " "for movement are disabled. " "This choice is not reachable.".format(c.title) ) if isinstance(c, Separator) or c.shortcut_key is None: continue # noinspection PyShadowingNames def _reg_binding(i, keys): # trick out late evaluation with a "function factory": # https://stackoverflow.com/a/3431699 @bindings.add(keys, eager=True) def select_choice(event): ic.pointed_at = i _reg_binding(i, c.shortcut_key) def move_cursor_down(event): ic.select_next() while not ic.is_selection_valid(): ic.select_next() def move_cursor_up(event): ic.select_previous() while not ic.is_selection_valid(): ic.select_previous() if use_arrow_keys: bindings.add(Keys.Down, eager=True)(move_cursor_down) bindings.add(Keys.Up, eager=True)(move_cursor_up) if use_jk_keys: bindings.add("j", eager=True)(move_cursor_down) bindings.add("k", eager=True)(move_cursor_up) @bindings.add(Keys.ControlM, eager=True) def set_answer(event): ic.is_answered = True event.app.exit(result=ic.get_pointed_at().value) @bindings.add(Keys.Any) def other(event): """Disallow inserting other text. """ pass return Question( Application( layout=layout, key_bindings=bindings, style=merged_style, **utils.used_kwargs(kwargs, Application.__init__), ) )
def text(message: Text, default: Text = "", validate: Union[Validator, Callable[[Text], bool], None] = None, # noqa qmark: Text = DEFAULT_QUESTION_PREFIX, style: Optional[Style] = None, path_autocomplete=False, exec_autocomplete=False, custom_autocomplete=None, ** kwargs: Any) -> Question: """Prompt the user to enter a free text message. This question type can be used to prompt the user for some text input. Args: message: Question text default: Default value will be returned if the user just hits enter. validate: Require the entered value to pass a validation. The value can not be submited until the validator accepts it (e.g. to check minimum password length). This can either be a function accepting the input and returning a boolean, or an class reference to a subclass of the prompt toolkit Validator class. qmark: Question prefix displayed in front of the question. By default this is a `?` style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. Returns: Question: Question instance, ready to be prompted (using `.ask()`). """ merged_style = merge_styles([DEFAULT_STYLE, style]) validator = build_validator(validate) def get_prompt_tokens(): return [("class:qmark", qmark), ("class:question", ' {} '.format(message))] promptArgs = dict({ 'style': merged_style, 'validator': validator, 'complete_style': CompleteStyle.READLINE_LIKE, }) if path_autocomplete: promptArgs['completer'] = PathCompleter( expanduser=True, delimiters=' \t\n;,') elif exec_autocomplete: promptArgs['completer'] = ExecutableCompleter(delimiters=' \t\n;,') elif custom_autocomplete is not None and len(custom_autocomplete): promptArgs['completer'] = WordCompleter( custom_autocomplete, ignore_case=True, sentence=True) p = PromptSession(get_prompt_tokens, **promptArgs, **kwargs) p.default_buffer.reset(Document(default)) return Question(p.app)
def autocomplete( message: Text, choices: List[Text], default: Text = "", qmark: Text = DEFAULT_QUESTION_PREFIX, completer: Optional[Completer] = None, meta_information: Optional[Dict[Text, Any]] = None, ignore_case: bool = True, match_middle: bool = True, complete_style: CompleteStyle = CompleteStyle.COLUMN, validate: Any = None, style: Optional[Style] = None, **kwargs: Any, ) -> Question: """Prompt the user to enter a message with autocomplete help. Args: message: Question text choices: Items shown in the selection, this contains items as strings default: Default return value (single value). qmark: Question prefix displayed in front of the question. By default this is a `?` completer: A prompt_toolkit `Completer` implementation. If not set, a questionary completer implementation will be used. meta_information: A dictionary with information/anything about choices. ignore_case: If true autocomplete would ignore case. match_middle: If true autocomplete would search in every string position not only in string begin. complete_style: How autocomplete menu would be shown, it could be COLUMN, MULTI_COLUMN or READLINE_LIKE validate: Require the entered value to pass a validation. The value can not be submitted until the validator accepts it (e.g. to check minimum password length). This can either be a function accepting the input and returning a boolean, or an class reference to a subclass of the prompt toolkit Validator class. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. Returns: Question: Question instance, ready to be prompted (using `.ask()`). """ merged_style = merge_styles([DEFAULT_STYLE, style]) def get_prompt_tokens() -> List[Tuple[Text, Text]]: return [("class:qmark", qmark), ("class:question", " {} ".format(message))] def get_meta_style( meta: Optional[Dict[Text, Any]]) -> Optional[Dict[Text, Any]]: if meta: for key in meta: meta[key] = HTML("<text>{}</text>").format(meta[key]) return meta validator = build_validator(validate) if completer is None: if not choices: raise ValueError( "No choices is given, you should use Text question.") # use the default completer completer = WordCompleter( choices, ignore_case=ignore_case, meta_information=get_meta_style(meta_information), match_middle=match_middle, ) p = PromptSession( get_prompt_tokens, lexer=SimpleLexer("class:answer"), style=merged_style, completer=completer, validator=validator, complete_style=complete_style, **kwargs, ) p.default_buffer.reset(Document(default)) return Question(p.app)
def text( message: str, default: str = "", validate: Any = None, qmark: str = DEFAULT_QUESTION_PREFIX, style: Optional[Style] = None, multiline: bool = False, instruction: Optional[str] = None, lexer: Optional[Lexer] = None, **kwargs: Any, ) -> Question: """Prompt the user to enter a free text message. This question type can be used to prompt the user for some text input. Example: >>> import questionary >>> questionary.text("What's your first name?").ask() ? What's your first name? Tom 'Tom' .. image:: ../images/text.gif This is just a really basic example, the prompt can be customised using the parameters. Args: message: Question text. default: Default value will be returned if the user just hits enter. validate: Require the entered value to pass a validation. The value can not be submitted until the validator accepts it (e.g. to check minimum password length). This can either be a function accepting the input and returning a boolean, or an class reference to a subclass of the prompt toolkit Validator class. qmark: Question prefix displayed in front of the question. By default this is a ``?``. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. multiline: If ``True``, multiline input will be enabled. instruction: Write instructions for the user if needed. If ``None`` and ``multiline=True``, some instructions will appear. lexer: Supply a valid lexer to style the answer. Leave empty to use a simple one by default. kwargs: Additional arguments, they will be passed to prompt toolkit. Returns: :class:`Question`: Question instance, ready to be prompted (using ``.ask()``). """ merged_style = merge_styles([DEFAULT_STYLE, style]) lexer = lexer or SimpleLexer("class:answer") validator = build_validator(validate) if instruction is None and multiline: instruction = INSTRUCTION_MULTILINE def get_prompt_tokens() -> List[Tuple[str, str]]: result = [("class:qmark", qmark), ("class:question", " {} ".format(message))] if instruction: result.append(("class:instruction", " {} ".format(instruction))) return result p = PromptSession( get_prompt_tokens, style=merged_style, validator=validator, lexer=lexer, multiline=multiline, **kwargs, ) p.default_buffer.reset(Document(default)) return Question(p.app)
def checkbox( message: str, choices: Sequence[Union[str, Choice, Dict[str, Any]]], default: Optional[str] = None, validate: Callable[[List[str]], Union[bool, str]] = lambda a: True, qmark: str = DEFAULT_QUESTION_PREFIX, style: Optional[Style] = None, use_pointer: bool = True, initial_choice: Optional[Union[str, Choice, Dict[str, Any]]] = None, **kwargs: Any, ) -> Question: """Ask the user to select from a list of items. This is a multiselect, the user can choose one, none or many of the items. Example: >>> import questionary >>> questionary.checkbox( ... 'Select toppings', ... choices=[ ... "Cheese", ... "Tomato", ... "Pineapple", ... ]).ask() ? Select toppings done (2 selections) ['Cheese', 'Pineapple'] .. image:: ../images/checkbox.gif This is just a realy basic example, the prompt can be customised using the parameters. Args: message: Question text choices: Items shown in the selection, this can contain :class:`Choice` or or :class:`Separator` objects or simple items as strings. Passing :class:`Choice` objects, allows you to configure the item more (e.g. preselecting it or disabling it). default: Default return value (single value). If you want to preselect multiple items, use ``Choice("foo", checked=True)`` instead. validate: Require the entered value to pass a validation. The value can not be submitted until the validator accepts it (e.g. to check minimum password length). This should be a function accepting the input and returning a boolean. Alternatively, the return value may be a string (indicating failure), which contains the error message to be displayed. qmark: Question prefix displayed in front of the question. By default this is a ``?``. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. use_pointer: Flag to enable the pointer in front of the currently highlighted element. initial_choice: A value corresponding to a selectable item in the choices, to initially set the pointer position to. Returns: :class:`Question`: Question instance, ready to be prompted (using ``.ask()``). """ merged_style = merge_styles([ DEFAULT_STYLE, # Disable the default inverted colours bottom-toolbar behaviour (for # the error message). However it can be re-enabled with a custom # style. Style([("bottom-toolbar", "noreverse")]), style, ]) if not callable(validate): raise ValueError("validate must be callable") ic = InquirerControl(choices, default, use_pointer=use_pointer, initial_choice=initial_choice) def get_prompt_tokens() -> List[Tuple[str, str]]: tokens = [] tokens.append(("class:qmark", qmark)) tokens.append(("class:question", " {} ".format(message))) if ic.is_answered: nbr_selected = len(ic.selected_options) if nbr_selected == 0: tokens.append(("class:answer", "done")) elif nbr_selected == 1: if isinstance(ic.get_selected_values()[0].title, list): ts = ic.get_selected_values()[0].title tokens.append(( "class:answer", "".join([token[1] for token in ts]), # type:ignore )) else: tokens.append(( "class:answer", "[{}]".format(ic.get_selected_values()[0].title), )) else: tokens.append(("class:answer", "done ({} selections)".format(nbr_selected))) else: tokens.append(( "class:instruction", "(Use arrow keys to move, " "<space> to select, " "<a> to toggle, " "<i> to invert)", )) return tokens def get_selected_values() -> List[Any]: return [c.value for c in ic.get_selected_values()] def perform_validation(selected_values: List[str]) -> bool: verdict = validate(selected_values) valid = verdict is True if not valid: if verdict is False: error_text = INVALID_INPUT else: error_text = str(verdict) error_message = FormattedText([("class:validation-toolbar", error_text)]) ic.error_message = (error_message if not valid and ic.submission_attempted else None) return valid layout = common.create_inquirer_layout(ic, get_prompt_tokens, **kwargs) bindings = KeyBindings() @bindings.add(Keys.ControlQ, eager=True) @bindings.add(Keys.ControlC, eager=True) def _(event): event.app.exit(exception=KeyboardInterrupt, style="class:aborting") @bindings.add(" ", eager=True) def toggle(_event): pointed_choice = ic.get_pointed_at().value if pointed_choice in ic.selected_options: ic.selected_options.remove(pointed_choice) else: ic.selected_options.append(pointed_choice) perform_validation(get_selected_values()) @bindings.add("i", eager=True) def invert(_event): inverted_selection = [ c.value for c in ic.choices if not isinstance(c, Separator) and c.value not in ic.selected_options and not c.disabled ] ic.selected_options = inverted_selection perform_validation(get_selected_values()) @bindings.add("a", eager=True) def all(_event): all_selected = True # all choices have been selected for c in ic.choices: if (not isinstance(c, Separator) and c.value not in ic.selected_options and not c.disabled): # add missing ones ic.selected_options.append(c.value) all_selected = False if all_selected: ic.selected_options = [] perform_validation(get_selected_values()) @bindings.add(Keys.Down, eager=True) @bindings.add("j", eager=True) def move_cursor_down(_event): ic.select_next() while not ic.is_selection_valid(): ic.select_next() @bindings.add(Keys.Up, eager=True) @bindings.add("k", eager=True) def move_cursor_up(_event): ic.select_previous() while not ic.is_selection_valid(): ic.select_previous() @bindings.add(Keys.ControlM, eager=True) def set_answer(event): selected_values = get_selected_values() ic.submission_attempted = True if perform_validation(selected_values): ic.is_answered = True event.app.exit(result=selected_values) @bindings.add(Keys.Any) def other(_event): """Disallow inserting other text. """ pass return Question( Application( layout=layout, key_bindings=bindings, style=merged_style, **utils.used_kwargs(kwargs, Application.__init__), ))
def confirm( message: str, default: bool = True, qmark: str = DEFAULT_QUESTION_PREFIX, style: Optional[Style] = None, auto_enter: bool = True, **kwargs: Any, ) -> Question: """A yes or no question. The user can either confirm or deny. This question type can be used to prompt the user for a confirmation of a yes-or-no question. If the user just hits enter, the default value will be returned. Example: >>> import questionary >>> questionary.confirm("Are you amazed?").ask() ? Are you amazed? Yes True .. image:: ../images/confirm.gif This is just a realy basic example, the prompt can be customised using the parameters. Args: message: Question text. default: Default value will be returned if the user just hits enter. qmark: Question prefix displayed in front of the question. By default this is a ``?``. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. auto_enter: If set to `False`, the user needs to press the 'enter' key to accept their answer. If set to `True`, a valid input will be accepted without the need to press 'Enter'. Returns: :class:`Question`: Question instance, ready to be prompted (using `.ask()`). """ merged_style = merge_styles([DEFAULT_STYLE, style]) status = {"answer": None, "complete": False} def get_prompt_tokens(): tokens = [] tokens.append(("class:qmark", qmark)) tokens.append(("class:question", " {} ".format(message))) if not status["complete"]: instruction = YES_OR_NO if default else NO_OR_YES tokens.append(("class:instruction", "{} ".format(instruction))) if status["answer"] is not None: answer = YES if status["answer"] else NO tokens.append(("class:answer", answer)) return to_formatted_text(tokens) def exit_with_result(event): status["complete"] = True event.app.exit(result=status["answer"]) bindings = KeyBindings() @bindings.add(Keys.ControlQ, eager=True) @bindings.add(Keys.ControlC, eager=True) def _(event): event.app.exit(exception=KeyboardInterrupt, style="class:aborting") @bindings.add("n") @bindings.add("N") def key_n(event): status["answer"] = False if auto_enter: exit_with_result(event) @bindings.add("y") @bindings.add("Y") def key_y(event): status["answer"] = True if auto_enter: exit_with_result(event) @bindings.add(Keys.ControlH) def key_backspace(event): status["answer"] = None @bindings.add(Keys.ControlM, eager=True) def set_answer(event): if status["answer"] is None: status["answer"] = default exit_with_result(event) @bindings.add(Keys.Any) def other(event): """Disallow inserting other text.""" pass return Question( PromptSession( get_prompt_tokens, key_bindings=bindings, style=merged_style, **kwargs ).app )