Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
349: Building Command-Line Tools with argparse
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 usingstore_truethat, 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 of1).
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.
There are no comments for now.