Introduction
A Command-line interface (CLI) is a way to interact with computers using textual commands.
A lot of tools that don't require GUIs are written as CLI tools/utilities. Although Python has the built-in argparse
module, other libraries with similar functionality do exist.
These libraries can help us in writing CLI scripts, providing services like parsing options and flags to much more advanced CLI functionality.
This article discusses the Python Fire library, written by Google Inc., a useful tool to create CLI with minimal code.
General Form of CLI Applications
Before we start with the Fire
library let's try to understand the basics of command-line interface programs in general. Depending on the program and command, the general pattern of a CLI can be summed up as follows:
prompt command parameter1 parameter2 ... parameterN
- prompt is a sequence of characters that prompts the user to input a command
- command is the name of the program that the user is executing (e.g.
ls
) - parameters are optional tokens that augment or modify the command output
A CLI program is executed by typing the name of the program after the prompt
appears, in this case the $ symbol.
Here we are using the ls
command that returns a list of file names in a directory, the current directory being the default:
$ ls
README.md
python
You can modify the behavior or output of a command-line program by providing it with a list of tokens or parameters better known as flags. Let's try out a flag of the ls
command:
$ ls -l
-rwxrwxrwx 1 pandeytapan pandeytapan 10 Sep 23 18:29 README.md
drwxrwxrwx 1 pandeytapan pandeytapan 512 Sep 23 18:29 python
As you can see, after passing the -l
flag, we get additional information for each entry like the owner, group, and file size.
Flags which have a single hyphen (-
) are called short options, while those with two hyphens (--
) are called long options. Both kinds can be used together in a single command, like in the following example:
$ ls -l --time-style=full-iso
-rwxrwxrwx 1 pandeytapan pandeytapan 10 2020-09-23 18:29:25.501149000 +0530 README.md
drwxrwxrwx 1 pandeytapan pandeytapan 512 2020-09-23 18:29:25.506148600 +0530 python
The difference between short and long options:
- Short options can be chained together
- If we want to use both the
-l
and-a
short options we just type-al
- Short options are denoted by a single character while long options have a full hyphen-separated name and cannot be chained together.
The --time-style
flag works with the -l
flag and controls the display time format for a directory listing.
A CLI provides an easy way for the user to configure and run an application from the command line. Google's Python Fire library makes it easy to add a CLI processing component to any existing Python script.
Let's see how to make a command line application using Python Fire.
Installation
Let's go ahead and install the library using pip
:
$ pip install fire
Python Fire
works on any Python object i.e. functions, classes, dictionaries, lists etc. Let's try to understand the usage of the Python Fire
library through some examples.
Generating CLI Application with Python Fire
Let's make a script, say, fire_cli.py
and put a function within it:
def greet_mankind():
"""Greets you with Hello World"""
return 'Hello World'
On running this program on Python shell the output is:
>>> from fire_cli import greet_mankind
>>> greet_mankind()
'Hello World'
>>>
We can easily turn this script into a CLI application using Python Fire:
import fire
def greet_mankind():
"""
Returns a textual message
"""
return 'Hello World'
if __name__ == '__main__':
fire.Fire()
The fire.Fire()
call turns the module i.e. fire_cli.py
into a Fire CLI application. Moreover it has exposed the greet_mankind()
function as a command, automatically.
Now we can save and run the above script as CLI as follows:
$ python fire_greet_mk_cli.py greet_mankind
Hello World
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
As a refresher, let's break down the call:
- $ is the prompt
- python is the command interpreter
- fire_cli.py is the module that contains the CLI command
- greet_mankind is the command
Passing Arguments to a Command
Let us make another CLI application that takes a name as a parameter and displays a custom greeting message:
import fire
def greetings(name):
'''
Returns a greeting message
Parameters
----------
name : string
String that represents the addresses name
Returns
-------
string
greeting message concatenated with name
'''
return 'Hello %s' % name
if __name__ == '__main__':
fire.Fire()
Here, we've now got a function that accepts a string - name
. Python Fire automatically picks this up and if we supply an argument after the greetings
call, it'll bind that input to the name
parameter. We've also added a comment as a sort of documentation for the --help
command.
Here's how we can run this command from the command line:
$ python fire_greet_cli.py greetings Robin
Hello Robin
A Fire CLI application can use --help
flags to check the command description generated from Python docs:
python fire_greet_cli.py greetings --help
NAME
fire_greet_cli.py greetings - Returns a greeting message
SYNOPSIS
fire_greet_cli.py greetings NAME
DESCRIPTION
Returns a greetings message
POSITIONAL ARGUMENTS
NAME
String that represents the addresses name
NOTES
You can also use flags syntax for POSITIONAL ARGUMENTS
Setting a Function as Entry Point
With slight modification we can control the exposure of the greetings()
function to the command-line and set it as the default entry-point:
import fire
def greetings(name):
'''
Returns a greeting message
:param name: string argument
:return: greeting message appended with name
'''
return 'Hello %s' % name
if __name__ == '__main__':
fire.Fire(greetings)
This is how we will run the command now:
$ python fire_greet_cli.py Robin
Hello Robin
So this time we no longer need to call the command as we have defined greetings
implicitly as an entry point using Fire()
. One thing to be noted here is that with this version, we can only pass a single argument :
$ python fire_greet_cli.py Robin Hood
ERROR: Could not consume arg: Hood
...
$ python fire_greet_cli.py Robin
Hello Robin
Arguments Parsing
The Fire library also works with classes. Let's define a class CustomSequence
that generates and returns a list of numbers between start
and end
:
import fire
class CustomSequence:
'''Class that generates a sequence of numbers'''
def __init__(self, offset=1):
'''
Parameters
----------
offset : int, optional
Number controlling the difference between two generated values
'''
self.offset = offset
def generate(self, start, stop):
'''
Generates the sequence of numbers
Parameters
----------
start : int
Number that represents the elements lower bound
stop : int
Number that represents the elements upper bound
Returns
-------
string
a string that represents the generated sequence
'''
return ' '.join(str(item) for item in range(start, stop, self.offset))
if __name__ == '__main__':
fire.Fire(CustomSequence)
This is how we generate a sequence using this command-line utility:
$ python fire_gen_cli.py generate 1 10
1 2 3 4 5 6 7 8 9
We used a class instead of a function because unlike functions if we want to pass an argument to the constructor, it always has to be represented as a command line flag with double hyphens (e.g. --offset=2
).
Therefore, our CLI application supports an optional argument --offset
that will be passed on to the class constructor. This modifies the output by controlling the difference between two consecutive generated values:
Here's the output with offset value of 2:
$ python fire_gen_cli.py generate 1 10 --offset=2
1 3 5 7 9
The constructor's arguments are always passed using the flag syntax whereas arguments to other methods or functions are passed positionally or by name :
$ python fire_gen_cli.py generate --start=10 --stop=20
10 11 12 13 14 15 16 17 18 19
$ python fire_gen_cli.py generate 10 20
10 11 12 13 14 15 16 17 18 19
$ python fire_gen_cli.py generate --start=10 --stop=20 --offset=2
10 12 14 16 18
We can check the usage of thegenerate
command using the --help
flag. This will give the usage information for the CLI :
$ python fire_gen_cli.py generate --help
INFO: Showing help with the command 'fire_gen_cli.py generate -- --help'.
NAME
fire_gen_cli.py generate - Generates the sequence of numbers
SYNOPSIS
fire_gen_cli.py generate START STOP
DESCRIPTION
Generates the sequence of numbers
POSITIONAL ARGUMENTS
START
Number that represents the first value for the sequence
STOP
Number that represents the ending value for the sequence
NOTES
You can also use flags syntax for POSITIONAL ARGUMENTS
Using --help
with the module gives us its usage information:
$ python fire_gen_cli.py --help
INFO: Showing help with the command 'fire_gen_cli.py -- --help'.
NAME
fire_gen_cli.py - Class that generates a sequence of numbers
SYNOPSIS
fire_gen_cli.py <flags>
DESCRIPTION
Class that generates a sequence of numbers
FLAGS
--offset=OFFSET
Fire Flags
Fire CLIs come with many built-in flags. We have already seen --help
, though, another useful flag is --interactive
. Using this flag puts us in Python REPL mode, with the module already defined.
This is quite useful for testing commands:
$ python fire_greet_cli.py -- --interactive
Fire is starting a Python REPL with the following objects:
Modules: fire
Objects: component, fire_greet_cli.py, greetings, result, trace
Python 3.7.8 (tags/v3.7.8:4b47a5b6ba, Jun 28 2020, 08:53:46) [MSC v.1916 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.16.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: greetings("Robin")
Out[1]: 'Hello Robin'
Note that Fire flags should be separated from other options with two hyphens (
--
), so if we want to use both--help
and--interactive
flags in a command, it would look something like this:
$ python fire_greet_cli.py -- --help --interactive
Conclusion
Google's Python Fire library is a quick and easy way to generate command line interfaces (CLIs) for nearly any Python object.
In this article, we've gone over how to install Python Fire, as well as generate simple command-line interfaces.