patternpythonMinor
How random can you get?
Viewed 0 times
randomcanyougethow
Problem
I work for a service desk and change A LOT of passwords. Most the time the end user is a complete fool that doesn't understand the requirements for the password they want to use. So I created a program that will generate random passwords based on a randomness level (1 - 3).
Here's how the levels work:
What I would like to know is if there is anything I can do better inside of this program, along with a way to make more readable strings output from the
```
import optparse
import string
import random
import itertools
import os
from random import randint
init_cons = (set(string.ascii_lowercase) - set("aeiou")
- set("qxc")
| {"bl", "br", "cl", "cr", "dr", "fl", "fr", "gl", "gr", "pl", "pr", "sk", "sl", "sm", "sn", "sp", "st",
"str", "sw", "tr"}
)
final_cons = (set(string.ascii_uppercase) - set("aeiou")
- set("qxcsj")
| {"ct", "ft", "mp", "nd", "ng", "nk", "nt",
"pt", "sk", "sp", "ss", "st"}
)
vowels = "aeiou"
SYLLABLES = map(''.join, itertools.product(init_cons,
vowels,
final_cons))
opts = optparse.OptionParser()
opts.add_option("-l", "--level", metavar="RANDOMNESS LEVEL",
dest="level", help="How random do you want the passwords [1-3]")
opts.add_option("-a", "--add", metavar="PASS TO ADD",
dest="add", help="Add a password to the password file")
opts.add_option("--test-demo", action="store_true", dest="test",
help="Run the program 5 times in each level")
(options, a
Here's how the levels work:
- Reads the passwords from a file that you made, these are the most readable passwords
- Runs through a
gibberishfunction and returns somewhat word like passwords complete with uppercase, lowercase, integer, and special character
- Completely random string, no pattern to it, has uppercase, lowercase, integers, and special characters.
What I would like to know is if there is anything I can do better inside of this program, along with a way to make more readable strings output from the
gibberish function.```
import optparse
import string
import random
import itertools
import os
from random import randint
init_cons = (set(string.ascii_lowercase) - set("aeiou")
- set("qxc")
| {"bl", "br", "cl", "cr", "dr", "fl", "fr", "gl", "gr", "pl", "pr", "sk", "sl", "sm", "sn", "sp", "st",
"str", "sw", "tr"}
)
final_cons = (set(string.ascii_uppercase) - set("aeiou")
- set("qxcsj")
| {"ct", "ft", "mp", "nd", "ng", "nk", "nt",
"pt", "sk", "sp", "ss", "st"}
)
vowels = "aeiou"
SYLLABLES = map(''.join, itertools.product(init_cons,
vowels,
final_cons))
opts = optparse.OptionParser()
opts.add_option("-l", "--level", metavar="RANDOMNESS LEVEL",
dest="level", help="How random do you want the passwords [1-3]")
opts.add_option("-a", "--add", metavar="PASS TO ADD",
dest="add", help="Add a password to the password file")
opts.add_option("--test-demo", action="store_true", dest="test",
help="Run the program 5 times in each level")
(options, a
Solution
CLI
Did you read the deprecation notice on the
-
Convert to integers and limit available choices to 1, 2 and 3:
-
Disallow specifying two options at once, using groups:
or subcommands:
I like the last version much as it helps separate the logic and the associated values:
Files
Your
You also happen to use the plain filename in this function and take it as a parameter in
You may also note the use of
Lastly, I would use an absolute path for the password file rather than a file in the current working directory. It let you easily launch it from an other folder or even make it executable and put it in your
This way,
Default values
As you don't allow access to these through the command line, you may want to define default values for each of your "main" functions. You already had them for
Did you read the deprecation notice on the
optparse module? Use argparse instead, it will help you simplify the code.-
Convert to integers and limit available choices to 1, 2 and 3:
parser = argparse.ArgumentParser(description='TODO')
parser.add_argument("-l", "--level", metavar="RANDOMNESS LEVEL", type=int, choices=range(1,4),
dest="level", help="How random do you want the passwords [1-3]")
parser.add_argument("-a", "--add", metavar="PASS TO ADD",
dest="add", help="Add a password to the password file")
parser.add_argument("--test-demo", action="store_true", dest="test",
help="Run the program 5 times in each level")
args = parser.parse_args()-
Disallow specifying two options at once, using groups:
parser = argparse.ArgumentParser(description='TODO')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-l", "--level", metavar="RANDOMNESS LEVEL", type=int, choices=range(1,4),
dest="level", help="How random do you want the passwords [1-3]")
group.add_argument("-a", "--add", metavar="PASS TO ADD",
dest="add", help="Add a password to the password file")
group.add_argument("--test-demo", action="store_true", dest="test",
help="Run the program 5 times in each level")
args = parser.parse_args()or subcommands:
parser = argparse.ArgumentParser(description='TODO')
commands = parser.add_subparser(metavar='command', dest='command')
generate = commands.add_parser('generate', help='create a new password')
generate.add_argument('level', type=int, choices=[1, 2, 3],
help="How random do you want the passwords [1-3]")
store = commands.add_parser('store', help='Add a password to the password file')
store.add_argument('password', help='A password to store in the file')
commands.add_parser('demo', help="Run the program 5 times in each level")
args = parser.parse_args()
if args.command is None:
parser.error('command is required')I like the last version much as it helps separate the logic and the associated values:
import argparse
def command_line_parser():
parser = argparse.ArgumentParser(description='TODO')
commands = parser.add_subparser(metavar='command', dest='command')
generate = commands.add_parser('generate', help='create a new password')
generate.add_argument('level', type=int, choices=[1, 2, 3],
help="How random do you want the passwords [1-3]")
store = commands.add_parser('store', help='Add a password to the password file')
store.add_argument('password', help='A password to store in the file')
commands.add_parser('demo', help="Run the program 5 times in each level")
args = parser.parse_args()
if args.command is None:
parser.error('command is required')
return args
...
if __name__ == '__main__':
options = command_line_parser()
if options.command == 'generate':
print(console_main(options.level))
elif options.command == 'store':
add_to_file(options.password)
elif options.command == 'demo':
test_levels(5)Files
Your
add_to_file function append the provided word into the file. There is a special mode to the open function exactly for that purpose:def add_to_file(word):
with open("pass_list.txt", "a") as words:
print(word, file=words)You also happen to use the plain filename in this function and take it as a parameter in
read_pass_from_file. I would rather define the default filename as a constant that will be used in each function:PASSWORD_FILE = 'pass_list.txt'
def add_to_file(word):
with open(PASSWORD_FILE, "a") as words:
print(word, file=words)
def read_pass_from_file():
with open(PASSWORD_FILE, "r") as password:
data = password.readlines()
return random.choice(data).rstrip('\n')You may also note the use of
.readlines() instead of .read().splitlines() that required twice as much as memory. The drawback being that, contrary to splitlines, readlines will keep the newline at the end of each string, so we need to clean it.Lastly, I would use an absolute path for the password file rather than a file in the current working directory. It let you easily launch it from an other folder or even make it executable and put it in your
PATH:import os.path
PASSWORD_FILE = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'pass_list.txt')This way,
pass_list.txt will always be a file in the same folder than your script.Default values
As you don't allow access to these through the command line, you may want to define default values for each of your "main" functions. You already had them for
random_strings, we just modified the logic for read_pass_from_file, so there is only gibberish missing. Doing so will allow you to simplify console_main since it now receive integers directCode Snippets
parser = argparse.ArgumentParser(description='TODO')
parser.add_argument("-l", "--level", metavar="RANDOMNESS LEVEL", type=int, choices=range(1,4),
dest="level", help="How random do you want the passwords [1-3]")
parser.add_argument("-a", "--add", metavar="PASS TO ADD",
dest="add", help="Add a password to the password file")
parser.add_argument("--test-demo", action="store_true", dest="test",
help="Run the program 5 times in each level")
args = parser.parse_args()parser = argparse.ArgumentParser(description='TODO')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-l", "--level", metavar="RANDOMNESS LEVEL", type=int, choices=range(1,4),
dest="level", help="How random do you want the passwords [1-3]")
group.add_argument("-a", "--add", metavar="PASS TO ADD",
dest="add", help="Add a password to the password file")
group.add_argument("--test-demo", action="store_true", dest="test",
help="Run the program 5 times in each level")
args = parser.parse_args()parser = argparse.ArgumentParser(description='TODO')
commands = parser.add_subparser(metavar='command', dest='command')
generate = commands.add_parser('generate', help='create a new password')
generate.add_argument('level', type=int, choices=[1, 2, 3],
help="How random do you want the passwords [1-3]")
store = commands.add_parser('store', help='Add a password to the password file')
store.add_argument('password', help='A password to store in the file')
commands.add_parser('demo', help="Run the program 5 times in each level")
args = parser.parse_args()
if args.command is None:
parser.error('command is required')import argparse
def command_line_parser():
parser = argparse.ArgumentParser(description='TODO')
commands = parser.add_subparser(metavar='command', dest='command')
generate = commands.add_parser('generate', help='create a new password')
generate.add_argument('level', type=int, choices=[1, 2, 3],
help="How random do you want the passwords [1-3]")
store = commands.add_parser('store', help='Add a password to the password file')
store.add_argument('password', help='A password to store in the file')
commands.add_parser('demo', help="Run the program 5 times in each level")
args = parser.parse_args()
if args.command is None:
parser.error('command is required')
return args
...
if __name__ == '__main__':
options = command_line_parser()
if options.command == 'generate':
print(console_main(options.level))
elif options.command == 'store':
add_to_file(options.password)
elif options.command == 'demo':
test_levels(5)def add_to_file(word):
with open("pass_list.txt", "a") as words:
print(word, file=words)Context
StackExchange Code Review Q#145854, answer score: 7
Revisions (0)
No revisions yet.