InquirerPrompt 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
InquirerPy/utils.py ADDED
@@ -0,0 +1,291 @@
1
+ """Module contains shared utility functions and typing aliases."""
2
+
3
+ import math
4
+ import os
5
+ import shutil
6
+ from typing import (
7
+ TYPE_CHECKING,
8
+ Any,
9
+ Callable,
10
+ Dict,
11
+ List,
12
+ NamedTuple,
13
+ Optional,
14
+ Tuple,
15
+ Union,
16
+ )
17
+
18
+ from prompt_toolkit import print_formatted_text
19
+ from prompt_toolkit.application import run_in_terminal
20
+ from prompt_toolkit.application.current import get_app
21
+ from prompt_toolkit.formatted_text import FormattedText
22
+ from prompt_toolkit.styles import Style
23
+ from prompt_toolkit.validation import Validator
24
+
25
+ from InquirerPy.exceptions import InvalidArgument
26
+
27
+ if TYPE_CHECKING:
28
+ from prompt_toolkit.filters.base import FilterOrBool
29
+
30
+ from InquirerPy.base.control import Choice
31
+
32
+ __all__ = [
33
+ "get_style",
34
+ "calculate_height",
35
+ "InquirerPyStyle",
36
+ "patched_print",
37
+ "color_print",
38
+ ]
39
+
40
+
41
+ class InquirerPyStyle(NamedTuple):
42
+ """`InquirerPy` Style class.
43
+
44
+ Used as a helper class to enforce the method `get_style` to be used
45
+ while also avoiding :class:`dict` to be passed into prompts.
46
+
47
+ Note:
48
+ The class is an instance of :class:`typing.NamedTuple`.
49
+
50
+ Warning:
51
+ You should not directly be using this class besides for type hinting
52
+ purposes. Obtain an instance of this class using :func:`.get_style`.
53
+ """
54
+
55
+ dict: Dict[str, str]
56
+
57
+
58
+ InquirerPySessionResult = Dict[Union[str, int], Optional[Union[str, bool, List[Any]]]]
59
+ InquirerPyChoice = Union[List[Any], List["Choice"], List[Dict[str, Any]]]
60
+ InquirerPyListChoices = Union[
61
+ Callable[["InquirerPySessionResult"], InquirerPyChoice],
62
+ InquirerPyChoice,
63
+ ]
64
+ InquirerPyValidate = Union[Callable[[Any], bool], "Validator"]
65
+ InquirerPyQuestions = Union[List[Dict[str, Any]], Dict[str, Any]]
66
+ InquirerPyMessage = Union[str, Callable[["InquirerPySessionResult"], str]]
67
+ InquirerPyDefault = Union[Any, Callable[["InquirerPySessionResult"], Any]]
68
+ InquirerPyKeybindings = Dict[
69
+ str, List[Dict[str, Union[str, "FilterOrBool", List[str]]]]
70
+ ]
71
+
72
+
73
+ def get_style(
74
+ style: Optional[Dict[str, str]] = None, style_override: bool = True
75
+ ) -> InquirerPyStyle:
76
+ """Obtain an :class:`.InquirerPyStyle` instance which can be consumed by the `style` parameter in prompts.
77
+
78
+ Tip:
79
+ This function supports ENV variables.
80
+
81
+ For all the color ENV variable names, refer to the :ref:`ENV <pages/env:Style>` documentation.
82
+
83
+ Note:
84
+ If no style is provided, then a default theme based on `one dark <https://github.com/joshdick/onedark.vim#color-reference>`_
85
+ color palette is applied.
86
+
87
+ Note:
88
+ Priority: style parameter -> ENV variable -> default style
89
+
90
+ Args:
91
+ style: The dictionary of style classes and their colors, If nothing is passed, the style will be resolved to the :ref:`pages/style:Default Style`.
92
+ style_override: A boolean to determine if the supplied `style` parameter should be merged with the :ref:`pages/style:Default Style` or override them.
93
+ By default, the supplied style will overwrite the :ref:`pages/style:Default Style`.
94
+
95
+ Returns:
96
+ An instance of :class:`.InquirerPyStyle`.
97
+
98
+ Examples:
99
+ >>> from InquirerPy import get_style
100
+ >>> from InquirerPy import inquirer
101
+ >>> style = get_style({"questionmark": "#ffffff", "answer": "#000000"}, style_override=False)
102
+ >>> result = inquirer.confirm(message="Confirm?", style=style).execute()
103
+ """
104
+ if not style_override or style is None:
105
+ if not style:
106
+ style = {}
107
+ result = {
108
+ "questionmark": os.getenv("INQUIRERPY_STYLE_QUESTIONMARK", "#e5c07b"),
109
+ "answermark": os.getenv("INQUIRERPY_STYLE_ANSWERMARK", "#e5c07b"),
110
+ "answer": os.getenv("INQUIRERPY_STYLE_ANSWER", "#61afef"),
111
+ "input": os.getenv("INQUIRERPY_STYLE_INPUT", "#98c379"),
112
+ "question": os.getenv("INQUIRERPY_STYLE_QUESTION", ""),
113
+ "answered_question": os.getenv("INQUIRERPY_STYLE_ANSWERED_QUESTION", ""),
114
+ "instruction": os.getenv("INQUIRERPY_STYLE_INSTRUCTION", "#abb2bf"),
115
+ "long_instruction": os.getenv(
116
+ "INQUIRERPY_STYLE_LONG_INSTRUCTION", "#abb2bf"
117
+ ),
118
+ "pointer": os.getenv("INQUIRERPY_STYLE_POINTER", "#61afef"),
119
+ "checkbox": os.getenv("INQUIRERPY_STYLE_CHECKBOX", "#98c379"),
120
+ "separator": os.getenv("INQUIRERPY_STYLE_SEPARATOR", ""),
121
+ "skipped": os.getenv("INQUIRERPY_STYLE_SKIPPED", "#5c6370"),
122
+ "validator": os.getenv("INQUIRERPY_STYLE_VALIDATOR", ""),
123
+ "marker": os.getenv("INQUIRERPY_STYLE_MARKER", "#e5c07b"),
124
+ "fuzzy_prompt": os.getenv("INQUIRERPY_STYLE_FUZZY_PROMPT", "#c678dd"),
125
+ "fuzzy_info": os.getenv("INQUIRERPY_STYLE_FUZZY_INFO", "#abb2bf"),
126
+ "fuzzy_border": os.getenv("INQUIRERPY_STYLE_FUZZY_BORDER", "#4b5263"),
127
+ "fuzzy_match": os.getenv("INQUIRERPY_STYLE_FUZZY_MATCH", "#c678dd"),
128
+ "spinner_pattern": os.getenv("INQUIRERPY_STYLE_SPINNER_PATTERN", "#e5c07b"),
129
+ "spinner_text": os.getenv("INQUIRERPY_STYLE_SPINNER_TEXT", ""),
130
+ **style,
131
+ }
132
+ else:
133
+ result = {
134
+ "questionmark": os.getenv("INQUIRERPY_STYLE_QUESTIONMARK", ""),
135
+ "answermark": os.getenv("INQUIRERPY_STYLE_ANSWERMARK", ""),
136
+ "answer": os.getenv("INQUIRERPY_STYLE_ANSWER", ""),
137
+ "input": os.getenv("INQUIRERPY_STYLE_INPUT", ""),
138
+ "question": os.getenv("INQUIRERPY_STYLE_QUESTION", ""),
139
+ "answered_question": os.getenv("INQUIRERPY_STYLE_ANSWERED_QUESTION", ""),
140
+ "instruction": os.getenv("INQUIRERPY_STYLE_INSTRUCTION", ""),
141
+ "long_instruction": os.getenv("INQUIRERPY_STYLE_LONG_INSTRUCTION", ""),
142
+ "pointer": os.getenv("INQUIRERPY_STYLE_POINTER", ""),
143
+ "checkbox": os.getenv("INQUIRERPY_STYLE_CHECKBOX", ""),
144
+ "separator": os.getenv("INQUIRERPY_STYLE_SEPARATOR", ""),
145
+ "skipped": os.getenv("INQUIRERPY_STYLE_SKIPPED", ""),
146
+ "validator": os.getenv("INQUIRERPY_STYLE_VALIDATOR", ""),
147
+ "marker": os.getenv("INQUIRERPY_STYLE_MARKER", ""),
148
+ "fuzzy_prompt": os.getenv("INQUIRERPY_STYLE_FUZZY_PROMPT", ""),
149
+ "fuzzy_info": os.getenv("INQUIRERPY_STYLE_FUZZY_INFO", ""),
150
+ "fuzzy_border": os.getenv("INQUIRERPY_STYLE_FUZZY_BORDER", ""),
151
+ "fuzzy_match": os.getenv("INQUIRERPY_STYLE_FUZZY_MATCH", ""),
152
+ "spinner_pattern": os.getenv("INQUIRERPY_STYLE_SPINNER_PATTERN", ""),
153
+ "spinner_text": os.getenv("INQUIRERPY_STYLE_SPINNER_TEXT", ""),
154
+ **style,
155
+ }
156
+
157
+ if result.get("fuzzy_border"):
158
+ result["frame.border"] = result.pop("fuzzy_border")
159
+ if result.get("validator"):
160
+ result["validation-toolbar"] = result.pop("validator")
161
+ result["bottom-toolbar"] = "noreverse"
162
+ return InquirerPyStyle(result)
163
+
164
+
165
+ def calculate_height(
166
+ height: Optional[Union[int, str]],
167
+ max_height: Optional[Union[int, str]],
168
+ height_offset: int = 2,
169
+ ) -> Tuple[Optional[int], int]:
170
+ """Calculate the `height` and `max_height` for the main question contents.
171
+
172
+ Tip:
173
+ The parameter `height`/`max_height` can be specified by either a :class:`string` or :class:`int`.
174
+
175
+ When `height`/`max_height` is :class:`str`:
176
+ It will set the height to a percentage based on the value provided.
177
+ You can optionally add the '%' sign which will be ignored while processing.
178
+
179
+ Example: "60%" or "60" (60% of the current terminal visible lines)
180
+
181
+ When `height`/`max_height` is :class:`int`:
182
+ It will set the height to exact number of lines based on the value provided.
183
+
184
+ Example: 20 (20 lines in terminal)
185
+
186
+ Note:
187
+ If `max_height` is not provided or is None, the default `max_height` will be configured to `70%` for
188
+ best visual presentation in the terminal.
189
+
190
+ Args:
191
+ height: The desired height in either percentage as string or exact value as int.
192
+ max_height: Maximum acceptable height in either percentage as string or exact value as int.
193
+ height_offset: Height offset to apply to the height.
194
+
195
+ Returns:
196
+ A :class:`tuple` with the first value being the desired height and the second value being
197
+ the maximum height.
198
+
199
+ Raises:
200
+ InvalidArgument: The provided `height`/`max_height` is not able to to be converted to int.
201
+
202
+ Examples:
203
+ >>> calculate_height(height="60%", max_height="100%")
204
+ """
205
+ try:
206
+ _, term_lines = shutil.get_terminal_size()
207
+ term_lines = term_lines
208
+ if not height:
209
+ dimmension_height = None
210
+ else:
211
+ if isinstance(height, str):
212
+ height = height.replace("%", "")
213
+ height = int(height)
214
+ dimmension_height = (
215
+ math.floor(term_lines * (height / 100)) - height_offset
216
+ )
217
+ else:
218
+ dimmension_height = height
219
+
220
+ if not max_height:
221
+ max_height = "70%" if not height else "100%"
222
+ if isinstance(max_height, str):
223
+ max_height = max_height.replace("%", "")
224
+ max_height = int(max_height)
225
+ dimmension_max_height = (
226
+ math.floor(term_lines * (max_height / 100)) - height_offset
227
+ )
228
+ else:
229
+ dimmension_max_height = max_height
230
+
231
+ if dimmension_height and dimmension_height > dimmension_max_height:
232
+ dimmension_height = dimmension_max_height
233
+ if dimmension_height and dimmension_height <= 0:
234
+ dimmension_height = 1
235
+ if dimmension_max_height <= 0:
236
+ dimmension_max_height = 1
237
+ return dimmension_height, dimmension_max_height
238
+
239
+ except ValueError:
240
+ raise InvalidArgument(
241
+ "prompt argument height/max_height needs to be type of an int or str"
242
+ )
243
+
244
+
245
+ def patched_print(*values) -> None:
246
+ """Patched :func:`print` that can print values without interrupting the prompt.
247
+
248
+ See Also:
249
+ :func:`print`
250
+ :func:`~prompt_toolkit.application.run_in_terminal`
251
+
252
+ Args:
253
+ *values: Refer to :func:`print`.
254
+
255
+ Examples:
256
+ >>> patched_print("Hello World")
257
+ """
258
+
259
+ def _print():
260
+ print(*values)
261
+
262
+ run_in_terminal(_print)
263
+
264
+
265
+ def color_print(
266
+ formatted_text: List[Tuple[str, str]], style: Optional[Dict[str, str]] = None
267
+ ) -> None:
268
+ """Print colored text leveraging :func:`~prompt_toolkit.shortcuts.print_formatted_text`.
269
+
270
+ This function automatically handles printing the text without interrupting the
271
+ current prompt.
272
+
273
+ Args:
274
+ formatted_text: A list of formatted_text.
275
+ style: Style to apply to `formatted_text` in :class:`dictionary` form.
276
+
277
+ Example:
278
+ >>> color_print(formatted_text=[("class:aa", "hello "), ("class:bb", "world")], style={"aa": "red", "bb": "blue"})
279
+ >>> color_print([("red", "yes"), ("", " "), ("blue", "no")])
280
+ """
281
+
282
+ def _print():
283
+ print_formatted_text(
284
+ FormattedText(formatted_text),
285
+ style=Style.from_dict(style) if style else None,
286
+ )
287
+
288
+ if get_app().is_running:
289
+ run_in_terminal(_print)
290
+ else:
291
+ _print()
@@ -0,0 +1,166 @@
1
+ """Module contains pre-built validators."""
2
+
3
+ import re
4
+ from pathlib import Path
5
+ from typing import Optional
6
+
7
+ from prompt_toolkit.validation import ValidationError, Validator
8
+
9
+ __all__ = [
10
+ "PathValidator",
11
+ "EmptyInputValidator",
12
+ "PasswordValidator",
13
+ "NumberValidator",
14
+ ]
15
+
16
+
17
+ class NumberValidator(Validator):
18
+ """:class:`~prompt_toolkit.validation.Validator` to validate if input is a number.
19
+
20
+ Args:
21
+ message: Error message to display in the validatation toolbar when validation failed.
22
+ float_allowed: Allow input to contain floating number (with decimal).
23
+ """
24
+
25
+ def __init__(
26
+ self, message: str = "Input should be a number", float_allowed: bool = False
27
+ ) -> None:
28
+ self._message = message
29
+ self._float_allowed = float_allowed
30
+
31
+ def validate(self, document) -> None:
32
+ """Check if user input is a valid number.
33
+
34
+ This method is used internally by `prompt_toolkit <https://python-prompt-toolkit.readthedocs.io/en/master/>`_.
35
+
36
+ See Also:
37
+ https://python-prompt-toolkit.readthedocs.io/en/master/pages/asking_for_input.html?highlight=validator#input-validation
38
+ """
39
+ try:
40
+ if self._float_allowed:
41
+ float(document.text)
42
+ else:
43
+ int(document.text)
44
+ except ValueError:
45
+ raise ValidationError(
46
+ message=self._message, cursor_position=document.cursor_position
47
+ )
48
+
49
+
50
+ class PathValidator(Validator):
51
+ """:class:`~prompt_toolkit.validation.Validator` to validate if input is a valid filepath on the system.
52
+
53
+ Args:
54
+ message: Error message to display in the validatation toolbar when validation failed.
55
+ is_file: Explicitly check if the input is a valid file on the system.
56
+ is_dir: Explicitly check if the input is a valid directory/folder on the system.
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ message: str = "Input is not a valid path",
62
+ is_file: bool = False,
63
+ is_dir: bool = False,
64
+ ) -> None:
65
+ self._message = message
66
+ self._is_file = is_file
67
+ self._is_dir = is_dir
68
+
69
+ def validate(self, document) -> None:
70
+ """Check if user input is a filepath that exists on the system based on conditions.
71
+
72
+ This method is used internally by `prompt_toolkit <https://python-prompt-toolkit.readthedocs.io/en/master/>`_.
73
+
74
+ See Also:
75
+ https://python-prompt-toolkit.readthedocs.io/en/master/pages/asking_for_input.html?highlight=validator#input-validation
76
+ """
77
+ path = Path(document.text).expanduser()
78
+ if self._is_file and not path.is_file():
79
+ raise ValidationError(
80
+ message=self._message,
81
+ cursor_position=document.cursor_position,
82
+ )
83
+ elif self._is_dir and not path.is_dir():
84
+ raise ValidationError(
85
+ message=self._message,
86
+ cursor_position=document.cursor_position,
87
+ )
88
+ elif not path.exists():
89
+ raise ValidationError(
90
+ message=self._message,
91
+ cursor_position=document.cursor_position,
92
+ )
93
+
94
+
95
+ class EmptyInputValidator(Validator):
96
+ """:class:`~prompt_toolkit.validation.Validator` to validate if the input is empty.
97
+
98
+ Args:
99
+ message: Error message to display in the validatation toolbar when validation failed.
100
+ """
101
+
102
+ def __init__(self, message: str = "Input cannot be empty") -> None:
103
+ self._message = message
104
+
105
+ def validate(self, document) -> None:
106
+ """Check if user input is empty.
107
+
108
+ This method is used internally by `prompt_toolkit <https://python-prompt-toolkit.readthedocs.io/en/master/>`_.
109
+
110
+ See Also:
111
+ https://python-prompt-toolkit.readthedocs.io/en/master/pages/asking_for_input.html?highlight=validator#input-validation
112
+ """
113
+ if not len(document.text) > 0:
114
+ raise ValidationError(
115
+ message=self._message,
116
+ cursor_position=document.cursor_position,
117
+ )
118
+
119
+
120
+ class PasswordValidator(Validator):
121
+ """:class:`~prompt_toolkit.validation.Validator` to validate password compliance.
122
+
123
+ Args:
124
+ message: Error message to display in the validatation toolbar when validation failed.
125
+ length: The minimum length of the password.
126
+ cap: Password should include at least one capital letter.
127
+ special: Password should include at least one special char "@$!%*#?&".
128
+ number: Password should include at least one number.
129
+ """
130
+
131
+ def __init__(
132
+ self,
133
+ message: str = "Input is not compliant with the password constraints",
134
+ length: Optional[int] = None,
135
+ cap: bool = False,
136
+ special: bool = False,
137
+ number: bool = False,
138
+ ) -> None:
139
+ password_pattern = r"^"
140
+ if cap:
141
+ password_pattern += r"(?=.*[A-Z])"
142
+ if special:
143
+ password_pattern += r"(?=.*[@$!%*#?&])"
144
+ if number:
145
+ password_pattern += r"(?=.*[0-9])"
146
+ password_pattern += r"."
147
+ if length:
148
+ password_pattern += r"{%s,}" % length
149
+ else:
150
+ password_pattern += r"*"
151
+ password_pattern += r"$"
152
+ self._re = re.compile(password_pattern)
153
+ self._message = message
154
+
155
+ def validate(self, document) -> None:
156
+ """Check if user input is compliant with the specified password constraints.
157
+
158
+ This method is used internally by `prompt_toolkit <https://python-prompt-toolkit.readthedocs.io/en/master/>`_.
159
+
160
+ See Also:
161
+ https://python-prompt-toolkit.readthedocs.io/en/master/pages/asking_for_input.html?highlight=validator#input-validation
162
+ """
163
+ if not self._re.match(document.text):
164
+ raise ValidationError(
165
+ message=self._message, cursor_position=document.cursor_position
166
+ )
@@ -0,0 +1,217 @@
1
+ Metadata-Version: 2.4
2
+ Name: InquirerPrompt
3
+ Version: 0.3.0
4
+ Summary: Python port of Inquirer.js — a collection of common interactive command-line user interfaces (community-maintained fork of InquirerPy)
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: cli,prompt-toolkit,commandline,inquirer,development,interactive,prompt
8
+ Author: Kevin Zhuang
9
+ Author-email: kevin7441@gmail.com
10
+ Maintainer: Tobias Hochgürtel
11
+ Maintainer-email: tobiashochguertel@users.noreply.github.com
12
+ Requires-Python: >=3.9,<4.0
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: Microsoft
18
+ Classifier: Operating System :: Unix
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
26
+ Classifier: Topic :: Software Development
27
+ Classifier: Topic :: Software Development :: Libraries
28
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
29
+ Classifier: Topic :: Software Development :: User Interfaces
30
+ Provides-Extra: docs
31
+ Requires-Dist: Sphinx (>=4.1.2,<5.0.0) ; extra == "docs"
32
+ Requires-Dist: furo (>=2021.8.17b43,<2022.0.0) ; extra == "docs"
33
+ Requires-Dist: myst-parser (>=0.15.1,<0.16.0) ; extra == "docs"
34
+ Requires-Dist: pfzy (>=0.3.1,<0.4.0)
35
+ Requires-Dist: prompt-toolkit (>=3.0.1,<4.0.0)
36
+ Requires-Dist: sphinx-autobuild (>=2021.3.14,<2022.0.0) ; extra == "docs"
37
+ Requires-Dist: sphinx-copybutton (>=0.4.0,<0.5.0) ; extra == "docs"
38
+ Project-URL: Documentation, https://InquirerPrompt.readthedocs.io
39
+ Project-URL: Repository, https://github.com/tobiashochguertel/InquirerPy
40
+ Description-Content-Type: text/markdown
41
+
42
+ # InquirerPy (published as **InquirerPrompt**)
43
+
44
+ > **🍴 Community-Maintained Fork**
45
+ >
46
+ > This is a community fork of [kazhala/InquirerPy](https://github.com/kazhala/InquirerPy), maintained at
47
+ > [`tobiashochguertel/InquirerPy`](https://github.com/tobiashochguertel/InquirerPy).
48
+ >
49
+ > The original project is a fantastic library that we rely on and deeply appreciate. Unfortunately the upstream
50
+ > has been quiet for some time, and the community has accumulated valuable bug fixes, compatibility updates,
51
+ > and feature contributions that have not been reviewed or merged. Rather than letting those improvements go to
52
+ > waste, we decided to maintain this fork so that InquirerPy stays healthy and up-to-date for everyone.
53
+ >
54
+ > **Our goal is to contribute all improvements back upstream** — if the original author becomes active again
55
+ > we will happily defer to them. Until then, we are keeping the lights on here. 🙏
56
+ >
57
+ > | Item | Detail |
58
+ > |------|--------|
59
+ > | Upstream | [kazhala/InquirerPy](https://github.com/kazhala/InquirerPy) @ `0.3.4` (last commit Nov 2022) |
60
+ > | This fork | [tobiashochguertel/InquirerPy](https://github.com/tobiashochguertel/InquirerPy) |
61
+ > | Install | `pip install InquirerPrompt` (import still: `import InquirerPy`) |
62
+ > | Docs | [InquirerPrompt.readthedocs.io](https://InquirerPrompt.readthedocs.io) |
63
+ >
64
+ > If you are the original author and would like to resume ownership or collaborate, please open an issue — we
65
+ > would love to work together!
66
+
67
+ ---
68
+
69
+ [![CI](https://github.com/tobiashochguertel/InquirerPy/workflows/CI/badge.svg)](https://github.com/tobiashochguertel/InquirerPy/actions?query=workflow%3ACI)
70
+ [![Coverage](https://codecov.io/gh/tobiashochguertel/InquirerPy/branch/master/graph/badge.svg)](https://codecov.io/gh/tobiashochguertel/InquirerPy)
71
+ [![Version](https://img.shields.io/pypi/pyversions/InquirerPrompt)](https://pypi.org/project/InquirerPrompt/)
72
+ [![PyPi](https://img.shields.io/pypi/v/InquirerPrompt)](https://pypi.org/project/InquirerPrompt/)
73
+ [![Upstream](https://img.shields.io/badge/upstream-kazhala%2FInquirerPy-blue)](https://github.com/kazhala/InquirerPy)
74
+
75
+ Documentation: [InquirerPrompt.readthedocs.io](https://InquirerPrompt.readthedocs.io)
76
+
77
+ <!-- start intro -->
78
+
79
+ ## Introduction
80
+
81
+ `InquirerPy` is a Python port of the famous [Inquirer.js](https://github.com/SBoudrias/Inquirer.js/) (A collection of common interactive command line user interfaces).
82
+ This project is a re-implementation of the [PyInquirer](https://github.com/CITGuru/PyInquirer) project, with bug fixes of known issues, new prompts, backward compatible APIs
83
+ as well as more customisation options.
84
+
85
+ <!-- end intro -->
86
+
87
+ ![Demo](https://github.com/kazhala/gif/blob/master/InquirerPy-demo.gif)
88
+
89
+ ## Motivation
90
+
91
+ [PyInquirer](https://github.com/CITGuru/PyInquirer) is a great Python port of [Inquirer.js](https://github.com/SBoudrias/Inquirer.js/), however, the project is slowly reaching
92
+ to an unmaintained state with various issues left behind and no intention to implement more feature requests. I was heavily relying on this library for other projects but
93
+ could not proceed due to the limitations.
94
+
95
+ Some noticeable ones that bother me the most:
96
+
97
+ - hard limit on `prompt_toolkit` version 1.0.3
98
+ - various color issues
99
+ - various cursor issues
100
+ - No options for VI/Emacs navigation key bindings
101
+ - Pagination option doesn't work
102
+
103
+ This project uses python3.7+ type hinting with focus on resolving above issues while providing greater customisation options.
104
+
105
+ ## Requirements
106
+
107
+ ### OS
108
+
109
+ Leveraging [prompt_toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit), `InquirerPy` works cross platform for all OS. Although Unix platform may have a better experience than Windows.
110
+
111
+ ### Python
112
+
113
+ ```
114
+ python >= 3.7
115
+ ```
116
+
117
+ ## Getting Started
118
+
119
+ Checkout full documentation **[here](https://InquirerPrompt.readthedocs.io/)**.
120
+
121
+ ### Install
122
+
123
+ ```sh
124
+ pip3 install InquirerPy
125
+ ```
126
+
127
+ ### Quick Start
128
+
129
+ #### Classic Syntax (PyInquirer)
130
+
131
+ ```python
132
+ from InquirerPy import prompt
133
+
134
+ questions = [
135
+ {"type": "input", "message": "What's your name:", "name": "name"},
136
+ {"type": "confirm", "message": "Confirm?", "name": "confirm"},
137
+ ]
138
+ result = prompt(questions)
139
+ name = result["name"]
140
+ confirm = result["confirm"]
141
+ ```
142
+
143
+ #### Alternate Syntax
144
+
145
+ ```python
146
+ from InquirerPy import inquirer
147
+
148
+ name = inquirer.text(message="What's your name:").execute()
149
+ confirm = inquirer.confirm(message="Confirm?").execute()
150
+ ```
151
+
152
+ <!-- start migration -->
153
+
154
+ ## Migrating from PyInquirer
155
+
156
+ Most APIs from [PyInquirer](https://github.com/CITGuru/PyInquirer) should be compatible with `InquirerPy`. If you have discovered more incompatible APIs, please
157
+ create an issue or directly update README via a pull request.
158
+
159
+ ### EditorPrompt
160
+
161
+ `InquirerPy` does not support [editor](https://github.com/CITGuru/PyInquirer#editor---type-editor) prompt as of now.
162
+
163
+ ### CheckboxPrompt
164
+
165
+ The following table contains the mapping of incompatible parameters.
166
+
167
+ | PyInquirer | InquirerPy |
168
+ | --------------- | --------------- |
169
+ | pointer_sign | pointer |
170
+ | selected_sign | enabled_symbol |
171
+ | unselected_sign | disabled_symbol |
172
+
173
+ ### Style
174
+
175
+ Every style keys from [PyInquirer](https://github.com/CITGuru/PyInquirer) is present in `InquirerPy` except the ones in the following table.
176
+
177
+ | PyInquirer | InquirerPy |
178
+ | ---------- | ---------- |
179
+ | selected | pointer |
180
+
181
+ Although `InquirerPy` support all the keys from [PyInquirer](https://github.com/CITGuru/PyInquirer), the styling works slightly different.
182
+ Please refer to the [Style](https://InquirerPrompt.readthedocs.io/en/latest/pages/style.html) documentation for detailed information.
183
+
184
+ <!-- end migration -->
185
+
186
+ ## Similar projects
187
+
188
+ ### questionary
189
+
190
+ [questionary](https://github.com/tmbo/questionary) is a fantastic fork which supports `prompt_toolkit` 3.0.0+ with performance improvement and more customisation options.
191
+ It's already a well established and stable library.
192
+
193
+ Comparing with [questionary](https://github.com/tmbo/questionary), `InquirerPy` offers even more customisation options in styles, UI as well as key bindings. `InquirerPy` also provides a new
194
+ and powerful [fuzzy](https://InquirerPrompt.readthedocs.io/en/latest/pages/prompts/fuzzy.html) prompt.
195
+
196
+ ### python-inquirer
197
+
198
+ [python-inquirer](https://github.com/magmax/python-inquirer) is another great Python port of [Inquirer.js](https://github.com/SBoudrias/Inquirer.js/). Instead of using `prompt_toolkit`, it
199
+ leverages the library `blessed` to implement the UI.
200
+
201
+ Before implementing `InquirerPy`, this library came up as an alternative. It's a more stable library comparing to the original [PyInquirer](https://github.com/CITGuru/PyInquirer), however
202
+ it has a rather limited customisation options and an older UI which did not solve the issues I was facing described in the [Motivation](#Motivation) section.
203
+
204
+ Comparing with [python-inquirer](https://github.com/magmax/python-inquirer), `InquirerPy` offers a slightly better UI,
205
+ more customisation options in key bindings and styles, providing pagination as well as more prompts.
206
+
207
+ ## Credit
208
+
209
+ This project is based on the great work done by the following projects & their authors.
210
+
211
+ - [PyInquirer](https://github.com/CITGuru/PyInquirer)
212
+ - [prompt_toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit)
213
+
214
+ ## License
215
+
216
+ This project is licensed under [MIT](https://github.com/kazhala/InquirerPy/blob/master/LICENSE).
217
+