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
- .gitattributes +68 -35
- .venv/.gitignore +1 -0
- .venv/Lib/site-packages/InquirerPy/__init__.py +2 -0
- .venv/Lib/site-packages/InquirerPy/__pycache__/__init__.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/__pycache__/enum.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/__pycache__/exceptions.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/__pycache__/inquirer.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/__pycache__/resolver.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/__pycache__/separator.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/__pycache__/utils.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/__pycache__/validator.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/base/__init__.py +15 -0
- .venv/Lib/site-packages/InquirerPy/base/__pycache__/__init__.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/base/__pycache__/complex.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/base/__pycache__/control.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/base/__pycache__/list.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/base/__pycache__/simple.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/base/complex.py +294 -0
- .venv/Lib/site-packages/InquirerPy/base/control.py +227 -0
- .venv/Lib/site-packages/InquirerPy/base/list.py +238 -0
- .venv/Lib/site-packages/InquirerPy/base/simple.py +378 -0
- .venv/Lib/site-packages/InquirerPy/containers/__init__.py +1 -0
- .venv/Lib/site-packages/InquirerPy/containers/__pycache__/__init__.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/containers/__pycache__/instruction.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/containers/__pycache__/message.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/containers/__pycache__/spinner.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/containers/__pycache__/validation.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/containers/instruction.py +38 -0
- .venv/Lib/site-packages/InquirerPy/containers/message.py +42 -0
- .venv/Lib/site-packages/InquirerPy/containers/spinner.py +108 -0
- .venv/Lib/site-packages/InquirerPy/containers/validation.py +60 -0
- .venv/Lib/site-packages/InquirerPy/enum.py +7 -0
- .venv/Lib/site-packages/InquirerPy/exceptions.py +25 -0
- .venv/Lib/site-packages/InquirerPy/inquirer.py +17 -0
- .venv/Lib/site-packages/InquirerPy/prompts/__init__.py +11 -0
- .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/__init__.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/checkbox.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/confirm.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/expand.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/filepath.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/fuzzy.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/input.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/list.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/number.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/rawlist.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/prompts/__pycache__/secret.cpython-310.pyc +0 -0
- .venv/Lib/site-packages/InquirerPy/prompts/checkbox.py +244 -0
- .venv/Lib/site-packages/InquirerPy/prompts/confirm.py +196 -0
- .venv/Lib/site-packages/InquirerPy/prompts/expand.py +458 -0
- .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 |
+
)
|