Interactive Prompts
Interactive prompts collect user input for wizards, confirmations, and guided setup. Use Click's built-in prompts for simple cases; questionary for rich selection UIs.
Recipe
import questionary
name = questionary.text("Project name:").ask()
framework = questionary.select("Framework?", choices=["FastAPI", "Django", "Flask"]).ask()
confirmed = questionary.confirm("Proceed?").ask()When to reach for this:
- Project scaffolding wizards (
cookiecutter-style) - Destructive operations needing confirmation
- Multi-step setup where flags would be unwieldy
Working Example
import questionary
from rich.console import Console
console = Console()
def run_setup_wizard() -> dict:
if not questionary.confirm("Run interactive setup?").ask():
raise SystemExit("Aborted")
answers = {
"name": questionary.text("Project name:", validate=lambda t: len(t) > 0 or "Required").ask(),
"framework": questionary.select(
"Web framework:",
choices=["FastAPI", "Django", "Flask"],
).ask(),
"database": questionary.checkbox(
"Features:",
choices=["postgres", "redis", "celery"],
).ask(),
"deploy": questionary.select(
"Deployment target:",
choices=["Docker", "Lambda", "VPS"],
).ask(),
}
if not questionary.confirm(f"Create {answers['name']} with {answers['framework']}?").ask():
raise SystemExit("Aborted")
return answers
if __name__ == "__main__":
config = run_setup_wizard()
console.print(f"[green]Creating project: {config}[/green]")What this demonstrates:
- Text, select, checkbox, and confirm prompt types
- Validation on text input
- Abort on cancellation (Ctrl+C or No)
- Rich output after wizard completes
Deep Dive
Prompt Selection
| Library | Best for |
|---|---|
click.confirm/prompt | Simple confirm/text |
questionary | Select, checkbox, autocomplete |
prompt_toolkit | Full custom REPL |
InquirerPy | Alternative to questionary |
Gotchas
- Interactive prompts in CI - hangs forever. Fix: detect
CI=trueor--yesflag to skip prompts. - No default for destructive confirm - accidental yes. Fix: default=False on
confirm(). - Password echo - visible on screen. Fix:
questionary.password()orclick.prompt(hide_input=True). - Prompts without non-interactive fallback - breaks scripting. Fix: every prompt needs a CLI flag equivalent.
- Unicode on Windows terminals - display issues. Fix: Rich Console or enable UTF-8 terminal.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| CLI flags only | Scriptable automation | Guided first-time setup |
| web-based setup | Complex configuration | Terminal-only tool |
| config file | Repeatable deploys | Initial onboarding |
FAQs
questionary or click.prompt?
click.prompt for simple text/confirm. questionary for select, checkbox, autocomplete.
How do I skip prompts in CI?
if os.environ.get("CI"): use defaults or --yes / --non-interactive flag.
How do I handle Ctrl+C?
questionary returns None on cancel. Check and exit gracefully.
Password input?
questionary.password("API key:").ask().
Autocomplete?
questionary.autocomplete("Path:", choices=file_list).ask().
Can I style prompts?
questionary supports style dictionaries for colors.
Testing interactive CLIs?
Mock questionary.text(...).ask() return values in pytest.
Multi-page wizards?
Chain prompts in functions; show progress with Rich.
prompt_toolkit when?
Building a full REPL or syntax-highlighted input. Heavier than questionary.
Default selections?
questionary.select(..., default="FastAPI").ask().
Related
- Click - built-in confirm/prompt
- Rich Output - formatted wizard output
- Config & Environment - post-wizard config
- CLI Best Practices - interactive vs scriptable
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.