A few days ago I posted my password generator project to help me learn and become more comfortable. I got a lot of great replies from that and I've sense updated and would love another look at the program.
I've made it so that I can import it and use it to generator a password. I've also added support for completely custom subsets of characters.
Throw any suggests or comments you have! Anything is welcome.
import string
from string import ascii_lowercase
from string import ascii_uppercase
from string import digits as numeric
from string import punctuation
import secrets
import argparse
from argparse import HelpFormatter
def generate_characters(character_set, character_amount):
for _ in range(0, character_amount):
yield secrets.choice(character_set)
def shuffle(input_str):
output = ""
for _ in range(0, len(input_str)):
index = secrets.randbelow(len(input_str))
output += "".join(input_str[index])
input_str = "".join([input_str[:index], input_str[index + 1 :]])
return output
def generate_password(password_length,
subset_lowercase=ascii_lowercase, subset_uppercase=ascii_uppercase,
subset_numeric=numeric, subset_special="!@#$%^&*",
min_lowercase=1, min_uppercase=1,
min_numeric=1, min_special=1):
superset = "".join([subset_lowercase, subset_uppercase, subset_numeric, subset_special])
password = "".join(generate_characters(subset_lowercase, min_lowercase))
password += "".join(generate_characters(subset_uppercase, min_uppercase))
password += "".join(generate_characters(subset_numeric, min_numeric))
password += "".join(generate_characters(subset_special, min_special))
password += "".join(generate_characters(superset, password_length-len(password)))
return shuffle(password)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
formatter_class=HelpFormatter,
description="Generates a password",
usage="")
parser.add_argument(
"-len",
"--length",
type=int,
default=24,
dest="password_length",
help="Length of the generated password")
parser.add_argument(
"-lc",
"--lower",
type=int,
default=1,
dest="min_lowercase",
help="Minimum number of lowercase alpha characters")
parser.add_argument(
"-uc",
"--upper",
type=int,
default=1,
dest="min_uppercase",
help="Minimum number of uppercase alpha characters")
parser.add_argument(
"-num",
"--numeric",
type=int,
default=1,
dest="min_numeric",
help="Minimum number of numeric characters")
parser.add_argument(
"-sp",
"--special",
type=int,
default=1,
dest="min_special",
help="Minimum number of special characters")
parser.add_argument(
"-ext",
"--extended",
action="store_const",
default=False,
const=True,
dest="special_extended",
help="Toggles the extended special character subset. Passwords may not be accepted by all services")
parser.add_argument(
"-sl",
"--subset_lower",
type=str,
default=ascii_lowercase,
dest="subset_lower",
help="Allows for a custom subset of lowercase characters")
parser.add_argument(
"-su",
"--subset_upper",
type=str,
default=ascii_uppercase,
dest="subset_upper",
help="Allows for a custom subset of uppercase characters")
parser.add_argument(
"-sn",
"--subset_numeric",
type=str,
default=numeric,
dest="subset_numeric",
help="Allows for a custom subset of numeric characters")
parser.add_argument(
"-ss",
"--subset_special",
default="",
type=str,
dest="subset_special",
help="Allows for a custom subset of special characters")
args = parser.parse_args()
if args.subset_special:
special = args.subset_special
elif args.special_extended:
special = punctuation
else:
special = "!@#$%^&*"
generated_password = generate_password(
args.password_length,
args.subset_lower,
args.subset_upper,
args.subset_numeric,
special,
args.min_lowercase,
args.min_uppercase,
args.min_numeric,
args.min_special,
)
print("Password:", generated_password)