mohalmah commited on
Commit
7ae159b
·
verified ·
1 Parent(s): 1442777

Migrate to Nebius DeepSeek AI with Enhanced Features (#2)

Browse files

- Migrate to Nebius DeepSeek AI with Enhanced Features (46f2aa952637ac9c44791b033c81d583556ef272)

This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +68 -35
  2. .venv/.gitignore +1 -0
  3. .venv/Lib/site-packages/InquirerPy/__init__.py +2 -0
  4. .venv/Lib/site-packages/InquirerPy/__pycache__/__init__.cpython-310.pyc +0 -0
  5. .venv/Lib/site-packages/InquirerPy/__pycache__/enum.cpython-310.pyc +0 -0
  6. .venv/Lib/site-packages/InquirerPy/__pycache__/exceptions.cpython-310.pyc +0 -0
  7. .venv/Lib/site-packages/InquirerPy/__pycache__/inquirer.cpython-310.pyc +0 -0
  8. .venv/Lib/site-packages/InquirerPy/__pycache__/resolver.cpython-310.pyc +0 -0
  9. .venv/Lib/site-packages/InquirerPy/__pycache__/separator.cpython-310.pyc +0 -0
  10. .venv/Lib/site-packages/InquirerPy/__pycache__/utils.cpython-310.pyc +0 -0
  11. .venv/Lib/site-packages/InquirerPy/__pycache__/validator.cpython-310.pyc +0 -0
  12. .venv/Lib/site-packages/InquirerPy/base/__init__.py +15 -0
  13. .venv/Lib/site-packages/InquirerPy/base/__pycache__/__init__.cpython-310.pyc +0 -0
  14. .venv/Lib/site-packages/InquirerPy/base/__pycache__/complex.cpython-310.pyc +0 -0
  15. .venv/Lib/site-packages/InquirerPy/base/__pycache__/control.cpython-310.pyc +0 -0
  16. .venv/Lib/site-packages/InquirerPy/base/__pycache__/list.cpython-310.pyc +0 -0
  17. .venv/Lib/site-packages/InquirerPy/base/__pycache__/simple.cpython-310.pyc +0 -0
  18. .venv/Lib/site-packages/InquirerPy/base/complex.py +294 -0
  19. .venv/Lib/site-packages/InquirerPy/base/control.py +227 -0
  20. .venv/Lib/site-packages/InquirerPy/base/list.py +238 -0
  21. .venv/Lib/site-packages/InquirerPy/base/simple.py +378 -0
  22. .venv/Lib/site-packages/InquirerPy/containers/__init__.py +1 -0
  23. .venv/Lib/site-packages/InquirerPy/containers/__pycache__/__init__.cpython-310.pyc +0 -0
  24. .venv/Lib/site-packages/InquirerPy/containers/__pycache__/instruction.cpython-310.pyc +0 -0
  25. .venv/Lib/site-packages/InquirerPy/containers/__pycache__/message.cpython-310.pyc +0 -0
  26. .venv/Lib/site-packages/InquirerPy/containers/__pycache__/spinner.cpython-310.pyc +0 -0
  27. .venv/Lib/site-packages/InquirerPy/containers/__pycache__/validation.cpython-310.pyc +0 -0
  28. .venv/Lib/site-packages/InquirerPy/containers/instruction.py +38 -0
  29. .venv/Lib/site-packages/InquirerPy/containers/message.py +42 -0
  30. .venv/Lib/site-packages/InquirerPy/containers/spinner.py +108 -0
  31. .venv/Lib/site-packages/InquirerPy/containers/validation.py +60 -0
  32. .venv/Lib/site-packages/InquirerPy/enum.py +7 -0
  33. .venv/Lib/site-packages/InquirerPy/exceptions.py +25 -0
  34. .venv/Lib/site-packages/InquirerPy/inquirer.py +17 -0
  35. .venv/Lib/site-packages/InquirerPy/prompts/__init__.py +11 -0
  36. .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/__init__.cpython-310.pyc +0 -0
  37. .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/checkbox.cpython-310.pyc +0 -0
  38. .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/confirm.cpython-310.pyc +0 -0
  39. .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/expand.cpython-310.pyc +0 -0
  40. .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/filepath.cpython-310.pyc +0 -0
  41. .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/fuzzy.cpython-310.pyc +0 -0
  42. .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/input.cpython-310.pyc +0 -0
  43. .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/list.cpython-310.pyc +0 -0
  44. .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/number.cpython-310.pyc +0 -0
  45. .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/rawlist.cpython-310.pyc +0 -0
  46. .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/secret.cpython-310.pyc +0 -0
  47. .venv/Lib/site-packages/InquirerPy/prompts/checkbox.py +244 -0
  48. .venv/Lib/site-packages/InquirerPy/prompts/confirm.py +196 -0
  49. .venv/Lib/site-packages/InquirerPy/prompts/expand.py +458 -0
  50. .venv/Lib/site-packages/InquirerPy/prompts/filepath.py +192 -0
.gitattributes CHANGED
@@ -1,35 +1,68 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ .venv/Lib/site-packages/__pycache__/typing_extensions.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
37
+ .venv/Lib/site-packages/charset_normalizer/md__mypyc.cp310-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
38
+ .venv/Lib/site-packages/huggingface_hub/__pycache__/hf_api.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
39
+ .venv/Lib/site-packages/huggingface_hub/inference/__pycache__/_client.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
40
+ .venv/Lib/site-packages/huggingface_hub/inference/_generated/__pycache__/_async_client.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
41
+ .venv/Lib/site-packages/idna/__pycache__/idnadata.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
42
+ .venv/Lib/site-packages/idna/__pycache__/uts46data.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
43
+ .venv/Lib/site-packages/pip/_vendor/__pycache__/typing_extensions.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
44
+ .venv/Lib/site-packages/pip/_vendor/distlib/t64-arm.exe filter=lfs diff=lfs merge=lfs -text
45
+ .venv/Lib/site-packages/pip/_vendor/distlib/t64.exe filter=lfs diff=lfs merge=lfs -text
46
+ .venv/Lib/site-packages/pip/_vendor/distlib/w64-arm.exe filter=lfs diff=lfs merge=lfs -text
47
+ .venv/Lib/site-packages/pip/_vendor/distlib/w64.exe filter=lfs diff=lfs merge=lfs -text
48
+ .venv/Lib/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
49
+ .venv/Lib/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
50
+ .venv/Lib/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
51
+ .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_emoji_codes.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
52
+ .venv/Lib/site-packages/pkg_resources/__pycache__/__init__.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
53
+ .venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__pycache__/more.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
54
+ .venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/__pycache__/core.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
55
+ .venv/Lib/site-packages/setuptools/_vendor/more_itertools/__pycache__/more.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
56
+ .venv/Lib/site-packages/setuptools/_vendor/pyparsing/__pycache__/core.cpython-310.pyc filter=lfs diff=lfs merge=lfs -text
57
+ .venv/Lib/site-packages/setuptools/cli-arm64.exe filter=lfs diff=lfs merge=lfs -text
58
+ .venv/Lib/site-packages/setuptools/gui-arm64.exe filter=lfs diff=lfs merge=lfs -text
59
+ .venv/Lib/site-packages/yaml/_yaml.cp310-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
60
+ .venv/Scripts/huggingface-cli.exe filter=lfs diff=lfs merge=lfs -text
61
+ .venv/Scripts/normalizer.exe filter=lfs diff=lfs merge=lfs -text
62
+ .venv/Scripts/pip.exe filter=lfs diff=lfs merge=lfs -text
63
+ .venv/Scripts/pip3.10.exe filter=lfs diff=lfs merge=lfs -text
64
+ .venv/Scripts/pip3.exe filter=lfs diff=lfs merge=lfs -text
65
+ .venv/Scripts/python.exe filter=lfs diff=lfs merge=lfs -text
66
+ .venv/Scripts/pythonw.exe filter=lfs diff=lfs merge=lfs -text
67
+ .venv/Scripts/tiny-agents.exe filter=lfs diff=lfs merge=lfs -text
68
+ .venv/Scripts/tqdm.exe filter=lfs diff=lfs merge=lfs -text
.venv/.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ *
.venv/Lib/site-packages/InquirerPy/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ from InquirerPy.resolver import prompt, prompt_async
2
+ from InquirerPy.utils import get_style
.venv/Lib/site-packages/InquirerPy/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (295 Bytes). View file
 
.venv/Lib/site-packages/InquirerPy/__pycache__/enum.cpython-310.pyc ADDED
Binary file (513 Bytes). View file
 
.venv/Lib/site-packages/InquirerPy/__pycache__/exceptions.cpython-310.pyc ADDED
Binary file (1.19 kB). View file
 
.venv/Lib/site-packages/InquirerPy/__pycache__/inquirer.cpython-310.pyc ADDED
Binary file (895 Bytes). View file
 
.venv/Lib/site-packages/InquirerPy/__pycache__/resolver.cpython-310.pyc ADDED
Binary file (6.98 kB). View file
 
.venv/Lib/site-packages/InquirerPy/__pycache__/separator.cpython-310.pyc ADDED
Binary file (1.18 kB). View file
 
.venv/Lib/site-packages/InquirerPy/__pycache__/utils.cpython-310.pyc ADDED
Binary file (9.27 kB). View file
 
.venv/Lib/site-packages/InquirerPy/__pycache__/validator.cpython-310.pyc ADDED
Binary file (6.1 kB). View file
 
.venv/Lib/site-packages/InquirerPy/base/__init__.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module contains base class for prompts.
2
+
3
+ BaseSimplePrompt ← InputPrompt ← SecretPrompt ...
4
+
5
+ BaseComplexPrompt
6
+
7
+ BaseListPrompt ← FuzzyPrompt
8
+
9
+ ListPrompt ← ExpandPrompt ...
10
+ """
11
+
12
+ from .complex import BaseComplexPrompt, FakeDocument
13
+ from .control import Choice, InquirerPyUIListControl
14
+ from .list import BaseListPrompt
15
+ from .simple import BaseSimplePrompt
.venv/Lib/site-packages/InquirerPy/base/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (643 Bytes). View file
 
.venv/Lib/site-packages/InquirerPy/base/__pycache__/complex.cpython-310.pyc ADDED
Binary file (10.6 kB). View file
 
.venv/Lib/site-packages/InquirerPy/base/__pycache__/control.cpython-310.pyc ADDED
Binary file (7.74 kB). View file
 
.venv/Lib/site-packages/InquirerPy/base/__pycache__/list.cpython-310.pyc ADDED
Binary file (6.95 kB). View file
 
.venv/Lib/site-packages/InquirerPy/base/__pycache__/simple.cpython-310.pyc ADDED
Binary file (12.9 kB). View file
 
.venv/Lib/site-packages/InquirerPy/base/complex.py ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Contains the interface class :class:`.BaseComplexPrompt` for more complex prompts and the mocked document class :class:`.FakeDocument`."""
2
+ import shutil
3
+ from dataclasses import dataclass
4
+ from typing import Any, Callable, List, Optional, Tuple, Union
5
+
6
+ from prompt_toolkit.application import Application
7
+ from prompt_toolkit.enums import EditingMode
8
+ from prompt_toolkit.filters.base import Condition, FilterOrBool
9
+ from prompt_toolkit.key_binding.key_bindings import KeyHandlerCallable
10
+ from prompt_toolkit.keys import Keys
11
+
12
+ from InquirerPy.base.simple import BaseSimplePrompt
13
+ from InquirerPy.enum import INQUIRERPY_KEYBOARD_INTERRUPT
14
+ from InquirerPy.utils import (
15
+ InquirerPySessionResult,
16
+ InquirerPyStyle,
17
+ InquirerPyValidate,
18
+ )
19
+
20
+
21
+ @dataclass
22
+ class FakeDocument:
23
+ """A fake `prompt_toolkit` document class.
24
+
25
+ Work around to allow non-buffer type :class:`~prompt_toolkit.layout.UIControl` to use
26
+ :class:`~prompt_toolkit.validation.Validator`.
27
+
28
+ Args:
29
+ text: Content to be validated.
30
+ cursor_position: Fake cursor position.
31
+ """
32
+
33
+ text: str
34
+ cursor_position: int = 0
35
+
36
+
37
+ class BaseComplexPrompt(BaseSimplePrompt):
38
+ """A base class to create a more complex prompt that will involve :class:`~prompt_toolkit.application.Application`.
39
+
40
+ Note:
41
+ This class does not create :class:`~prompt_toolkit.layout.Layout` nor :class:`~prompt_toolkit.application.Application`,
42
+ it only contains the necessary attributes and helper functions to be consumed.
43
+
44
+ Note:
45
+ Use :class:`~InquirerPy.base.BaseListPrompt` to create a complex list prompt which involves multiple choices. It has
46
+ more methods and helper function implemented.
47
+
48
+ See Also:
49
+ :class:`~InquirerPy.base.BaseListPrompt`
50
+ :class:`~InquirerPy.prompts.fuzzy.FuzzyPrompt`
51
+ """
52
+
53
+ def __init__(
54
+ self,
55
+ message: Union[str, Callable[[InquirerPySessionResult], str]],
56
+ style: Optional[InquirerPyStyle] = None,
57
+ border: bool = False,
58
+ vi_mode: bool = False,
59
+ qmark: str = "?",
60
+ amark: str = "?",
61
+ instruction: str = "",
62
+ long_instruction: str = "",
63
+ transformer: Optional[Callable[[Any], Any]] = None,
64
+ filter: Optional[Callable[[Any], Any]] = None,
65
+ validate: Optional[InquirerPyValidate] = None,
66
+ invalid_message: str = "Invalid input",
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
+ super().__init__(
74
+ message=message,
75
+ style=style,
76
+ vi_mode=vi_mode,
77
+ qmark=qmark,
78
+ amark=amark,
79
+ instruction=instruction,
80
+ transformer=transformer,
81
+ filter=filter,
82
+ invalid_message=invalid_message,
83
+ validate=validate,
84
+ wrap_lines=wrap_lines,
85
+ raise_keyboard_interrupt=raise_keyboard_interrupt,
86
+ mandatory=mandatory,
87
+ mandatory_message=mandatory_message,
88
+ session_result=session_result,
89
+ )
90
+ self._invalid_message = invalid_message
91
+ self._rendered = False
92
+ self._invalid = False
93
+ self._loading = False
94
+ self._application: Application
95
+ self._long_instruction = long_instruction
96
+ self._border = border
97
+ self._height_offset = 2 # prev prompt result + current prompt question
98
+ if self._border:
99
+ self._height_offset += 2
100
+ if self._long_instruction:
101
+ self._height_offset += 1
102
+ self._validation_window_bottom_offset = 0 if not self._long_instruction else 1
103
+ if self._wrap_lines:
104
+ self._validation_window_bottom_offset += (
105
+ self.extra_long_instruction_line_count
106
+ )
107
+
108
+ self._is_vim_edit = Condition(lambda: self._editing_mode == EditingMode.VI)
109
+ self._is_invalid = Condition(lambda: self._invalid)
110
+ self._is_displaying_long_instruction = Condition(
111
+ lambda: self._long_instruction != ""
112
+ )
113
+
114
+ def _redraw(self) -> None:
115
+ """Redraw the application UI."""
116
+ self._application.invalidate()
117
+
118
+ def register_kb(
119
+ self, *keys: Union[Keys, str], filter: FilterOrBool = True
120
+ ) -> Callable[[KeyHandlerCallable], KeyHandlerCallable]:
121
+ """Decorate keybinding registration function.
122
+
123
+ Ensure that the `invalid` state is cleared on next keybinding entered.
124
+ """
125
+ kb_dec = super().register_kb(*keys, filter=filter)
126
+
127
+ def decorator(func: KeyHandlerCallable) -> KeyHandlerCallable:
128
+ @kb_dec
129
+ def executable(event):
130
+ if self._invalid:
131
+ self._invalid = False
132
+ func(event)
133
+
134
+ return executable
135
+
136
+ return decorator
137
+
138
+ def _exception_handler(self, _, context) -> None:
139
+ """Set exception handler for the event loop.
140
+
141
+ Skip the question and raise exception.
142
+
143
+ Args:
144
+ loop: Current event loop.
145
+ context: Exception context.
146
+ """
147
+ self._status["answered"] = True
148
+ self._status["result"] = INQUIRERPY_KEYBOARD_INTERRUPT
149
+ self._status["skipped"] = True
150
+ self._application.exit(exception=context["exception"])
151
+
152
+ def _after_render(self, app: Optional[Application]) -> None:
153
+ """Run after the :class:`~prompt_toolkit.application.Application` is rendered/updated.
154
+
155
+ Since this function is fired up on each render, adding a check on `self._rendered` to
156
+ process logics that should only run once.
157
+
158
+ Set event loop exception handler here, since its guaranteed that the event loop is running
159
+ in `_after_render`.
160
+ """
161
+ if not self._rendered:
162
+ self._rendered = True
163
+
164
+ self._keybinding_factory()
165
+ self._on_rendered(app)
166
+
167
+ def _set_error(self, message: str) -> None:
168
+ """Set error message and set invalid state.
169
+
170
+ Args:
171
+ message: Error message to display.
172
+ """
173
+ self._invalid_message = message
174
+ self._invalid = True
175
+
176
+ def _get_error_message(self) -> List[Tuple[str, str]]:
177
+ """Obtain the error message dynamically.
178
+
179
+ Returns:
180
+ FormattedText in list of tuple format.
181
+ """
182
+ return [
183
+ (
184
+ "class:validation-toolbar",
185
+ self._invalid_message,
186
+ )
187
+ ]
188
+
189
+ def _on_rendered(self, _: Optional[Application]) -> None:
190
+ """Run once after the UI is rendered. Acts like `ComponentDidMount`."""
191
+ pass
192
+
193
+ def _get_prompt_message(self) -> List[Tuple[str, str]]:
194
+ """Get the prompt message to display.
195
+
196
+ Returns:
197
+ Formatted text in list of tuple format.
198
+ """
199
+ pre_answer = (
200
+ "class:instruction",
201
+ " %s " % self.instruction if self.instruction else " ",
202
+ )
203
+ post_answer = ("class:answer", " %s" % self.status["result"])
204
+ return super()._get_prompt_message(pre_answer, post_answer)
205
+
206
+ def _run(self) -> Any:
207
+ """Run the application."""
208
+ return self.application.run()
209
+
210
+ async def _run_async(self) -> None:
211
+ """Run the application asynchronously."""
212
+ return await self.application.run_async()
213
+
214
+ @property
215
+ def application(self) -> Application:
216
+ """Get the application.
217
+
218
+ :class:`.BaseComplexPrompt` requires :attr:`.BaseComplexPrompt._application` to be defined since this class
219
+ doesn't implement :class:`~prompt_toolkit.layout.Layout` and :class:`~prompt_toolkit.application.Application`.
220
+
221
+ Raises:
222
+ NotImplementedError: When `self._application` is not defined.
223
+ """
224
+ if not self._application:
225
+ raise NotImplementedError
226
+ return self._application
227
+
228
+ @application.setter
229
+ def application(self, value: Application) -> None:
230
+ self._application = value
231
+
232
+ @property
233
+ def height_offset(self) -> int:
234
+ """int: Height offset to apply."""
235
+ if not self._wrap_lines:
236
+ return self._height_offset
237
+ return self.extra_line_count + self._height_offset
238
+
239
+ @property
240
+ def total_message_length(self) -> int:
241
+ """int: Total length of the message."""
242
+ total_message_length = 0
243
+ if self._qmark:
244
+ total_message_length += len(self._qmark)
245
+ total_message_length += 1 # Extra space if qmark is present
246
+ total_message_length += len(str(self._message))
247
+ total_message_length += 1 # Extra space between message and instruction
248
+ total_message_length += len(str(self._instruction))
249
+ if self._instruction:
250
+ total_message_length += 1 # Extra space behind the instruction
251
+ return total_message_length
252
+
253
+ @property
254
+ def extra_message_line_count(self) -> int:
255
+ """int: Get the extra lines created caused by line wrapping.
256
+
257
+ Minus 1 on the totoal message length as we only want the extra line.
258
+ 24 // 24 will equal to 1 however we only want the value to be 1 when we have 25 char
259
+ which will create an extra line.
260
+ """
261
+ term_width, _ = shutil.get_terminal_size()
262
+ return (self.total_message_length - 1) // term_width
263
+
264
+ @property
265
+ def extra_long_instruction_line_count(self) -> int:
266
+ """int: Get the extra lines created caused by line wrapping.
267
+
268
+ See Also:
269
+ :attr:`.BaseComplexPrompt.extra_message_line_count`
270
+ """
271
+ if self._long_instruction:
272
+ term_width, _ = shutil.get_terminal_size()
273
+ return (len(self._long_instruction) - 1) // term_width
274
+ else:
275
+ return 0
276
+
277
+ @property
278
+ def extra_line_count(self) -> int:
279
+ """Get the extra lines created caused by line wrapping.
280
+
281
+ Used mainly to calculate how much additional offset should be applied when getting
282
+ the height.
283
+
284
+ Returns:
285
+ Total extra lines created due to line wrapping.
286
+ """
287
+ result = 0
288
+
289
+ # message wrap
290
+ result += self.extra_message_line_count
291
+ # long instruction wrap
292
+ result += self.extra_long_instruction_line_count
293
+
294
+ return result
.venv/Lib/site-packages/InquirerPy/base/control.py ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Contains the content control class :class:`.InquirerPyUIListControl`."""
2
+ from abc import abstractmethod
3
+ from dataclasses import asdict, dataclass
4
+ from typing import Any, Callable, Dict, List, Optional, Tuple, cast
5
+
6
+ from prompt_toolkit.layout.controls import FormattedTextControl
7
+
8
+ from InquirerPy.exceptions import InvalidArgument, RequiredKeyNotFound
9
+ from InquirerPy.separator import Separator
10
+ from InquirerPy.utils import InquirerPyListChoices, InquirerPySessionResult
11
+
12
+ __all__ = ["Choice", "InquirerPyUIListControl"]
13
+
14
+
15
+ @dataclass
16
+ class Choice:
17
+ """Class to create choices for list type prompts.
18
+
19
+ A simple dataclass that can be used as an alternate to using :class:`dict`
20
+ when working with choices.
21
+
22
+ Args:
23
+ value: The value of the choice when user selects this choice.
24
+ name: The value that should be presented to the user prior/after selection of the choice.
25
+ This value is optional, if not provided, it will fallback to the string representation of `value`.
26
+ enabled: Indicates if the choice should be pre-selected.
27
+ This only has effects when the prompt has `multiselect` enabled.
28
+ """
29
+
30
+ value: Any
31
+ name: Optional[str] = None
32
+ enabled: bool = False
33
+
34
+ def __post_init__(self):
35
+ """Assign strinify value to name if not present."""
36
+ if self.name is None:
37
+ self.name = str(self.value)
38
+
39
+
40
+ class InquirerPyUIListControl(FormattedTextControl):
41
+ """A base class to create :class:`~prompt_toolkit.layout.UIControl` to display list type contents.
42
+
43
+ Args:
44
+ choices(InquirerPyListChoices): List of choices to display as the content.
45
+ Can also be a callable or async callable that returns a list of choices.
46
+ default: Default value, this will affect the cursor position.
47
+ multiselect: Indicate if the current prompt has `multiselect` enabled.
48
+ session_result: Current session result.
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ choices: InquirerPyListChoices,
54
+ default: Any = None,
55
+ multiselect: bool = False,
56
+ session_result: Optional[InquirerPySessionResult] = None,
57
+ ) -> None:
58
+ self._session_result = session_result or {}
59
+ self._selected_choice_index: int = 0
60
+ self._choice_func = None
61
+ self._multiselect = multiselect
62
+ self._default = (
63
+ default
64
+ if not isinstance(default, Callable)
65
+ else cast(Callable, default)(self._session_result)
66
+ )
67
+ self._raw_choices = (
68
+ choices
69
+ if not isinstance(choices, Callable)
70
+ else cast(Callable, choices)(self._session_result)
71
+ )
72
+ self._choices = self._get_choices(self._raw_choices, self._default)
73
+ self._safety_check()
74
+ self._format_choices()
75
+ super().__init__(self._get_formatted_choices)
76
+
77
+ def _get_choices(self, choices: List[Any], default: Any) -> List[Dict[str, Any]]:
78
+ """Process the raw user input choices and format it into dictionary.
79
+
80
+ Args:
81
+ choices: List of chices to display.
82
+ default: Default value, this will affect the :attr:`.InquirerPyUIListControl.selected_choice_index`
83
+
84
+ Returns:
85
+ List of choices.
86
+
87
+ Raises:
88
+ RequiredKeyNotFound: When the provided choice is missing the `name` or `value` key.
89
+ """
90
+ processed_choices: List[Dict[str, Any]] = []
91
+ try:
92
+ for index, choice in enumerate(choices, start=0):
93
+ if isinstance(choice, dict):
94
+ if choice["value"] == default:
95
+ self.selected_choice_index = index
96
+ processed_choices.append(
97
+ {
98
+ "name": str(choice["name"]),
99
+ "value": choice["value"],
100
+ "enabled": choice.get("enabled", False)
101
+ if self._multiselect
102
+ else False,
103
+ }
104
+ )
105
+ elif isinstance(choice, Separator):
106
+ if self.selected_choice_index == index:
107
+ self.selected_choice_index = (
108
+ self.selected_choice_index + 1
109
+ ) % len(choices)
110
+ processed_choices.append(
111
+ {"name": str(choice), "value": choice, "enabled": False}
112
+ )
113
+ elif isinstance(choice, Choice):
114
+ dict_choice = asdict(choice)
115
+ if dict_choice["value"] == default:
116
+ self.selected_choice_index = index
117
+ if not self._multiselect:
118
+ dict_choice["enabled"] = False
119
+ processed_choices.append(dict_choice)
120
+ else:
121
+ if choice == default:
122
+ self.selected_choice_index = index
123
+ processed_choices.append(
124
+ {"name": str(choice), "value": choice, "enabled": False}
125
+ )
126
+ except KeyError:
127
+ raise RequiredKeyNotFound(
128
+ "dictionary type of choice require a 'name' key and a 'value' key"
129
+ )
130
+ return processed_choices
131
+
132
+ @property
133
+ def selected_choice_index(self) -> int:
134
+ """int: Current highlighted index."""
135
+ return self._selected_choice_index
136
+
137
+ @selected_choice_index.setter
138
+ def selected_choice_index(self, value: int) -> None:
139
+ self._selected_choice_index = value
140
+
141
+ @property
142
+ def choices(self) -> List[Dict[str, Any]]:
143
+ """List[Dict[str, Any]]: Get all processed choices."""
144
+ return self._choices
145
+
146
+ @choices.setter
147
+ def choices(self, value: List[Dict[str, Any]]) -> None:
148
+ self._choices = value
149
+
150
+ def _safety_check(self) -> None:
151
+ """Validate processed choices.
152
+
153
+ Check if the choices are empty or if it only contains :class:`~InquirerPy.separator.Separator`.
154
+ """
155
+ if not self.choices:
156
+ raise InvalidArgument("argument choices cannot be empty")
157
+ should_proceed: bool = False
158
+ for choice in self.choices:
159
+ if not isinstance(choice["value"], Separator):
160
+ should_proceed = True
161
+ break
162
+ if not should_proceed:
163
+ raise InvalidArgument(
164
+ "argument choices should contain choices other than separator"
165
+ )
166
+
167
+ def _get_formatted_choices(self) -> List[Tuple[str, str]]:
168
+ """Get all choices in formatted text format.
169
+
170
+ Returns:
171
+ List of choices in formatted text form.
172
+ """
173
+ display_choices = []
174
+
175
+ for index, choice in enumerate(self.choices):
176
+ if index == self.selected_choice_index:
177
+ display_choices += self._get_hover_text(choice)
178
+ else:
179
+ display_choices += self._get_normal_text(choice)
180
+ display_choices.append(("", "\n"))
181
+ if display_choices:
182
+ display_choices.pop()
183
+ return display_choices
184
+
185
+ def _format_choices(self) -> None:
186
+ """Perform post processing on the choices.
187
+
188
+ Additional customisation to the choices after :meth:`.InquirerPyUIListControl._get_choices` call.
189
+ """
190
+ pass
191
+
192
+ @abstractmethod
193
+ def _get_hover_text(self, choice) -> List[Tuple[str, str]]:
194
+ """Generate the formatted text for hovered choice.
195
+
196
+ Returns:
197
+ Formatted text in list of tuple format.
198
+ """
199
+ pass
200
+
201
+ @abstractmethod
202
+ def _get_normal_text(self, choice) -> List[Tuple[str, str]]:
203
+ """Generate the formatted text for non-hovered choices.
204
+
205
+ Returns:
206
+ Formatted text in list of tuple format.
207
+ """
208
+ pass
209
+
210
+ @property
211
+ def choice_count(self) -> int:
212
+ """int: Total count of choices."""
213
+ return len(self.choices)
214
+
215
+ @property
216
+ def selection(self) -> Dict[str, Any]:
217
+ """Dict[str, Any]: Current selected choice."""
218
+ return self.choices[self.selected_choice_index]
219
+
220
+ @property
221
+ def loading(self) -> bool:
222
+ """bool: Indicate if the content control is loading."""
223
+ return self._loading
224
+
225
+ @loading.setter
226
+ def loading(self, value: bool) -> None:
227
+ self._loading = value
.venv/Lib/site-packages/InquirerPy/base/list.py ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Contains the base class :class:`.BaseListPrompt` which can be used to create a prompt involving choices."""
2
+ from abc import abstractmethod
3
+ from typing import Any, Callable, List, Optional
4
+
5
+ from prompt_toolkit.filters.base import Condition
6
+ from prompt_toolkit.keys import Keys
7
+
8
+ from InquirerPy.base.complex import BaseComplexPrompt
9
+ from InquirerPy.base.control import InquirerPyUIListControl
10
+ from InquirerPy.separator import Separator
11
+ from InquirerPy.utils import (
12
+ InquirerPyKeybindings,
13
+ InquirerPyMessage,
14
+ InquirerPySessionResult,
15
+ InquirerPyStyle,
16
+ InquirerPyValidate,
17
+ )
18
+
19
+
20
+ class BaseListPrompt(BaseComplexPrompt):
21
+ """A base class to create a complex prompt involving choice selections (i.e. list) using `prompt_toolkit` Application.
22
+
23
+ Note:
24
+ This class does not create :class:`~prompt_toolkit.layout.Layout` nor :class:`~prompt_toolkit.application.Application`,
25
+ it only contains the necessary attributes and helper functions to be consumed.
26
+
27
+ See Also:
28
+ :class:`~InquirerPy.prompts.list.ListPrompt`
29
+ :class:`~InquirerPy.prompts.fuzzy.FuzzyPrompt`
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ message: InquirerPyMessage,
35
+ style: Optional[InquirerPyStyle] = None,
36
+ vi_mode: bool = False,
37
+ qmark: str = "?",
38
+ amark: str = "?",
39
+ instruction: str = "",
40
+ long_instruction: str = "",
41
+ border: bool = False,
42
+ transformer: Optional[Callable[[Any], Any]] = None,
43
+ filter: Optional[Callable[[Any], Any]] = None,
44
+ validate: Optional[InquirerPyValidate] = None,
45
+ invalid_message: str = "Invalid input",
46
+ multiselect: bool = False,
47
+ keybindings: Optional[InquirerPyKeybindings] = None,
48
+ cycle: bool = True,
49
+ wrap_lines: bool = True,
50
+ raise_keyboard_interrupt: bool = True,
51
+ mandatory: bool = True,
52
+ mandatory_message: str = "Mandatory prompt",
53
+ session_result: Optional[InquirerPySessionResult] = None,
54
+ ) -> None:
55
+ super().__init__(
56
+ message=message,
57
+ style=style,
58
+ border=border,
59
+ vi_mode=vi_mode,
60
+ qmark=qmark,
61
+ amark=amark,
62
+ transformer=transformer,
63
+ filter=filter,
64
+ invalid_message=invalid_message,
65
+ validate=validate,
66
+ instruction=instruction,
67
+ long_instruction=long_instruction,
68
+ wrap_lines=wrap_lines,
69
+ raise_keyboard_interrupt=raise_keyboard_interrupt,
70
+ mandatory=mandatory,
71
+ mandatory_message=mandatory_message,
72
+ session_result=session_result,
73
+ )
74
+
75
+ self._content_control: InquirerPyUIListControl
76
+ self._multiselect = multiselect
77
+ self._is_multiselect = Condition(lambda: self._multiselect)
78
+ self._cycle = cycle
79
+
80
+ if not keybindings:
81
+ keybindings = {}
82
+
83
+ self.kb_maps = {
84
+ "down": [
85
+ {"key": "down"},
86
+ {"key": "c-n", "filter": ~self._is_vim_edit},
87
+ {"key": "j", "filter": self._is_vim_edit},
88
+ ],
89
+ "up": [
90
+ {"key": "up"},
91
+ {"key": "c-p", "filter": ~self._is_vim_edit},
92
+ {"key": "k", "filter": self._is_vim_edit},
93
+ ],
94
+ "toggle": [
95
+ {"key": "space"},
96
+ ],
97
+ "toggle-down": [
98
+ {"key": Keys.Tab},
99
+ ],
100
+ "toggle-up": [
101
+ {"key": Keys.BackTab},
102
+ ],
103
+ "toggle-all": [
104
+ {"key": "alt-r"},
105
+ {"key": "c-r"},
106
+ ],
107
+ "toggle-all-true": [
108
+ {"key": "alt-a"},
109
+ {"key": "c-a"},
110
+ ],
111
+ "toggle-all-false": [],
112
+ **keybindings,
113
+ }
114
+
115
+ self.kb_func_lookup = {
116
+ "down": [{"func": self._handle_down}],
117
+ "up": [{"func": self._handle_up}],
118
+ "toggle": [{"func": self._handle_toggle_choice}],
119
+ "toggle-down": [
120
+ {"func": self._handle_toggle_choice},
121
+ {"func": self._handle_down},
122
+ ],
123
+ "toggle-up": [
124
+ {"func": self._handle_toggle_choice},
125
+ {"func": self._handle_up},
126
+ ],
127
+ "toggle-all": [{"func": self._handle_toggle_all}],
128
+ "toggle-all-true": [{"func": self._handle_toggle_all, "args": [True]}],
129
+ "toggle-all-false": [{"func": self._handle_toggle_all, "args": [False]}],
130
+ }
131
+
132
+ @property
133
+ def content_control(self) -> InquirerPyUIListControl:
134
+ """Get the content controller object.
135
+
136
+ Needs to be an instance of :class:`~InquirerPy.base.control.InquirerPyUIListControl`.
137
+
138
+ Each :class:`.BaseComplexPrompt` requires a `content_control` to display custom
139
+ contents for the prompt.
140
+
141
+ Raises:
142
+ NotImplementedError: When `self._content_control` is not found.
143
+ """
144
+ if not self._content_control:
145
+ raise NotImplementedError
146
+ return self._content_control
147
+
148
+ @content_control.setter
149
+ def content_control(self, value: InquirerPyUIListControl) -> None:
150
+ self._content_control = value
151
+
152
+ @property
153
+ def result_name(self) -> Any:
154
+ """Get the result value that should be printed to the terminal.
155
+
156
+ In multiselect scenario, return result as a list.
157
+ """
158
+ if self._multiselect:
159
+ return [choice["name"] for choice in self.selected_choices]
160
+ else:
161
+ try:
162
+ return self.content_control.selection["name"]
163
+ except IndexError:
164
+ return ""
165
+
166
+ @property
167
+ def result_value(self) -> Any:
168
+ """Get the result value that should return to the user.
169
+
170
+ In multiselect scenario, return result as a list.
171
+ """
172
+ if self._multiselect:
173
+ return [choice["value"] for choice in self.selected_choices]
174
+ else:
175
+ try:
176
+ return self.content_control.selection["value"]
177
+ except IndexError:
178
+ return ""
179
+
180
+ @property
181
+ def selected_choices(self) -> List[Any]:
182
+ """List[Any]: Get all user selected choices."""
183
+
184
+ def filter_choice(choice):
185
+ return not isinstance(choice, Separator) and choice["enabled"]
186
+
187
+ return list(filter(filter_choice, self.content_control.choices))
188
+
189
+ def _handle_down(self, _) -> bool:
190
+ """Handle event when user attempts to move down.
191
+
192
+ Returns:
193
+ Boolean indicating if the action hits the cap.
194
+ """
195
+ if self._cycle:
196
+ self.content_control.selected_choice_index = (
197
+ self.content_control.selected_choice_index + 1
198
+ ) % self.content_control.choice_count
199
+ return False
200
+ else:
201
+ self.content_control.selected_choice_index += 1
202
+ if (
203
+ self.content_control.selected_choice_index
204
+ >= self.content_control.choice_count
205
+ ):
206
+ self.content_control.selected_choice_index = (
207
+ self.content_control.choice_count - 1
208
+ )
209
+ return True
210
+ return False
211
+
212
+ def _handle_up(self, _) -> bool:
213
+ """Handle event when user attempts to move up.
214
+
215
+ Returns:
216
+ Boolean indicating if the action hits the cap.
217
+ """
218
+ if self._cycle:
219
+ self.content_control.selected_choice_index = (
220
+ self.content_control.selected_choice_index - 1
221
+ ) % self.content_control.choice_count
222
+ return False
223
+ else:
224
+ self.content_control.selected_choice_index -= 1
225
+ if self.content_control.selected_choice_index < 0:
226
+ self.content_control.selected_choice_index = 0
227
+ return True
228
+ return False
229
+
230
+ @abstractmethod
231
+ def _handle_toggle_choice(self, event) -> None:
232
+ """Handle event when user attempting to toggle the state of the chocie."""
233
+ pass
234
+
235
+ @abstractmethod
236
+ def _handle_toggle_all(self, event, value: bool) -> None:
237
+ """Handle event when user attempting to alter the state of all choices."""
238
+ pass
.venv/Lib/site-packages/InquirerPy/base/simple.py ADDED
@@ -0,0 +1,378 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Contains the base class :class:`.BaseSimplePrompt`."""
2
+ import os
3
+ import re
4
+ from abc import ABC, abstractmethod
5
+ from typing import (
6
+ TYPE_CHECKING,
7
+ Any,
8
+ Callable,
9
+ Dict,
10
+ List,
11
+ Optional,
12
+ Tuple,
13
+ Union,
14
+ cast,
15
+ )
16
+
17
+ from prompt_toolkit.enums import EditingMode
18
+ from prompt_toolkit.filters.base import Condition, FilterOrBool
19
+ from prompt_toolkit.key_binding.key_bindings import KeyBindings, KeyHandlerCallable
20
+ from prompt_toolkit.keys import Keys
21
+ from prompt_toolkit.styles.style import Style
22
+ from prompt_toolkit.validation import Validator
23
+
24
+ from InquirerPy.enum import INQUIRERPY_KEYBOARD_INTERRUPT
25
+ from InquirerPy.exceptions import RequiredKeyNotFound
26
+ from InquirerPy.utils import (
27
+ InquirerPyMessage,
28
+ InquirerPySessionResult,
29
+ InquirerPyStyle,
30
+ InquirerPyValidate,
31
+ get_style,
32
+ )
33
+
34
+ if TYPE_CHECKING:
35
+ from prompt_toolkit.key_binding.key_processor import KeyPressEvent
36
+
37
+
38
+ class BaseSimplePrompt(ABC):
39
+ """The base class to create a simple terminal input prompt.
40
+
41
+ Note:
42
+ No actual :class:`~prompt_toolkit.application.Application` is created by this class.
43
+ This class only creates some common interface and attributes that can be easily used
44
+ by `prompt_toolkit`.
45
+
46
+ To have a functional prompt, you'll at least have to implement the :meth:`.BaseSimplePrompt._run`
47
+ and :meth:`.BaseSimplePrompt._get_prompt_message`.
48
+
49
+ See Also:
50
+ :class:`~InquirerPy.prompts.input.InputPrompt`
51
+ """
52
+
53
+ def __init__(
54
+ self,
55
+ message: InquirerPyMessage,
56
+ style: Optional[InquirerPyStyle] = None,
57
+ vi_mode: bool = False,
58
+ qmark: str = "?",
59
+ amark: str = "?",
60
+ instruction: str = "",
61
+ validate: Optional[InquirerPyValidate] = None,
62
+ invalid_message: str = "Invalid input",
63
+ transformer: Optional[Callable[[Any], Any]] = None,
64
+ filter: Optional[Callable[[Any], Any]] = None,
65
+ default: Any = "",
66
+ wrap_lines: bool = True,
67
+ raise_keyboard_interrupt: bool = True,
68
+ mandatory: bool = True,
69
+ mandatory_message: str = "Mandatory prompt",
70
+ session_result: Optional[InquirerPySessionResult] = None,
71
+ ) -> None:
72
+ self._mandatory = mandatory
73
+ self._mandatory_message = mandatory_message
74
+ self._result = session_result or {}
75
+ self._message = (
76
+ message
77
+ if not isinstance(message, Callable)
78
+ else cast(Callable, message)(self._result)
79
+ )
80
+ self._instruction = instruction
81
+ self._default = (
82
+ default if not isinstance(default, Callable) else default(self._result)
83
+ )
84
+ self._style = Style.from_dict(style.dict if style else get_style().dict)
85
+ self._qmark = qmark
86
+ self._amark = amark
87
+ self._status = {"answered": False, "result": None, "skipped": False}
88
+ self._kb = KeyBindings()
89
+ self._lexer = "class:input"
90
+ self._transformer = transformer
91
+ self._filter = filter
92
+ self._wrap_lines = wrap_lines
93
+ self._editing_mode = (
94
+ EditingMode.VI
95
+ if vi_mode or bool(os.getenv("INQUIRERPY_VI_MODE", False))
96
+ else EditingMode.EMACS
97
+ )
98
+ if isinstance(validate, Validator):
99
+ self._validator = validate
100
+ else:
101
+ self._validator = Validator.from_callable(
102
+ validate if validate else lambda _: True,
103
+ invalid_message,
104
+ move_cursor_to_end=True,
105
+ )
106
+ self._raise_kbi = not os.getenv(
107
+ "INQUIRERPY_NO_RAISE_KBI", not raise_keyboard_interrupt
108
+ )
109
+ self._is_rasing_kbi = Condition(lambda: self._raise_kbi)
110
+
111
+ self._kb_maps = {
112
+ "answer": [{"key": Keys.Enter}],
113
+ "interrupt": [
114
+ {"key": "c-c", "filter": self._is_rasing_kbi},
115
+ {"key": "c-d", "filter": ~self._is_rasing_kbi},
116
+ ],
117
+ "skip": [{"key": "c-z"}, {"key": "c-c", "filter": ~self._is_rasing_kbi}],
118
+ }
119
+ self._kb_func_lookup = {
120
+ "answer": [{"func": self._handle_enter}],
121
+ "interrupt": [{"func": self._handle_interrupt}],
122
+ "skip": [{"func": self._handle_skip}],
123
+ }
124
+
125
+ def _keybinding_factory(self):
126
+ """Register all keybindings in `self._kb_maps`.
127
+
128
+ It's required to call this function at the end of prompt constructor if
129
+ it inherits from :class:`~InquirerPy.base.simple.BaseSimplePrompt` or
130
+ :class:`~InquirerPy.base.complex.BaseComplexPrompt`.
131
+ """
132
+
133
+ def _factory(keys, filter, action):
134
+ if action not in self.kb_func_lookup:
135
+ raise RequiredKeyNotFound(f"keybinding action {action} not found")
136
+ if not isinstance(keys, list):
137
+ keys = [keys]
138
+
139
+ @self.register_kb(*keys, filter=filter)
140
+ def _(event):
141
+ for method in self.kb_func_lookup[action]:
142
+ method["func"](event, *method.get("args", []))
143
+
144
+ for key, item in self.kb_maps.items():
145
+ if not isinstance(item, list):
146
+ item = [item]
147
+ for kb in item:
148
+ _factory(kb["key"], kb.get("filter", Condition(lambda: True)), key)
149
+
150
+ @abstractmethod
151
+ def _set_error(self, message: str) -> None:
152
+ """Set the error message for the prompt.
153
+
154
+ Args:
155
+ message: Error message to set.
156
+ """
157
+ pass
158
+
159
+ def _handle_skip(self, event: Optional["KeyPressEvent"]) -> None:
160
+ """Handle the event when attempting to skip a prompt.
161
+
162
+ Skip the prompt if the `_mandatory` field is False, otherwise
163
+ show an error message that the prompt cannot be skipped.
164
+ """
165
+ if not self._mandatory:
166
+ self.status["answered"] = True
167
+ self.status["skipped"] = True
168
+ self.status["result"] = None
169
+ if event:
170
+ event.app.exit(result=None)
171
+ else:
172
+ self._set_error(message=self._mandatory_message)
173
+
174
+ def _handle_interrupt(self, event: Optional["KeyPressEvent"]) -> None:
175
+ """Handle the event when a KeyboardInterrupt signal is sent."""
176
+ self.status["answered"] = True
177
+ self.status["result"] = INQUIRERPY_KEYBOARD_INTERRUPT
178
+ self.status["skipped"] = True
179
+ if event:
180
+ event.app.exit(result=INQUIRERPY_KEYBOARD_INTERRUPT)
181
+
182
+ @abstractmethod
183
+ def _handle_enter(self, event: Optional["KeyPressEvent"]) -> None:
184
+ """Handle the event when user attempt to answer the question."""
185
+ pass
186
+
187
+ @property
188
+ def status(self) -> Dict[str, Any]:
189
+ """Dict[str, Any]: Get current prompt status.
190
+
191
+ The status contains 3 keys: "answered" and "result".
192
+ answered: If the current prompt is answered.
193
+ result: The result of the user answer.
194
+ skipped: If the prompt is skipped.
195
+ """
196
+ return self._status
197
+
198
+ @status.setter
199
+ def status(self, value) -> None:
200
+ self._status = value
201
+
202
+ def register_kb(
203
+ self, *keys: Union[Keys, str], filter: FilterOrBool = True, **kwargs
204
+ ) -> Callable[[KeyHandlerCallable], KeyHandlerCallable]:
205
+ """Keybinding registration decorator.
206
+
207
+ This decorator wraps around the :meth:`prompt_toolkit.key_binding.KeyBindings.add` with
208
+ added feature to process `alt` realted keybindings.
209
+
210
+ By default, `prompt_toolkit` doesn't process `alt` related keybindings,
211
+ it requires `alt-ANY` to `escape` + `ANY`.
212
+
213
+ Args:
214
+ keys: The keys to bind that can trigger the function.
215
+ filter: :class:`~prompt_toolkit.filter.Condition` to indicate if this keybinding should be active.
216
+
217
+ Returns:
218
+ A decorator that should be applied to the function thats intended to be active when the keys
219
+ are pressed.
220
+
221
+ Examples:
222
+ >>> @self.register_kb("alt-j")
223
+ ... def test(event):
224
+ ... pass
225
+ """
226
+ alt_pattern = re.compile(r"^alt-(.*)")
227
+
228
+ def decorator(func: KeyHandlerCallable) -> KeyHandlerCallable:
229
+ formatted_keys = []
230
+ for key in keys:
231
+ match = alt_pattern.match(key)
232
+ if match:
233
+ formatted_keys.append("escape")
234
+ formatted_keys.append(match.group(1))
235
+ else:
236
+ formatted_keys.append(key)
237
+
238
+ @self._kb.add(*formatted_keys, filter=filter, **kwargs)
239
+ def executable(event) -> None:
240
+ func(event)
241
+
242
+ return executable
243
+
244
+ return decorator
245
+
246
+ @abstractmethod
247
+ def _get_prompt_message(
248
+ self, pre_answer: Tuple[str, str], post_answer: Tuple[str, str]
249
+ ) -> List[Tuple[str, str]]:
250
+ """Get the question message in formatted text form to display in the prompt.
251
+
252
+ This function is mainly used to render the question message dynamically based
253
+ on the current status (answered or not answered) of the prompt.
254
+
255
+ Note:
256
+ The function requires implementation when inheriting :class:`.BaseSimplePrompt`.
257
+ You should call `super()._get_prompt_message(pre_answer, post_answer)` in
258
+ the implemented `_get_prompt_message`.
259
+
260
+ Args:
261
+ pre_answer: The message to display before the question is answered.
262
+ post_answer: The information to display after the question is answered.
263
+
264
+ Returns:
265
+ Formatted text in list of tuple format.
266
+ """
267
+ display_message = []
268
+ if self.status["skipped"]:
269
+ display_message.append(("class:skipped", self._qmark))
270
+ display_message.append(
271
+ ("class:skipped", "%s%s " % (" " if self._qmark else "", self._message))
272
+ )
273
+ elif self.status["answered"]:
274
+ display_message.append(("class:answermark", self._amark))
275
+ display_message.append(
276
+ (
277
+ "class:answered_question",
278
+ "%s%s" % (" " if self._amark else "", self._message),
279
+ )
280
+ )
281
+ display_message.append(
282
+ post_answer
283
+ if not self._transformer
284
+ else (
285
+ "class:answer",
286
+ " %s" % self._transformer(self.status["result"]),
287
+ )
288
+ )
289
+ else:
290
+ display_message.append(("class:questionmark", self._qmark))
291
+ display_message.append(
292
+ (
293
+ "class:question",
294
+ "%s%s" % (" " if self._qmark else "", self._message),
295
+ )
296
+ )
297
+ display_message.append(pre_answer)
298
+ return display_message
299
+
300
+ @abstractmethod
301
+ def _run(self) -> Any:
302
+ """Abstractmethod to enforce a run function is implemented.
303
+
304
+ All prompt instance requires a `_run` call to initialise and run an instance of
305
+ `PromptSession` or `Application`.
306
+ """
307
+ pass
308
+
309
+ @abstractmethod
310
+ async def _run_async(self) -> Any:
311
+ """Abstractmethod to enforce a run function is implemented.
312
+
313
+ All prompt instance requires a `_run_async` call to initialise and run an instance of
314
+ `PromptSession` or `Application`.
315
+ """
316
+ pass
317
+
318
+ def execute(self, raise_keyboard_interrupt: Optional[bool] = None) -> Any:
319
+ """Run the prompt and get the result.
320
+
321
+ Args:
322
+ raise_keyboard_interrupt: **Deprecated**. Set this parameter on the prompt initialisation instead.
323
+
324
+ Returns:
325
+ Value of the user answer. Types varies depending on the prompt.
326
+
327
+ Raises:
328
+ KeyboardInterrupt: When `ctrl-c` is pressed and `raise_keyboard_interrupt` is True.
329
+ """
330
+ result = self._run()
331
+ if raise_keyboard_interrupt is not None:
332
+ self._raise_kbi = not os.getenv(
333
+ "INQUIRERPY_NO_RAISE_KBI", not raise_keyboard_interrupt
334
+ )
335
+ if result == INQUIRERPY_KEYBOARD_INTERRUPT:
336
+ raise KeyboardInterrupt
337
+ if not self._filter:
338
+ return result
339
+ return self._filter(result)
340
+
341
+ async def execute_async(self) -> None:
342
+ """Run the prompt asynchronously and get the result.
343
+
344
+ Returns:
345
+ Value of the user answer. Types varies depending on the prompt.
346
+
347
+ Raises:
348
+ KeyboardInterrupt: When `ctrl-c` is pressed and `raise_keyboard_interrupt` is True.
349
+ """
350
+ result = await self._run_async()
351
+ if result == INQUIRERPY_KEYBOARD_INTERRUPT:
352
+ raise KeyboardInterrupt
353
+ if not self._filter:
354
+ return result
355
+ return self._filter(result)
356
+
357
+ @property
358
+ def instruction(self) -> str:
359
+ """str: Instruction to display next to question."""
360
+ return self._instruction
361
+
362
+ @property
363
+ def kb_maps(self) -> Dict[str, Any]:
364
+ """Dict[str, Any]: Keybinding mappings."""
365
+ return self._kb_maps
366
+
367
+ @kb_maps.setter
368
+ def kb_maps(self, value: Dict[str, Any]) -> None:
369
+ self._kb_maps = {**self._kb_maps, **value}
370
+
371
+ @property
372
+ def kb_func_lookup(self) -> Dict[str, Any]:
373
+ """Dict[str, Any]: Keybinding function lookup mappings.."""
374
+ return self._kb_func_lookup
375
+
376
+ @kb_func_lookup.setter
377
+ def kb_func_lookup(self, value: Dict[str, Any]) -> None:
378
+ self._kb_func_lookup = {**self._kb_func_lookup, **value}
.venv/Lib/site-packages/InquirerPy/containers/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .spinner import SpinnerWindow
.venv/Lib/site-packages/InquirerPy/containers/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (228 Bytes). View file
 
.venv/Lib/site-packages/InquirerPy/containers/__pycache__/instruction.cpython-310.pyc ADDED
Binary file (1.72 kB). View file
 
.venv/Lib/site-packages/InquirerPy/containers/__pycache__/message.cpython-310.pyc ADDED
Binary file (1.76 kB). View file
 
.venv/Lib/site-packages/InquirerPy/containers/__pycache__/spinner.cpython-310.pyc ADDED
Binary file (4.54 kB). View file
 
.venv/Lib/site-packages/InquirerPy/containers/__pycache__/validation.cpython-310.pyc ADDED
Binary file (2.36 kB). View file
 
.venv/Lib/site-packages/InquirerPy/containers/instruction.py ADDED
@@ -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)]
.venv/Lib/site-packages/InquirerPy/containers/message.py ADDED
@@ -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
+ )
.venv/Lib/site-packages/InquirerPy/containers/spinner.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module contains spinner related resources.
2
+
3
+ Note:
4
+ The spinner is not a standalone spinner to run in the terminal
5
+ but rather a `prompt_toolkit` :class:`~prompt_toolkit.layout.Window` that displays a spinner.
6
+
7
+ Use library such as `yaspin <https://github.com/pavdmyt/yaspin>`_ if you need a plain spinner.
8
+ """
9
+ import asyncio
10
+ from typing import TYPE_CHECKING, Callable, List, NamedTuple, Optional, Tuple, Union
11
+
12
+ from prompt_toolkit.filters.utils import to_filter
13
+ from prompt_toolkit.layout.containers import ConditionalContainer, Window
14
+ from prompt_toolkit.layout.controls import FormattedTextControl
15
+
16
+ if TYPE_CHECKING:
17
+ from prompt_toolkit.filters.base import Filter
18
+
19
+ __all__ = ["SPINNERS", "SpinnerWindow"]
20
+
21
+
22
+ class SPINNERS(NamedTuple):
23
+ """Presets of spinner patterns.
24
+
25
+ See Also:
26
+ https://github.com/pavdmyt/yaspin/blob/master/yaspin/data/spinners.json
27
+
28
+ This only contains some basic ones thats ready to use. For more patterns, checkout the
29
+ URL above.
30
+
31
+ Examples:
32
+ >>> from InquirerPy import inquirer
33
+ >>> from InquirerPy.spinner import SPINNERS
34
+ >>> inquirer.select(message="", choices=lambda _: [1, 2, 3], spinner_pattern=SPINNERS.dots)
35
+ """
36
+
37
+ dots = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
38
+ dots2 = ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"]
39
+ line = ["-", "\\", "|", "/"]
40
+ line2 = ["⠂", "-", "–", "—", "–", "-"]
41
+ pipe = ["┤", "┘", "┴", "└", "├", "┌", "┬", "┐"]
42
+ star = ["✶", "✸", "✹", "✺", "✹", "✷"]
43
+ star2 = ["+", "x", "*"]
44
+ flip = ["_", "_", "_", "-", "`", "`", "'", "´", "-", "_", "_", "_"]
45
+ hamburger = ["☱", "☲", "☴"]
46
+ grow_vertical = ["▁", "▃", "▄", "▅", "▆", "▇", "▆", "▅", "▄", "▃"]
47
+ grow_horizontal = ["▏", "▎", "▍", "▌", "▋", "▊", "▉", "▊", "▋", "▌", "▍", "▎"]
48
+ box_bounce = ["▖", "▘", "▝", "▗"]
49
+ triangle = ["◢", "◣", "◤", "◥"]
50
+ arc = ["◜", "◠", "◝", "◞", "◡", "◟"]
51
+ circle = ["◡", "⊙", "◠"]
52
+
53
+
54
+ class SpinnerWindow(ConditionalContainer):
55
+ """Conditional `prompt_toolkit` :class:`~prompt_toolkit.layout.Window` that displays a spinner.
56
+
57
+ Args:
58
+ loading: A :class:`~prompt_toolkit.filters.Condition` to indicate if the spinner should be visible.
59
+ redraw: A redraw function (i.e. :meth:`~prompt_toolkit.application.Application.invalidate`) to refresh the UI.
60
+ pattern: List of pattern to display as the spinner.
61
+ delay: Spinner refresh frequency.
62
+ text: Loading text to display.
63
+ """
64
+
65
+ def __init__(
66
+ self,
67
+ loading: "Filter",
68
+ redraw: Callable[[], None],
69
+ pattern: Optional[Union[List[str], SPINNERS]] = None,
70
+ delay: float = 0.1,
71
+ text: str = "",
72
+ ) -> None:
73
+ self._loading = to_filter(loading)
74
+ self._spinning = False
75
+ self._redraw = redraw
76
+ self._pattern = pattern or SPINNERS.line
77
+ self._char = self._pattern[0]
78
+ self._delay = delay
79
+ self._text = text or "Loading ..."
80
+
81
+ super().__init__(
82
+ content=Window(content=FormattedTextControl(text=self._get_text)),
83
+ filter=self._loading,
84
+ )
85
+
86
+ def _get_text(self) -> List[Tuple[str, str]]:
87
+ """Dynamically get the text for the :class:`~prompt_toolkit.layout.Window`.
88
+
89
+ Returns:
90
+ Formatted text.
91
+ """
92
+ return [
93
+ ("class:spinner_pattern", self._char),
94
+ ("", " "),
95
+ ("class:spinner_text", self._text),
96
+ ]
97
+
98
+ async def start(self) -> None:
99
+ """Start the spinner."""
100
+ if self._spinning:
101
+ return
102
+ self._spinning = True
103
+ while self._loading():
104
+ for char in self._pattern:
105
+ await asyncio.sleep(self._delay)
106
+ self._char = char
107
+ self._redraw()
108
+ self._spinning = False
.venv/Lib/site-packages/InquirerPy/containers/validation.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module contains :class:`.ValidationWindow` which can be used to display error."""
2
+
3
+ from typing import Optional
4
+
5
+ from prompt_toolkit.filters.base import FilterOrBool
6
+ from prompt_toolkit.formatted_text.base import AnyFormattedText
7
+ from prompt_toolkit.layout.containers import ConditionalContainer, Float, Window
8
+ from prompt_toolkit.layout.controls import FormattedTextControl
9
+
10
+
11
+ class ValidationWindow(ConditionalContainer):
12
+ """Conditional `prompt_toolkit` :class:`~prompt_toolkit.layout.Window` that displays error.
13
+
14
+ Args:
15
+ invalid_message: Error message to display when error occured.
16
+ filter: Condition to display the error window.
17
+ """
18
+
19
+ def __init__(
20
+ self, invalid_message: AnyFormattedText, filter: FilterOrBool, **kwargs
21
+ ) -> None:
22
+ super().__init__(
23
+ Window(
24
+ FormattedTextControl(invalid_message), dont_extend_height=True, **kwargs
25
+ ),
26
+ filter=filter,
27
+ )
28
+
29
+
30
+ class ValidationFloat(Float):
31
+ """:class:`~prompt_toolkit.layout.Float` wrapper around :class:`.ValidationWindow`.
32
+
33
+ Args:
34
+ invalid_message: Error message to display when error occured.
35
+ filter: Condition to display the error window.
36
+ left: Distance to left.
37
+ right: Distance to right.
38
+ bottom: Distance to bottom.
39
+ top: Distance to top.
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ invalid_message: AnyFormattedText,
45
+ filter: FilterOrBool,
46
+ left: Optional[int] = None,
47
+ right: Optional[int] = None,
48
+ bottom: Optional[int] = None,
49
+ top: Optional[int] = None,
50
+ **kwargs
51
+ ) -> None:
52
+ super().__init__(
53
+ content=ValidationWindow(
54
+ invalid_message=invalid_message, filter=filter, **kwargs
55
+ ),
56
+ left=left,
57
+ right=right,
58
+ bottom=bottom,
59
+ top=top,
60
+ )
.venv/Lib/site-packages/InquirerPy/enum.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ """Module contains common constants."""
2
+ INQUIRERPY_KEYBOARD_INTERRUPT: str = "INQUIRERPY_KEYBOARD_INTERRUPT"
3
+
4
+ INQUIRERPY_POINTER_SEQUENCE: str = "\u276f"
5
+ INQUIRERPY_FILL_CIRCLE_SEQUENCE: str = "\u25c9"
6
+ INQUIRERPY_EMPTY_CIRCLE_SEQUENCE: str = "\u25cb"
7
+ INQUIRERPY_QMARK_SEQUENCE: str = "\u003f"
.venv/Lib/site-packages/InquirerPy/exceptions.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module contains exceptions that will be raised by `InquirerPy`."""
2
+
3
+
4
+ class InvalidArgument(Exception):
5
+ """Provided argument is invalid.
6
+
7
+ Args:
8
+ message: Exception message.
9
+ """
10
+
11
+ def __init__(self, message: str = "invalid argument"):
12
+ self._message = message
13
+ super().__init__(self._message)
14
+
15
+
16
+ class RequiredKeyNotFound(Exception):
17
+ """Missing required keys in dictionary.
18
+
19
+ Args:
20
+ message: Exception message.
21
+ """
22
+
23
+ def __init__(self, message="required key not found"):
24
+ self.message = message
25
+ super().__init__(self.message)
.venv/Lib/site-packages/InquirerPy/inquirer.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Servers as another entry point for `InquirerPy`.
2
+
3
+ See Also:
4
+ :ref:`index:Alternate Syntax`.
5
+
6
+ `inquirer` directly interact with individual prompt classes. It’s more flexible, easier to customise and also provides IDE type hintings/completions.
7
+ """
8
+ from InquirerPy.prompts import CheckboxPrompt as checkbox
9
+ from InquirerPy.prompts import ConfirmPrompt as confirm
10
+ from InquirerPy.prompts import ExpandPrompt as expand
11
+ from InquirerPy.prompts import FilePathPrompt as filepath
12
+ from InquirerPy.prompts import FuzzyPrompt as fuzzy
13
+ from InquirerPy.prompts import InputPrompt as text
14
+ from InquirerPy.prompts import ListPrompt as select
15
+ from InquirerPy.prompts import NumberPrompt as number
16
+ from InquirerPy.prompts import RawlistPrompt as rawlist
17
+ from InquirerPy.prompts import SecretPrompt as secret
.venv/Lib/site-packages/InquirerPy/prompts/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module contains import of all prompts classes."""
2
+ from InquirerPy.prompts.checkbox import CheckboxPrompt
3
+ from InquirerPy.prompts.confirm import ConfirmPrompt
4
+ from InquirerPy.prompts.expand import ExpandPrompt
5
+ from InquirerPy.prompts.filepath import FilePathPrompt
6
+ from InquirerPy.prompts.fuzzy import FuzzyPrompt
7
+ from InquirerPy.prompts.input import InputPrompt
8
+ from InquirerPy.prompts.list import ListPrompt
9
+ from InquirerPy.prompts.number import NumberPrompt
10
+ from InquirerPy.prompts.rawlist import RawlistPrompt
11
+ from InquirerPy.prompts.secret import SecretPrompt
.venv/Lib/site-packages/InquirerPy/prompts/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (867 Bytes). View file
 
.venv/Lib/site-packages/InquirerPy/prompts/__pycache__/checkbox.cpython-310.pyc ADDED
Binary file (9.8 kB). View file
 
.venv/Lib/site-packages/InquirerPy/prompts/__pycache__/confirm.cpython-310.pyc ADDED
Binary file (8.15 kB). View file
 
.venv/Lib/site-packages/InquirerPy/prompts/__pycache__/expand.cpython-310.pyc ADDED
Binary file (16.7 kB). View file
 
.venv/Lib/site-packages/InquirerPy/prompts/__pycache__/filepath.cpython-310.pyc ADDED
Binary file (8.14 kB). View file
 
.venv/Lib/site-packages/InquirerPy/prompts/__pycache__/fuzzy.cpython-310.pyc ADDED
Binary file (22.5 kB). View file
 
.venv/Lib/site-packages/InquirerPy/prompts/__pycache__/input.cpython-310.pyc ADDED
Binary file (10.1 kB). View file
 
.venv/Lib/site-packages/InquirerPy/prompts/__pycache__/list.cpython-310.pyc ADDED
Binary file (13.5 kB). View file
 
.venv/Lib/site-packages/InquirerPy/prompts/__pycache__/number.cpython-310.pyc ADDED
Binary file (18.7 kB). View file
 
.venv/Lib/site-packages/InquirerPy/prompts/__pycache__/rawlist.cpython-310.pyc ADDED
Binary file (11.5 kB). View file
 
.venv/Lib/site-packages/InquirerPy/prompts/__pycache__/secret.cpython-310.pyc ADDED
Binary file (6.21 kB). View file
 
.venv/Lib/site-packages/InquirerPy/prompts/checkbox.py ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module contains the class to create a checkbox prompt."""
2
+ from typing import Any, Callable, List, Optional, Tuple, Union
3
+
4
+ from prompt_toolkit.validation import ValidationError
5
+
6
+ from InquirerPy.base import FakeDocument, InquirerPyUIListControl
7
+ from InquirerPy.enum import (
8
+ INQUIRERPY_EMPTY_CIRCLE_SEQUENCE,
9
+ INQUIRERPY_FILL_CIRCLE_SEQUENCE,
10
+ INQUIRERPY_POINTER_SEQUENCE,
11
+ )
12
+ from InquirerPy.prompts.list import ListPrompt
13
+ from InquirerPy.separator import Separator
14
+ from InquirerPy.utils import (
15
+ InquirerPyKeybindings,
16
+ InquirerPyListChoices,
17
+ InquirerPyMessage,
18
+ InquirerPySessionResult,
19
+ InquirerPyStyle,
20
+ InquirerPyValidate,
21
+ )
22
+
23
+ __all__ = ["CheckboxPrompt"]
24
+
25
+
26
+ class InquirerPyCheckboxControl(InquirerPyUIListControl):
27
+ """An :class:`~prompt_toolkit.layout.UIControl` class that displays a list of choices.
28
+
29
+ Reference the parameter definition in :class:`.CheckboxPrompt`.
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ choices: InquirerPyListChoices,
35
+ default: Any,
36
+ pointer: str,
37
+ enabled_symbol: str,
38
+ disabled_symbol: str,
39
+ session_result: Optional[InquirerPySessionResult],
40
+ ) -> None:
41
+ """Initialise required attributes and call base class."""
42
+ self._pointer = pointer
43
+ self._enabled_symbol = enabled_symbol
44
+ self._disabled_symbol = disabled_symbol
45
+ super().__init__(
46
+ choices=choices,
47
+ default=default,
48
+ session_result=session_result,
49
+ multiselect=True,
50
+ )
51
+
52
+ def _format_choices(self) -> None:
53
+ pass
54
+
55
+ def _get_hover_text(self, choice) -> List[Tuple[str, str]]:
56
+ display_choices = []
57
+ display_choices.append(("class:pointer", self._pointer))
58
+ if self._pointer:
59
+ display_choices.append(("", " "))
60
+ if not isinstance(choice["value"], Separator):
61
+ display_choices.append(
62
+ (
63
+ "class:checkbox",
64
+ self._enabled_symbol
65
+ if choice["enabled"]
66
+ else self._disabled_symbol,
67
+ )
68
+ )
69
+ if self._enabled_symbol and self._disabled_symbol:
70
+ display_choices.append(("", " "))
71
+ display_choices.append(("[SetCursorPosition]", ""))
72
+ display_choices.append(("class:pointer", choice["name"]))
73
+ return display_choices
74
+
75
+ def _get_normal_text(self, choice) -> List[Tuple[str, str]]:
76
+ display_choices = []
77
+ display_choices.append(("", len(self._pointer) * " "))
78
+ if self._pointer:
79
+ display_choices.append(("", " "))
80
+ if not isinstance(choice["value"], Separator):
81
+ display_choices.append(
82
+ (
83
+ "class:checkbox",
84
+ self._enabled_symbol
85
+ if choice["enabled"]
86
+ else self._disabled_symbol,
87
+ )
88
+ )
89
+ if self._enabled_symbol and self._disabled_symbol:
90
+ display_choices.append(("", " "))
91
+ display_choices.append(("", choice["name"]))
92
+ else:
93
+ display_choices.append(("class:separator", choice["name"]))
94
+ return display_choices
95
+
96
+
97
+ class CheckboxPrompt(ListPrompt):
98
+ """Create a prompt which displays a list of checkboxes to toggle.
99
+
100
+ A wrapper class around :class:`~prompt_toolkit.application.Application`.
101
+
102
+ User can toggle on/off on each checkbox.
103
+
104
+ Works very similar to :class:`~InquirerPy.prompts.list.ListPrompt` with `multiselect` enabled,
105
+ the main difference is visual/UI and also when not toggling anything, the result will be empty.
106
+
107
+ Args:
108
+ message: The question to ask the user.
109
+ Refer to :ref:`pages/dynamic:message` documentation for more details.
110
+ choices: List of choices to display and select.
111
+ Refer to :ref:`pages/dynamic:choices` documentation for more details.
112
+ style: An :class:`InquirerPyStyle` instance.
113
+ Refer to :ref:`Style <pages/style:Alternate Syntax>` documentation for more details.
114
+ vi_mode: Use vim keybinding for the prompt.
115
+ Refer to :ref:`pages/kb:Keybindings` documentation for more details.
116
+ default: Set the default value of the prompt.
117
+ This will be used to determine which choice is highlighted (current selection),
118
+ The default value should be the value of one of the choices.
119
+ Refer to :ref:`pages/dynamic:default` documentation for more details.
120
+ separator: Separator symbol. Custom symbol that will be used as a separator between the choice index number and the choices.
121
+ qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered.
122
+ amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered.
123
+ pointer: Pointer symbol. Customer symbol that will be used to indicate the current choice selection.
124
+ enabled_symbol: Checkbox ticked symbol. Custom symbol which indicate the checkbox is ticked.
125
+ disabled_symbol: Checkbox not ticked symbol. Custom symbol which indicate the checkbox is not ticked.
126
+ instruction: Short instruction to display next to the question.
127
+ long_instruction: Long instructions to display at the bottom of the prompt.
128
+ validate: Add validation to user input.
129
+ The main use case for this prompt would be when `multiselect` is True, you can enforce a min/max selection.
130
+ Refer to :ref:`pages/validator:Validator` documentation for more details.
131
+ invalid_message: Error message to display when user input is invalid.
132
+ Refer to :ref:`pages/validator:Validator` documentation for more details.
133
+ transformer: A function which performs additional transformation on the value that gets printed to the terminal.
134
+ Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`.
135
+ Refer to :ref:`pages/dynamic:transformer` documentation for more details.
136
+ filter: A function which performs additional transformation on the result.
137
+ This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`.
138
+ Refer to :ref:`pages/dynamic:filter` documentation for more details.
139
+ height: Preferred height of the prompt.
140
+ Refer to :ref:`pages/height:Height` documentation for more details.
141
+ max_height: Max height of the prompt.
142
+ Refer to :ref:`pages/height:Height` documentation for more details.
143
+ border: Create border around the choice window.
144
+ keybindings: Customise the builtin keybindings.
145
+ Refer to :ref:`pages/kb:Keybindings` for more details.
146
+ show_cursor: Display cursor at the end of the prompt.
147
+ Set to False to hide the cursor.
148
+ cycle: Return to top item if hit bottom during navigation or vice versa.
149
+ wrap_lines: Soft wrap question lines when question exceeds the terminal width.
150
+ raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result
151
+ will be `None` and the question is skiped.
152
+ mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped.
153
+ mandatory_message: Error message to show when user attempts to skip mandatory prompt.
154
+ session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`.
155
+
156
+ Examples:
157
+ >>> from InquirerPy import inquirer
158
+ >>> result = inquirer.checkbox(message="Select:", choices=[1, 2, 3]).execute()
159
+ >>> print(result)
160
+ [1]
161
+ """
162
+
163
+ def __init__(
164
+ self,
165
+ message: InquirerPyMessage,
166
+ choices: InquirerPyListChoices,
167
+ default: Any = None,
168
+ style: Optional[InquirerPyStyle] = None,
169
+ vi_mode: bool = False,
170
+ qmark: str = "?",
171
+ amark: str = "?",
172
+ pointer: str = INQUIRERPY_POINTER_SEQUENCE,
173
+ enabled_symbol: str = INQUIRERPY_FILL_CIRCLE_SEQUENCE,
174
+ disabled_symbol: str = INQUIRERPY_EMPTY_CIRCLE_SEQUENCE,
175
+ border: bool = False,
176
+ instruction: str = "",
177
+ long_instruction: str = "",
178
+ transformer: Optional[Callable[[Any], Any]] = None,
179
+ filter: Optional[Callable[[Any], Any]] = None,
180
+ height: Optional[Union[int, str]] = None,
181
+ max_height: Optional[Union[int, str]] = None,
182
+ validate: Optional[InquirerPyValidate] = None,
183
+ invalid_message: str = "Invalid input",
184
+ keybindings: Optional[InquirerPyKeybindings] = None,
185
+ show_cursor: bool = True,
186
+ cycle: bool = True,
187
+ wrap_lines: bool = True,
188
+ raise_keyboard_interrupt: bool = True,
189
+ mandatory: bool = True,
190
+ mandatory_message: str = "Mandatory prompt",
191
+ session_result: Optional[InquirerPySessionResult] = None,
192
+ ) -> None:
193
+ self.content_control = InquirerPyCheckboxControl(
194
+ choices=choices,
195
+ default=default,
196
+ pointer=pointer,
197
+ enabled_symbol=enabled_symbol,
198
+ disabled_symbol=disabled_symbol,
199
+ session_result=session_result,
200
+ )
201
+ super().__init__(
202
+ message=message,
203
+ choices=choices,
204
+ style=style,
205
+ border=border,
206
+ vi_mode=vi_mode,
207
+ qmark=qmark,
208
+ amark=amark,
209
+ instruction=instruction,
210
+ long_instruction=long_instruction,
211
+ transformer=transformer,
212
+ filter=filter,
213
+ height=height,
214
+ max_height=max_height,
215
+ validate=validate,
216
+ invalid_message=invalid_message,
217
+ multiselect=True,
218
+ keybindings=keybindings,
219
+ show_cursor=show_cursor,
220
+ cycle=cycle,
221
+ wrap_lines=wrap_lines,
222
+ raise_keyboard_interrupt=raise_keyboard_interrupt,
223
+ mandatory=mandatory,
224
+ mandatory_message=mandatory_message,
225
+ session_result=session_result,
226
+ )
227
+
228
+ def _handle_enter(self, event) -> None:
229
+ """Override this method to force empty array result.
230
+
231
+ When user does not select anything, exit with empty list.
232
+
233
+ Args:
234
+ event: Keypress event.
235
+ """
236
+ try:
237
+ fake_document = FakeDocument(self.result_value)
238
+ self._validator.validate(fake_document) # type: ignore
239
+ except ValidationError:
240
+ self._invalid = True
241
+ else:
242
+ self.status["answered"] = True
243
+ self.status["result"] = self.result_name
244
+ event.app.exit(result=self.result_value)
.venv/Lib/site-packages/InquirerPy/prompts/confirm.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module contains the class to create a confirm prompt."""
2
+ from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple
3
+
4
+ from prompt_toolkit.buffer import ValidationState
5
+ from prompt_toolkit.keys import Keys
6
+ from prompt_toolkit.shortcuts import PromptSession
7
+ from prompt_toolkit.validation import ValidationError
8
+
9
+ from InquirerPy.base import BaseSimplePrompt
10
+ from InquirerPy.exceptions import InvalidArgument
11
+ from InquirerPy.utils import (
12
+ InquirerPyDefault,
13
+ InquirerPyKeybindings,
14
+ InquirerPyMessage,
15
+ InquirerPySessionResult,
16
+ InquirerPyStyle,
17
+ )
18
+
19
+ if TYPE_CHECKING:
20
+ from prompt_toolkit.input.base import Input
21
+ from prompt_toolkit.key_binding.key_processor import KeyPressEvent
22
+ from prompt_toolkit.output.base import Output
23
+
24
+ __all__ = ["ConfirmPrompt"]
25
+
26
+
27
+ class ConfirmPrompt(BaseSimplePrompt):
28
+ """Create a prompt that provides 2 options (confirm/deny) and controlled via single keypress.
29
+
30
+ A wrapper class around :class:`~prompt_toolkit.shortcuts.PromptSession`.
31
+
32
+ Args:
33
+ message: The question to ask the user.
34
+ Refer to :ref:`pages/dynamic:message` documentation for more details.
35
+ style: An :class:`InquirerPyStyle` instance.
36
+ Refer to :ref:`Style <pages/style:Alternate Syntax>` documentation for more details.
37
+ vi_mode: Used for compatibility .
38
+ default: Set the default value of the prompt, should be either `True` or `False`.
39
+ This affects the value returned when user directly hit `enter` key.
40
+ Refer to :ref:`pages/dynamic:default` documentation for more details.
41
+ qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered.
42
+ amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered.
43
+ instruction: Short instruction to display next to the question.
44
+ long_instruction: Long instructions to display at the bottom of the prompt.
45
+ transformer: A function which performs additional transformation on the value that gets printed to the terminal.
46
+ Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`.
47
+ Refer to :ref:`pages/dynamic:transformer` documentation for more details.
48
+ filter: A function which performs additional transformation on the result.
49
+ This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`.
50
+ Refer to :ref:`pages/dynamic:filter` documentation for more details.
51
+ keybindings: Customise the builtin keybindings.
52
+ Refer to :ref:`pages/kb:Keybindings` for more details.
53
+ wrap_lines: Soft wrap question lines when question exceeds the terminal width.
54
+ confirm_letter: Letter used to confirm the prompt. A keybinding will be created for this letter.
55
+ Default is `y` and pressing `y` will answer the prompt with value `True`.
56
+ reject_letter: Letter used to reject the prompt. A keybinding will be created for this letter.
57
+ Default is `n` and pressing `n` will answer the prompt with value `False`.
58
+ raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result
59
+ will be `None` and the question is skiped.
60
+ mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped.
61
+ mandatory_message: Error message to show when user attempts to skip mandatory prompt.
62
+ session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`.
63
+ input: Used internally and will be removed in future updates.
64
+ output: Used internally and will be removed in future updates.
65
+
66
+ Examples:
67
+ >>> from InquirerPy import inquirer
68
+ >>> result = inquirer.confirm(message="Confirm?").execute()
69
+ >>> print(result)
70
+ True
71
+ """
72
+
73
+ def __init__(
74
+ self,
75
+ message: InquirerPyMessage,
76
+ style: Optional[InquirerPyStyle] = None,
77
+ default: InquirerPyDefault = False,
78
+ vi_mode: bool = False,
79
+ qmark: str = "?",
80
+ amark: str = "?",
81
+ instruction: str = "",
82
+ long_instruction: str = "",
83
+ transformer: Optional[Callable[[bool], Any]] = None,
84
+ filter: Optional[Callable[[bool], Any]] = None,
85
+ keybindings: Optional[InquirerPyKeybindings] = None,
86
+ wrap_lines: bool = True,
87
+ confirm_letter: str = "y",
88
+ reject_letter: str = "n",
89
+ raise_keyboard_interrupt: bool = True,
90
+ mandatory: bool = True,
91
+ mandatory_message: str = "Mandatory prompt",
92
+ session_result: Optional[InquirerPySessionResult] = None,
93
+ input: Optional["Input"] = None,
94
+ output: Optional["Output"] = None,
95
+ ) -> None:
96
+ vi_mode = False
97
+ super().__init__(
98
+ message=message,
99
+ style=style,
100
+ vi_mode=vi_mode,
101
+ qmark=qmark,
102
+ amark=amark,
103
+ instruction=instruction,
104
+ transformer=transformer,
105
+ filter=filter,
106
+ default=default,
107
+ wrap_lines=wrap_lines,
108
+ raise_keyboard_interrupt=raise_keyboard_interrupt,
109
+ mandatory=mandatory,
110
+ mandatory_message=mandatory_message,
111
+ session_result=session_result,
112
+ )
113
+ if not isinstance(self._default, bool):
114
+ raise InvalidArgument(
115
+ f"{type(self).__name__} argument default should be type of bool"
116
+ )
117
+ self._confirm_letter = confirm_letter
118
+ self._reject_letter = reject_letter
119
+
120
+ if not keybindings:
121
+ keybindings = {}
122
+ self.kb_maps = {
123
+ "confirm": [
124
+ {"key": self._confirm_letter},
125
+ {"key": self._confirm_letter.upper()},
126
+ ],
127
+ "reject": [
128
+ {"key": self._reject_letter},
129
+ {"key": self._reject_letter.upper()},
130
+ ],
131
+ "any": [{"key": Keys.Any}],
132
+ **keybindings,
133
+ }
134
+ self.kb_func_lookup = {
135
+ "confirm": [{"func": self._handle_confirm}],
136
+ "reject": [{"func": self._handle_reject}],
137
+ "any": [{"func": lambda _: None}],
138
+ }
139
+ self._keybinding_factory()
140
+
141
+ self._session = PromptSession(
142
+ message=self._get_prompt_message,
143
+ key_bindings=self._kb,
144
+ style=self._style,
145
+ wrap_lines=self._wrap_lines,
146
+ bottom_toolbar=[("class:long_instruction", long_instruction)]
147
+ if long_instruction
148
+ else None,
149
+ input=input,
150
+ output=output,
151
+ )
152
+
153
+ def _set_error(self, message: str) -> None:
154
+ self._session.default_buffer.validation_state = ValidationState.INVALID
155
+ self._session.default_buffer.validation_error = ValidationError(message=message)
156
+
157
+ def _handle_reject(self, event) -> None:
158
+ self._session.default_buffer.text = ""
159
+ self.status["answered"] = True
160
+ self.status["result"] = False
161
+ event.app.exit(result=False)
162
+
163
+ def _handle_confirm(self, event) -> None:
164
+ self._session.default_buffer.text = ""
165
+ self.status["answered"] = True
166
+ self.status["result"] = True
167
+ event.app.exit(result=True)
168
+
169
+ def _handle_enter(self, event: "KeyPressEvent") -> None:
170
+ self.status["answered"] = True
171
+ self.status["result"] = self._default
172
+ event.app.exit(result=self._default)
173
+
174
+ def _get_prompt_message(self) -> List[Tuple[str, str]]:
175
+ """Get message to display infront of the input buffer.
176
+
177
+ Returns:
178
+ Formatted text in list of tuple format.
179
+ """
180
+ if not self.instruction:
181
+ pre_answer = (
182
+ "class:instruction",
183
+ " (%s/%s) " % (self._confirm_letter.upper(), self._reject_letter)
184
+ if self._default
185
+ else " (%s/%s) " % (self._confirm_letter, self._reject_letter.upper()),
186
+ )
187
+ else:
188
+ pre_answer = ("class:instruction", " %s " % self.instruction)
189
+ post_answer = ("class:answer", " Yes" if self.status["result"] else " No")
190
+ return super()._get_prompt_message(pre_answer, post_answer)
191
+
192
+ def _run(self) -> bool:
193
+ return self._session.prompt()
194
+
195
+ async def _run_async(self) -> Any:
196
+ return await self._session.prompt_async()
.venv/Lib/site-packages/InquirerPy/prompts/expand.py ADDED
@@ -0,0 +1,458 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module contains the class to create an expand prompt."""
2
+ from dataclasses import dataclass
3
+ from typing import Any, Callable, List, Optional, Tuple, Union
4
+
5
+ from InquirerPy.base import BaseListPrompt, InquirerPyUIListControl
6
+ from InquirerPy.base.control import Choice
7
+ from InquirerPy.enum import INQUIRERPY_POINTER_SEQUENCE
8
+ from InquirerPy.exceptions import InvalidArgument, RequiredKeyNotFound
9
+ from InquirerPy.prompts.list import ListPrompt
10
+ from InquirerPy.separator import Separator
11
+ from InquirerPy.utils import (
12
+ InquirerPyDefault,
13
+ InquirerPyKeybindings,
14
+ InquirerPyListChoices,
15
+ InquirerPyMessage,
16
+ InquirerPySessionResult,
17
+ InquirerPyStyle,
18
+ InquirerPyValidate,
19
+ )
20
+
21
+ __all__ = ["ExpandPrompt", "ExpandHelp", "ExpandChoice"]
22
+
23
+
24
+ @dataclass
25
+ class ExpandHelp:
26
+ """Help choice for the :class:`.ExpandPrompt`.
27
+
28
+ Args:
29
+ key: The key to bind to toggle the expansion of the prompt.
30
+ message: The help message.
31
+ """
32
+
33
+ key: str = "h"
34
+ message: str = "Help, list all choices"
35
+
36
+
37
+ @dataclass
38
+ class ExpandChoice(Choice):
39
+ """Choice class for :class:`.ExpandPrompt`.
40
+
41
+ See Also:
42
+ :class:`~InquirerPy.base.control.Choice`
43
+
44
+ Args:
45
+ value: The value of the choice when user selects this choice.
46
+ name: The value that should be presented to the user prior/after selection of the choice.
47
+ This value is optional, if not provided, it will fallback to the string representation of `value`.
48
+ enabled: Indicates if the choice should be pre-selected.
49
+ This only has effects when the prompt has `multiselect` enabled.
50
+ key: Char to bind to the choice. Pressing this value will jump to the choice,
51
+ If this value is missing, the first char of the `str(value)` will be used as the key.
52
+ """
53
+
54
+ key: Optional[str] = None
55
+
56
+ def __post_init__(self):
57
+ """Assign stringify value to name and also create key using the first char of the value if not present."""
58
+ super().__post_init__()
59
+ if self.key is None:
60
+ self.key = str(self.value)[0].lower()
61
+
62
+
63
+ class InquirerPyExpandControl(InquirerPyUIListControl):
64
+ """An :class:`~prompt_toolkit.layout.UIControl` class that displays a list of choices.
65
+
66
+ Reference the parameter definition in :class:`.ExpandPrompt`.
67
+ """
68
+
69
+ def __init__(
70
+ self,
71
+ choices: InquirerPyListChoices,
72
+ default: Any,
73
+ pointer: str,
74
+ separator: str,
75
+ expand_help: ExpandHelp,
76
+ expand_pointer: str,
77
+ marker: str,
78
+ session_result: Optional[InquirerPySessionResult],
79
+ multiselect: bool,
80
+ marker_pl: str,
81
+ ) -> None:
82
+ self._pointer = pointer
83
+ self._separator = separator
84
+ self._expanded = False
85
+ self._expand_pointer = expand_pointer
86
+ self._marker = marker
87
+ self._marker_pl = marker_pl
88
+ self._expand_help = expand_help
89
+ super().__init__(
90
+ choices=choices,
91
+ default=default,
92
+ session_result=session_result,
93
+ multiselect=multiselect,
94
+ )
95
+
96
+ def _format_choices(self) -> None:
97
+ self._key_maps = {}
98
+ try:
99
+ count = 0
100
+ separator_count = 0
101
+ for raw_choice, choice in zip(self._raw_choices, self.choices): # type: ignore
102
+ if (
103
+ not isinstance(raw_choice, dict)
104
+ and not isinstance(raw_choice, Separator)
105
+ and not isinstance(raw_choice, ExpandChoice)
106
+ ):
107
+ raise InvalidArgument(
108
+ "expand prompt argument choices requires each choice to be type of dictionary or Separator or ExpandChoice"
109
+ )
110
+ if isinstance(raw_choice, Separator):
111
+ separator_count += 1
112
+ else:
113
+ choice["key"] = (
114
+ raw_choice.key
115
+ if isinstance(raw_choice, ExpandChoice)
116
+ else raw_choice["key"]
117
+ )
118
+ self._key_maps[choice["key"]] = count
119
+ count += 1
120
+ except KeyError:
121
+ raise RequiredKeyNotFound(
122
+ "expand prompt choice requires a key 'key' to exists"
123
+ )
124
+
125
+ self.choices.append(
126
+ {
127
+ "key": self._expand_help.key,
128
+ "value": self._expand_help,
129
+ "name": self._expand_help.message,
130
+ "enabled": False,
131
+ }
132
+ )
133
+ self._key_maps[self._expand_help.key] = len(self.choices) - 1
134
+
135
+ first_valid_choice_index = 0
136
+ while isinstance(self.choices[first_valid_choice_index]["value"], Separator):
137
+ first_valid_choice_index += 1
138
+ if self.selected_choice_index == first_valid_choice_index:
139
+ for index, choice in enumerate(self.choices):
140
+ if isinstance(choice["value"], Separator):
141
+ continue
142
+ if choice["key"] == self._default:
143
+ self.selected_choice_index = index
144
+ break
145
+
146
+ def _get_formatted_choices(self) -> List[Tuple[str, str]]:
147
+ """Override this parent class method as expand require visual switch of content.
148
+
149
+ Two types of mode:
150
+ * non expand mode
151
+ * expand mode
152
+ """
153
+ if self._expanded:
154
+ return super()._get_formatted_choices()
155
+ else:
156
+ display_choices = []
157
+ display_choices.append(("class:pointer", self._expand_pointer))
158
+ display_choices.append(
159
+ ("", self.choices[self.selected_choice_index]["name"])
160
+ )
161
+ return display_choices
162
+
163
+ def _get_hover_text(self, choice) -> List[Tuple[str, str]]:
164
+ display_choices = []
165
+ display_choices.append(("class:pointer", self._pointer))
166
+ display_choices.append(
167
+ (
168
+ "class:marker",
169
+ self._marker if choice["enabled"] else self._marker_pl,
170
+ )
171
+ )
172
+ if not isinstance(choice["value"], Separator):
173
+ display_choices.append(
174
+ ("class:pointer", "%s%s" % (choice["key"], self._separator))
175
+ )
176
+ display_choices.append(("[SetCursorPosition]", ""))
177
+ display_choices.append(("class:pointer", choice["name"]))
178
+ return display_choices
179
+
180
+ def _get_normal_text(self, choice) -> List[Tuple[str, str]]:
181
+ display_choices = []
182
+ display_choices.append(("", len(self._pointer) * " "))
183
+ display_choices.append(
184
+ (
185
+ "class:marker",
186
+ self._marker if choice["enabled"] else self._marker_pl,
187
+ )
188
+ )
189
+ if not isinstance(choice["value"], Separator):
190
+ display_choices.append(("", "%s%s" % (choice["key"], self._separator)))
191
+ display_choices.append(("", choice["name"]))
192
+ else:
193
+ display_choices.append(("class:separator", choice["name"]))
194
+ return display_choices
195
+
196
+
197
+ class ExpandPrompt(ListPrompt):
198
+ """Create a compact prompt with the ability to expand.
199
+
200
+ A wrapper class around :class:`~prompt_toolkit.application.Application`.
201
+
202
+ Contains a list of chocies binded to a shortcut letter.
203
+ The prompt can be expanded using `h` key.
204
+
205
+ Args:
206
+ message: The question to ask the user.
207
+ Refer to :ref:`pages/dynamic:message` documentation for more details.
208
+ choices: List of choices to display and select.
209
+ Refer to :ref:`pages/prompts/expand:Choices` documentation for more details.
210
+ style: An :class:`InquirerPyStyle` instance.
211
+ Refer to :ref:`Style <pages/style:Alternate Syntax>` documentation for more details.
212
+ vi_mode: Use vim keybinding for the prompt.
213
+ Refer to :ref:`pages/kb:Keybindings` documentation for more details.
214
+ default: Set the default value of the prompt.
215
+ This will be used to determine which choice is highlighted (current selection),
216
+ The default value should the value of one of the choices.
217
+ For :class:`.ExpandPrompt` specifically, default value can also be a `choice["key"]` which is the shortcut key for the choice.
218
+ Refer to :ref:`pages/dynamic:default` documentation for more details.
219
+ separator: Separator symbol. Custom symbol that will be used as a separator between the choice index number and the choices.
220
+ help_msg: This parameter is DEPRECATED. Use expand_help instead.
221
+ expand_help: The help configuration for the prompt. Must be an instance of :class:`.ExpandHelp`.
222
+ If this value is None, the default help key will be binded to `h` and the default help message would be
223
+ "Help, List all choices."
224
+ expand_pointer: Pointer symbol before prompt expansion. Custom symbol that will be displayed to indicate the prompt is not expanded.
225
+ qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered.
226
+ amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered.
227
+ pointer: Pointer symbol. Customer symbol that will be used to indicate the current choice selection.
228
+ instruction: Short instruction to display next to the question.
229
+ long_instruction: Long instructions to display at the bottom of the prompt.
230
+ validate: Add validation to user input.
231
+ The main use case for this prompt would be when `multiselect` is True, you can enforce a min/max selection.
232
+ Refer to :ref:`pages/validator:Validator` documentation for more details.
233
+ invalid_message: Error message to display when user input is invalid.
234
+ Refer to :ref:`pages/validator:Validator` documentation for more details.
235
+ transformer: A function which performs additional transformation on the value that gets printed to the terminal.
236
+ Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`.
237
+ Refer to :ref:`pages/dynamic:transformer` documentation for more details.
238
+ filter: A function which performs additional transformation on the result.
239
+ This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`.
240
+ Refer to :ref:`pages/dynamic:filter` documentation for more details.
241
+ height: Preferred height of the prompt.
242
+ Refer to :ref:`pages/height:Height` documentation for more details.
243
+ max_height: Max height of the prompt.
244
+ Refer to :ref:`pages/height:Height` documentation for more details.
245
+ multiselect: Enable multi-selection on choices.
246
+ You can use `validate` parameter to control min/max selections.
247
+ Setting to True will also change the result from a single value to a list of values.
248
+ marker: Marker Symbol. Custom symbol to indicate if a choice is selected.
249
+ This will take effects when `multiselect` is True.
250
+ marker_pl: Marker place holder when the choice is not selected.
251
+ This is empty space by default.
252
+ border: Create border around the choice window.
253
+ keybindings: Customise the builtin keybindings.
254
+ Refer to :ref:`pages/kb:Keybindings` for more details.
255
+ show_cursor: Display cursor at the end of the prompt.
256
+ Set to False to hide the cursor.
257
+ cycle: Return to top item if hit bottom during navigation or vice versa.
258
+ wrap_lines: Soft wrap question lines when question exceeds the terminal width.
259
+ raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result
260
+ will be `None` and the question is skiped.
261
+ mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped.
262
+ mandatory_message: Error message to show when user attempts to skip mandatory prompt.
263
+ session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`.
264
+
265
+ Examples:
266
+ >>> from InquirerPy import inquirer
267
+ >>> result = inquirer.expand(message="Select one:", choices[{"name": "1", "value": "1", "key": "a"}]).execute()
268
+ >>> print(result)
269
+ "1"
270
+ """
271
+
272
+ def __init__(
273
+ self,
274
+ message: InquirerPyMessage,
275
+ choices: InquirerPyListChoices,
276
+ default: InquirerPyDefault = "",
277
+ style: Optional[InquirerPyStyle] = None,
278
+ vi_mode: bool = False,
279
+ qmark: str = "?",
280
+ amark: str = "?",
281
+ pointer: str = " ",
282
+ separator: str = ") ",
283
+ help_msg: str = "Help, list all choices",
284
+ expand_help: Optional[ExpandHelp] = None,
285
+ expand_pointer: str = "%s " % INQUIRERPY_POINTER_SEQUENCE,
286
+ instruction: str = "",
287
+ long_instruction: str = "",
288
+ transformer: Optional[Callable[[Any], Any]] = None,
289
+ filter: Optional[Callable[[Any], Any]] = None,
290
+ height: Optional[Union[int, str]] = None,
291
+ max_height: Optional[Union[int, str]] = None,
292
+ multiselect: bool = False,
293
+ marker: str = INQUIRERPY_POINTER_SEQUENCE,
294
+ marker_pl: str = " ",
295
+ border: bool = False,
296
+ validate: Optional[InquirerPyValidate] = None,
297
+ invalid_message: str = "Invalid input",
298
+ keybindings: Optional[InquirerPyKeybindings] = None,
299
+ show_cursor: bool = True,
300
+ cycle: bool = True,
301
+ wrap_lines: bool = True,
302
+ raise_keyboard_interrupt: bool = True,
303
+ mandatory: bool = True,
304
+ mandatory_message: str = "Mandatory prompt",
305
+ session_result: Optional[InquirerPySessionResult] = None,
306
+ ) -> None:
307
+ if expand_help is None:
308
+ expand_help = ExpandHelp(message=help_msg)
309
+ self._expand_help = expand_help
310
+ self.content_control: InquirerPyExpandControl = InquirerPyExpandControl(
311
+ choices=choices,
312
+ default=default,
313
+ pointer=pointer,
314
+ separator=separator,
315
+ expand_help=expand_help,
316
+ expand_pointer=expand_pointer,
317
+ marker=marker,
318
+ marker_pl=marker_pl,
319
+ session_result=session_result,
320
+ multiselect=multiselect,
321
+ )
322
+ super().__init__(
323
+ message=message,
324
+ choices=choices,
325
+ style=style,
326
+ border=border,
327
+ vi_mode=vi_mode,
328
+ qmark=qmark,
329
+ amark=amark,
330
+ instruction=instruction,
331
+ long_instruction=long_instruction,
332
+ transformer=transformer,
333
+ filter=filter,
334
+ height=height,
335
+ max_height=max_height,
336
+ validate=validate,
337
+ invalid_message=invalid_message,
338
+ multiselect=multiselect,
339
+ keybindings=keybindings,
340
+ show_cursor=show_cursor,
341
+ cycle=cycle,
342
+ wrap_lines=wrap_lines,
343
+ raise_keyboard_interrupt=raise_keyboard_interrupt,
344
+ mandatory=mandatory,
345
+ mandatory_message=mandatory_message,
346
+ session_result=session_result,
347
+ )
348
+
349
+ def _on_rendered(self, _) -> None:
350
+ """Override this method to apply custom keybindings.
351
+
352
+ Needs to creat these kb in the callback due to `after_render`
353
+ retrieve the choices asynchronously.
354
+ """
355
+
356
+ def keybinding_factory(key):
357
+ @self.register_kb(key.lower())
358
+ def keybinding(_) -> None:
359
+ if key == self._expand_help.key:
360
+ self.content_control._expanded = not self.content_control._expanded
361
+ else:
362
+ self.content_control.selected_choice_index = (
363
+ self.content_control._key_maps[key]
364
+ )
365
+
366
+ return keybinding
367
+
368
+ for choice in self.content_control.choices:
369
+ if not isinstance(choice["value"], Separator):
370
+ keybinding_factory(choice["key"])
371
+
372
+ def _handle_up(self, event) -> None:
373
+ """Handle the event when user attempt to move up.
374
+
375
+ Overriding this method to skip the help choice.
376
+ """
377
+ if not self.content_control._expanded:
378
+ return
379
+ while True:
380
+ cap = BaseListPrompt._handle_up(self, event)
381
+ if not isinstance(
382
+ self.content_control.selection["value"], Separator
383
+ ) and not isinstance(self.content_control.selection["value"], ExpandHelp):
384
+ break
385
+ else:
386
+ if cap and not self._cycle:
387
+ self._handle_down(event)
388
+ break
389
+
390
+ def _handle_down(self, event) -> None:
391
+ """Handle the event when user attempt to move down.
392
+
393
+ Overriding this method to skip the help choice.
394
+ """
395
+ if not self.content_control._expanded:
396
+ return
397
+ while True:
398
+ cap = BaseListPrompt._handle_down(self, event)
399
+ if not isinstance(
400
+ self.content_control.selection["value"], Separator
401
+ ) and not isinstance(self.content_control.selection["value"], ExpandHelp):
402
+ break
403
+ elif (
404
+ isinstance(self.content_control.selection["value"], ExpandHelp)
405
+ and not self._cycle
406
+ ):
407
+ self._handle_up(event)
408
+ break
409
+ else:
410
+ if cap and not self._cycle:
411
+ self._handle_up(event)
412
+ break
413
+
414
+ @property
415
+ def instruction(self) -> str:
416
+ """Construct the instruction behind the question.
417
+
418
+ If _instruction exists, use that.
419
+
420
+ :return: The instruction text.
421
+ """
422
+ return (
423
+ "(%s)" % "".join(self.content_control._key_maps.keys())
424
+ if not self._instruction
425
+ else self._instruction
426
+ )
427
+
428
+ def _get_prompt_message(self) -> List[Tuple[str, str]]:
429
+ """Return the formatted text to display in the prompt.
430
+
431
+ Overriding this method to allow multiple formatted class to be displayed.
432
+ """
433
+ display_message = super()._get_prompt_message()
434
+ if not self.status["answered"]:
435
+ display_message.append(
436
+ ("class:input", self.content_control.selection["key"])
437
+ )
438
+ return display_message
439
+
440
+ def _handle_toggle_all(self, _, value: Optional[bool] = None) -> None:
441
+ """Override this method to ignore `ExpandHelp`.
442
+
443
+ :param value: Specify a value to toggle.
444
+ """
445
+ if not self.content_control._expanded:
446
+ return
447
+ for choice in self.content_control.choices:
448
+ if isinstance(choice["value"], Separator) or isinstance(
449
+ choice["value"], ExpandHelp
450
+ ):
451
+ continue
452
+ choice["enabled"] = value if value else not choice["enabled"]
453
+
454
+ def _handle_toggle_choice(self, event) -> None:
455
+ """Override this method to ignore keypress when not expanded."""
456
+ if not self.content_control._expanded:
457
+ return
458
+ super()._handle_toggle_choice(event)
.venv/Lib/site-packages/InquirerPy/prompts/filepath.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module contains the class to create filepath prompt and filepath completer class."""
2
+ import os
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING, Any, Callable, Generator, Optional
5
+
6
+ from prompt_toolkit.completion import Completer, Completion
7
+ from prompt_toolkit.completion.base import ThreadedCompleter
8
+
9
+ from InquirerPy.prompts.input import InputPrompt
10
+ from InquirerPy.utils import (
11
+ InquirerPyDefault,
12
+ InquirerPyKeybindings,
13
+ InquirerPyMessage,
14
+ InquirerPySessionResult,
15
+ InquirerPyStyle,
16
+ InquirerPyValidate,
17
+ )
18
+
19
+ if TYPE_CHECKING:
20
+ from prompt_toolkit.input.base import Input
21
+ from prompt_toolkit.output.base import Output
22
+
23
+ __all__ = ["FilePathPrompt", "FilePathCompleter"]
24
+
25
+
26
+ class FilePathCompleter(Completer):
27
+ """An auto completion class which generates system filepath.
28
+
29
+ See Also:
30
+ :class:`~prompt_toolkit.completion.Completer`
31
+
32
+ Args:
33
+ only_directories: Only complete directories.
34
+ only_files: Only complete files.
35
+ """
36
+
37
+ def __init__(self, only_directories: bool = False, only_files: bool = False):
38
+ self._only_directories = only_directories
39
+ self._only_files = only_files
40
+ self._delimiter = "/" if os.name == "posix" else "\\"
41
+
42
+ def get_completions(
43
+ self, document, complete_event
44
+ ) -> Generator[Completion, None, None]:
45
+ """Get a list of valid system paths."""
46
+ if document.text == "~":
47
+ return
48
+
49
+ validation = lambda file, doc_text: str(file).startswith(doc_text)
50
+
51
+ if document.cursor_position == 0:
52
+ dirname = Path.cwd()
53
+ validation = lambda file, doc_text: True
54
+ elif document.text.startswith("~"):
55
+ dirname = Path(os.path.dirname(f"{Path.home()}{document.text[1:]}"))
56
+ validation = lambda file, doc_text: str(file).startswith(
57
+ f"{Path.home()}{doc_text[1:]}"
58
+ )
59
+ elif document.text.startswith(f".{self._delimiter}"):
60
+ dirname = Path(os.path.dirname(document.text))
61
+ validation = lambda file, doc_text: str(file).startswith(doc_text[2:])
62
+ else:
63
+ dirname = Path(os.path.dirname(document.text))
64
+
65
+ for item in self._get_completion(document, dirname, validation):
66
+ yield item
67
+
68
+ def _get_completion(
69
+ self, document, path, validation
70
+ ) -> Generator[Completion, None, None]:
71
+ if not path.is_dir():
72
+ return
73
+ for file in path.iterdir():
74
+ if self._only_directories and not file.is_dir():
75
+ continue
76
+ if self._only_files and not file.is_file():
77
+ continue
78
+ if validation(file, document.text):
79
+ file_name = file.name
80
+ display_name = file_name
81
+ if file.is_dir():
82
+ display_name = f"{file_name}{self._delimiter}"
83
+ yield Completion(
84
+ file.name,
85
+ start_position=-1 * len(os.path.basename(document.text)),
86
+ display=display_name,
87
+ )
88
+
89
+
90
+ class FilePathPrompt(InputPrompt):
91
+ """Create a prompt that provides auto completion for system filepaths.
92
+
93
+ A wrapper class around :class:`~prompt_toolkit.shortcuts.PromptSession`.
94
+
95
+ Args:
96
+ message: The question to ask the user.
97
+ Refer to :ref:`pages/dynamic:message` documentation for more details.
98
+ style: An :class:`InquirerPyStyle` instance.
99
+ Refer to :ref:`Style <pages/style:Alternate Syntax>` documentation for more details.
100
+ vi_mode: Use vim keybinding for the prompt.
101
+ Refer to :ref:`pages/kb:Keybindings` documentation for more details.
102
+ default: Set the default text value of the prompt.
103
+ Refer to :ref:`pages/dynamic:default` documentation for more details.
104
+ qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered.
105
+ amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered.
106
+ instruction: Short instruction to display next to the question.
107
+ long_instruction: Long instructions to display at the bottom of the prompt.
108
+ multicolumn_complete: Change the auto-completion UI to a multi column display.
109
+ validate: Add validation to user input.
110
+ Refer to :ref:`pages/validator:Validator` documentation for more details.
111
+ invalid_message: Error message to display when user input is invalid.
112
+ Refer to :ref:`pages/validator:Validator` documentation for more details.
113
+ transformer: A function which performs additional transformation on the value that gets printed to the terminal.
114
+ Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`.
115
+ Refer to :ref:`pages/dynamic:transformer` documentation for more details.
116
+ filter: A function which performs additional transformation on the result.
117
+ This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`.
118
+ Refer to :ref:`pages/dynamic:filter` documentation for more details.
119
+ keybindings: Customise the builtin keybindings.
120
+ Refer to :ref:`pages/kb:Keybindings` for more details.
121
+ wrap_lines: Soft wrap question lines when question exceeds the terminal width.
122
+ only_directories: Only complete directories.
123
+ only_files: Only complete files.
124
+ raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result
125
+ will be `None` and the question is skiped.
126
+ mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped.
127
+ mandatory_message: Error message to show when user attempts to skip mandatory prompt.
128
+ session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`.
129
+ input: Used internally and will be removed in future updates.
130
+ output: Used internally and will be removed in future updates.
131
+
132
+ Examples:
133
+ >>> from InquirerPy import inquirer
134
+ >>> result = inquirer.filepath(message="Enter a path:").execute()
135
+ >>> print(result)
136
+ /home/ubuntu/README.md
137
+ """
138
+
139
+ def __init__(
140
+ self,
141
+ message: InquirerPyMessage,
142
+ style: Optional[InquirerPyStyle] = None,
143
+ vi_mode: bool = False,
144
+ default: InquirerPyDefault = "",
145
+ qmark: str = "?",
146
+ amark: str = "?",
147
+ instruction: str = "",
148
+ long_instruction: str = "",
149
+ multicolumn_complete: bool = False,
150
+ validate: Optional[InquirerPyValidate] = None,
151
+ invalid_message: str = "Invalid input",
152
+ only_directories: bool = False,
153
+ only_files: bool = False,
154
+ transformer: Optional[Callable[[str], Any]] = None,
155
+ filter: Optional[Callable[[str], Any]] = None,
156
+ keybindings: Optional[InquirerPyKeybindings] = None,
157
+ wrap_lines: bool = True,
158
+ raise_keyboard_interrupt: bool = True,
159
+ mandatory: bool = True,
160
+ mandatory_message: str = "Mandatory prompt",
161
+ session_result: Optional[InquirerPySessionResult] = None,
162
+ input: Optional["Input"] = None,
163
+ output: Optional["Output"] = None,
164
+ ) -> None:
165
+ super().__init__(
166
+ message=message,
167
+ style=style,
168
+ vi_mode=vi_mode,
169
+ default=default,
170
+ qmark=qmark,
171
+ amark=amark,
172
+ instruction=instruction,
173
+ long_instruction=long_instruction,
174
+ completer=ThreadedCompleter(
175
+ FilePathCompleter(
176
+ only_directories=only_directories, only_files=only_files
177
+ )
178
+ ),
179
+ multicolumn_complete=multicolumn_complete,
180
+ validate=validate,
181
+ invalid_message=invalid_message,
182
+ transformer=transformer,
183
+ filter=filter,
184
+ keybindings=keybindings,
185
+ wrap_lines=wrap_lines,
186
+ raise_keyboard_interrupt=raise_keyboard_interrupt,
187
+ mandatory=mandatory,
188
+ mandatory_message=mandatory_message,
189
+ session_result=session_result,
190
+ input=input,
191
+ output=output,
192
+ )