Password Generator in Python
Beginner · Useful tools · about 35 minutes to build · 106 lines
Choose a length and the kinds of characters to use, and get random passwords with a note on how strong they are.
The program
Press Run and answer its questions in the box that appears under the code.
Open it in the full editor to save it, share it and keep building.
Sample run
What the program printed, and what was typed (in yellow).
Password generator Password length (8-64, Enter for 16): abc Please type a whole number. Password length (8-64, Enter for 16): 5 Please choose a number from 8 to 64. Password length (8-64, Enter for 16): Include uppercase letters? (Y/n): Include digits? (Y/n): y Include symbols? (Y/n): How many passwords (1-10, Enter for 1): 3 Using lowercase, uppercase, digits, symbols (75 possible characters): 1. 8Ahk#wM$Hnm6zj8S 2. Jm8e5s=fIZx6aq7G 3. pI9@pyc^f-&*_JjS Strength: strong (about 100 bits of randomness) Make more passwords? (y/N): y Password length (8-64, Enter for 16): 10 Include uppercase letters? (Y/n): n Include digits? (Y/n): n Include symbols? (Y/n): n How many passwords (1-10, Enter for 1): Using lowercase (26 possible characters): 1. cxlhzpjdsq Strength: weak, a computer could crack it quickly (about 47 bits of randomness) Make more passwords? (y/N): n Goodbye. Keep your passwords in a password manager.
How it is built
1. Questions with a default answer
Pressing Enter returns the default, which the prompt shows in capitals as Y/n or y/N. Any answer other than y, yes, n or no asks again.
def ask_yes_no(prompt, default): """Return True or False; pressing Enter gives the default.""" hint = "Y/n" if default else "y/N" while True: answer = input(f"{prompt} ({hint}): ").strip().lower() if answer == "": return default if answer in ("y", "yes"): return True if answer in ("n", "no"): return False print(" Please answer y or n.")
2. Choosing the characters
Lowercase letters are always used; each other set joins the dictionary when the user says yes. string.ascii_uppercase and string.digits save typing out the alphabet and the digits.
def choose_character_sets(): """Ask which kinds of characters to use; lowercase letters are always in.""" sets = {"lowercase": string.ascii_lowercase} if ask_yes_no("Include uppercase letters?", True): sets["uppercase"] = string.ascii_uppercase if ask_yes_no("Include digits?", True): sets["digits"] = string.digits if ask_yes_no("Include symbols?", True): sets["symbols"] = SYMBOLS return sets
3. Building the password
One character comes from each chosen set first, so a password that should have digits always has one. secrets.choice picks the rest and a final shuffle mixes the positions; secrets uses the system's secure randomness, which random does not.
def make_password(length, sets): """Build a password with at least one character from every chosen set.""" # One character from each set first, so no set is missing by chance password = [secrets.choice(group) for group in sets.values()] pool = "".join(sets.values()) while len(password) < length: password.append(secrets.choice(pool)) # Shuffle so those first characters are not always at the front secrets.SystemRandom().shuffle(password) return "".join(password)
4. How strong is it
Each character drawn from a pool of pool_size adds log2(pool_size) bits, so longer passwords and bigger pools both help. Under 50 bits is weak and 80 or more is strong.
def strength_note(length, pool_size): """Rate a password by how many bits of randomness it holds.""" bits = length * math.log2(pool_size) if bits < 50: verdict = "weak, a computer could crack it quickly" elif bits < 80: verdict = "good for most accounts" else: verdict = "strong" return f"{verdict} (about {bits:.0f} bits of randomness)"
5. The main loop
Each round asks the questions, prints the passwords with their strength and asks whether to make more. Enter at that last question means no, so the program ends.
def main(): """Ask for the options, print the passwords, and repeat if wanted.""" print("Password generator") while True: print() length = ask_number("Password length", MIN_LENGTH, MAX_LENGTH, DEFAULT_LENGTH) sets = choose_character_sets() count = ask_number("How many passwords", 1, MAX_COUNT, 1) pool_size = len("".join(sets.values())) print() print(f"Using {', '.join(sets)} ({pool_size} possible characters):") for number in range(1, count + 1): print(f" {number:>2}. {make_password(length, sets)}") print(f"Strength: {strength_note(length, pool_size)}") print() if not ask_yes_no("Make more passwords?", False): break print("Goodbye. Keep your passwords in a password manager.")
Make it bigger
- Add an option to leave out look-alike characters such as
0,O,land1. - Make passphrases from random words, like
river-candle-orbit-mango. - Rate a password the user types with the same
strength_notefunction. - Let the user type their own set of symbols, for sites that allow only some.
What it practises
- secrets module
- string constants
- dictionaries
- default answers
- math.log2