Search justacoding.blog. [Enter] to search. Click anywhere to close.

November 24th, 2022

Build Your Own Python Password Generator

Let’s create a simple Python password generator.

The password generator will be accessible via the terminal, and you’ll be able to configure various options to customise the output (password length, whether the password should contain special characters and so on).

So with that in mind, let’s begin!

The full password generator script

We’re keeping this as simple as possible, and as such, our Python password generator functionality is encapsulated in one single script.

Here it is:

import math
import secrets
import string
import random

# Our generated password can contain different types
# of charcter, so let's store these character types
# by accessing them from the imported string module
letters = string.ascii_letters
digits = string.digits
special_chars = string.punctuation

# To avoid code duplication, add a simple helper 
# function to receive the user's y/n input and 
# validate/parse it as required
def yes_or_no_input(text):
    while True: 
        input_var = input(text)
        if input_var != "y" and input_var != "n":
            print("Please enter y for yes or n for no")
            continue
        else:
            break
    return input_var == "y"

# Receive the user's required password length with 
# some simple validation
while True: 
    try:
        length = int(input("Desired length? "))
        if length < 0 or length > 24:
            print("Please enter a number between 1 and 24")
            continue
    except ValueError:
        print("Please enter a valid number between 1 and 24")
        continue
    else:
        break
    
contains_letters = yes_or_no_input("Contains letters? (y/n) ")

contains_digits = yes_or_no_input("Contains digits? (y/n) ")

contains_special_characters = yes_or_no_input("Contains special characters? (y/n) ")

# We want an equal number of different character types
# for the sake of ease, so divide the required password
# length by the amount of different character type
# constraints (rounding up if necessary) to determine the
# chunk size per each character type
constraints = 0 
if contains_letters: 
    constraints += 1
if contains_digits: 
    constraints += 1
if contains_special_characters: 
    constraints += 1

chunk_size = math.ceil(length / constraints)

# Build an array containing the randomly generated
# characters, making sure to only include the character
# types specified by the user
output_members = []

for i in range(chunk_size):
    if contains_letters:
        output_members.append(secrets.choice(letters))
    if contains_digits: 
        output_members.append(secrets.choice(digits))
    if contains_special_characters: 
        output_members.append(secrets.choice(special_chars))

# The characters were added in a predictable pattern
# Let's shuffle them all around to make the order
# a little more random
random.shuffle(output_members)

# Let's truncate the string to only be the required length
# It's possible that this string can be too long based on input
# Ie. required length 10 divided by 3 constraints = 3.33, which 
# is rounded up to 4 = 12
print(''.join(output_members)[0:length])

That’s the entirety of the program.

Using the script

To use the password generator, you can simply type this into your terminal:

python3 password-generator.py

That’s based on using Python 3 with a script file named password-generator.py — tweak as necessary!

You’ll then be prompted for your inputs and can follow through the steps to generated a password.

Prompting for user input

To add a little bit of customisation to the password generator, we’ll be prompting for user input via the terminal.

We’ll ask the user for:

  • The password length (must be between 1 and 24 characters)
  • Whether the password should contain letters
  • Whether the password should contain numbers
  • Whether the password should contain special characters

Based on these parameters, we’ll tailor the password generation functionality later on in the script. The generated password will conform to these specifications, this will be achieved by utilising some simple logic — we’ll go through that shortly.

Here’s a standard way of requesting and receiving user input in Python:

while True: 
    try:
        length = int(input("Desired length? "))
        if length < 0 or length > 24:
            print("Please enter a number between 1 and 24")
            continue
    except ValueError:
        print("Please enter a valid number between 1 and 24")
        continue
    else:
        break

The backbone here is Python’s input function. This is what allows user input, and we can store this input in a variable for use within our program. That’s what we’re doing here (storing the input in the length variable).

The other important thing to note is the use of the while loop.

In simple terms, this mechanism allows us to more easily validate the user input.

