Common task patterns and recipes for inquirer-textual.
result = prompts.text("Input:")
# ALWAYS check command first
if result.command == 'select':
# Safe to use result.value
process(result.value)
elif result.command == 'quit':
print("User quit")
exit(0)def handle_result(result):
"""Standard result handler."""
if result.command == 'quit':
# Critical exit
exit(0)
elif result.command == 'cancel':
return None
elif result.command == 'select':
return result.value
else:
# Unknown command
return None# Define once, use everywhere
COMMON_SHORTCUTS = [
Shortcut('escape', 'cancel', 'Cancel'),
Shortcut('ctrl+h', 'help', 'Help')
]
result1 = prompts.text("Field 1:", shortcuts=COMMON_SHORTCUTS)
result2 = prompts.text("Field 2:", shortcuts=COMMON_SHORTCUTS)from textual.validation import Validator, ValidationResult
class CustomValidator(Validator):
def __init__(self, param):
super().__init__()
self.param = param
def validate(self, value: str) -> ValidationResult:
if condition_met(value, self.param):
return self.success()
return self.failure("Error message")
result = prompts.text("Input:", validators=CustomValidator(param))from textual.validation import Function
# Email validator
email_validator = Function(
lambda s: '@' in s and '.' in s,
"Invalid email format"
)
# Min length validator
min_length = lambda n: Function(
lambda s: len(s) >= n,
f"Minimum {n} characters"
)
# No spaces validator
no_spaces = Function(
lambda s: ' ' not in s,
"No spaces allowed"
)
# Alphanumeric validator
alphanumeric = Function(
lambda s: s.isalnum(),
"Only letters and numbers allowed"
)
result = prompts.text("Username:", validators=[min_length(3), no_spaces, alphanumeric])from textual.validation import Validator, ValidationResult
def create_range_validator(min_val: int, max_val: int) -> Validator:
"""Create validator for numeric range."""
class RangeValidator(Validator):
def validate(self, value: str) -> ValidationResult:
try:
num = int(value)
if min_val <= num <= max_val:
return self.success()
return self.failure(f"Must be between {min_val} and {max_val}")
except ValueError:
return self.failure("Must be a number")
return RangeValidator()
result = prompts.text("Age:", validators=create_range_validator(0, 120))def get_validated_input():
"""Get input with post-prompt validation and retry."""
while True:
result = prompts.text("Enter positive number:")
if result.command != 'select':
return None
try:
num = int(result.value)
if num > 0:
return num
else:
print("Must be positive")
except ValueError:
print("Must be a number")
value = get_validated_input()from inquirer_textual.common.Choice import Choice
def create_choices_from_dict(items):
"""Create Choice objects from dictionary list."""
return [
Choice(item['display_name'], data=item)
for item in items
]
items = [
{'display_name': 'Option A', 'id': 1, 'config': {...}},
{'display_name': 'Option B', 'id': 2, 'config': {...}}
]
choices = create_choices_from_dict(items)
result = prompts.select("Choose:", choices)
if result.command == 'select':
item_id = result.value.data['id']from inquirer_textual.common.Choice import Choice
actions = [
Choice("View", command="view"),
Choice("Edit", command="edit"),
Choice("Delete", command="delete"),
Choice("Cancel", command="cancel")
]
result = prompts.select("Action:", actions)
action_handlers = {
'view': lambda: view_item(),
'edit': lambda: edit_item(),
'delete': lambda: delete_item(),
'cancel': lambda: None
}
if result.command in action_handlers:
action_handlers[result.command]()from inquirer_textual.common.Choice import Choice
result = prompts.select("Pick:", [Choice("A", data={"id": 1}), Choice("B", data={"id": 2})])
if result.command == 'select':
if isinstance(result.value, Choice):
# Type-safe access to data
item_id: int = result.value.data["id"]
else:
# Handle string case
item_name: str = result.valuedef collect_user_data():
"""Collect user data with fixed form."""
widgets = [
InquirerText("First name:"),
InquirerText("Last name:"),
InquirerText("Email:"),
InquirerNumber("Age:")
]
result = prompts.multi(widgets)
if result.command == 'select':
first, last, email, age_str = result.value
return {
'first_name': first,
'last_name': last,
'email': email,
'age': int(age_str)
}
return None
data = collect_user_data()def build_dynamic_form(field_specs):
"""Build form from field specifications."""
widgets = []
for spec in field_specs:
if spec['type'] == 'text':
widgets.append(InquirerText(spec['label']))
elif spec['type'] == 'number':
widgets.append(InquirerNumber(spec['label']))
elif spec['type'] == 'select':
widgets.append(InquirerSelect(spec['label'], spec['choices']))
elif spec['type'] == 'confirm':
widgets.append(InquirerConfirm(spec['label'], default=spec.get('default', False)))
result = prompts.multi(widgets)
return result.value if result.command == 'select' else None
fields = [
{'type': 'text', 'label': 'Name:'},
{'type': 'select', 'label': 'Country:', 'choices': ['US', 'UK', 'CA']},
{'type': 'confirm', 'label': 'Subscribe:', 'default': True}
]
data = build_dynamic_form(fields)def ask_text(app, message, default='', validators=None, shortcuts=None):
"""Reusable text prompt helper."""
widget = InquirerText(message, default=default, validators=validators)
result = app.prompt(widget, shortcuts=shortcuts)
return result.value if result.command == 'select' else None
def ask_select(app, message, choices, default=None, shortcuts=None):
"""Reusable select prompt helper."""
widget = InquirerSelect(message, choices, default=default)
result = app.prompt(widget, shortcuts=shortcuts)
return result.value if result.command == 'select' else None
def ask_confirm(app, message, default=False):
"""Reusable confirm prompt helper."""
widget = InquirerConfirm(message, default=default)
result = app.prompt(widget)
return result.value if result.command == 'select' else False
def registration_flow(app):
name = ask_text(app, "Name:")
if not name:
return
email = ask_text(app, "Email:")
if not email:
return
subscribe = ask_confirm(app, "Subscribe?", default=True)
app.stop({'name': name, 'email': email, 'subscribe': subscribe})class FlowState:
def __init__(self):
self.data = {}
self.current_step = 0
def add(self, key, value):
self.data[key] = value
self.current_step += 1
def get_data(self):
return self.data
def stateful_flow(app):
state = FlowState()
name_result = app.prompt(InquirerText("Name:"))
if name_result.command != 'select':
return
state.add('name', name_result.value)
email_result = app.prompt(InquirerText("Email:"))
if email_result.command != 'select':
return
state.add('email', email_result.value)
app.stop(state.get_data())def wizard_flow(app):
steps = []
# Step 1
name_result = app.prompt(InquirerText("Name:"))
if name_result.command != 'select':
return
steps.append(('name', name_result.value))
# Step 2
role_result = app.prompt(InquirerSelect("Role:", ["Dev", "Designer", "Manager"]))
if role_result.command != 'select':
return
steps.append(('role', role_result.value))
# Confirm
confirm_result = app.prompt(InquirerConfirm("Submit?"))
if confirm_result.command == 'select' and confirm_result.value:
app.stop(dict(steps))def login():
"""Simple login form."""
username_result = prompts.text("Username:")
if username_result.command != 'select':
return None
password_result = prompts.secret("Password:")
if password_result.command != 'select':
return None
return {
'username': username_result.value,
'password': password_result.value
}
credentials = login()
if credentials:
authenticate(credentials['username'], credentials['password'])def configure_app():
"""Application configuration wizard."""
widgets = [
InquirerText("App name:"),
InquirerSelect("Environment:", ["Development", "Staging", "Production"]),
InquirerNumber("Port:"),
InquirerCheckbox("Features:", ["Auth", "Database", "Caching", "Logging"]),
InquirerConfirm("Enable debug mode?", default=False)
]
result = prompts.multi(widgets)
if result.command == 'select':
name, env, port_str, features, debug = result.value
return {
'name': name,
'environment': env,
'port': int(port_str),
'features': features,
'debug': debug
}
return None
config = configure_app()
if config:
save_config(config)def collect_feedback():
"""User feedback survey."""
def flow(app):
# Satisfaction
satisfied = app.prompt(InquirerConfirm("Satisfied with service?", default=True))
feedback = {'satisfied': satisfied.value}
if not satisfied.value:
# Dissatisfied - ask for details
issue = app.prompt(InquirerSelect(
"Main issue:",
["Too slow", "Too expensive", "Missing features", "Poor support"]
))
feedback['issue'] = issue.value
details = app.prompt(InquirerText("Please elaborate:"))
feedback['details'] = details.value
else:
# Satisfied - ask for rating
rating = app.prompt(InquirerSelect(
"Rating:",
["5 - Excellent", "4 - Good", "3 - Okay"]
))
feedback['rating'] = rating.value
app.stop(feedback)
app = InquirerApp()
result = app.run(inline=True, inquiry_func=flow)
return result.value
feedback = collect_feedback()def collect_items():
"""Collect multiple items until done."""
def flow(app):
items = []
while True:
item = app.prompt(InquirerText("Enter item (empty to finish):"))
if item.command != 'select':
break
if not item.value.strip():
break
items.append(item.value)
more = app.prompt(InquirerConfirm("Add another?", default=True))
if not more.value:
break
app.stop(items)
app = InquirerApp()
result = app.run(inline=True, inquiry_func=flow)
return result.value
items = collect_items()
print(f"Collected {len(items)} items")def show_menu():
"""Interactive menu system."""
menu_items = [
Choice("Create New", command="create"),
Choice("View Existing", command="view"),
Choice("Edit", command="edit"),
Choice("Delete", command="delete"),
Choice("Settings", command="settings"),
Choice("Exit", command="exit")
]
while True:
result = prompts.select("Main Menu:", menu_items)
if result.command == 'create':
create_item()
elif result.command == 'view':
view_items()
elif result.command == 'edit':
edit_item()
elif result.command == 'delete':
delete_item()
elif result.command == 'settings':
show_settings()
elif result.command == 'exit' or result.command == 'quit':
break
show_menu()def conditional_survey():
"""Survey with conditional questions."""
def flow(app):
data = {}
# Q1: User type
user_type = app.prompt(InquirerSelect(
"Are you a:",
["Student", "Professional", "Hobbyist"]
))
if user_type.command != 'select':
return
data['user_type'] = user_type.value
# Q2: Conditional on user type
if user_type.value == "Student":
school = app.prompt(InquirerText("School name:"))
data['school'] = school.value
year = app.prompt(InquirerSelect("Year:", ["1st", "2nd", "3rd", "4th"]))
data['year'] = year.value
elif user_type.value == "Professional":
company = app.prompt(InquirerText("Company:"))
data['company'] = company.value
experience = app.prompt(InquirerNumber("Years of experience:"))
data['experience'] = int(experience.value)
# Common question for all
interests = app.prompt(InquirerCheckbox(
"Interests:",
["Python", "JavaScript", "Machine Learning", "Web Dev"]
))
data['interests'] = interests.value
app.stop(data)
app = InquirerApp()
result = app.run(inline=True, inquiry_func=flow)
return result.value
data = conditional_survey()def get_valid_email():
"""Get valid email with retry."""
def flow(app):
while True:
email = app.prompt(InquirerText("Email:"))
if email.command != 'select':
return
if '@' in email.value and '.' in email.value:
app.stop(email.value)
return
else:
# Show error and retry
app.prompt(InquirerText("Invalid email! Press Enter to retry"))
app = InquirerApp()
result = app.run(inline=True, inquiry_func=flow)
return result.value
email = get_valid_email()def get_config_with_fallback():
"""Get config with fallback defaults."""
result = prompts.text("Server URL (leave empty for localhost):")
if result.command != 'select':
# User quit - use default
return {'url': 'localhost'}
url = result.value.strip() or 'localhost'
return {'url': url}
config = get_config_with_fallback()