Generating Passwords with Python
This is a walkthrough of a simple Python script that generates a random string value. This script supports some basic password rules that are often used by web sites such as length and minimum character requirements.

I pulled this script together randomly yesterday. There are more than enough web sites with password generators, and password managers like BitWarden, LastPass and even Apple's recently liberated Passwords application all have functionality built in (likely with more features).
In my case this script came from an early morning "I should try this.." moment as I was changing passwords on a few sites. As always with Python, I was amazed at how quickly I was able to throw together a "give me a random/relatively strong passwords" script. In less than 15 minutes I had a working product.
I decided to continue working through some over-engineering. The final result shown below does all of the following:
- Calling the script with no parameters returns an 18-character string with 2 uppercase letters, 3 digits, and 3 special characters.
- You can pass arguments to enable prompts, ex.
--prompt=y
When you enable prompts you can customize the length of the string and the minimum number of characters in each category. - Prompt also allows you to limit the special characters used in the generated output (many web sites only allow certain special characters).
- The output string will not contain any duplicate characters; I added this as a quick fix for issues with consecutive character checks in some web sites.
- You can also bypass prompts and still create a longer password by passing ex.
--length=20
when running the script.
import argparse, string, secrets
#supports passing arguments to the script - by default user will be prompted each time
parser = argparse.ArgumentParser(description='Password Generator')
parser.add_argument('--prompt', action="store", dest='prompt', default='n')
parser.add_argument('--length', action='store', dest='pw_length', default=18)
args = parser.parse_args()
pwl = int(args.pw_length)
if(args.prompt == "y"):
#input prompts
passwordLength = int(input(f"Password length ({pwl}):").strip() or pwl)
minUpper = int(input("Minimum upper case (2):").strip() or "2")
minDigit = int(input("Minimum digits (3):").strip() or "3")
minSpecial = int(input("Minimum special characters (3):").strip() or "3")
specialAllowed = str(input("Special characters allowed (any):").strip() or string.punctuation)
else:
passwordLength = pwl
minUpper = 2
minDigit = 3
minSpecial = 3
specialAllowed = string.punctuation
#calculate length of random string after requirements
specialLength = (minUpper + minDigit + minSpecial)
remainingLength = passwordLength - specialLength
#math needs to work
if(remainingLength < 0):
print(f'Password length ({passwordLength}) should be greater than the individual character lengths ({specialLength})')
quit()
#remove any duplicate characters provided
specialAllowed = ''.join(set(specialAllowed))
if(len(specialAllowed) < minSpecial):
print(f'Number of special characters provided ({len(specialAllowed)}) is less than the minimum required ({minSpecial})')
quit()
characterPool = ""
#upper case join/set will prevent same character selection
alphas = ""
while (len(alphas) <= minUpper):
alphas = alphas + secrets.choice(string.ascii_uppercase)
alphas = ''.join(set(alphas))
#numbers
digits = ""
while(len(digits) <= minDigit):
digits = digits + secrets.choice(string.digits)
digits = ''.join(set(digits))
#special characters
specials = ""
while(len(specials) <= minSpecial):
specials = specials + secrets.choice(specialAllowed)
specials = ''.join(set(specials))
#fill remaining
otherChars = ""
while(len(otherChars) <= remainingLength):
otherChars = otherChars + secrets.choice(string.ascii_lowercase)
otherChars = ''.join(set(otherChars))
# final character set with all required minimum characters plus random
characterPool = (alphas + digits + specials + otherChars)
characterPool = ''.join(set(characterPool))
#shuffle the selected characters and make sure no duplicates exist
while(len(characterPool) <= passwordLength):
characterPool = characterPool + secrets.choice(string.printable)
characterPool = ''.join(set(characterPool))
password = ""
#final pool should be unique characters
while (len(password) <= passwordLength):
password = password + secrets.choice(characterPool)
password = ''.join(set(password))
#print(password)
print(password)
The script has a few sanity checks included to make sure the character minimums don't exceed the overall requested length. There are plenty of opportunities to expand or enhance this to suit individual requirements.
Here's a quick breakdown of the functions used in this script:
- argparser enables passing of arguments
- input prompts for values
- secrets.choice(seq) Return a random element from the non-empty sequence seq
- string.join(iterable) Returns a string which is the concatenation of the strings in iterable.
- set(iterable) removes duplicate characters from iterable, here it is used to remove any duplicate characters from the string.
Here's all the combinations you can use with this script as written above with example outputs (no, these are not passwords I used).
Zero Input
password_generator.py
:q2tako3sXhlN6Pv`c-
Change Length of String
password_generator.py --length=24
a#tAvbj`8eqz9Bhod2mc}gu(k
Enable Prompts
password_generator.py --prompt=y
Password length (18):18
Minimum upper case (2):3
Minimum digits (3):4
Minimum special characters (3):3
Special characters allowed (any):#$^*-
^Mt23Ser5f1F0#q*dTv
If you are looking for faster or simpler solutions:
- secrets.token_urlsafe([nbytes=none]) will generate a very reasonable/similar output as the above script in a single line of code. nbytes allows you to specify the length, although it's not exact (each byte will result in approximately 1.3 characters).
- The rstr module has a very useful set of string functions. I originally thought about using a regex pattern to generate a string, and the xeger function in this library already does this!
- No time for code? Try Random Password Generator or Security.org
This was a great and (relatively) fast learning opportunity that showcases a simple but powerful way to use Python to solve real problems.