0
# Core Classes and Configuration
1
2
Essential classes for building and customizing prompts, including choice configuration, styling, validation, conditional logic, and utility functions for enhanced prompt functionality.
3
4
## Capabilities
5
6
### Question Class
7
8
Core question class that serves as the foundation for all interactive prompts with execution methods and conditional logic.
9
10
```python { .api }
11
class Question:
12
application: Application[Any]
13
should_skip_question: bool
14
default: Any
15
16
def __init__(self, application: Application[Any]) -> None:
17
"""
18
Initialize question with prompt_toolkit application.
19
20
Args:
21
application: Configured prompt_toolkit Application instance
22
"""
23
24
def ask(self, patch_stdout: bool = False,
25
kbi_msg: str = DEFAULT_KBI_MESSAGE) -> Any:
26
"""
27
Execute question synchronously with error handling.
28
29
Args:
30
patch_stdout: Patch stdout to prevent interference
31
kbi_msg: Message displayed on keyboard interrupt
32
33
Returns:
34
User response value
35
36
Raises:
37
KeyboardInterrupt: User cancelled with appropriate message
38
"""
39
40
def unsafe_ask(self, patch_stdout: bool = False) -> Any:
41
"""
42
Execute question synchronously without error handling.
43
44
Args:
45
patch_stdout: Patch stdout to prevent interference
46
47
Returns:
48
User response value
49
"""
50
51
def ask_async(self, patch_stdout: bool = False,
52
kbi_msg: str = DEFAULT_KBI_MESSAGE) -> Any:
53
"""
54
Execute question asynchronously with error handling.
55
56
Args:
57
patch_stdout: Patch stdout to prevent interference
58
kbi_msg: Message displayed on keyboard interrupt
59
60
Returns:
61
User response value
62
63
Raises:
64
KeyboardInterrupt: User cancelled with appropriate message
65
"""
66
67
def unsafe_ask_async(self, patch_stdout: bool = False) -> Any:
68
"""
69
Execute question asynchronously without error handling.
70
71
Args:
72
patch_stdout: Patch stdout to prevent interference
73
74
Returns:
75
User response value
76
"""
77
78
def skip_if(self, condition: bool, default: Any = None) -> Question:
79
"""
80
Conditionally skip question execution.
81
82
Args:
83
condition: If True, skip question and return default
84
default: Value to return when question is skipped
85
86
Returns:
87
Self for method chaining
88
"""
89
```
90
91
### Choice Configuration
92
93
Advanced choice configuration for selection prompts with custom values, descriptions, shortcuts, and states.
94
95
```python { .api }
96
class Choice:
97
title: FormattedText
98
value: Optional[Any]
99
disabled: Optional[str]
100
checked: Optional[bool]
101
shortcut_key: Optional[Union[str, bool]]
102
description: Optional[str]
103
104
def __init__(self, title: FormattedText, value: Optional[Any] = None,
105
disabled: Optional[str] = None, checked: Optional[bool] = False,
106
shortcut_key: Optional[Union[str, bool]] = True,
107
description: Optional[str] = None) -> None:
108
"""
109
Configure a choice for selection prompts.
110
111
Args:
112
title: Display text for the choice (can be formatted text)
113
value: Return value when choice is selected (defaults to title)
114
disabled: Reason text if choice is disabled (None = enabled)
115
checked: Initially selected state for checkbox prompts
116
shortcut_key: Keyboard shortcut (True = auto-generate, str = custom, False = none)
117
description: Additional description text shown with choice
118
"""
119
120
@staticmethod
121
def build(c: Union[str, Choice, Dict]) -> Choice:
122
"""
123
Build Choice from string, existing Choice, or dictionary.
124
125
Args:
126
c: Choice specification as string, Choice instance, or dict
127
128
Returns:
129
Choice instance
130
"""
131
132
def get_shortcut_title(self) -> str:
133
"""
134
Get formatted shortcut display text.
135
136
Returns:
137
Formatted string showing shortcut key with title
138
"""
139
140
@property
141
def shortcut_key(self) -> Optional[str]:
142
"""Get the shortcut key for this choice."""
143
144
@property
145
def auto_shortcut(self) -> bool:
146
"""Check if shortcut is auto-generated."""
147
```
148
149
### Separator Class
150
151
Visual separators for organizing choice lists in selection prompts.
152
153
```python { .api }
154
class Separator(Choice):
155
default_separator: str = "-" * 15
156
line: str
157
158
def __init__(self, line: Optional[str] = None) -> None:
159
"""
160
Create a visual separator for choice lists.
161
162
Args:
163
line: Custom separator text (default: 15 dashes)
164
"""
165
```
166
167
### Form Classes
168
169
Form container and field definition classes for multi-question workflows.
170
171
```python { .api }
172
class FormField(NamedTuple):
173
key: str
174
question: Question
175
"""
176
Named tuple representing a question within a form.
177
178
Attributes:
179
key: Field identifier for result dictionary
180
question: Question instance to execute
181
"""
182
183
class Form:
184
form_fields: Sequence[FormField]
185
186
def __init__(self, *form_fields: FormField) -> None:
187
"""
188
Initialize form with FormField instances.
189
190
Args:
191
*form_fields: FormField instances defining the form structure
192
"""
193
194
def ask(self, patch_stdout: bool = False,
195
kbi_msg: str = DEFAULT_KBI_MESSAGE) -> Dict[str, Any]:
196
"""
197
Execute form synchronously with error handling.
198
199
Args:
200
patch_stdout: Patch stdout to prevent interference
201
kbi_msg: Message displayed on keyboard interrupt
202
203
Returns:
204
Dictionary mapping field keys to user responses
205
"""
206
207
def unsafe_ask(self, patch_stdout: bool = False) -> Dict[str, Any]:
208
"""Execute form synchronously without error handling."""
209
210
def ask_async(self, patch_stdout: bool = False,
211
kbi_msg: str = DEFAULT_KBI_MESSAGE) -> Dict[str, Any]:
212
"""Execute form asynchronously with error handling."""
213
214
def unsafe_ask_async(self, patch_stdout: bool = False) -> Dict[str, Any]:
215
"""Execute form asynchronously without error handling."""
216
```
217
218
## Utility Functions
219
220
### Formatted Text Output
221
222
Enhanced text output with styling support.
223
224
```python { .api }
225
def print(text: str, style: Optional[str] = None, **kwargs) -> None:
226
"""
227
Print formatted text with optional styling.
228
229
Args:
230
text: Text to print
231
style: Style specification for the text
232
**kwargs: Additional formatting arguments
233
"""
234
235
def press_any_key_to_continue(message: Optional[str] = None,
236
style: Optional[Style] = None,
237
**kwargs) -> Question:
238
"""
239
Create a prompt that waits for any key press.
240
241
Args:
242
message: Custom message to display (default: "Press any key to continue...")
243
style: Custom styling configuration
244
**kwargs: Additional prompt_toolkit arguments
245
246
Returns:
247
Question instance that waits for key press
248
"""
249
```
250
251
### Style and Validation Utilities
252
253
Helper functions for styling and validation configuration.
254
255
```python { .api }
256
def merge_styles_default(styles: List[Optional[Style]]) -> Style:
257
"""
258
Merge custom styles with default questionary styling.
259
260
Args:
261
styles: List of Style instances to merge
262
263
Returns:
264
Merged Style instance
265
"""
266
267
def build_validator(validate: Any) -> Optional[Validator]:
268
"""
269
Convert validation input to Validator instance.
270
271
Args:
272
validate: Validation function, Validator instance, or None
273
274
Returns:
275
Validator instance or None
276
"""
277
```
278
279
## Constants and Configuration
280
281
### UI Constants
282
283
```python { .api }
284
# Response constants
285
YES: str = "Yes"
286
NO: str = "No"
287
YES_OR_NO: str = "(Y/n)"
288
NO_OR_YES: str = "(y/N)"
289
290
# UI element constants
291
DEFAULT_SELECTED_POINTER: str = "»"
292
INDICATOR_SELECTED: str = "●"
293
INDICATOR_UNSELECTED: str = "○"
294
DEFAULT_QUESTION_PREFIX: str = "?"
295
296
# Message constants
297
DEFAULT_KBI_MESSAGE: str = "\nCancelled by user\n"
298
INVALID_INPUT: str = "Invalid input"
299
INSTRUCTION_MULTILINE: str = "Press ESC then ENTER to submit"
300
301
# Style constants
302
DEFAULT_STYLE: Style # Default prompt styling configuration
303
```
304
305
### Type Definitions
306
307
```python { .api }
308
# Text formatting type
309
FormattedText = Union[str, List[Tuple[str, str]], List[Tuple[str, str, Callable]], None]
310
311
# Re-exported from prompt_toolkit
312
class Style:
313
"""Style configuration for prompt appearance."""
314
315
class Validator:
316
"""Base class for input validation."""
317
318
def validate(self, document: Document) -> None:
319
"""
320
Validate document and raise ValidationError if invalid.
321
322
Args:
323
document: Input document to validate
324
325
Raises:
326
ValidationError: If validation fails
327
"""
328
329
class ValidationError(Exception):
330
"""Exception raised when input validation fails."""
331
332
def __init__(self, message: str = "", cursor_position: int = 0) -> None:
333
"""
334
Initialize validation error.
335
336
Args:
337
message: Error message to display
338
cursor_position: Cursor position for error highlighting
339
"""
340
```
341
342
## Usage Examples
343
344
### Custom Question Configuration
345
346
```python
347
import questionary
348
from questionary import Question, Choice, Separator
349
350
# Create choices with custom configuration
351
choices = [
352
Choice("High Priority", value="high", shortcut_key="h",
353
description="Urgent tasks requiring immediate attention"),
354
Choice("Medium Priority", value="medium", shortcut_key="m",
355
description="Important tasks with flexible timing"),
356
Choice("Low Priority", value="low", shortcut_key="l",
357
description="Nice-to-have tasks for later"),
358
Separator("────────────"),
359
Choice("Cancel", value=None, shortcut_key="c")
360
]
361
362
priority = questionary.select(
363
"Select task priority:",
364
choices=choices,
365
show_description=True
366
).ask()
367
368
print(f"Selected priority: {priority}")
369
```
370
371
### Conditional Question Execution
372
373
```python
374
import questionary
375
376
# Create questions with conditional logic
377
enable_feature = questionary.confirm("Enable advanced features?").ask()
378
379
# Skip configuration if feature is disabled
380
config_question = questionary.text(
381
"Enter configuration:"
382
).skip_if(
383
condition=not enable_feature,
384
default="default_config"
385
)
386
387
config = config_question.ask()
388
print(f"Configuration: {config}")
389
```
390
391
### Custom Validation
392
393
```python
394
import questionary
395
from questionary import Validator, ValidationError
396
397
class PortValidator(Validator):
398
def validate(self, document):
399
try:
400
port = int(document.text)
401
if not (1 <= port <= 65535):
402
raise ValidationError(
403
message="Port must be between 1 and 65535",
404
cursor_position=len(document.text)
405
)
406
except ValueError:
407
raise ValidationError(
408
message="Port must be a number",
409
cursor_position=len(document.text)
410
)
411
412
port = questionary.text(
413
"Enter port number:",
414
validate=PortValidator()
415
).ask()
416
```
417
418
### Advanced Choice Building
419
420
```python
421
import questionary
422
from questionary import Choice
423
424
# Build choices from different sources
425
string_choices = ["Option 1", "Option 2"]
426
dict_choices = [
427
{"title": "Database Setup", "value": "db", "disabled": "Not available"},
428
{"title": "Web Server", "value": "web"}
429
]
430
431
# Convert all to Choice objects
432
processed_choices = [Choice.build(c) for c in string_choices + dict_choices]
433
434
selection = questionary.select(
435
"Choose setup option:",
436
choices=processed_choices
437
).ask()
438
```
439
440
### Custom Separators
441
442
```python
443
import questionary
444
from questionary import Choice, Separator
445
446
# Create themed choice list with separators
447
menu_choices = [
448
Choice("New Project", value="new"),
449
Choice("Open Project", value="open"),
450
Separator("═══ Recent Projects ═══"),
451
Choice("Project Alpha", value="alpha"),
452
Choice("Project Beta", value="beta"),
453
Separator("═══ Tools ═══"),
454
Choice("Settings", value="settings"),
455
Choice("Help", value="help")
456
]
457
458
selection = questionary.select(
459
"Main Menu:",
460
choices=menu_choices
461
).ask()
462
```
463
464
### Form with Manual Field Creation
465
466
```python
467
import questionary
468
from questionary import FormField, Form
469
470
# Create form fields manually for precise control
471
fields = [
472
FormField("username", questionary.text("Username:")),
473
FormField("password", questionary.password("Password:")),
474
FormField("remember", questionary.confirm("Remember login?", default=True))
475
]
476
477
login_form = Form(*fields)
478
credentials = login_form.ask()
479
480
print(f"Logging in {credentials['username']}")
481
if credentials['remember']:
482
print("Login will be remembered")
483
```
484
485
### Styling Integration
486
487
```python
488
import questionary
489
from prompt_toolkit.styles import Style
490
491
# Custom style configuration
492
custom_style = Style([
493
('question', 'fg:#ff0066 bold'),
494
('answer', 'fg:#44ff00 bold'),
495
('pointer', 'fg:#673ab7 bold'),
496
('highlighted', 'bg:#673ab7 fg:#ffffff'),
497
('selected', 'fg:#cc5454'),
498
('separator', 'fg:#cc5454'),
499
('instruction', 'fg:#888888'),
500
('text', '#ffffff'),
501
('disabled', 'fg:#858585 italic')
502
])
503
504
# Apply styling to questions
505
styled_input = questionary.text(
506
"Enter your name:",
507
style=custom_style
508
).ask()
509
510
styled_selection = questionary.select(
511
"Choose option:",
512
choices=["Option A", "Option B", "Option C"],
513
style=custom_style
514
).ask()
515
```