Spaces:
Sleeping
Sleeping
| """Contains the base class :class:`.BaseListPrompt` which can be used to create a prompt involving choices.""" | |
| from abc import abstractmethod | |
| from typing import Any, Callable, List, Optional | |
| from prompt_toolkit.filters.base import Condition | |
| from prompt_toolkit.keys import Keys | |
| from InquirerPy.base.complex import BaseComplexPrompt | |
| from InquirerPy.base.control import InquirerPyUIListControl | |
| from InquirerPy.separator import Separator | |
| from InquirerPy.utils import ( | |
| InquirerPyKeybindings, | |
| InquirerPyMessage, | |
| InquirerPySessionResult, | |
| InquirerPyStyle, | |
| InquirerPyValidate, | |
| ) | |
| class BaseListPrompt(BaseComplexPrompt): | |
| """A base class to create a complex prompt involving choice selections (i.e. list) using `prompt_toolkit` Application. | |
| Note: | |
| This class does not create :class:`~prompt_toolkit.layout.Layout` nor :class:`~prompt_toolkit.application.Application`, | |
| it only contains the necessary attributes and helper functions to be consumed. | |
| See Also: | |
| :class:`~InquirerPy.prompts.list.ListPrompt` | |
| :class:`~InquirerPy.prompts.fuzzy.FuzzyPrompt` | |
| """ | |
| def __init__( | |
| self, | |
| message: InquirerPyMessage, | |
| style: Optional[InquirerPyStyle] = None, | |
| vi_mode: bool = False, | |
| qmark: str = "?", | |
| amark: str = "?", | |
| instruction: str = "", | |
| long_instruction: str = "", | |
| border: bool = False, | |
| transformer: Optional[Callable[[Any], Any]] = None, | |
| filter: Optional[Callable[[Any], Any]] = None, | |
| validate: Optional[InquirerPyValidate] = None, | |
| invalid_message: str = "Invalid input", | |
| multiselect: bool = False, | |
| keybindings: Optional[InquirerPyKeybindings] = None, | |
| cycle: bool = True, | |
| wrap_lines: bool = True, | |
| raise_keyboard_interrupt: bool = True, | |
| mandatory: bool = True, | |
| mandatory_message: str = "Mandatory prompt", | |
| session_result: Optional[InquirerPySessionResult] = None, | |
| ) -> None: | |
| super().__init__( | |
| message=message, | |
| style=style, | |
| border=border, | |
| vi_mode=vi_mode, | |
| qmark=qmark, | |
| amark=amark, | |
| transformer=transformer, | |
| filter=filter, | |
| invalid_message=invalid_message, | |
| validate=validate, | |
| instruction=instruction, | |
| long_instruction=long_instruction, | |
| wrap_lines=wrap_lines, | |
| raise_keyboard_interrupt=raise_keyboard_interrupt, | |
| mandatory=mandatory, | |
| mandatory_message=mandatory_message, | |
| session_result=session_result, | |
| ) | |
| self._content_control: InquirerPyUIListControl | |
| self._multiselect = multiselect | |
| self._is_multiselect = Condition(lambda: self._multiselect) | |
| self._cycle = cycle | |
| if not keybindings: | |
| keybindings = {} | |
| self.kb_maps = { | |
| "down": [ | |
| {"key": "down"}, | |
| {"key": "c-n", "filter": ~self._is_vim_edit}, | |
| {"key": "j", "filter": self._is_vim_edit}, | |
| ], | |
| "up": [ | |
| {"key": "up"}, | |
| {"key": "c-p", "filter": ~self._is_vim_edit}, | |
| {"key": "k", "filter": self._is_vim_edit}, | |
| ], | |
| "toggle": [ | |
| {"key": "space"}, | |
| ], | |
| "toggle-down": [ | |
| {"key": Keys.Tab}, | |
| ], | |
| "toggle-up": [ | |
| {"key": Keys.BackTab}, | |
| ], | |
| "toggle-all": [ | |
| {"key": "alt-r"}, | |
| {"key": "c-r"}, | |
| ], | |
| "toggle-all-true": [ | |
| {"key": "alt-a"}, | |
| {"key": "c-a"}, | |
| ], | |
| "toggle-all-false": [], | |
| **keybindings, | |
| } | |
| self.kb_func_lookup = { | |
| "down": [{"func": self._handle_down}], | |
| "up": [{"func": self._handle_up}], | |
| "toggle": [{"func": self._handle_toggle_choice}], | |
| "toggle-down": [ | |
| {"func": self._handle_toggle_choice}, | |
| {"func": self._handle_down}, | |
| ], | |
| "toggle-up": [ | |
| {"func": self._handle_toggle_choice}, | |
| {"func": self._handle_up}, | |
| ], | |
| "toggle-all": [{"func": self._handle_toggle_all}], | |
| "toggle-all-true": [{"func": self._handle_toggle_all, "args": [True]}], | |
| "toggle-all-false": [{"func": self._handle_toggle_all, "args": [False]}], | |
| } | |
| def content_control(self) -> InquirerPyUIListControl: | |
| """Get the content controller object. | |
| Needs to be an instance of :class:`~InquirerPy.base.control.InquirerPyUIListControl`. | |
| Each :class:`.BaseComplexPrompt` requires a `content_control` to display custom | |
| contents for the prompt. | |
| Raises: | |
| NotImplementedError: When `self._content_control` is not found. | |
| """ | |
| if not self._content_control: | |
| raise NotImplementedError | |
| return self._content_control | |
| def content_control(self, value: InquirerPyUIListControl) -> None: | |
| self._content_control = value | |
| def result_name(self) -> Any: | |
| """Get the result value that should be printed to the terminal. | |
| In multiselect scenario, return result as a list. | |
| """ | |
| if self._multiselect: | |
| return [choice["name"] for choice in self.selected_choices] | |
| else: | |
| try: | |
| return self.content_control.selection["name"] | |
| except IndexError: | |
| return "" | |
| def result_value(self) -> Any: | |
| """Get the result value that should return to the user. | |
| In multiselect scenario, return result as a list. | |
| """ | |
| if self._multiselect: | |
| return [choice["value"] for choice in self.selected_choices] | |
| else: | |
| try: | |
| return self.content_control.selection["value"] | |
| except IndexError: | |
| return "" | |
| def selected_choices(self) -> List[Any]: | |
| """List[Any]: Get all user selected choices.""" | |
| def filter_choice(choice): | |
| return not isinstance(choice, Separator) and choice["enabled"] | |
| return list(filter(filter_choice, self.content_control.choices)) | |
| def _handle_down(self, _) -> bool: | |
| """Handle event when user attempts to move down. | |
| Returns: | |
| Boolean indicating if the action hits the cap. | |
| """ | |
| if self._cycle: | |
| self.content_control.selected_choice_index = ( | |
| self.content_control.selected_choice_index + 1 | |
| ) % self.content_control.choice_count | |
| return False | |
| else: | |
| self.content_control.selected_choice_index += 1 | |
| if ( | |
| self.content_control.selected_choice_index | |
| >= self.content_control.choice_count | |
| ): | |
| self.content_control.selected_choice_index = ( | |
| self.content_control.choice_count - 1 | |
| ) | |
| return True | |
| return False | |
| def _handle_up(self, _) -> bool: | |
| """Handle event when user attempts to move up. | |
| Returns: | |
| Boolean indicating if the action hits the cap. | |
| """ | |
| if self._cycle: | |
| self.content_control.selected_choice_index = ( | |
| self.content_control.selected_choice_index - 1 | |
| ) % self.content_control.choice_count | |
| return False | |
| else: | |
| self.content_control.selected_choice_index -= 1 | |
| if self.content_control.selected_choice_index < 0: | |
| self.content_control.selected_choice_index = 0 | |
| return True | |
| return False | |
| def _handle_toggle_choice(self, event) -> None: | |
| """Handle event when user attempting to toggle the state of the chocie.""" | |
| pass | |
| def _handle_toggle_all(self, event, value: bool) -> None: | |
| """Handle event when user attempting to alter the state of all choices.""" | |
| pass | |