Generating Random Password in Python - Practical Guide
Tags: python, password, security, encryption
Published: 2020-03-30 07:24:53

When following a registration process, everything goes well until we are to fill in password. Choosing a password has always linger the registration flow. In this post we'll learn how to create a password generator program in python. 

## Pre-requisite
All that is required to follow along is a basic knowledge of python and an IDE(Integrated Development Environment).

## Folder Structure
I have app.py file in a folder py-password and i'll be working on this file in this post.

Password is often a combination of letters (lowercase and uppercase), digits and punctuation. Lets create some global variables to store letters (lowercase and uppercase), digits and punctuation respectively. To achieve this task, python have a builtin *string* library which contains a collection of string constants. 

**app.py**
```python
import string
import random

LETTERS = string.ascii_letters
NUMBERS = string.digits  
PUNCTUATION = string.punctuation    

print( LETTERS )
print( NUMBERS )
print( PUNCTUATION )
```

From above code, _string_ library was imported and we accessed it's properties ascii_letters, digits and punctuation which returns lowercase and uppercase characters, numbers and punctuation respectively. 

Let's see the actual values of those variables. Run the application using `python app.py`

**terminal result**
```
$
   abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
   0123456789
   !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
$
```

> **NOTE:**
> _python's builtin string library have printable property that can generate alphanumerical character sequence, but for the purpose of this application, we will stick to the current approach of having them separated._

Clean up code base by removing all print statement.

It'll be nice if we can specify how long a password should be, for that we'll create a function that receives a password length as an input from a user. 

**app.py**

```python
...
...

def get_password_length():
    '''
      Retrieves the length of a password
      :returns number <class 'int'>
    '''
    length = input("How long do you want your password: ")
    return int(length)
```
The above code utilizes python's builtin *input* function to retrieve a value and converts it to numeric using python's builtin *int* function.

> **NOTE:**
> _When python's builtin input function is used to retrieve value from a user, the value is always a string. This was why it was necessary to convert the given value to numeric using python's builtin int function._

To generate unique random passwords, we'll make use of python's builtin random library. Below is a method which generates a unique random password with a specified length. 

**app.py**
```python
...
...

def password_generator(length=8):
    '''
    Generates a random password having the specified length
    :length -> length of password to be generated. Defaults to 8
        if nothing is specified.
    :returns string <class 'str'>
    '''
    # create alphanumerical from string constants
    printable = f'{LETTERS}{NUMBERS}{PUNCTUATION}'

    # convert printable from string to list and shuffle
    printable = list(printable)
    random.shuffle(printable)

    # generate random password and convert to string
    random_password = random.choices(printable, k=length)
    random_password = ''.join(random_password)
    return random_password
```

The above function is defined with one parameter **length** which defaults to 8 (the recommended minimum length of a password is 8). Let's give above code some explanations:

##### # create alphanumerical from string constants
We already have some global variables declared, which holds string constants (digits, punctuation, characters(lowercase and uppercase) respectively). Those constants are concatenated using python's _**F**_ string and saved in a variable named **printable**. 

##### # convert printable from string to list and shuffle
**printable** variable which holds an alphanumerical value (string: character sequence) is converted to a list (this is compulsory). Shuffling the list makes it difficult to have same password generated twice. _random.shuffle()_ takes a list as it's argument and changes the list item position randomly.  


> **Error that may occur**
> _If printable is passed to random.shuffle() without converting it first to a list, it'll result to a **TypeError**_
> _**TypeError**: 'str' object does not support item assignment_

##### # generate random password and convert to string
_random.choices_ takes couple of arguments but we are more concerned about the first and last argument. The first argument( **population** ) accepts an iterable and the last argument ( **k** ) specifies the number of items that will be randomly choosen from the given iterable. When calling random.choices we passed printable as the first argument and length (a parameter on our function) as the last argument.

