Command-line arguments: Part 2: argparse

Command-line arguments: Part 2: argparse

And...We're back, once more with our CLI programming. We're taking a deeper dive into the waters of command-line arguments, this time, looking at a friend to argv ; using argparse. That's right, it's a whole family.

Last time we talked, we had a whole array of possibilities with using argv, but what if I told you there is much more that could be done while using its big brother module? Let's see what this just means.

Before, we used argv to get the square of a number, passing in the number we wanted the square of and getting the result. What if we had a new user who did not know how to work with the program and you weren't there to explain your new-found love for CLI programs? What if you wanted your program to do more than just print the result right to your face?

Okay, I had to laugh a little at that last bit. Let's give it a shot on how we would get this to work seamlessly.

# advanced_square.py
# get the square of numbers using command_line_arguments

import argparse

parser = argparse.ArgumentParser(description="get the square of the input value")

parser.add_argument("square", type=int)

arguments = parser.parse_args()

print(arguments.square**2)

If you haven't noticed yet, we have used the very same example we used in the last section only this time, we called in some help. Time to open the cookie jar.

parser = argparse.ArgumentParser(description="get the square of the input value")

Above, we are simply giving the program description. ( What does this program do? ). Unlike the previous section where we just parse in arguments and use a list to get the item we want, here, we specify using the below line:

parser.add_argument("square", type=int)

Above, apart from giving the program description, we have added an argument which is an object of type int, a number. We state beforehand and tell the program to expect this type of integer. We tell the program to name this variable square so that we can retrieve it with that same variable name.

Take a look at this line:

arguments = parser.parse_args()

All our arguments added are contained in our variable called parser. Using parse_args is a way of getting our variables from the whole lot we might have added and within it, we have a tuple-like object:

Namespace(square=8)
# note that the number after the equal sign depends on what you have #passed to the program as an argument

Adding more arguments would increase its size with the respective mapping of value to the variable name. For simplicity's sake, we place all this into a variable called arguments which will hold whatever arguments we may have passed to the program after which, we call the variable we want to work with its name using arguments.square and do the math printing it to the terminal. Hence,

python advanced_square.py 8
64

Note how using argparse gives us a little push help. For instance, with the current file, if we use the common -h or --help for assistance when we have amnesia, we get something like this:

python advanced_square.py --help
usage: advanced_square.py [-h] square 

get the square of the input value

positional arguments:
  square      the input value whose square is needed

optional arguments:
  -h, --help  show this help message and exit

Our program description is nicely printed for anyone who would want to know about what we do within the program while our arguments are printed with their details. We pass in the number we want to square because we simply cannot square an empty space! Wait, can we?

Now as much as we would love to, we cannot keep subtracting numbers all day! Just to show you how powerful argparse can be, we are going to modify one of the programs we did in the second piece of File management with python. Do go through that, if you haven't already, and get back once done.

We modify the program as below:

import os
import shutil
import glob
import argparse


def organize(folder):
    """Organize files according to extension """

    # get into the directory passed
    os.chdir(folder)
    all_files = [x for x in os.listdir('.')]

    file_types = set((os.path.splitext(f)[1] for f in all_files))

    for ftype in file_types:
        new_directory = ftype.replace(".", '')
        os.mkdir(new_directory)

        for fname in glob.glob(f'*.{ftype[1:]}'):
            shutil.move(fname, new_directory)



def main():
    parser = argparse.ArgumentParser(description="sort files according to file extension")

    parser.add_argument("path", help="Path of directory with unsortsed files")
    parser.add_argument("-v","--verbose", help="Descriptive message of directory after sort", action="store_true")


    arguments = parser.parse_args()

    organize(arguments.path)

    if arguments.verbose:
        print("\n The following files were extensions were found and organized successfully...")
        print(os.listdir('.'))
    else:
        print("Done!")


main()

Now that is much better! Things are finally starting to take shape. You have the option to either:

  1. Pass the relative/absolute path of the messy directory to clean

  2. Pass the path to the directory with an optional argument -v or --verbose to list the folders in the cleaned up directory.

We intend to advance on our knowledge on command-line arguments in next week's article, but for now, take a break, relax and let it sink in.

You can go through the code section, at TheGreenCodes and or give out your thoughts or ideas on just what you think is possible with what we have done so far!

Cheers!