Skip to content

questionary

questionary is a Python library based on Prompt Toolkit to effortlessly building pretty command line interfaces. It makes it very easy to query your user for input.

Installation

pip install questionary

Usage

Asking a single question

Questionary ships with a lot of different Question Types to provide the right prompt for the right question. All of them work in the same way though.

import questionary

answer = questionary.text("What's your first name").ask()

Since our question is a text prompt, answer will contain the text the user typed after they submitted it.

Asking Multiple Questions

You can use the form() function to ask a collection of Questions. The questions will be asked in the order they are passed to questionary.form.

import questionary

answers = questionary.form(
    first=questionary.confirm("Would you like the next question?", default=True),
    second=questionary.select("Select item", choices=["item1", "item2", "item3"]),
).ask()

The output will have the following format:

{'first': True, 'second': 'item2'}

The prompt() function also allows you to ask a collection of questions, however instead of taking Question instances, it takes a dictionary:

import questionary

questions = [
    {
        "type": "confirm",
        "name": "first",
        "message": "Would you like the next question?",
        "default": True,
    },
    {
        "type": "select",
        "name": "second",
        "message": "Select item",
        "choices": ["item1", "item2", "item3"],
    },
]

questionary.prompt(questions)

Conditionally skip questions

Sometimes it is helpful to be able to skip a question based on a condition. To avoid the need for an if around the question, you can pass the condition when you create the question:

import questionary

DISABLED = True
response = questionary.confirm("Are you amazed?").skip_if(DISABLED, default=True).ask()

If the condition (in this case DISABLED) is True, the question will be skipped and the default value gets returned, otherwise the user will be prompted as usual and the default value will be ignored.

Exit when using control + c

If you want the question to exit when it receives a KeyboardInterrupt event, use unsafe_ask instead of ask.

Question types

The different question types are meant to cover different use cases. The parameters and configuration options are explained in detail for each type. But before we get into to many details, here is a cheatsheet with the available question types:

  • Use Text to ask for free text input.

  • Use Password to ask for free text where the text is hidden.

  • Use File Path to ask for a file or directory path with autocompletion.

  • Use Confirmation to ask a yes or no question.

>>> questionary.confirm("Are you amazed?").ask()
? Are you amazed? Yes
True
  • Use Select to ask the user to select one item from a beautiful list.

  • Use Raw Select to ask the user to select one item from a list.

  • Use Checkbox to ask the user to select any number of items from a list.

  • Use Autocomplete to ask for free text with autocomplete help.

Check the examples to see them in action and how to use them.

Autocomplete answers

If you want autocomplete with fuzzy finding use:

import questionary
from prompt_toolkit.completion import FuzzyWordCompleter

questionary.autocomplete(
    "Save to (q to cancel): ",
    choices=destination_directories,
    completer=FuzzyWordCompleter(destination_directories),
).ask()

Styling

Don't highlight the selected option by default

If you don't want to highlight the default choice in the select question use the next style:

from questionary import Style

choice = select(
    "Question title: ",
    choices=["a", "b", "c"],
    default="a",
    style=Style([("selected", "noreverse")]),
).ask()

Testing

Testing questionary code can be challenging because it involves interactive prompts that expect user input. However, there are ways to automate the testing process. You can use libraries like pexpect, pytest, and pytest-mock to simulate user input and test the behavior of your code.

Unit testing

Here’s how you can approach testing questionary code using pytest-mock to mock questionary functions

You can mock questionary functions like questionary.select().ask() to simulate user choices without actual user interaction.

Testing a single questionary.text prompt

Function to Test

Let's assume you have a function that asks the user for their name:

import questionary

def ask_name() -> str:
    name = questionary.text("What's your name?").ask()
    return name
Test Using pytest-mock

You can test this function by mocking the questionary.text prompt to simulate the user's input.

import pytest
from your_module import ask_name

def test_ask_name(mocker):
    # Mock the text function to simulate user input
    mock_text = mocker.patch('questionary.text')

    # Define the response for the prompt
    mock_text.return_value.ask.return_value = "Alice"

    result = ask_name()

    assert result == "Alice"

Test a function that has many questions

Here’s an example of how to test a function that contains two questionary.text prompts using pytest-mock.

Function to Test

Let's assume you have a function that asks for the first and last names of a user:

import questionary

def ask_full_name() -> dict:
    first_name = questionary.text("What's your first name?").ask()
    last_name = questionary.text("What's your last name?").ask()
    return {"first_name": first_name, "last_name": last_name}
Test Using pytest-mock

You can mock both questionary.text calls to simulate user input for both the first and last names:

import pytest
from your_module import ask_full_name

def test_ask_full_name(mocker):
    # Mock the text function for the first name prompt
    mock_text_first = mocker.patch('questionary.text')
    # Define the response for the first name prompt
    mock_text_first.side_effect = ["Alice", "Smith"]

    result = ask_full_name()

    assert result == {"first_name": "Alice", "last_name": "Smith"}

Integration testing

To test questionary code, follow the guidelines of testing prompt_toolkit.

References