DEV Community

Cover image for Create a CLI application with Python
Kanak Tanwar
Kanak Tanwar

Posted on

Create a CLI application with Python

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 in git commit)
  • Arguments - An optional/required piece of data used by a command/subcommand for processing (like "initial commit" in git commit -m "initial commit")
  • Option/flag - An option argument that modifies the commands behaviour (like -m in git 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
Enter fullscreen mode Exit fullscreen mode
  • 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 that calcli 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
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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")

Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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",
)
Enter fullscreen mode Exit fullscreen mode

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 the calcli.__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)

Collapse
 
aarushi_bhatia_578922f673 profile image
Aarushi Bhatia

Good job Kanak 👏