Command Line Interface Programming in Python



This article discusses how you can create a CLI for your Python programs using the example in which we create a basic “text file manager”.
Let`s go over some basics first.

What is the CLI?

Interface command line or command language interpreter (CLI), also known as command line user interface, console user interface, and symbolic user interface (CUI), is a means of interacting with a computer program where the user (or client) issues commands to the program as sequential lines text (command line). ( Wiki )

CLI benefits:

  • Requires fewer resources
  • Concise and powerful
  • Expert-friendly
  • Easier to automate with scripts

Why use the CLI in your Python program?

  • Having even a very simple CLI for your program can make it easier life for everyone to change parameters, including programmers, but not programmers either.
  • The CLI for your program can also make it easier to automate starting and changing variables in your program when you want to run a program with a cronjob or perhaps by calling os.system.

Now let`s start creating a “Text File Manager”. Here we will use the built-in Python library called Argparse .

About Argparse:

  • This makes it easier to write user-friendly command line interfaces.
  • The program will figure out what arguments it requires and argparse will figure out how to parse it from sys.argv.
  • The argparse module also automatically generates help and usage messages and throws errors when users give the program invalid arguments.

Ok, let`s let`s start with a really simple program to understand what argparse does.

# import required modules

import argparse

 
# create b parser object

parser = argparse. ArgumentParser (description = "An addition program" )

 
# add an argument

parser.add_argument ( "add" , nargs = `*` , metavar = "num" , type = int

help = "All the numbers separated by spaces will be added." )

 
# parse arguments from standard input

args = parser.parse_args ()

 
# check if the add argument has any -or input data.
# If so, print the sum of the given numbers

if len (args.add)! = 0 :

print ( sum (args.add))