_join_ method was passed a result from _random.choices_ (which returns a list as result) and called on an empty string ( ' ' ) that acts as a separator. This just help to join (like it's name says) all list item returned from random.choices as a single string with no spaces.

Alright, now that we've gone through all code statement, lets' test the application.

**app.py**
```python
def password_generator(length=8):
    ....
    ....

# testing password generator with it's default length of 8
password_one = password_generator()

# testing password generator using user's input as length
password_length = get_password_length()
password_two = password_generator(password_length)

print("password one (" + str(len(password_one)) + "):\t\t" + password_one )
print("password one (" + str(len(password_two)) + "):\t\t" + password_two )
```
If you run the file from your terminal/command prompt using `python app.py` you should have a result that resembles (but not exactly) what i have below 

**terminal result**
```
C:\Users\spaceofmiah\dev\Python\core\py-password (master -> origin)
$ python app.py
  How long do you want your password: 12
  password one (8):               }8yI;[(%
  password one (12):              N@DAiQncR^1t
```
Alright, we now have a functional app. If you've come this far WELL DONE!!! 🎉🎊 

App is functioning well, but can be improved on. 

Including a functionality that allows us to determine if we want our password to be all digits, letters, punctuation or a combination of either digits, punctuation or letters would be a great addition to the application. Let's see how that can be achieved.

To begin with, let's clean up our code by removing those tests included previously. Completed code clean up ? alright, let's move on. 

To complete this task, we'll create two functions. First function will be used to retrieve a user's choice for password character combination (digits, punctuation, letters or mixture of either one or more).

**app.py**
```python
......
......


def password_combination_choice():
    '''
    Prompt a user to choose password character combination which could either be digits, letters, 
    punctuation or combibation of either of them.

    :returns list <class 'list'> of boolean. Defaults to [True, True, True] for invalid choice
        0th list item represents digits
        1st list item represents letters
        2nd list item represents punctuation
    '''
    # retrieve a user's password character combination choice
    want_digits = input("Want digits ? (True or False) : ")
    want_letters = input("Want letters ? (True or False): ")
    want_puncts = input("Want punctuation ? (True or False): ")

    # convert those choices from string to boolean type
    try:
        want_digits = eval(want_digits.title())
        want_puncts = eval(want_puncts.title())
        want_letters = eval(want_letters.title())
        return [want_digits, want_letters, want_puncts]

    except NameError as e:
        print("Invalid value. Use either True or False")
        print("Invalidity returns a default, try again to regenerate")

    return [True, True, True]
```

The above code retrieves user input which must be either _True_ or _False_, if anything else is specified a default is returned. 

Let's create another function which will utilize a user's choice (for password character combination) to generate string value that'll be used to create random password. 

**app.py**
```python
def fetch_string_constant(choice_list):
    '''
    Returns a string constant based on users choice_list.
    Returned string constant can either be digits, letters, punctuation or
    combination of them.
    : choice_list --> list <class 'list'> of boolean
        0th list item represents digits    
            True to add digits to constant False otherwise
        1st list item represents letters   
            True to add letters to constant False otherwise
        2nd list item represents punctuation
            True to add punctuation to constant False otherwise
    '''
    string_constant = ''
    string_constant += NUMBERS if choice_list[0] else ''
    string_constant += LETTERS if choice_list[1] else ''
    string_constant += PUNCTUATION if choice_list[2] else ''
    return string_constant
```
The above function returns a string constant base on the choice_list. Ternary if operator was used to create the string constant. Now that we have our string constant generator,let's update *password_generator* function to utilize it, change it's signature definition and update doctype. 

```python
def password_generator(cbl, length=8):
    '''
    Generates a random password having the specified length
    :length -> length of password to be generated. Defaults to 8
        if nothing is specified
    :cbl-> a list of boolean values representing a user choice for 
        string constant to be used to generate password.
        0th list item represents digits    
             True to add digits to constant False otherwise
        1st list item represents letters   
             True to add letters to constant False otherwise
        2nd list item represents punctuation
             True to add punctuation to constant False otherwise
    :returns string <class 'str'>
    '''
    # create alphanumerical by fetching string constant
    printable = fetch_string_constant(cbl)
```
One more step before testing our password generator app. Let's create an entry point for the application.
```python
def fetch_string_constant():
    .......
    .......

# application entry point 
if __name__ == '__main__':
    length = get_password_length()
    choice_list = password_combination_choice()
    password = password_generator(choice_list, length)
    print(password)
```

Alright, now we have our password generator application all set up. Let's give it a test :- launch the application by running the below command on your terminal/command prompt

`python app.py`

**terminal result**
```
C:\Users\spaceofmiah\dev\Python\core\py-password (master -> origin)
λ python app.py

  How long do you want your password: 12
  Want digits ? (True or False) : True
  Want letters ? (True or False): True
  Want punctuation ? (True or False): False

  yjGmG09xpt07
```
> _Kindly note: your result gotten will be different from mine as the password is randomly generated._

If you've come this far, you not only have a working password generator application, you've as well gotten familiar with python's builtin _string_ and _random_ library. 

Click the **Like** button to pop my heart red 🙂. 
Don't leave without clicking the **Follow** button ( Thank You For Doing So ✌)

**Link to the complete code base can be found on my [github](https://github.com/spaceofmiah/py-password/blob/b7ef7eecb35e3a44795279eff31bf540724b0cfe/app.py)**
_If you've spot any errors, have questions or contributions, i will be more than glad to hear it. Kindly leave a comment below._

**Click on the *Share* button to help get this post around**