Adding flag support to Gustavo

slaterslater profile image Anthony Slater ・3 min read

I've been tinkering with a command line tool that searches a source file for URLs and generates a report of their status.

GitHub logo slaterslater / gustavo

Print a colourized report of all HTTP urls in a file

Gus received a recent PR to add windows & unix style command line arguments. Once this PR was merged, Gus could accept and do the following:

$ python gus.py -f some.file  // checks the URLs in some.file
$ python gus.py -v            // shows version info 
$ python gus.py -h            // shows usage instructions

--file, --version and --help work too.

A great start, but why stop there? I knew I wanted Gus to handle many more arguments, so it no longer made sense to check sys.argv against strings through a series of conditional statements. I decided to import argparse to do this heavy lifting for me.

The first thing I did was change the default output of the program. Instead of automatically producing a Rich Text File, output to the console would be standard. If the user wants a Rich Text File as the output, they can pass Gus
-r or --rtf from the command line.

Next I updated the checker function by swapping the http.client library for requests. I renamed get_status_code() to get_status() since it would be returning the code and description as a dictionary. I figured this would be helpful down the road when determining the output format (console or RTF).

def get_status(url):
    conn = requests.head(url, timeout=2.5)
    code = conn.status_code
    series = str(code)[0]
    desc = 'UNKN'
    if series == '2':
      desc = 'GOOD'
    elif series == '4':
      desc = 'FAIL'
    return {'code':code, 'desc':desc}
  except:       # all exceptions default to status == 400
    return {'code':400, 'desc':'FAIL'}

One thing I want to point out before moving on... Notice how all exceptions default to status == 400. I'm not really happy with this, so I opened a new issue to improve exception handling. Hopefully in the near future I (or someone else 🤞) will have the time to work on this.

Whichever output format the user chooses, I decided it will be displayed or produced once all URLs have been checked. Since Gus doesn't yet leverage threading, this can take a while. In the event that Gus is taking a long time, I wanted to show the user that everything is ok by showing the progress. After a little research I came up with the following:

print(f'\r Checking URL {list.index(string)} of {len(list)}', end="\r")

By putting the above print statement in my for loop, each iteration over-writes itself and provides a single line progress update to the user.

Once everything worked as expected, I created two new branches to develop two new features:

At this point I was very happy to have argparse on board. Argparse was already sending a string parameter output to the main function. The value of output could either be "std" or "rtf" and adding "json" into the mix was easy peasy.

In order to support --all, --good, and --bad flags, argparse needed to send a list parameter wanted to the main function. By default, the wanted list would be equal to:


Passing --good or --bad restrict the values of the above list accordingly. In all cases, the values in wanted get checked against the returned dictionary from get_status() and if there is a match, the formatted string is saved for later output.

Since many functions need to know what the user's output choice is, I decided to make it a global variable instead of passing it from function to function. I'm not sure if this is bad form, but the code looks pretty clean and everything worked as intended.

To date, these are all the options for Gus:

Command line arguments Description
-f FILENAME, --file FILENAME location of source file to be checked
-a, --all output includes all results (this is default)
-g, --good output includes only [GOOD] results
-b, --bad output includes only [FAIL] results
-r, --rtf output as colourized rich text file
-j, --json output as JSON
-h, --help show information on how to use the tool and exit
-v, --version show program's version number and exit

The hard part was the merging of two branches. It was supposed to be a recursive three-way merge, but it ended up a dog's breakfast... I'm certain I screwed up the merging, but the code I want is up on Github so I guess I can't complain. I have two more features planned for Gus, so I'll try merging branches again soon.


Editor guide