16
Building CLIs With python-fire
I write content for AWS, Kubernetes, Python, JavaScript and more. To view all the latest content, be sure to visit my blog and subscribe to my newsletter. Follow me on Twitter.
This is Day 6 of the #100DaysOfPython challenge.
This post will use the Python Fire library to work through a simple example of setting up the library.
Let's create the hello-fire
directory and install python-fire
.
We will also create a file cli.py
to hold our CLI script.
# Make the `hello-fire` directory
$ mkdir hello-fire
$ cd hello-fire
# Make file the CLI script
$ touch cli.py
# Init the virtual environment
$ pipenv --three
$ pipenv install python-fire
We are now ready to add a script.
For our demo example, we are going to take a slightly modified version of grouping commands script to demo how to run subcommands.
Our aim is to have the following commands:
Command | Description |
---|---|
ingestion run |
Print a message to the console to highlight that we have run the ingestion script |
digestion status |
Print a message based on the value of the Digestion class satiated property |
digestion run |
Print a message to the console to highlight that we have run the digestion script and set the value of satiated to True
|
run |
Run both ingestion and digestion run commands and the digestion status |
We set the base commands through the Pipeline
class and the subcommands through their own class that is initiated as properties of the Pipeline
class.
If you notice the optional volume
argument for DigestionStage.run
, it is used to set the volume of the Digestion
class based on an argument passed to the CLI (defaulting to 1).
#!/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)
To run the script, we need to ensure we are running the Pipenv virtual environment.
We can do this with pipenv shell
.
Once, in the shell, we can run our script and see the results:
$ python cli.py ingestion run
# Ingesting! Nom nom nom...
$ python cli.py digestion run
# Burp!
$ python cli.py digestion run --volume=10
# Burp! Burp! Burp! Burp! Burp! Burp! Burp! Burp! Burp! Burp!
$ python cli.py status
# Not satiated.
$ python cli.py run
# Ingesting! Nom nom nom...
# Burp!
# Satiated.
# Pipeline complete
Running through our script, we can now see the results of our work.
Notice that the status
property of the Digestion class is set to True
when we run the run
command and the number of "Burp!" messages printed is based on the volume
argument passed to the run
command.
Today's post demonstrated how to use the python-fire
package to write easier CLI scripts with their own subcommands and instance-managed state.
Of most languages that I have used, it must be said that python-fire
has been one of the most approachable libraries I have seen for building out CLI tools.
- The ABCs of Pipenv for the minimum you will need.
- Hello, JupyterLab.
- Python Fire
- Pipenv
Photo credit: cullansmith
Originally posted on my blog. To see new posts without delay, read the posts there and subscribe to my newsletter.
16