Let`s look at some important points related to the above with the specified program:

  • First of all, we imported the argparse module.
  • Then created the ArgumentParser object, and also provided a description of our program.
  • Now we can populate our parser object with information by adding arguments. In this example, we have created a add argument. Many arguments can be passed to the add_argument function. Here I will explain the ones I used in the above example:
    argument 1: (“add”) This is nothing more than the name of the argument. We`ll use this name to access the add arguments by typing args.add .
    argument 2: (nargs = & # 39; * & # 39;) The number of command line arguments to be used. Pointing to & # 39; * & # 39; means it can be any no. arguments, that is, from 0 to anything.
    argument 3: (metavar = & # 39; num & # 39;) The name of the argument in usage messages.
    argument 4: (type = int) The type to which the command line argument should be converted. This is the default.
    argument 5: (help) a short description of what the argument does.
  • Once we`ve identified all the arguments, it`s time to parse the arguments from the command line stdin. To do this, we use the parse_args () function.
  • Now we can simply check to see if the input has triggered a specific argument. Here we check the length of args.add to check if there is any data received from the input. Note that argument values ​​are returned as a list.
  • There are two types of arguments: positional and optional.
    Positional — these are the ones that don`t need any specification to invoke. Whereas, optional arguments must be specified first by their name (which starts with a “-”, “-” is also an abbreviation).
  • You can always use the optional –help or -h argument to see the help message.
    Here is an example (Python script was saved as add.py):
  • Now let`s look at another example where our the positional argument add .
  • Another feature worth mentioning is the way argparse throws errors when users give the program wrong arguments.

So this was a basic example to get you familiar with the concept of argparse and the CLI. Now let`s move on to our program “Text File Manager” .

# import required modules

import os

import argparse

 
# Error messages

INVALID_FILETYPE_MSG = "Error: Invalid file format.% s must be a .txt file. "

INVALID_PATH_MSG = " Error: Invalid file path / name. Path% s does not exist. "

  

 

def validate _file (file_name):

"" "

check file name and path.

"" "

  if not valid_path (file_name):

print (INVALID_PATH_MSG % (file_name ))

quit ()

elif not valid_filetype (file_name):

print (INVALID _FILETYPE_MSG % (file_name))

quit ()

return

 

def valid_filetype (file_name):

# check file type

return file_name.endswith ( ` .txt` )

 

def valid_path (path):

# check file path

return os.path.exists (path)

 

  

  

def read (args):

# get file name / path

file_name = args.read [ 0 ]

 

# check file name / path

validate_file (file_name)

  

# read and print file content

  with open (file_name, `r` ) as f:

  print (f.read ())

  

 

def show (args):

  # get the path to the directory

dir_path = args.show [ 0 ]

  

  # check path

if not valid_path (dir_path):

print ( "Error: No such directory found. " )

  exit ( )

 

# get text files in the directory

  files = [f for f in os.listdir (dir_path) if valid_filetype (f)]

print ( "{} text files found." . format ( len (files)))

print ( ` ` . join (f for f in files))

 

 

def delete (args):

# get file name / path

file_name = args.delete [ 0 ]

  

  # validate file name / path

validate_file (file_name)

 

# delete file

os.remove (file_name)

print ( " Successfully deleted {}. " . format (file_name))

 

 

def copy (args):

# file to copy

  file1 = args.copy [ 0 ]

# file to copy

  file2 = args.copy [ 1 ]

 

# check file for copy

validate_file (file1)

 

# check file type 2

  if not valid_filetype (file2):

  print (INVALID_FILETYPE_MSG % (file2))

exit ()

 

# copy file1 to file2

with open (file1, `r` ) as f1:

with open (file2, ` w` ) as f2:

f2.write (f1.read ())

print ( "Successfully copied {} to {}." . format (file1, file2))

 

 

def rename (args):

# old filename

old_filename = args.rename [ 0 ]

# new file name

new_filename = args.rename [ 1 ]

 

# check the file to be renamed

validate_file (old_filename)

 

# check the type of the new file name

if not valid_filetype (new_filename):

print (INVALID_FILETYPE_MSG % (new_filename))

  exit ()

 

# renaming

os.rename (old_filename, new_filename)

  print ( "Successfully renamed {} to {}." . format (old_filename, new_filename))

def main ():

# create parser object

parser = argparse.ArgumentParser (description = "A text file manager!" )

 

# defining arguments for the parser object

  parser.add_argument ( " - r " , " - -read " , type = str , nargs = 1 ,

metavar = " file_name " , default = None ,

  help = "Opens and reads the specified text file." )

 

parser.add_argument ( "- s" , "- show" , type = str , nargs = 1 ,

  metavar = "path" , default = None ,

help = "Shows all the text files on specified directory path.

Type `.` for current directory. ")

  

parser.add_argument ( "- d" , "- delete" , type = str , nargs = 1 ,

metavar = "file_name" , default = None ,

  help = " Deletes the specified text file. " )

 

parser.add_argument ( " - c " , "- copy" , type = str , nargs = 2 ,

metavar = ( ` file1` , `file2` ), help = "Copy file1 contents to

  file2 Warning: file2 will get overwritten. ")

  

  parser.add_argument ( "- rename" , type = str , nargs = 2 ,

metavar = ( `old_name` , ` new_name` ) ,

help = "Renames the specified file to a new name." )

 

# parse arguments from standard input

args = parser.parse_args ( )

 

# calling functions depending on the type of argument

  if args.read! = None :

read (args)

elif args.show! = None :

show (args)

  elif args.delete! = None :

delete (args)

elif args.copy! = None :

  copy (args)

elif args.rename! = None :

rename (args)

  

 

if __ name__ = = "__ main__" :

# main function call

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         main ()

After the previous example, the above code seems self-explanatory. 
All we did was add a bunch of arguments to our file manager program. Note that all of these arguments are optional. So, we are using some if-elif statements to match the command line input to the correct function of the argument type so that the request can be processed. 
Here are some screenshots that describe the use of the above program:

  • Help message (Python script was saved as tfmanager.py):
  • Here are examples of operations using the text file manager:

So it was an example of a simple Python CLI program we made. Many complex CLIs can be easily created with the Argparse module. I hope these examples serve as