Skip to Content
Course content

349: Building Command-Line Tools with argparse

Click on the "Edit" button in the top corner of the screen to edit your slide content.

If you've ever written a script that you had to run from the terminal, you probably started by using sys.argv. It's fine for the simplest tasks, but the moment you need optional flags, default values, or a help menu, sys.argv becomes a nightmare of manual index checking and if/else blocks. That's where argparse comes in. It's the standard library's way of making your scripts feel like professional CLI tools.

To show you how this works, let's build a small tool called site-check. The goal is simple: we give it a URL, and it tells us if the site is up. We'll want it to be flexible enough to handle a custom timeout and a "verbose" mode for more detail.

Defining the core requirements

I always start by creating the ArgumentParser object. Think of this as the "brain" of your command line interface; it holds all the rules for what your script is allowed to accept. For our first argument, we need the URL. Since the tool is useless without a URL, we'll make this a positional argument.

import argparse
import requests

parser = argparse.ArgumentParser(description="Check if a website is online.")
parser.add_argument("url", help="The URL of the site to check (e.g., https://google.com)")

args = parser.parse_args()
print(f"Checking {args.url}...")

If you run this now with python site_check.py https://google.com, it works. If you run it without an argument, argparse automatically generates a helpful error message and usage guide for you. This is the immediate win over sys.argv.

Adding flexibility with optional flags

Now, we don't want the script to hang forever if a site is down. We should add a timeout. I want this to be optional—meaning it has a sensible default, but the user can override it if they're on a slow connection. In argparse, any argument starting with a dash (- or --) is treated as optional.

# Adding this to our parser setup
parser.add_argument("-t", "--timeout", help="Timeout in seconds", type=int)

I specified type=int because argparse treats everything as a string by default. Doing the conversion here saves us from wrapping int() around the variable later in the logic.

The "NoneType" trap

Here is where I usually trip up when I'm moving fast. I wrote the logic to use the timeout like this:

try:
    response = requests.get(args.url, timeout=args.timeout)
    print("Site is up!")
except requests.exceptions.RequestException as e:
    print(f"Site is down: {e}")

I tested it by passing --timeout 5 and it worked great. But then I ran it without the timeout flag, and the script crashed with a TypeError. Why? Because I didn't provide a default value. When the user omits an optional argument, argparse assigns it None. requests.get() doesn't know what to do with a None timeout.

The fix is simple: always provide a default for optional arguments that your logic depends on.

parser.add_argument("-t", "--timeout", help="Timeout in seconds", type=int, default=10)

Toggling behavior with boolean flags

Finally, let's add a "verbose" mode. We don't want the user to type --verbose True; that's clunky. We just want them to include -v to see the status code. For this, we use action="store_true".

parser.add_argument("-v", "--verbose", action="store_true", help="Show the HTTP status code")

# ... inside the try block ...
if args.verbose:
    print(f"Status Code: {response.status_code}")

Now the full logic is clean. The parser handles the validation, the types, and the help menu, leaving us to focus on the actual functionality. When you combine these, you get a tool that feels native to the operating system.




📋 Practical Task

Build a File-Word-Counter CLI

Your task is to create a command-line tool that counts the words in a specific text file. Your script should implement the following requirements using argparse:

  • Positional Argument: filename (The path to the .txt file to be read).
  • Optional Argument: --ignore-case (A boolean flag using store_true that, when present, ensures the word count is case-insensitive).
  • Optional Argument: --min-length (An integer argument that only counts words longer than the specified number of characters. This should have a default value of 1).

The script should print the final count to the console. Make sure to handle the case where the file might not exist to prevent the tool from crashing ungracefully.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.