Spaces:
Sleeping
Sleeping
"""Module contains the class to create an input prompt.""" | |
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union | |
from prompt_toolkit.buffer import ValidationState | |
from prompt_toolkit.completion import NestedCompleter | |
from prompt_toolkit.completion.base import Completer | |
from prompt_toolkit.filters.base import Condition | |
from prompt_toolkit.keys import Keys | |
from prompt_toolkit.lexers import SimpleLexer | |
from prompt_toolkit.shortcuts.prompt import CompleteStyle, PromptSession | |
from prompt_toolkit.validation import ValidationError | |
from InquirerPy.base import BaseSimplePrompt | |
from InquirerPy.enum import INQUIRERPY_POINTER_SEQUENCE | |
from InquirerPy.exceptions import InvalidArgument | |
from InquirerPy.utils import ( | |
InquirerPyDefault, | |
InquirerPyKeybindings, | |
InquirerPyMessage, | |
InquirerPySessionResult, | |
InquirerPyStyle, | |
InquirerPyValidate, | |
) | |
if TYPE_CHECKING: | |
from prompt_toolkit.input.base import Input | |
from prompt_toolkit.key_binding.key_processor import KeyPressEvent | |
from prompt_toolkit.output.base import Output | |
__all__ = ["InputPrompt"] | |
class InputPrompt(BaseSimplePrompt): | |
"""Create a text prompt that accepts user input. | |
A wrapper class around :class:`~prompt_toolkit.shortcuts.PromptSession`. | |
Args: | |
message: The question to ask the user. | |
Refer to :ref:`pages/dynamic:message` documentation for more details. | |
style: An :class:`InquirerPyStyle` instance. | |
Refer to :ref:`Style <pages/style:Alternate Syntax>` documentation for more details. | |
vi_mode: Use vim keybinding for the prompt. | |
Refer to :ref:`pages/kb:Keybindings` documentation for more details. | |
default: Set the default text value of the prompt. | |
Refer to :ref:`pages/dynamic:default` documentation for more details. | |
qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered. | |
amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered. | |
instruction: Short instruction to display next to the question. | |
long_instruction: Long instructions to display at the bottom of the prompt. | |
completer: Add auto completion to the prompt. | |
Refer to :ref:`pages/prompts/input:Auto Completion` documentation for more details. | |
multicolumn_complete: Change the auto-completion UI to a multi column display. | |
multiline: Enable multiline edit. While multiline edit is active, pressing `enter` won't complete the answer. | |
and will create a new line. Use `esc` followd by `enter` to complete the question. | |
validate: Add validation to user input. | |
Refer to :ref:`pages/validator:Validator` documentation for more details. | |
invalid_message: Error message to display when user input is invalid. | |
Refer to :ref:`pages/validator:Validator` documentation for more details. | |
transformer: A function which performs additional transformation on the value that gets printed to the terminal. | |
Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. | |
Refer to :ref:`pages/dynamic:transformer` documentation for more details. | |
filter: A function which performs additional transformation on the result. | |
This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. | |
Refer to :ref:`pages/dynamic:filter` documentation for more details. | |
keybindings: Customise the builtin keybindings. | |
Refer to :ref:`pages/kb:Keybindings` for more details. | |
wrap_lines: Soft wrap question lines when question exceeds the terminal width. | |
raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result | |
will be `None` and the question is skiped. | |
is_password: Used internally for :class:`~InquirerPy.prompts.secret.SecretPrompt`. | |
mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped. | |
mandatory_message: Error message to show when user attempts to skip mandatory prompt. | |
session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`. | |
input: Used internally and will be removed in future updates. | |
output: Used internally and will be removed in future updates. | |
Examples: | |
>>> from InquirerPy import inquirer | |
>>> result = inquirer.text(message="Enter your name:").execute() | |
>>> print(f"Name: {result}") | |
Name: Michael | |
""" | |
def __init__( | |
self, | |
message: InquirerPyMessage, | |
style: Optional[InquirerPyStyle] = None, | |
vi_mode: bool = False, | |
default: InquirerPyDefault = "", | |
qmark: str = "?", | |
amark: str = "?", | |
instruction: str = "", | |
long_instruction: str = "", | |
completer: Optional[Union[Dict[str, Optional[str]], "Completer"]] = None, | |
multicolumn_complete: bool = False, | |
multiline: bool = False, | |
validate: Optional[InquirerPyValidate] = None, | |
invalid_message: str = "Invalid input", | |
transformer: Optional[Callable[[str], Any]] = None, | |
filter: Optional[Callable[[str], Any]] = None, | |
keybindings: Optional[InquirerPyKeybindings] = None, | |
wrap_lines: bool = True, | |
raise_keyboard_interrupt: bool = True, | |
is_password: bool = False, | |
mandatory: bool = True, | |
mandatory_message: str = "Mandatory prompt", | |
session_result: Optional[InquirerPySessionResult] = None, | |
input: Optional["Input"] = None, | |
output: Optional["Output"] = None, | |
) -> None: | |
super().__init__( | |
message, | |
style, | |
vi_mode=vi_mode, | |
qmark=qmark, | |
amark=amark, | |
instruction=instruction, | |
validate=validate, | |
invalid_message=invalid_message, | |
transformer=transformer, | |
filter=filter, | |
session_result=session_result, | |
default=default, | |
wrap_lines=wrap_lines, | |
mandatory=mandatory, | |
mandatory_message=mandatory_message, | |
raise_keyboard_interrupt=raise_keyboard_interrupt, | |
) | |
if not isinstance(self._default, str): | |
raise InvalidArgument( | |
f"{type(self).__name__} argument 'default' should be type of str" | |
) | |
self._completer = None | |
if isinstance(completer, dict): | |
self._completer = NestedCompleter.from_nested_dict(completer) | |
elif isinstance(completer, Completer): | |
self._completer = completer | |
self._multiline = multiline | |
self._complete_style = ( | |
CompleteStyle.COLUMN | |
if not multicolumn_complete | |
else CompleteStyle.MULTI_COLUMN | |
) | |
def is_multiline(): | |
return self._multiline | |
if not keybindings: | |
keybindings = {} | |
self.kb_maps = { | |
"answer": [ | |
{"key": Keys.Enter, "filter": ~is_multiline}, | |
{"key": [Keys.Escape, Keys.Enter], "filter": is_multiline}, | |
], | |
"completion": [{"key": "c-space"}], | |
**keybindings, | |
} | |
self.kb_func_lookup = {"completion": [{"func": self._handle_completion}]} | |
self._keybinding_factory() | |
self._session = PromptSession( | |
message=self._get_prompt_message, | |
key_bindings=self._kb, | |
style=self._style, | |
completer=self._completer, | |
validator=self._validator, | |
validate_while_typing=False, | |
input=input, | |
output=output, | |
editing_mode=self._editing_mode, | |
lexer=SimpleLexer(self._lexer), | |
is_password=is_password, | |
multiline=self._multiline, | |
complete_style=self._complete_style, | |
wrap_lines=wrap_lines, | |
bottom_toolbar=[("class:long_instruction", long_instruction)] | |
if long_instruction | |
else None, | |
) | |
def _set_error(self, message: str) -> None: | |
self._session.default_buffer.validation_state = ValidationState.INVALID | |
self._session.default_buffer.validation_error = ValidationError(message=message) | |
def _handle_enter(self, event: "KeyPressEvent") -> None: | |
try: | |
self._session.validator.validate(self._session.default_buffer) # type: ignore | |
except ValidationError: | |
self._session.default_buffer.validate_and_handle() | |
else: | |
self.status["answered"] = True | |
self.status["result"] = self._session.default_buffer.text | |
self._session.default_buffer.text = "" | |
event.app.exit(result=self.status["result"]) | |
def _handle_completion(self, event) -> None: | |
if self._completer is None: | |
return | |
buff = event.app.current_buffer | |
if buff.complete_state: | |
buff.complete_next() | |
else: | |
buff.start_completion(select_first=False) | |
def _get_prompt_message( | |
self, | |
pre_answer: Optional[Tuple[str, str]] = None, | |
post_answer: Optional[Tuple[str, str]] = None, | |
) -> List[Tuple[str, str]]: | |
"""Get message to display infront of the input buffer. | |
Args: | |
pre_answer: The formatted text to display before answering the question. | |
post_answer: The formatted text to display after answering the question. | |
Returns: | |
Formatted text in list of tuple format. | |
""" | |
if not pre_answer: | |
if self._multiline and not self._instruction: | |
pre_answer = ("class:instruction", " ESC + Enter to finish input") | |
else: | |
pre_answer = ( | |
"class:instruction", | |
" %s " % self.instruction if self.instruction else " ", | |
) | |
if not post_answer: | |
if self._multiline and self.status["result"]: | |
lines = self.status["result"].split("\n") | |
if len(lines) > 1: | |
number_of_chars = len("".join(lines[1:])) | |
lines[0] += "...[%s char%s]" % ( | |
number_of_chars, | |
"s" if number_of_chars > 1 else "", | |
) | |
post_answer = ("class:answer", " %s" % lines[0]) | |
else: | |
post_answer = ("class:answer", " %s" % self.status["result"]) | |
formatted_message = super()._get_prompt_message(pre_answer, post_answer) | |
if not self.status["answered"] and self._multiline: | |
formatted_message.append( | |
("class:questionmark", "\n%s " % INQUIRERPY_POINTER_SEQUENCE) | |
) | |
return formatted_message | |
def _run(self) -> str: | |
return self._session.prompt(default=self._default) | |
async def _run_async(self) -> Any: | |
return await self._session.prompt_async(default=self._default) | |