Back to home

PrerequisitesGetting startedSimple input promptWorking with dialogsSummaryResources and further reading
CLI Prompts In Python main image

CLI Prompts In Python

This post will use the PyInquirer library to demonstrate how to add some command line prompts to make it easier to build an interactive program.

It will build off the work done on learning Python Fire basics in a previous blog post.

Prerequisites

  1. Familiarity with Pipenv. See here for my post on Pipenv.
  2. Building CLIs with Python.

Getting started

This code works off what was done in Building CLIs with Python.

You should already have the code. In that blog post, all code was posted into a hello-fire directory with cli.py as the main file with a few dependencies already installed.

The code is available on my GitHub repo.

We are going to add the PyInquirer to our dependencies to use in the cli.py file:

1 2 # Add the lib $ pipenv install pyinquirer

At this stage, we had a cli.py file that looks like the following:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #!/usr/bin/env python import fire class IngestionStage(object): def run(self): return 'Ingesting! Nom nom nom...' class DigestionStage(object): def __init__(self): self.satiated = False def run(self, volume: int = 1) -> str: self.satiated = True return ' '.join(['Burp!'] * volume) def status(self): return 'Satiated.' if self.satiated else 'Not satiated.' class Pipeline(object): def __init__(self): self.ingestion = IngestionStage() self.digestion = DigestionStage() def run(self): print(self.ingestion.run()) print(self.digestion.run()) print(self.digestion.status()) return 'Pipeline complete' if __name__ == '__main__': fire.Fire(Pipeline)

Opening the virtual environment and running python cli.py [command] would run the program (ie python cli.py digestion run would output Burp!).

Simple input prompt

For our first prompt, we are going to update the DigestionStage class run method to default to 0 burps and request that we provide a volume.

We do this with Python Fire as a call without an value for the argument would raise an error.

We can import the prompt function and use that to get the input:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from PyInquirer import prompt # ... omitted for brevity class DigestionStage(object): def __init__(self): self.satiated = False # Update code with prompt question here def run(self, volume: int = 0) -> str: questions = [ { 'type': 'input', 'name': 'volume', 'message': 'How many burps?', } ] if volume == 0: volume = int(prompt(questions)['volume']) self.satiated = True return ' '.join(['Burp!'] * volume) def status(self): return 'Satiated.' if self.satiated else 'Not satiated.'

Now if we run our command line call python cli.py digestion run:

1 2 3 $ python cli.py digestion run ? How many burps? 5 Burp! Burp! Burp! Burp! Burp!

A simple input prompt is all we need to get started.

Note: We could have simply removed volume altogether but we have left is so that Python Fire can cast the input into an int via a flag --volume without a prompt.

1 2 3 $ python cli.py digestion run --volume=10 # ... no prompt Burp! Burp! Burp! Burp! Burp! Burp! Burp! Burp! Burp! Burp!

Working with dialogs

Prompt toolkit also comes with a way to create dialogs. There are choices between simple yes/no dialogs to helps for lists of options that operate as a "radio" or "checkbox".

Update the DigestionStage to look like the following:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class DigestionStage(object): def __init__(self): self.satiated = False def run(self, volume: int = 0) -> str: questions = [ { 'type': 'input', 'name': 'volume', 'message': 'How many burps?', } ] if volume == 0: volume = int(prompt(questions)['volume']) self.satiated = True return ' '.join(['Burp!'] * volume) # Update function with dialog questions here def breakfast(self): questions = [ { 'type': 'list', 'name': 'breakfast', 'message': 'What did you want for breakfast?', 'choices': ['eggs', 'bacon', 'toast'] } ] # We are going to use associative arrays to switch on the value of the key # to determine how many burps are required switcher = { 'eggs': 1, 'bacon': 2, 'toast': 3, } volume = switcher.get(prompt(questions)['breakfast'], 0) self.satiated = True return ' '.join(['Burp!'] * volume) def status(self): return 'Satiated.' if self.satiated else 'Not satiated.'

We have no added digestion breakfast command that we can run with python run digestion breakfast:

1 2 3 4 5 6 7 8 9 $ python cli.py digestion breakfast ? What did you want for breakfast? bacon Burp! Burp! $ python cli.py digestion breakfast ? What did you want for breakfast? eggs Burp! $ python cli.py digestion breakfast ? What did you want for breakfast? toast Burp! Burp! Burp!

Now we have a digestion breakfast command that will run the breakfast method and output the result of our input into the dialog.

Summary

We have used PyInquirer to demo a simple command line input as well as a way to pick an option from a list.

There are many examples that they have on their GitHub if you would like to learn more.

Resources and further reading

Photo credit: pawel_czerwinski

Dennis O'Keeffe

@dennisokeeffe92
  • Melbourne, Australia

Hi, I am a professional Software Engineer. Formerly of Culture Amp, UsabilityHub, Present Company and NightGuru.
I am currently working on workingoutloud.dev, Den Dribbles and LandPad .

Related articles


1,200+ PEOPLE ALREADY JOINED ❤️️

Get fresh posts + news direct to your inbox.

No spam. We only send you relevant content.