1 The Problem
We want a tool that creates a strong password: a random mix of letters, numbers, and symbols, of a length the user chooses. It teaches building a string from random choices — and an important lesson about which randomness is safe for security.
2 How to Think About It
Think about how a strong password is built, before any code:
3 The Build — explained part by part
Here is the complete generator. Each line is explained below.
import secrets
import string
# Build the pool of characters a password can use.
letters = string.ascii_letters # a-z and A-Z
digits = string.digits # 0-9
symbols = "!@#$%^&*"
pool = letters + digits + symbols
length = int(input("Password length: "))
# Pick `length` random characters from the pool, securely.
password = "".join(secrets.choice(pool) for _ in range(length))
print(f"Your password: {password}")
secrets gives cryptographically secure randomness, designed for passwords and tokens. The ordinary random module is predictable enough that an attacker could guess its output — never use it for security.import string — gives us ready-made character sets:
ascii_letters (all letters) and digits (0-9), so we do not type them by hand.pool = letters + digits + symbols — combine everything into one string of allowed characters.
secrets.choice(pool) — securely pick one random character from the pool.
"".join(... for _ in range(length)) — do that pick
length times and glue the characters together into the final password. The _ means “we do not need the counter, just repeat”.
random.choice instead of secrets.choice — ordinary random is predictable and unsafe for passwords.secrets for anything security-related.+ in a loop to build the string — works, but "".join(...) is the clean way."".join(generator) to assemble many characters.4 Test & Prove Each Part
We cannot predict a random password, but we can prove the rules it must always follow.
import secrets
import string
POOL = string.ascii_letters + string.digits + "!@#$%^&*"
def generate(length):
return "".join(secrets.choice(POOL) for _ in range(length))
def test_correct_length():
"""A length-12 request gives 12 characters."""
assert len(generate(12)) == 12
def test_chars_from_pool():
"""Every character is from the allowed pool."""
for ch in generate(50):
assert ch in POOL
def test_passwords_differ():
"""Two passwords are almost never identical."""
assert generate(16) != generate(16)
Run with pytest -v. We test the guarantees: correct length, only allowed characters, and uniqueness. We cannot test the exact output — that is the whole point of randomness.
5 The Interface
What it expects
Password length: 16What it returns
Your password: K9$mP2!xQ7@vL4nR6 Run It & Automate It
python3 password.pyEnter a length and get a strong, secure password.
Jenkins runs the tests automatically — each line explained below.
Password length: 16
Your password: K9$mP2!xQ7@vL4nRValueError on the length.16.random. Use secrets.choice instead.// Jenkinsfile — automated tests on every change.
pipeline {
agent any
stages {
stage('Get the code') { steps { checkout scm } } // download the code
stage('Install') { steps { sh 'python3 -m venv venv && . venv/bin/activate && pip install pytest' } } // test tool
stage('Test') { steps { sh '. venv/bin/activate && pytest -v' } } // run every test
}
post {
success { echo 'All tests passed.' }
failure { echo 'A test failed — look above.' }
}
}
- Guarantee variety. Ensure at least one digit and one symbol. (Teaches: checking conditions.)
- Strength meter. Rate the password weak/strong by length. (Teaches: branching on rules.)
- Memorable mode. Join random words instead of characters. (Teaches: word lists.)