Search code examples
pythonterminalcommand-line-interface

Create Python CLI with select interface


I'd like to create a Python CLI with an item selection interface that allows users to choose an item from a list. Something like:

Select a fruit (up/down to select and enter to confirm):
[x] Apple
[ ] Banana
[ ] Orange

I'd like users to be able to change their selection using the up/down arrows and press Enter to confirm.

Does a Python module exist with this functionality? I tried searching but couldn't find exactly what I wanted.

The select-shell Node.js package does exactly what I want.

The pick Python module does what I want, but it uses curses and opens up a simple GUI. I would like to avoid creating a GUI and keep all output in the terminal: this likely requires updating lines displayed to the terminal.

I'm currently using click but I don't believe it supports this functionality. I'm not sure how exactly to implement this kind of feature using cmd/readline and would appreciate any insight.


Solution

  • After a bit of searching, I found two libraries that met my needs!

    The first is python-inquirer, a Python port of Inquirer.js, a CLI library used by projects like Yeoman. I found this library to have a really nice API (built on top of blessings) but lacks polish when it comes to design/features.

    The second (which I will be using) is whaaaaat, another Python port of Inquirer. This library offers functionality much closer to the original Inquirer.js and is exactly what I needed. The API is less clean than that of python-inquirer, however.

    Examples:

    python-inquirer example:

    from pprint import pprint
    import inquirer
    
    questions = [
        inquirer.List(
            "size",
            message="What size do you need?",
            choices=["Jumbo", "Large", "Standard", "Medium", "Small", "Micro"],
        ),
    ]
    
    answers = inquirer.prompt(questions)
    pprint(answers)
    

    whaaaaat example:

    from whaaaaat import prompt, print_json, Separator
    
    questions = [
        {
            "type": "list",
            "name": "theme",
            "message": "What do you want to do?",
            "choices": [
                "Order a pizza",
                "Make a reservation",
                Separator(),
                "Ask for opening hours",
                {"name": "Contact support", "disabled": "Unavailable at this time"},
                "Talk to the receptionist",
            ],
        },
        {
            "type": "list",
            "name": "size",
            "message": "What size do you need?",
            "choices": ["Jumbo", "Large", "Standard", "Medium", "Small", "Micro"],
            "filter": lambda val: val.lower(),
        },
    ]
    
    answers = prompt(questions)
    print_json(answers)