Department SQL Class Exercise
Password Generator Exercise
This exercise will help you practice creating command-line interfaces in Python, starting with basic sys.argv
and progressing to the more powerful argparse
module.
Background
We’ll build a password generator that creates memorable passwords using random words, inspired by the XKCD comic about password strength. The idea is that phrases like “correct horse battery staple” are both secure and easier to remember than complex character-based passwords.
Setup
1. Download the Word List
You’ll need a word list file. Download one of these options:
- EFF’s Long Wordlist: https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt
- Simple English Wikipedia word list: https://github.com/first20hours/google-10000-english (use
google-10000-english-no-swears.txt
)
2. Project Structure
Create this folder structure:
password_generator/
├── data/
│ └── wordlist.txt # Your downloaded word list
├── starter_code.py # Provided starter code
├── password_gen_argv.py # Part 1: Your sys.argv solution
└── password_gen_argparse.py # Part 2: Your argparse solution
3. Starter Code
Here’s the basic password generation logic (starter_code.py
):
import random
from pathlib import Path
# Configuration constants
N = 5 # Number of words in the password
SEP = " " # Separator between words
# Path to word list file
# Path(".") gets the current directory, .parent goes up one level
# Alternative: you could use os.getcwd() or pathlib.Path.cwd()
WORDS_PATH = Path(".") / "data" / "wordlist.txt"
def generate_password(num_words=N, separator=SEP):
"""Generate a password using random words from the word list.
Args:
num_words (int): Number of words to include in the password
separator (str): String to use between words
Returns:
str: The generated password
"""
# Load word list from file
with open(WORDS_PATH, "r", encoding="utf-8") as f:
lst_words = [w.strip() for w in f.readlines()]
# Generate password by randomly sampling words without replacement
# random.sample() ensures each word appears only once in the password
result = random.sample(lst_words, k=num_words)
# Join words with separator
return separator.join(result)
def main():
"""Main function - generates and prints a password with default settings."""
password = generate_password()
print(f"Generated password: {password}")
if __name__ == "__main__":
# Load word list to show basic info
with open(WORDS_PATH, "r", encoding="utf-8") as f:
lst_words = [w.strip() for w in f.readlines()]
print(f"Loaded {len(lst_words)} words")
print("First 5 words:", lst_words[:5])
# Generate and display password
main()
Part 1: Using sys.argv
Create password_gen_argv.py
by copying the starter code and modifying the main()
function to accept command-line arguments using sys.argv
.
Requirements
Your script should support this usage:
python password_gen_argv.py 4
# Output: horse battery staple correct
python password_gen_argv.py 6
# Output: apple mountain river cloud forest happy
Expected Functionality
- Keep the existing
generate_password()
function - don’t modify it - Modify the
main()
function to handle command-line arguments fromsys.argv
- if the number of words is not specified, use a default of n=5, if n>10, go for n=10
Part 2: Using argparse
Create password_gen_argparse.py
by copying (use cp) the starter code and replacing the main()
function to use the argparse
module for much more sophisticated options.
Requirements
You can add the parser = argparse.ArgumentParser(description='My awesome program')
directly in the __main__
Your script should support this usage:
# Basic usage (default: 5 words, space separator, no digits)
python password_gen_argparse.py
# Output: horse battery staple correct
# OPTION: Specify number of words, default n=5
python password_gen_argparse.py -n 6
python password_gen_argparse.py --num-words 6
# Output: apple mountain river cloud forest happy
# OPTION: Different separators (--separator or -s) [space, dash, underscore, slash] only, default space
python password_gen_argparse.py -n 4 --separator dash
# Output: horse-battery-staple-correct
python password_gen_argparse.py -n 4 -s underscore
# Output: horse_battery_staple_correct
python password_gen_argparse.py -n 4 -s slash
# Output: horse/battery/staple/correct
# FLAG: Capitalize words (--capitalize or -c), default False, if present True
python password_gen_argparse.py -n 4 --capitalize
# Output: Horse Battery Staple Correct
python password_gen_argparse.py -n 4 -s dash -c
# Output: Horse-Battery-Staple-Correct
# FLAG: Add random digits (--add-digits or -d) default False if present True
python password_gen_argparse.py -n 4 --add-digits
# Output: horse3 battery7 staple1 correct9
# Combine all options
python password_gen_argparse.py -n 3 -s dash --capitalize --add-digits
# Output: Horse2-Battery5-Correct8
Expected Arguments
-n, --num-words
: Number of words (default: 4)-s, --separator
: Separator type with choices:space
,dash
,underscore
,slash
(default:space
)-c, --capitalize
: Capitalize each word (flag, default: False)-d, --add-digits
: Add random digit (0-9) after each word (flag, default: False)
Separator Mapping
You’ll need to map the separator names to actual characters:
separator_map = {
'space': ' ',
'dash': '-',
'underscore': '_',
'slash': '/'
}
Advanced Features to Include
- Input validation: Ensure number of words is reasonable (1-10)
- Error handling: Graceful handling of invalid separator choices (handled automatically by
choices=
) - Informative help text: Good descriptions for each argument
- Program description: Explain what the tool does in the help
Testing Your Solutions
Test Cases for Both Parts
Try these commands to verify your implementations work correctly:
# Basic functionality
python password_gen_argv.py 3
python password_gen_argparse.py -n 3
# Default behavior (should work for argparse, show usage for argv)
python password_gen_argv.py
python password_gen_argparse.py
# Edge cases
python password_gen_argv.py 1
python password_gen_argparse.py -n 1
# argparse-specific features
python password_gen_argparse.py -n 5 -s dash -c -d
python password_gen_argparse.py --num-words 2 --separator underscore --capitalize