We know the user input should be numeric, and we know that it should be a value between 1 and 24. Based on this, we should only break this while look once all conditions are met – otherwise, simply represent the question back to the user and inform them of their incorrect response.

If you enter an invalid value into the terminal when prompted for this parameter, you’ll see that that’s what happens.

A handy helper function for yes/no input

You’ll notice we are asking for the same type of input numerous times in this script – we want a yes or a no response from the user.

Instead of repeating the same snippet over and over, we can extract the necessary functionality to its own dedicated function. We can then more easily reuse this snippet as and when required:

def yes_or_no_input(text):
    while True: 
        input_var = input(text)
        if input_var != "y" and input_var != "n":
            print("Please enter y for yes or n for no")
            continue
        else:
            break
    return input_var == "y"

As these inputs are ultimately boolean values, we’re also parsing the response to a boolean here, also.

So y evaluates to True, whilst n evaluates to False (based on our logic here).

Reusing the function is as simple as doing:

contains_letters = yes_or_no_input("Contains letters? (y/n) ")

You should aim to reuse this kind of code wherever possible, generally, as it’ll lead to a more robust program that’s easier to work with and expand.

Determining the characters to be used in the password

At this point, we have the necessary variables that are required to formulate the password:

  • length
  • contains_letters
  • contains_digits
  • contains_special_characters

And based on our prior validation, we know that these members are actual valid and in the expected format/type.

With that said, let’s look at the mechanism used to actually generate the password itself!

Mixing in the different character types

There are many ways to do this kind of thing. Essentially, what we want to do here is to include the various character types (letters, digits, special characters) in an equal amount (more or less).

To do this, we can divide the required length by the amount of different character types (referenced by the constraints variable in my script) that should be present.

As an example, if the user wants a password with a length of 12, and they only want it to be comprised of letters and special characters – we’ll do 12 / 2, meaning each character type should occur 6 times within the generated password.

constraints = 0 
if contains_letters: 
    constraints += 1
if contains_digits: 
    constraints += 1
if contains_special_characters: 
    constraints += 1

chunk_size = math.ceil(length / constraints)

As you can see, chunk_size effectively references how big each “chunk” of each character type should be.

I’m then looping chunk_size amount of times to build an array of output members:

output_members = []

for i in range(chunk_size):
    if contains_letters:
        output_members.append(secrets.choice(letters))
    if contains_digits: 
        output_members.append(secrets.choice(digits))
    if contains_special_characters: 
        output_members.append(secrets.choice(special_chars))

And then using random.shuffle to make sure that the array is in a random order:

random.shuffle(output_members)

Generating the password itself

At this point, we have an array containing all of the characters that should comprise our final password.

However, because of the logic we have implemented, it may be possible that there are too many characters to fit into the password based on the user’s intended length.

Consider the following example.

  • The user wants a password 10 characters in length
  • They’ve opted for it to be comprised of all 3 character types (numbers, digits, special characters)
  • Based on our logic above, that means we’ll generate 3 chunks of 4 characters as we’re rounding up with math.ceil
  • So 10/3 = 3.33 is rounded to 4
  • … and 4 * 3 = 12

12 characters is too many, though, we only want 10.

So we just need to trim 2 letters from the end of password in this scenario:

print(''.join(output_members)[0:length])

Firstly we’re joining the array to convert it to a string, and then we’re truncating it back down to 10 characters.

And with that, our Python password generator is complete!

Where to now?

I hope you’ve enjoyed reading through this short article.

As you can see, the password generator is fairly basic, the next step may be to expand or extrapolate on the functionalities as per your requirement.

Things to think about:

  • What other mechanisms can you employ to have a random mix of the character types?
  • How would you measure the strength of a generated password?

There are quite a few different avenues to go down with this kind of functionality, this simple and short script should provide something of a good basis!

Other similar articles

Feel free to check out our Python section for similar articles to this one!

Much like this simple password generator; simple script examples have been provided, and the programs are generally explained step by step in significant detail each time.

Thanks for reading!

Have any questions? Ping me over at @justacodingblog
← Back to blog