Exemplo n.º 1
0
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()))
Exemplo n.º 2
0
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)
Exemplo n.º 3
0
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))
Exemplo n.º 4
0
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))
Exemplo n.º 5
0
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)
Exemplo n.º 6
0
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)
Exemplo n.º 7
0
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)
Exemplo n.º 8
0
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)
Exemplo n.º 9
0
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__),
        ))
Exemplo n.º 10
0
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__),
        )
    )
Exemplo n.º 11
0
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)
Exemplo n.º 12
0
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)
Exemplo n.º 13
0
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)
Exemplo n.º 14
0
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__),
        ))
Exemplo n.º 15
0
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
    )