A CLI(command line interface) application is a software tool that allows users to interact with an application directly through your terminal/command prompt.
They are not just useful but also low-key cool ;)
CLI Intro
A CLI application consists of various components -
-
Commands - The program that runs in the terminal window (like
git
) -
Subcommands - An optional identifier that comes after the command to do a certain task (like
commit
ingit commit
) -
Arguments - An optional/required piece of data used by a command/subcommand for processing (like
"initial commit"
ingit commit -m "initial commit"
) -
Option/flag - An option argument that modifies the commands behaviour (like
-m
ingit commit -m
)
Creating a CLI with argparse
Creating CLI applications may seem like a big deal but it is actually pretty simple. The difficult part in the project is usually the core logic and not the cli portion.
Python has a built in module argparse
which allows us to interact with the cli with minimal code.
Let's see how we can build a simple CLI calculator in python using argparse. It will have the capability to perform the 4 basic arithmetic operations and will be called from the CLI as such - calcli add 5 10
.
The code used in this post is available at this github repo
Directory Structure
.
├── calcli
│ ├── calculator.py
│ ├── __init__.py
│ └── __main__.py
├── README.md
├── requirements.txt
└── setup.py
-
calcli
is the name of the package/cli tool. - the
calculator.py
file has the logic for +,-,*,% operations. - the
__init__.py
is used to define thatcalcli
is to be treated as a package. this file will be empty. -
__main__.py
holds the logic to interact with the CLI. the main motive behind this post. -
setup.py
is used to package the application
calculator.py
class Calculator:
def __init__(self):
self.result = 0
def add(self, a, b):
self.result = a + b
return self.result
def sub(self, a, b):
self.result = a - b
return self.result
def mul(self, a, b):
self.result = a * b
return self.result
def div(self, a, b):
if b == 0:
raise ValueError("Division by 0 NOT supported")
self.result = a / b
return self.result
main.py
1) parser object - First we will create an object that will help us parse through the arguments given in CLI. This is done with using argparse.ArgumentParser(description="...")
.
import argparse
from calcli.calculator import Calculator
def main():
calculator = Calculator()
parser = argparse.ArgumentParser(description="A CLI Calculator Tool", epilog="Example calcli add 5 10")
if __name__ == "__main__":
main()
2) adding arguments - With argparse we can add arguments for commands and subcommands using the add_argument()
method.
The first 2 parameters of this method are the long-hand and short-hand names of the argument/option, followed by optional parameters like type
, default
, help
.
def main():
calculator = Calculator()
parser = argparse.ArgumentParser(description="A CLI Calculator Tool", epilog="Example calcli add 5 10")
parser.add_argument("--precision", "-p", type=int, default=2, help="Number of decimal places")
3) subparser - Subparsers are used to add subcommands (add, sub, div, mul in our case).
The title
is a placeholder for the subparser and the dest
is the place where the arguments passed from the cli will be stored.
def main():
calculator = Calculator()
parser = argparse.ArgumentParser(description="A CLI Calculator Tool", epilog="Example calcli add 5 10")
parser.add_argument("--precision", "-p", type=int, default=2, help="Number of decimal places")
subparsers = parser.add_subparsers(title="Operators", dest="operator")
4) adding subcommands - We can now add the subcommands (add, sub, mul, div) using the subparser we created. This is done with the help of add_parser()
method followed by using the add_argument()
method to add arguments for the subcommands.
def main():
calculator = Calculator()
parser = argparse.ArgumentParser(description="A CLI Calculator Tool", epilog="Example calcli add 5 10")
parser.add_argument("--precision", "-p", type=int, default=2, help="Number of decimal places")
subparsers = parser.add_subparsers(title="Operators", dest="operator")
add_parser = subparsers.add_parser("add", help="Add 2 numbers")
add_parser.add_argument("a", type=float, help="First number")
add_parser.add_argument("b", type=float, help="Second number")
sub_parser = subparsers.add_parser("sub", help="Subtract 2 numbers")
sub_parser.add_argument("a", type=float, help="First number")
sub_parser.add_argument("b", type=float, help="Second number")
mul_parser = subparsers.add_parser("mul", help="Multiply 2 numbers")
mul_parser.add_argument("a", type=float, help="First number")
mul_parser.add_argument("b", type=float, help="Second number")
div_parser = subparsers.add_parser("div", help="Divide 2 numbers")
div_parser.add_argument("a", type=float, help="First number")
div_parser.add_argument("b", type=float, help="Second number")
5) access cli arguments - You can get the arguments passed in a command using the parse_args()
method.
We can access the attributes of the args, which are the dest=...
parameter mentioned above, and perform actions with the arguments.
args = parser.parse_args()
if args.operator == "add":
result = round(calculator.add(args.a, args.b), args.precision)
print(f"Result: {result}")
elif args.operator == "sub":
result = round(calculator.sub(args.a, args.b), args.precision)
print(f"Result: {result}")
elif args.operator == "mul":
result = round(calculator.mul(args.a, args.b), args.precision)
print(f"Result: {result}")
elif args.operator == "div":
try:
result = round(calculator.div(args.a, args.b), args.precision)
print(f"Result: {result}")
except ValueError as e:
print(f"Error: {e}")
else:
parser.print_help()
You can now run the package with python -m calcli <op> <a> <b>
. This is because python treats calcli as a package.
However we want to access this functionality directly as a utility. This is done by creating a setup.py
file which allows us to make the package installable.
setup.py
setup.py
has a standard structure. You start by importing setuptools.setup
and use it to manage some metadate about the module we have just created.
from setuptools import setup
setup(
name="calcli",
version="6.9",
packages=["calcli"],
entry_points={
"console_scripts": ["calcli = calcli.__main__:main"],
},
description="A CLI calculator using argparse",
author="Your Name",
url="Project URL",
python_requires=">=3.6",
)
Let's take a look at the parameters in setup()
-
- name - name of the package
- version - current version
- entry_points - stores the commands that will be accessible through the CLI. here when we type
calcli
thecalcli.__main__:main
script will be executed
After you create a setup.py
you are ready to build the package.
Now, there are 2 options -
-
pip install .
- This builds the CLI tool. (Useful for testing) -
pip install -e .
- This builds the CLI tool in editable mode i.e. any changes you make will be reflected almost instantaneously. (Useful for development)
Run the command that you would like and you are done.
You CLI tool is ready.
Run calcli add 6 9
or calcli --help
or calcli div 6 9 -p 5
and so on.
That's all from my side. If you have any questions or suggestions do comment them.
Top comments (1)
Good job Kanak 👏