InquirerPrompt 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,239 @@
1
+ """Contains the base class :class:`.BaseListPrompt` which can be used to create a prompt involving choices."""
2
+
3
+ from abc import abstractmethod
4
+ from typing import Any, Callable, List, Optional
5
+
6
+ from prompt_toolkit.filters.base import Condition
7
+ from prompt_toolkit.keys import Keys
8
+
9
+ from InquirerPy.base.complex import BaseComplexPrompt
10
+ from InquirerPy.base.control import InquirerPyUIListControl
11
+ from InquirerPy.separator import Separator
12
+ from InquirerPy.utils import (
13
+ InquirerPyKeybindings,
14
+ InquirerPyMessage,
15
+ InquirerPySessionResult,
16
+ InquirerPyStyle,
17
+ InquirerPyValidate,
18
+ )
19
+
20
+
21
+ class BaseListPrompt(BaseComplexPrompt):
22
+ """A base class to create a complex prompt involving choice selections (i.e. list) using `prompt_toolkit` Application.
23
+
24
+ Note:
25
+ This class does not create :class:`~prompt_toolkit.layout.Layout` nor :class:`~prompt_toolkit.application.Application`,
26
+ it only contains the necessary attributes and helper functions to be consumed.
27
+
28
+ See Also:
29
+ :class:`~InquirerPy.prompts.list.ListPrompt`
30
+ :class:`~InquirerPy.prompts.fuzzy.FuzzyPrompt`
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ message: InquirerPyMessage,
36
+ style: Optional[InquirerPyStyle] = None,
37
+ vi_mode: bool = False,
38
+ qmark: str = "?",
39
+ amark: str = "?",
40
+ instruction: str = "",
41
+ long_instruction: str = "",
42
+ border: bool = False,
43
+ transformer: Optional[Callable[[Any], Any]] = None,
44
+ filter: Optional[Callable[[Any], Any]] = None,
45
+ validate: Optional[InquirerPyValidate] = None,
46
+ invalid_message: str = "Invalid input",
47
+ multiselect: bool = False,
48
+ keybindings: Optional[InquirerPyKeybindings] = None,
49
+ cycle: bool = True,
50
+ wrap_lines: bool = True,
51
+ raise_keyboard_interrupt: bool = True,
52
+ mandatory: bool = True,
53
+ mandatory_message: str = "Mandatory prompt",
54
+ session_result: Optional[InquirerPySessionResult] = None,
55
+ ) -> None:
56
+ super().__init__(
57
+ message=message,
58
+ style=style,
59
+ border=border,
60
+ vi_mode=vi_mode,
61
+ qmark=qmark,
62
+ amark=amark,
63
+ transformer=transformer,
64
+ filter=filter,
65
+ invalid_message=invalid_message,
66
+ validate=validate,
67
+ instruction=instruction,
68
+ long_instruction=long_instruction,
69
+ wrap_lines=wrap_lines,
70
+ raise_keyboard_interrupt=raise_keyboard_interrupt,
71
+ mandatory=mandatory,
72
+ mandatory_message=mandatory_message,
73
+ session_result=session_result,
74
+ )
75
+
76
+ self._content_control: InquirerPyUIListControl
77
+ self._multiselect = multiselect
78
+ self._is_multiselect = Condition(lambda: self._multiselect)
79
+ self._cycle = cycle
80
+
81
+ if not keybindings:
82
+ keybindings = {}
83
+
84
+ self.kb_maps = {
85
+ "down": [
86
+ {"key": "down"},
87
+ {"key": "c-n", "filter": ~self._is_vim_edit},
88
+ {"key": "j", "filter": self._is_vim_edit},
89
+ ],
90
+ "up": [
91
+ {"key": "up"},
92
+ {"key": "c-p", "filter": ~self._is_vim_edit},
93
+ {"key": "k", "filter": self._is_vim_edit},
94
+ ],
95
+ "toggle": [
96
+ {"key": "space"},
97
+ ],
98
+ "toggle-down": [
99
+ {"key": Keys.Tab},
100
+ ],
101
+ "toggle-up": [
102
+ {"key": Keys.BackTab},
103
+ ],
104
+ "toggle-all": [
105
+ {"key": "alt-r"},
106
+ {"key": "c-r"},
107
+ ],
108
+ "toggle-all-true": [
109
+ {"key": "alt-a"},
110
+ {"key": "c-a"},
111
+ ],
112
+ "toggle-all-false": [],
113
+ **keybindings,
114
+ }
115
+
116
+ self.kb_func_lookup = {
117
+ "down": [{"func": self._handle_down}],
118
+ "up": [{"func": self._handle_up}],
119
+ "toggle": [{"func": self._handle_toggle_choice}],
120
+ "toggle-down": [
121
+ {"func": self._handle_toggle_choice},
122
+ {"func": self._handle_down},
123
+ ],
124
+ "toggle-up": [
125
+ {"func": self._handle_toggle_choice},
126
+ {"func": self._handle_up},
127
+ ],
128
+ "toggle-all": [{"func": self._handle_toggle_all}],
129
+ "toggle-all-true": [{"func": self._handle_toggle_all, "args": [True]}],
130
+ "toggle-all-false": [{"func": self._handle_toggle_all, "args": [False]}],
131
+ }
132
+
133
+ @property
134
+ def content_control(self) -> InquirerPyUIListControl:
135
+ """Get the content controller object.
136
+
137
+ Needs to be an instance of :class:`~InquirerPy.base.control.InquirerPyUIListControl`.
138
+
139
+ Each :class:`.BaseComplexPrompt` requires a `content_control` to display custom
140
+ contents for the prompt.
141
+
142
+ Raises:
143
+ NotImplementedError: When `self._content_control` is not found.
144
+ """
145
+ if not self._content_control:
146
+ raise NotImplementedError
147
+ return self._content_control
148
+
149
+ @content_control.setter
150
+ def content_control(self, value: InquirerPyUIListControl) -> None:
151
+ self._content_control = value
152
+
153
+ @property
154
+ def result_name(self) -> Any:
155
+ """Get the result value that should be printed to the terminal.
156
+
157
+ In multiselect scenario, return result as a list.
158
+ """
159
+ if self._multiselect:
160
+ return [choice["name"] for choice in self.selected_choices]
161
+ else:
162
+ try:
163
+ return self.content_control.selection["name"]
164
+ except IndexError:
165
+ return ""
166
+
167
+ @property
168
+ def result_value(self) -> Any:
169
+ """Get the result value that should return to the user.
170
+
171
+ In multiselect scenario, return result as a list.
172
+ """
173
+ if self._multiselect:
174
+ return [choice["value"] for choice in self.selected_choices]
175
+ else:
176
+ try:
177
+ return self.content_control.selection["value"]
178
+ except IndexError:
179
+ return ""
180
+
181
+ @property
182
+ def selected_choices(self) -> List[Any]:
183
+ """List[Any]: Get all user selected choices."""
184
+
185
+ def filter_choice(choice):
186
+ return not isinstance(choice, Separator) and choice["enabled"]
187
+
188
+ return list(filter(filter_choice, self.content_control.choices))
189
+
190
+ def _handle_down(self, _) -> bool:
191
+ """Handle event when user attempts to move down.
192
+
193
+ Returns:
194
+ Boolean indicating if the action hits the cap.
195
+ """
196
+ if self._cycle:
197
+ self.content_control.selected_choice_index = (
198
+ self.content_control.selected_choice_index + 1
199
+ ) % self.content_control.choice_count
200
+ return False
201
+ else:
202
+ self.content_control.selected_choice_index += 1
203
+ if (
204
+ self.content_control.selected_choice_index
205
+ >= self.content_control.choice_count
206
+ ):
207
+ self.content_control.selected_choice_index = (
208
+ self.content_control.choice_count - 1
209
+ )
210
+ return True
211
+ return False
212
+
213
+ def _handle_up(self, _) -> bool:
214
+ """Handle event when user attempts to move up.
215
+
216
+ Returns:
217
+ Boolean indicating if the action hits the cap.
218
+ """
219
+ if self._cycle:
220
+ self.content_control.selected_choice_index = (
221
+ self.content_control.selected_choice_index - 1
222
+ ) % self.content_control.choice_count
223
+ return False
224
+ else:
225
+ self.content_control.selected_choice_index -= 1
226
+ if self.content_control.selected_choice_index < 0:
227
+ self.content_control.selected_choice_index = 0
228
+ return True
229
+ return False
230
+
231
+ @abstractmethod
232
+ def _handle_toggle_choice(self, event) -> None:
233
+ """Handle event when user attempting to toggle the state of the chocie."""
234
+ pass
235
+
236
+ @abstractmethod
237
+ def _handle_toggle_all(self, event, value: bool) -> None:
238
+ """Handle event when user attempting to alter the state of all choices."""
239
+ pass
@@ -0,0 +1,379 @@
1
+ """Contains the base class :class:`.BaseSimplePrompt`."""
2
+
3
+ import os
4
+ import re
5
+ from abc import ABC, abstractmethod
6
+ from typing import (
7
+ TYPE_CHECKING,
8
+ Any,
9
+ Callable,
10
+ Dict,
11
+ List,
12
+ Optional,
13
+ Tuple,
14
+ Union,
15
+ cast,
16
+ )
17
+
18
+ from prompt_toolkit.enums import EditingMode
19
+ from prompt_toolkit.filters.base import Condition, FilterOrBool
20
+ from prompt_toolkit.key_binding.key_bindings import KeyBindings, KeyHandlerCallable
21
+ from prompt_toolkit.keys import Keys
22
+ from prompt_toolkit.styles.style import Style
23
+ from prompt_toolkit.validation import Validator
24
+
25
+ from InquirerPy.enum import INQUIRERPY_KEYBOARD_INTERRUPT
26
+ from InquirerPy.exceptions import RequiredKeyNotFound
27
+ from InquirerPy.utils import (
28
+ InquirerPyMessage,
29
+ InquirerPySessionResult,
30
+ InquirerPyStyle,
31
+ InquirerPyValidate,
32
+ get_style,
33
+ )
34
+
35
+ if TYPE_CHECKING:
36
+ from prompt_toolkit.key_binding.key_processor import KeyPressEvent
37
+
38
+
39
+ class BaseSimplePrompt(ABC):
40
+ """The base class to create a simple terminal input prompt.
41
+
42
+ Note:
43
+ No actual :class:`~prompt_toolkit.application.Application` is created by this class.
44
+ This class only creates some common interface and attributes that can be easily used
45
+ by `prompt_toolkit`.
46
+
47
+ To have a functional prompt, you'll at least have to implement the :meth:`.BaseSimplePrompt._run`
48
+ and :meth:`.BaseSimplePrompt._get_prompt_message`.
49
+
50
+ See Also:
51
+ :class:`~InquirerPy.prompts.input.InputPrompt`
52
+ """
53
+
54
+ def __init__(
55
+ self,
56
+ message: InquirerPyMessage,
57
+ style: Optional[InquirerPyStyle] = None,
58
+ vi_mode: bool = False,
59
+ qmark: str = "?",
60
+ amark: str = "?",
61
+ instruction: str = "",
62
+ validate: Optional[InquirerPyValidate] = None,
63
+ invalid_message: str = "Invalid input",
64
+ transformer: Optional[Callable[[Any], Any]] = None,
65
+ filter: Optional[Callable[[Any], Any]] = None,
66
+ default: Any = "",
67
+ wrap_lines: bool = True,
68
+ raise_keyboard_interrupt: bool = True,
69
+ mandatory: bool = True,
70
+ mandatory_message: str = "Mandatory prompt",
71
+ session_result: Optional[InquirerPySessionResult] = None,
72
+ ) -> None:
73
+ self._mandatory = mandatory
74
+ self._mandatory_message = mandatory_message
75
+ self._result = session_result or {}
76
+ self._message = (
77
+ message
78
+ if not isinstance(message, Callable)
79
+ else cast(Callable, message)(self._result)
80
+ )
81
+ self._instruction = instruction
82
+ self._default = (
83
+ default if not isinstance(default, Callable) else default(self._result)
84
+ )
85
+ self._style = Style.from_dict(style.dict if style else get_style().dict)
86
+ self._qmark = qmark
87
+ self._amark = amark
88
+ self._status = {"answered": False, "result": None, "skipped": False}
89
+ self._kb = KeyBindings()
90
+ self._lexer = "class:input"
91
+ self._transformer = transformer
92
+ self._filter = filter
93
+ self._wrap_lines = wrap_lines
94
+ self._editing_mode = (
95
+ EditingMode.VI
96
+ if vi_mode or bool(os.getenv("INQUIRERPY_VI_MODE", False))
97
+ else EditingMode.EMACS
98
+ )
99
+ if isinstance(validate, Validator):
100
+ self._validator = validate
101
+ else:
102
+ self._validator = Validator.from_callable(
103
+ validate if validate else lambda _: True,
104
+ invalid_message,
105
+ move_cursor_to_end=True,
106
+ )
107
+ self._raise_kbi = not os.getenv(
108
+ "INQUIRERPY_NO_RAISE_KBI", not raise_keyboard_interrupt
109
+ )
110
+ self._is_rasing_kbi = Condition(lambda: self._raise_kbi)
111
+
112
+ self._kb_maps = {
113
+ "answer": [{"key": Keys.Enter}],
114
+ "interrupt": [
115
+ {"key": "c-c", "filter": self._is_rasing_kbi},
116
+ {"key": "c-d", "filter": ~self._is_rasing_kbi},
117
+ ],
118
+ "skip": [{"key": "c-z"}, {"key": "c-c", "filter": ~self._is_rasing_kbi}],
119
+ }
120
+ self._kb_func_lookup = {
121
+ "answer": [{"func": self._handle_enter}],
122
+ "interrupt": [{"func": self._handle_interrupt}],
123
+ "skip": [{"func": self._handle_skip}],
124
+ }
125
+
126
+ def _keybinding_factory(self):
127
+ """Register all keybindings in `self._kb_maps`.
128
+
129
+ It's required to call this function at the end of prompt constructor if
130
+ it inherits from :class:`~InquirerPy.base.simple.BaseSimplePrompt` or
131
+ :class:`~InquirerPy.base.complex.BaseComplexPrompt`.
132
+ """
133
+
134
+ def _factory(keys, filter, action):
135
+ if action not in self.kb_func_lookup:
136
+ raise RequiredKeyNotFound(f"keybinding action {action} not found")
137
+ if not isinstance(keys, list):
138
+ keys = [keys]
139
+
140
+ @self.register_kb(*keys, filter=filter)
141
+ def _(event):
142
+ for method in self.kb_func_lookup[action]:
143
+ method["func"](event, *method.get("args", []))
144
+
145
+ for key, item in self.kb_maps.items():
146
+ if not isinstance(item, list):
147
+ item = [item]
148
+ for kb in item:
149
+ _factory(kb["key"], kb.get("filter", Condition(lambda: True)), key)
150
+
151
+ @abstractmethod
152
+ def _set_error(self, message: str) -> None:
153
+ """Set the error message for the prompt.
154
+
155
+ Args:
156
+ message: Error message to set.
157
+ """
158
+ pass
159
+
160
+ def _handle_skip(self, event: Optional["KeyPressEvent"]) -> None:
161
+ """Handle the event when attempting to skip a prompt.
162
+
163
+ Skip the prompt if the `_mandatory` field is False, otherwise
164
+ show an error message that the prompt cannot be skipped.
165
+ """
166
+ if not self._mandatory:
167
+ self.status["answered"] = True
168
+ self.status["skipped"] = True
169
+ self.status["result"] = None
170
+ if event:
171
+ event.app.exit(result=None)
172
+ else:
173
+ self._set_error(message=self._mandatory_message)
174
+
175
+ def _handle_interrupt(self, event: Optional["KeyPressEvent"]) -> None:
176
+ """Handle the event when a KeyboardInterrupt signal is sent."""
177
+ self.status["answered"] = True
178
+ self.status["result"] = INQUIRERPY_KEYBOARD_INTERRUPT
179
+ self.status["skipped"] = True
180
+ if event:
181
+ event.app.exit(result=INQUIRERPY_KEYBOARD_INTERRUPT)
182
+
183
+ @abstractmethod
184
+ def _handle_enter(self, event: Optional["KeyPressEvent"]) -> None:
185
+ """Handle the event when user attempt to answer the question."""
186
+ pass
187
+
188
+ @property
189
+ def status(self) -> Dict[str, Any]:
190
+ """Dict[str, Any]: Get current prompt status.
191
+
192
+ The status contains 3 keys: "answered" and "result".
193
+ answered: If the current prompt is answered.
194
+ result: The result of the user answer.
195
+ skipped: If the prompt is skipped.
196
+ """
197
+ return self._status
198
+
199
+ @status.setter
200
+ def status(self, value) -> None:
201
+ self._status = value
202
+
203
+ def register_kb(
204
+ self, *keys: Union[Keys, str], filter: FilterOrBool = True, **kwargs
205
+ ) -> Callable[[KeyHandlerCallable], KeyHandlerCallable]:
206
+ """Keybinding registration decorator.
207
+
208
+ This decorator wraps around the :meth:`prompt_toolkit.key_binding.KeyBindings.add` with
209
+ added feature to process `alt` realted keybindings.
210
+
211
+ By default, `prompt_toolkit` doesn't process `alt` related keybindings,
212
+ it requires `alt-ANY` to `escape` + `ANY`.
213
+
214
+ Args:
215
+ keys: The keys to bind that can trigger the function.
216
+ filter: :class:`~prompt_toolkit.filter.Condition` to indicate if this keybinding should be active.
217
+
218
+ Returns:
219
+ A decorator that should be applied to the function thats intended to be active when the keys
220
+ are pressed.
221
+
222
+ Examples:
223
+ >>> @self.register_kb("alt-j")
224
+ ... def test(event):
225
+ ... pass
226
+ """
227
+ alt_pattern = re.compile(r"^alt-(.*)")
228
+
229
+ def decorator(func: KeyHandlerCallable) -> KeyHandlerCallable:
230
+ formatted_keys = []
231
+ for key in keys:
232
+ match = alt_pattern.match(key)
233
+ if match:
234
+ formatted_keys.append("escape")
235
+ formatted_keys.append(match.group(1))
236
+ else:
237
+ formatted_keys.append(key)
238
+
239
+ @self._kb.add(*formatted_keys, filter=filter, **kwargs)
240
+ def executable(event) -> None:
241
+ func(event)
242
+
243
+ return executable
244
+
245
+ return decorator
246
+
247
+ @abstractmethod
248
+ def _get_prompt_message(
249
+ self, pre_answer: Tuple[str, str], post_answer: Tuple[str, str]
250
+ ) -> List[Tuple[str, str]]:
251
+ """Get the question message in formatted text form to display in the prompt.
252
+
253
+ This function is mainly used to render the question message dynamically based
254
+ on the current status (answered or not answered) of the prompt.
255
+
256
+ Note:
257
+ The function requires implementation when inheriting :class:`.BaseSimplePrompt`.
258
+ You should call `super()._get_prompt_message(pre_answer, post_answer)` in
259
+ the implemented `_get_prompt_message`.
260
+
261
+ Args:
262
+ pre_answer: The message to display before the question is answered.
263
+ post_answer: The information to display after the question is answered.
264
+
265
+ Returns:
266
+ Formatted text in list of tuple format.
267
+ """
268
+ display_message = []
269
+ if self.status["skipped"]:
270
+ display_message.append(("class:skipped", self._qmark))
271
+ display_message.append(
272
+ ("class:skipped", "%s%s " % (" " if self._qmark else "", self._message))
273
+ )
274
+ elif self.status["answered"]:
275
+ display_message.append(("class:answermark", self._amark))
276
+ display_message.append(
277
+ (
278
+ "class:answered_question",
279
+ "%s%s" % (" " if self._amark else "", self._message),
280
+ )
281
+ )
282
+ display_message.append(
283
+ post_answer
284
+ if not self._transformer
285
+ else (
286
+ "class:answer",
287
+ " %s" % self._transformer(self.status["result"]),
288
+ )
289
+ )
290
+ else:
291
+ display_message.append(("class:questionmark", self._qmark))
292
+ display_message.append(
293
+ (
294
+ "class:question",
295
+ "%s%s" % (" " if self._qmark else "", self._message),
296
+ )
297
+ )
298
+ display_message.append(pre_answer)
299
+ return display_message
300
+
301
+ @abstractmethod
302
+ def _run(self) -> Any:
303
+ """Abstractmethod to enforce a run function is implemented.
304
+
305
+ All prompt instance requires a `_run` call to initialise and run an instance of
306
+ `PromptSession` or `Application`.
307
+ """
308
+ pass
309
+
310
+ @abstractmethod
311
+ async def _run_async(self) -> Any:
312
+ """Abstractmethod to enforce a run function is implemented.
313
+
314
+ All prompt instance requires a `_run_async` call to initialise and run an instance of
315
+ `PromptSession` or `Application`.
316
+ """
317
+ pass
318
+
319
+ def execute(self, raise_keyboard_interrupt: Optional[bool] = None) -> Any:
320
+ """Run the prompt and get the result.
321
+
322
+ Args:
323
+ raise_keyboard_interrupt: **Deprecated**. Set this parameter on the prompt initialisation instead.
324
+
325
+ Returns:
326
+ Value of the user answer. Types varies depending on the prompt.
327
+
328
+ Raises:
329
+ KeyboardInterrupt: When `ctrl-c` is pressed and `raise_keyboard_interrupt` is True.
330
+ """
331
+ result = self._run()
332
+ if raise_keyboard_interrupt is not None:
333
+ self._raise_kbi = not os.getenv(
334
+ "INQUIRERPY_NO_RAISE_KBI", not raise_keyboard_interrupt
335
+ )
336
+ if result == INQUIRERPY_KEYBOARD_INTERRUPT:
337
+ raise KeyboardInterrupt
338
+ if not self._filter:
339
+ return result
340
+ return self._filter(result)
341
+
342
+ async def execute_async(self) -> Any:
343
+ """Run the prompt asynchronously and get the result.
344
+
345
+ Returns:
346
+ Value of the user answer. Types varies depending on the prompt.
347
+
348
+ Raises:
349
+ KeyboardInterrupt: When `ctrl-c` is pressed and `raise_keyboard_interrupt` is True.
350
+ """
351
+ result = await self._run_async()
352
+ if result == INQUIRERPY_KEYBOARD_INTERRUPT:
353
+ raise KeyboardInterrupt
354
+ if not self._filter:
355
+ return result
356
+ return self._filter(result)
357
+
358
+ @property
359
+ def instruction(self) -> str:
360
+ """str: Instruction to display next to question."""
361
+ return self._instruction
362
+
363
+ @property
364
+ def kb_maps(self) -> Dict[str, Any]:
365
+ """Dict[str, Any]: Keybinding mappings."""
366
+ return self._kb_maps
367
+
368
+ @kb_maps.setter
369
+ def kb_maps(self, value: Dict[str, Any]) -> None:
370
+ self._kb_maps = {**self._kb_maps, **value}
371
+
372
+ @property
373
+ def kb_func_lookup(self) -> Dict[str, Any]:
374
+ """Dict[str, Any]: Keybinding function lookup mappings.."""
375
+ return self._kb_func_lookup
376
+
377
+ @kb_func_lookup.setter
378
+ def kb_func_lookup(self, value: Dict[str, Any]) -> None:
379
+ self._kb_func_lookup = {**self._kb_func_lookup, **value}
@@ -0,0 +1,3 @@
1
+ __all__ = ["SpinnerWindow"]
2
+
3
+ from .spinner import SpinnerWindow
@@ -0,0 +1,38 @@
1
+ """Module contains :class:`.InstructionWindow` which can be used to display long instructions."""
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from prompt_toolkit.layout.containers import ConditionalContainer, Window
6
+ from prompt_toolkit.layout.controls import FormattedTextControl
7
+
8
+ if TYPE_CHECKING:
9
+ from prompt_toolkit.filters.base import FilterOrBool
10
+ from prompt_toolkit.formatted_text.base import AnyFormattedText
11
+
12
+
13
+ class InstructionWindow(ConditionalContainer):
14
+ """Conditional `prompt_toolkit` :class:`~prompt_toolkit.layout.Window` that displays long instructions.
15
+
16
+ Args:
17
+ message: Long instructions to display.
18
+ filter: Condition to display the instruction window.
19
+ """
20
+
21
+ def __init__(self, message: str, filter: "FilterOrBool", **kwargs) -> None:
22
+ self._message = message
23
+ super().__init__(
24
+ Window(
25
+ FormattedTextControl(text=self._get_message),
26
+ dont_extend_height=True,
27
+ **kwargs
28
+ ),
29
+ filter=filter,
30
+ )
31
+
32
+ def _get_message(self) -> "AnyFormattedText":
33
+ """Get long instruction to display.
34
+
35
+ Returns:
36
+ FormattedText in list of tuple format.
37
+ """
38
+ return [("class:long_instruction", self._message)]
@@ -0,0 +1,42 @@
1
+ """Module contains the main message window :class:`~prompt_toolkit.container.Container`."""
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from prompt_toolkit.layout.containers import ConditionalContainer, Window
6
+ from prompt_toolkit.layout.controls import FormattedTextControl
7
+ from prompt_toolkit.layout.dimension import LayoutDimension
8
+
9
+ if TYPE_CHECKING:
10
+ from prompt_toolkit.filters.base import FilterOrBool
11
+ from prompt_toolkit.formatted_text.base import AnyFormattedText
12
+
13
+
14
+ class MessageWindow(ConditionalContainer):
15
+ """Main window to display question to the user.
16
+
17
+ Args:
18
+ message: The message to display in the terminal.
19
+ filter: Condition that this message window should be displayed.
20
+ Use a loading condition to only display this window while its not loading.
21
+ wrap_lines: Enable line wrapping if the message is too long.
22
+ show_cursor: Display cursor.
23
+ """
24
+
25
+ def __init__(
26
+ self,
27
+ message: "AnyFormattedText",
28
+ filter: "FilterOrBool",
29
+ wrap_lines: bool = True,
30
+ show_cursor: bool = True,
31
+ **kwargs
32
+ ) -> None:
33
+ super().__init__(
34
+ content=Window(
35
+ height=LayoutDimension.exact(1) if not wrap_lines else None,
36
+ content=FormattedTextControl(message, show_cursor=show_cursor),
37
+ wrap_lines=wrap_lines,
38
+ dont_extend_height=True,
39
+ **kwargs
40
+ ),
41
+ filter=filter,
42
+ )