Interactive Prompts Guide¶
ASCIIColors provides a complete, drop-in replacement for the questionary library, offering interactive prompts for user input with beautiful styling and validation.
Installation Note¶
No additional installation needed! The prompts are built into ascii_colors.
Basic Usage¶
Import Styles¶
# Method 1: Module-like import (drop-in questionary replacement)
from ascii_colors import questionary
name = questionary.text("What's your name?").ask()
# Method 2: Direct imports
from ascii_colors import text, confirm, select, checkbox
name = text("What's your name?").ask()
# Method 3: Full path
from ascii_colors.questionary import text, confirm
name = text("What's your name?").ask()
Question Types¶
Text Input¶
Single-line text with optional default and validation:
from ascii_colors import questionary
# Basic text
name = questionary.text("What's your name?").ask()
# With default value
name = questionary.text(
"What's your name?",
default="Anonymous"
).ask()
# With validation
email = questionary.text(
"Enter email",
validate=lambda x: "@" in x or "Invalid email"
).ask()
# Multiline text
description = questionary.text(
"Enter description",
multiline=True
).ask()
Password Input¶
Hidden input with optional confirmation:
from ascii_colors import questionary
# Basic password (hidden)
password = questionary.password("Enter password").ask()
# With confirmation (enter twice)
password = questionary.password(
"Set password",
confirm=True,
confirm_message="Confirm password"
).ask()
Confirmation (Yes/No)¶
Boolean choice with keyboard shortcuts:
from ascii_colors import questionary
# Simple yes/no
if questionary.confirm("Continue?").ask():
print("Continuing...")
# With default
proceed = questionary.confirm(
"Proceed?",
default=True # Y is capitalized in [Y/n]
).ask()
# Default no
delete = questionary.confirm(
"Delete file?",
default=False # N is capitalized in [y/N]
).ask()
Single Selection (Select)¶
Arrow-key navigable list:
from ascii_colors import questionary
# Simple list
color = questionary.select(
"Favorite color?",
choices=["Red", "Green", "Blue", "Yellow"]
).ask()
# With dictionary choices (name shown, value returned)
format_choice = questionary.select(
"Export format",
choices=[
{"name": "JSON (recommended)", "value": "json"},
{"name": "YAML", "value": "yaml"},
{"name": "XML (legacy)", "value": "xml", "disabled": True},
{"name": "CSV", "value": "csv"}
],
default="json"
).ask() # Returns "json", "yaml", etc., not the display name
# With icons
action = questionary.select(
"Choose action",
choices=[
{"name": "🚀 Deploy", "value": "deploy"},
{"name": "🧪 Test", "value": "test"},
{"name": "📊 Metrics", "value": "metrics"}
]
).ask()
Multi-Selection (Checkbox)¶
Select multiple items with spacebar:
from ascii_colors import questionary
# Basic checkbox
features = questionary.checkbox(
"Select features",
choices=["Auth", "Logging", "Cache", "API"]
).ask() # Returns list like ["Auth", "Cache"]
# With pre-selected items
features = questionary.checkbox(
"Select toppings",
choices=[
{"name": "Cheese", "value": "cheese", "checked": True},
{"name": "Pepperoni", "value": "pepperoni"},
{"name": "Mushrooms", "value": "mushrooms", "checked": True}
]
).ask()
# Minimum selection requirement
required = questionary.checkbox(
"Select at least one",
choices=["Option A", "Option B", "Option C"],
min_selected=1
).ask()
# Keyboard shortcuts:
# ↑/↓ - Navigate
# Space - Toggle selection
# a - Toggle all
# Enter - Confirm
Autocomplete¶
Type to filter suggestions:
from ascii_colors import questionary
cities = ["Amsterdam", "Athens", "Bangkok", "Barcelona", "Berlin", "Boston"]
# Basic autocomplete
city = questionary.autocomplete(
"Enter city",
choices=cities
).ask()
# Case-insensitive with middle matching
tech = questionary.autocomplete(
"Search technology",
choices=["Python", "JavaScript", "TypeScript", "Java", "Go", "Rust"],
ignore_case=True,
match_middle=True, # "script" matches "JavaScript"
max_suggestions=5
).ask()
Forms (Multiple Questions)¶
Chain questions together:
from ascii_colors import questionary
# Sequential form
user_info = questionary.form(
questionary.text("First name"),
questionary.text("Last name"),
questionary.password("Password", confirm=True),
questionary.confirm("Subscribe to newsletter?", default=False)
).ask()
# Returns: {
# "First name": "...",
# "Last name": "...",
# "Password": "...",
# "Subscribe to newsletter?": True/False
# }
Advanced Features¶
Custom Validators¶
Create reusable validation logic:
from ascii_colors.questionary import Validator, ValidationError
class EmailValidator(Validator):
def validate(self, document):
if "@" not in document:
raise ValidationError("Email must contain @")
if "." not in document.split("@")[-1]:
raise ValidationError("Email must have valid domain")
class StrongPasswordValidator(Validator):
def validate(self, document):
if len(document) < 8:
raise ValidationError("Password must be at least 8 characters")
if not any(c.isupper() for c in document):
raise ValidationError("Need uppercase letter")
if not any(c.islower() for c in document):
raise ValidationError("Need lowercase letter")
if not any(c.isdigit() for c in document):
raise ValidationError("Need digit")
# Usage
email = questionary.text("Email:", validate=EmailValidator()).ask()
password = questionary.password(
"Create password",
validate=StrongPasswordValidator()
).ask()
Conditional Questions¶
Skip questions based on previous answers:
from ascii_colors import questionary
# Basic skip
is_company = questionary.confirm("Is this a company account?").ask()
company_name = questionary.text(
"Company name"
).skip_if(not is_company, default="N/A").ask()
# In forms, previous answers are available
def ask_details(answers):
if answers.get("is_company"):
return questionary.text("Company size")
return questionary.text("Personal website").skip_if(True, default=None)
# Note: skip_if returns the default value when condition is True
Custom Styling¶
Apply colors to prompts:
from ascii_colors import questionary, ASCIIColors
# Custom colors
name = questionary.text(
"Your name",
style={
"question": ASCIIColors.color_bright_green,
}
).ask()
# The style dict supports:
# - question: Prompt color
# - answer: Answer display color
# - pointer: Selection pointer color
# - highlight: Selected item highlight
# - text: Input text color
Error Handling¶
Keyboard Interrupt¶
Handle user cancellation gracefully:
from ascii_colors import questionary
try:
result = questionary.confirm("Continue?").ask()
except KeyboardInterrupt:
print("User cancelled")
result = None
# Or use the kbi_msg parameter
result = questionary.confirm("Continue?").ask(kbi_msg="Operation cancelled")
Unsafe Ask (No Exception Handling)¶
Let KeyboardInterrupt propagate:
# Regular ask catches KeyboardInterrupt
result = questionary.text("Name?").ask() # Returns None on Ctrl+C
# Unsafe ask propagates the exception
result = questionary.text("Name?").unsafe_ask() # Raises KeyboardInterrupt
Complete Examples¶
User Registration¶
from ascii_colors import questionary, rich
def register_user():
rich.print("[bold]User Registration[/bold]\n")
# Basic info
first_name = questionary.text("First name").ask()
last_name = questionary.text("Last name").ask()
# Email with validation
from ascii_colors.questionary import Validator, ValidationError
class EmailValidator(Validator):
def validate(self, document):
if "@" not in document or "." not in document.split("@")[-1]:
raise ValidationError("Please enter a valid email")
email = questionary.text("Email", validate=EmailValidator()).ask()
# Account type
is_premium = questionary.confirm(
"Upgrade to premium?",
default=False
).ask()
# Premium features selection
if is_premium:
features = questionary.checkbox(
"Select premium features",
choices=[
{"name": "Advanced analytics", "value": "analytics", "checked": True},
{"name": "Priority support", "value": "support", "checked": True},
{"name": "Custom integrations", "value": "integrations"}
]
).ask()
else:
features = []
# Password
password = questionary.password(
"Create password",
confirm=True
).ask()
# Terms
accept_terms = questionary.confirm(
"Accept terms of service?",
default=False
).ask()
if not accept_terms:
rich.print("[red]Registration cancelled[/red]")
return None
return {
"name": f"{first_name} {last_name}",
"email": email,
"premium": is_premium,
"features": features,
"password_set": bool(password)
}
Configuration Wizard¶
from ascii_colors import questionary, rich
def setup_project():
rich.print("[bold green]Project Setup Wizard[/bold green]\n")
# Project basics
config = questionary.form(
questionary.text("Project name"),
questionary.select(
"Project type",
choices=["Web App", "API", "CLI Tool", "Library"]
),
questionary.select(
"Python version",
choices=["3.8", "3.9", "3.10", "3.11", "3.12"],
default="3.11"
)
).ask()
# Features based on type
features = []
if config["Project type"] in ["Web App", "API"]:
features = questionary.checkbox(
"Select web features",
choices=[
"Authentication",
"Database",
"Caching",
"Background jobs",
"WebSocket"
]
).ask()
# Database selection if needed
db = None
if "Database" in (features or []):
db = questionary.select(
"Database",
choices=["PostgreSQL", "MySQL", "SQLite", "MongoDB"]
).ask()
# Deployment target
deploy = questionary.select(
"Deployment target",
choices=[
{"name": "Docker (recommended)", "value": "docker"},
{"name": "AWS", "value": "aws"},
{"name": "Google Cloud", "value": "gcp"},
{"name": "Azure", "value": "azure"},
{"name": "Self-hosted", "value": "self"}
]
).ask()
# CI/CD
cicd = questionary.confirm("Setup CI/CD?", default=True).ask()
rich.print("\n[bold]Configuration Summary:[/bold]")
for key, value in config.items():
rich.print(f" [dim]{key}:[/dim] [cyan]{value}[/cyan]")
rich.print(f" [dim]Features:[/dim] [cyan]{features or 'None'}[/cyan]")
rich.print(f" [dim]Database:[/dim] [cyan]{db or 'None'}[/cyan]")
rich.print(f" [dim]Deploy:[/dim] [cyan]{deploy}[/cyan]")
rich.print(f" [dim]CI/CD:[/dim] [cyan]{'Yes' if cicd else 'No'}[/cyan]")
return {
**config,
"features": features,
"database": db,
"deployment": deploy,
"cicd": cicd
}
Best Practices¶
Always handle KeyboardInterrupt - Users may cancel at any time
Use validators for input quality - Don’t rely on post-validation
Provide sensible defaults - Speed up common workflows
Group related questions in forms - Better UX than individual asks
Use conditional skips - Don’t ask irrelevant questions
Test with various terminal sizes - Ensure layout works
Provide clear, action-oriented labels - “Deploy to production” vs “Production”
API Compatibility¶
ASCIIColors’ questionary module is designed for drop-in compatibility:
Feature |
Status |
|---|---|
|
✓ Fully compatible |
|
✓ Fully compatible |
|
✓ Fully compatible |
|
✓ Fully compatible |
|
✓ Fully compatible |
|
✓ Fully compatible |
|
✓ Fully compatible |
Custom validators |
✓ Compatible |
|
✓ Compatible |
|
✓ Compatible |
|
✓ Compatible |
Styling dict |
✓ Compatible (subset) |
Differences from original questionary:
Simpler styling system (ANSI colors vs Rich styles)
No external dependencies
Built on ASCIIColors’ menu system
Keyboard navigation identical
See API Reference for complete class documentation.