aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDennis Eriksen <d@ennis.no>2023-11-10 09:55:41 +0100
committerDennis Eriksen <d@ennis.no>2023-11-10 09:55:41 +0100
commit0f791d539e5d870919abb4ad2357c04c2c6b5c41 (patch)
treed6d8242400fc9d459ac70f38c314c4afa17889ab
parenttrying out new project structure (diff)
downloadmakepass-0f791d539e5d870919abb4ad2357c04c2c6b5c41.tar.gz
moving python-file to src
so I can add ruff-config and venv without cluttering the main folder
-rw-r--r--.gitignore3
-rw-r--r--README.md12
l---------[-rwxr-xr-x]makepass.py319
-rwxr-xr-xsrc/python/makepass.py318
-rw-r--r--src/python/ruff.toml64
5 files changed, 398 insertions, 318 deletions
diff --git a/.gitignore b/.gitignore
index be203fc..8f6d588 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,6 @@
src/rust/target
src/rust/debug
src/go/makepass.go
+
+# virtualenvs
+src/python/venv
diff --git a/README.md b/README.md
index 8082199..3cea926 100644
--- a/README.md
+++ b/README.md
@@ -218,6 +218,18 @@ Perl version. I like this version <3
### Python
I don't really care for python, but here it is. It's pretty slow, but I don't really know Python. It should probably be faster.
+To lint and format with `ruff`, `ruff` must first be installed.
+
+```
+$ cd src/python
+$ python3 -m venv venv
+$ source venv/bin/activate
+$ pip install --upgrade pip
+$ pip install ruff
+$ ruff check makepass.py
+$ ruff format makepass.py
+```
+
### Rust
Rust-version. It's fast!
diff --git a/makepass.py b/makepass.py
index 233ae7d..b6e1064 100755..120000
--- a/makepass.py
+++ b/makepass.py
@@ -1,318 +1 @@
-#!/usr/bin/env python3
-#
-# Author : Dennis Eriksen <d@ennis.no>
-# File : makepass.py
-# Created : 2023-09-14
-# License : BSD-3-Clause
-#
-# Copyright (c) 2018-2023 Dennis Eriksen <d@ennis.no>
-
-import argparse
-import os
-import random
-import sys
-
-# Global variables
-
-MAX = 255
-RANGE_MAX = 42
-RANGE_MIN = 8
-PASS_WORDS = 8
-
-LOWER = "abcdefghijklmnopqrstuvwxyz"
-UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-DIGIT = "0123456789"
-OTHER = "!$%&#/()=?+-_,.;:<>[]{}|@*"
-
-ALPHA = LOWER + UPPER
-ALNUM = ALPHA + DIGIT
-EVERY = ALNUM + OTHER
-
-NORMAL = ALNUM + "_-"
-SPECIAL = EVERY
-
-DEFAULT_LENGTH = 0
-DEFAULT_NUMBER = 10
-DEFAULT_WORDLIST = "/usr/share/dict/words"
-
-
-class CommonPrintData:
- def __init__(self, length, printbool, printlen, col_width, col_num):
- self.length = length
- self.printbool = printbool
- self.printlen = printlen
- self.col_num = col_num
- self.col_width = col_width
-
-
-#
-# Main function
-#
-def main():
- normal = os.getenv("MAKEPASS_NORMAL") or NORMAL
- special = os.getenv("MAKEPASS_SPECIAL") or SPECIAL
- wordlist_file = os.getenv("MAKEPASS_WORDLIST") or DEFAULT_WORDLIST
-
- # Initialize parser
- parser = argparse.ArgumentParser(
- description="Process some strings.", add_help=False
- )
-
- # Add arguments
- parser.add_argument("-h", "--help", action="store_true")
- parser.add_argument("-l", "--length", type=int, help="Length of strings")
- parser.add_argument("-n", "--number", type=int, help="Number of strings")
- parser.add_argument("-p", "--printlen", action="store_true")
- parser.add_argument("arg_length", type=int, nargs="?")
-
- # Parse arguments
- args = parser.parse_args()
-
- if args.help:
- usage()
-
- # Some error-checking
- # length
- try:
- # env or default
- length = int(os.getenv("MAKEPASS_LENGTH") or DEFAULT_LENGTH)
-
- # -l flag
- if args.length is not None:
- if args.length < 0 or MAX < args.length:
- raise ValueError("-l")
- length = args.length
-
- # solo argument
- if args.arg_length is not None:
- length = args.arg_length
-
- # check result
- if length < 0 or MAX < length:
- raise ValueError("length")
- except ValueError as e:
- print(f"{e} must be a number between 0 and {MAX}")
- sys.exit(1)
-
- # number
- try:
- # env or default
- number = int(os.getenv("MAKEPASS_NUMBER") or DEFAULT_NUMBER)
-
- # -n flag
- if args.number is not None:
- if args.number < 1 or MAX < args.number:
- raise ValueError("-n")
- number = args.number
-
- # check result
- if number < 1 or MAX < number:
- raise ValueError("number")
- except ValueError as e:
- print(f"{e} must be a number between 1 and {MAX}")
- sys.exit(1)
-
- # Set some other stuff
-
- printbool = args.printlen
- if printbool:
- printlen = 3 if length < 100 else 4
- else:
- printlen = 0
-
- # get terminal width
- try:
- xwidth = os.get_terminal_size().columns
- except Exception as _:
- xwidth = 1
-
- # get width of columns to print
- if length == 0:
- col_width = RANGE_MAX + 2
- else:
- col_width = length + 2
-
- # number of columns to print
- col_num = int(xwidth / (col_width + printlen))
- if col_num == 0:
- col_num = 1
-
- # config
- config = CommonPrintData(
- length=length,
- printbool=printbool,
- printlen=printlen,
- col_width=col_width,
- col_num=col_num,
- )
-
- #
- # print passwords
- #
-
- # Generate and print normal and special passwords
- print_columns("Normal passwords", number, normal, config)
-
- print()
-
- print_columns(
- "Passwords with special characters", int(number / 3 * 2 + 1), special, config
- )
-
- # Generate and print passphrases if wordlist exists
- if os.path.isfile(wordlist_file):
- print()
-
- print("Generating passphrases: ")
- passphrase(int(number / 2), wordlist_file)
-
-
-#
-# Print passwords in neat columns
-#
-def print_columns(title: str, num: int, chars: str, c: CommonPrintData):
- # Generate random strings
- strings = [randstring(c.length, chars) for _ in range(num)]
-
- print(f"{title}:")
-
- for i in range(num):
- # Print the length of the string if printlen is set
- if c.printbool:
- print(f"{len(strings[i]):0{c.printlen-1}}", end=" ")
-
- # Print the string, left justified based on column width
- print(f"{strings[i]:<{c.col_width}}", end="")
-
- # If we have printed enough strings for a single line or the
- # rest of strings are fewer than column number, we break lines.
- i += 1
- if i % c.col_num == 0 or (i == num and i % c.col_num > 0):
- print()
-
-
-#
-# Generate random strings that can work as passwords
-#
-def randstring(length, chars):
- """
- Generate a random password of the specified length. Use special
- characters if special_chars option is True.
- """
- if length == 0:
- length = random.randint(RANGE_MIN, RANGE_MAX)
-
- password = []
- password.append(random.choice(ALPHA))
- for i in range(length - 2):
- password.append(random.choice(chars))
- password.append(random.choice(ALNUM))
-
- # Randomly reorder the characters
- return "".join(password)
-
-
-#
-# Passphrases!
-#
-def passphrase(number, file_name):
- """
- Generate a number of random passphrases from the wordlist.
- """
- try:
- with open(file_name, "r") as f:
- lines = f.read().splitlines()
- except IOError:
- print(f"Cannot open file: {file_name}")
- sys.exit(1)
-
- for i in range(number):
- word_list = random.choices(lines, k=5)
- passphrase = "-".join(word_list)
- print(passphrase)
-
-
-def usage():
- text = """NAME
- makepass - create several random passwords
-
-SYNOPSIS
- makepass [OPTIONS] [NUM]
-
- If a NUM is provided, passwords will be NUM characters long.
-
- By default `makepass` will output passwords from the three following classes:
-
- - Normal passwords - random strings with letters (both lower and upper
- case), numbers, and dashes and underscores.
-
- - Passwords with special characters - random strings generated from lower
- and upper case letters, numbers, and the following characters:
- !#$%&/()=?+-_,.;:<>[]{}|@*
-
- - Passphrases - if we find a dictionary, a series of eight random words
- from the dictionary, separated by dashes. The number of words can not be
- changed, but you do not have to use all of them. Use as mane as you want.
-
- The first character will always be alphabetic, and the last will always be
- alphanumeric.
-
-DESCRIPTION
- makepass has the following options:
-
- -h
- output this help-text
- -l
- length of passwords. See MAKEPASS_LENGTH below
- -n
- number of passwords. See MAKEPASS_NUMBER below
- -p
- print length of number
-
-ENVIRONMENT
- makepass examines the following environmental variables.
-
- MAKEPASS_LENGTH
- Specifies the length of passwords. Valid values are 0-255. If 0, a
- random value between 8 and 42 will be used for each password. -l
- overrides this environmental variable, and the argument NUM overrides
- that again. So `MAKEPASS_LENGTH=10 makepass -l 12 14` will give
- passwords that are 14 characters long, even though both -l and
- MAKEPASS_LENGTH also specifies a length.
-
- MAKEPASS_NUMBER
- The number of passwords to generate. This formula is used to determine
- how many passwords from each group should be generated:
- - (n) normal passwords
- - (n / 3 * 2 + 1) special passwords
- - (n / 2) passphrases
- Where n is 10 by default. Valid values for n are 1-255. Floating-poing
- math is not used, so results may vary.
-
- MAKEPASS_PRINTLEN
- If 1, print length of all passwords. If 0, don\'t.
-
- MAKEPASS_NORMAL
- String of characters from which to generate "normal" passwords.
- Defaults to:
- abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_
-
- MAKEPASS_SPECIAL
- String of characters from which to generate passwords with special
- characters. Defaults to the same characters as in MAKEPASS_NORMAL, plus
- these:
- !#$%&/()=?+-_,.;:<>[]{}|@*
-
- MAKEPASS_WORDLIST
- Specifies the dictionary we find words for passphrases in. If this is
- unset or empty, we try "/usr/share/dict/words". If that file does not
- exist, no passphrases will be provided.
-
-AUTHOR
- Dennis Eriksen <https://dnns.no>"""
- print(text)
- sys.exit(0)
-
-
-if __name__ == "__main__":
- main()
+src/python/makepass.py \ No newline at end of file
diff --git a/src/python/makepass.py b/src/python/makepass.py
new file mode 100755
index 0000000..233ae7d
--- /dev/null
+++ b/src/python/makepass.py
@@ -0,0 +1,318 @@
+#!/usr/bin/env python3
+#
+# Author : Dennis Eriksen <d@ennis.no>
+# File : makepass.py
+# Created : 2023-09-14
+# License : BSD-3-Clause
+#
+# Copyright (c) 2018-2023 Dennis Eriksen <d@ennis.no>
+
+import argparse
+import os
+import random
+import sys
+
+# Global variables
+
+MAX = 255
+RANGE_MAX = 42
+RANGE_MIN = 8
+PASS_WORDS = 8
+
+LOWER = "abcdefghijklmnopqrstuvwxyz"
+UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+DIGIT = "0123456789"
+OTHER = "!$%&#/()=?+-_,.;:<>[]{}|@*"
+
+ALPHA = LOWER + UPPER
+ALNUM = ALPHA + DIGIT
+EVERY = ALNUM + OTHER
+
+NORMAL = ALNUM + "_-"
+SPECIAL = EVERY
+
+DEFAULT_LENGTH = 0
+DEFAULT_NUMBER = 10
+DEFAULT_WORDLIST = "/usr/share/dict/words"
+
+
+class CommonPrintData:
+ def __init__(self, length, printbool, printlen, col_width, col_num):
+ self.length = length
+ self.printbool = printbool
+ self.printlen = printlen
+ self.col_num = col_num
+ self.col_width = col_width
+
+
+#
+# Main function
+#
+def main():
+ normal = os.getenv("MAKEPASS_NORMAL") or NORMAL
+ special = os.getenv("MAKEPASS_SPECIAL") or SPECIAL
+ wordlist_file = os.getenv("MAKEPASS_WORDLIST") or DEFAULT_WORDLIST
+
+ # Initialize parser
+ parser = argparse.ArgumentParser(
+ description="Process some strings.", add_help=False
+ )
+
+ # Add arguments
+ parser.add_argument("-h", "--help", action="store_true")
+ parser.add_argument("-l", "--length", type=int, help="Length of strings")
+ parser.add_argument("-n", "--number", type=int, help="Number of strings")
+ parser.add_argument("-p", "--printlen", action="store_true")
+ parser.add_argument("arg_length", type=int, nargs="?")
+
+ # Parse arguments
+ args = parser.parse_args()
+
+ if args.help:
+ usage()
+
+ # Some error-checking
+ # length
+ try:
+ # env or default
+ length = int(os.getenv("MAKEPASS_LENGTH") or DEFAULT_LENGTH)
+
+ # -l flag
+ if args.length is not None:
+ if args.length < 0 or MAX < args.length:
+ raise ValueError("-l")
+ length = args.length
+
+ # solo argument
+ if args.arg_length is not None:
+ length = args.arg_length
+
+ # check result
+ if length < 0 or MAX < length:
+ raise ValueError("length")
+ except ValueError as e:
+ print(f"{e} must be a number between 0 and {MAX}")
+ sys.exit(1)
+
+ # number
+ try:
+ # env or default
+ number = int(os.getenv("MAKEPASS_NUMBER") or DEFAULT_NUMBER)
+
+ # -n flag
+ if args.number is not None:
+ if args.number < 1 or MAX < args.number:
+ raise ValueError("-n")
+ number = args.number
+
+ # check result
+ if number < 1 or MAX < number:
+ raise ValueError("number")
+ except ValueError as e:
+ print(f"{e} must be a number between 1 and {MAX}")
+ sys.exit(1)
+
+ # Set some other stuff
+
+ printbool = args.printlen
+ if printbool:
+ printlen = 3 if length < 100 else 4
+ else:
+ printlen = 0
+
+ # get terminal width
+ try:
+ xwidth = os.get_terminal_size().columns
+ except Exception as _:
+ xwidth = 1
+
+ # get width of columns to print
+ if length == 0:
+ col_width = RANGE_MAX + 2
+ else:
+ col_width = length + 2
+
+ # number of columns to print
+ col_num = int(xwidth / (col_width + printlen))
+ if col_num == 0:
+ col_num = 1
+
+ # config
+ config = CommonPrintData(
+ length=length,
+ printbool=printbool,
+ printlen=printlen,
+ col_width=col_width,
+ col_num=col_num,
+ )
+
+ #
+ # print passwords
+ #
+
+ # Generate and print normal and special passwords
+ print_columns("Normal passwords", number, normal, config)
+
+ print()
+
+ print_columns(
+ "Passwords with special characters", int(number / 3 * 2 + 1), special, config
+ )
+
+ # Generate and print passphrases if wordlist exists
+ if os.path.isfile(wordlist_file):
+ print()
+
+ print("Generating passphrases: ")
+ passphrase(int(number / 2), wordlist_file)
+
+
+#
+# Print passwords in neat columns
+#
+def print_columns(title: str, num: int, chars: str, c: CommonPrintData):
+ # Generate random strings
+ strings = [randstring(c.length, chars) for _ in range(num)]
+
+ print(f"{title}:")
+
+ for i in range(num):
+ # Print the length of the string if printlen is set
+ if c.printbool:
+ print(f"{len(strings[i]):0{c.printlen-1}}", end=" ")
+
+ # Print the string, left justified based on column width
+ print(f"{strings[i]:<{c.col_width}}", end="")
+
+ # If we have printed enough strings for a single line or the
+ # rest of strings are fewer than column number, we break lines.
+ i += 1
+ if i % c.col_num == 0 or (i == num and i % c.col_num > 0):
+ print()
+
+
+#
+# Generate random strings that can work as passwords
+#
+def randstring(length, chars):
+ """
+ Generate a random password of the specified length. Use special
+ characters if special_chars option is True.
+ """
+ if length == 0:
+ length = random.randint(RANGE_MIN, RANGE_MAX)
+
+ password = []
+ password.append(random.choice(ALPHA))
+ for i in range(length - 2):
+ password.append(random.choice(chars))
+ password.append(random.choice(ALNUM))
+
+ # Randomly reorder the characters
+ return "".join(password)
+
+
+#
+# Passphrases!
+#
+def passphrase(number, file_name):
+ """
+ Generate a number of random passphrases from the wordlist.
+ """
+ try:
+ with open(file_name, "r") as f:
+ lines = f.read().splitlines()
+ except IOError:
+ print(f"Cannot open file: {file_name}")
+ sys.exit(1)
+
+ for i in range(number):
+ word_list = random.choices(lines, k=5)
+ passphrase = "-".join(word_list)
+ print(passphrase)
+
+
+def usage():
+ text = """NAME
+ makepass - create several random passwords
+
+SYNOPSIS
+ makepass [OPTIONS] [NUM]
+
+ If a NUM is provided, passwords will be NUM characters long.
+
+ By default `makepass` will output passwords from the three following classes:
+
+ - Normal passwords - random strings with letters (both lower and upper
+ case), numbers, and dashes and underscores.
+
+ - Passwords with special characters - random strings generated from lower
+ and upper case letters, numbers, and the following characters:
+ !#$%&/()=?+-_,.;:<>[]{}|@*
+
+ - Passphrases - if we find a dictionary, a series of eight random words
+ from the dictionary, separated by dashes. The number of words can not be
+ changed, but you do not have to use all of them. Use as mane as you want.
+
+ The first character will always be alphabetic, and the last will always be
+ alphanumeric.
+
+DESCRIPTION
+ makepass has the following options:
+
+ -h
+ output this help-text
+ -l
+ length of passwords. See MAKEPASS_LENGTH below
+ -n
+ number of passwords. See MAKEPASS_NUMBER below
+ -p
+ print length of number
+
+ENVIRONMENT
+ makepass examines the following environmental variables.
+
+ MAKEPASS_LENGTH
+ Specifies the length of passwords. Valid values are 0-255. If 0, a
+ random value between 8 and 42 will be used for each password. -l
+ overrides this environmental variable, and the argument NUM overrides
+ that again. So `MAKEPASS_LENGTH=10 makepass -l 12 14` will give
+ passwords that are 14 characters long, even though both -l and
+ MAKEPASS_LENGTH also specifies a length.
+
+ MAKEPASS_NUMBER
+ The number of passwords to generate. This formula is used to determine
+ how many passwords from each group should be generated:
+ - (n) normal passwords
+ - (n / 3 * 2 + 1) special passwords
+ - (n / 2) passphrases
+ Where n is 10 by default. Valid values for n are 1-255. Floating-poing
+ math is not used, so results may vary.
+
+ MAKEPASS_PRINTLEN
+ If 1, print length of all passwords. If 0, don\'t.
+
+ MAKEPASS_NORMAL
+ String of characters from which to generate "normal" passwords.
+ Defaults to:
+ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_
+
+ MAKEPASS_SPECIAL
+ String of characters from which to generate passwords with special
+ characters. Defaults to the same characters as in MAKEPASS_NORMAL, plus
+ these:
+ !#$%&/()=?+-_,.;:<>[]{}|@*
+
+ MAKEPASS_WORDLIST
+ Specifies the dictionary we find words for passphrases in. If this is
+ unset or empty, we try "/usr/share/dict/words". If that file does not
+ exist, no passphrases will be provided.
+
+AUTHOR
+ Dennis Eriksen <https://dnns.no>"""
+ print(text)
+ sys.exit(0)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/python/ruff.toml b/src/python/ruff.toml
new file mode 100644
index 0000000..1f0c2f8
--- /dev/null
+++ b/src/python/ruff.toml
@@ -0,0 +1,64 @@
+# Exclude a variety of commonly ignored directories.
+exclude = [
+ ".bzr",
+ ".direnv",
+ ".eggs",
+ ".git",
+ ".git-rewrite",
+ ".hg",
+ ".mypy_cache",
+ ".nox",
+ ".pants.d",
+ ".pytype",
+ ".ruff_cache",
+ ".svn",
+ ".tox",
+ ".venv",
+ "__pypackages__",
+ "_build",
+ "buck-out",
+ "build",
+ "dist",
+ "node_modules",
+ "venv",
+]
+
+# Same as Black.
+line-length = 88
+indent-width = 4
+
+# Assume Python 3.10
+target-version = "py310"
+
+[lint]
+# https://docs.astral.sh/ruff/rules/
+# Enable:
+# Pyflakes (`F`)
+# Pycodestyle (`E`)
+# Whitespace-warnings (`W`)
+# isort (`I`)
+select = ["E", "F", "W", "I"]
+
+# E501 too long lines
+# W191 tab indents - `ruff format` recommends this be ignored
+ignore = ["E501", "W191"]
+
+# Allow fix for all enabled rules (when `--fix`) is provided.
+fixable = ["ALL"]
+unfixable = []
+
+# Allow unused variables when underscore-prefixed.
+dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
+
+[format]
+# Like Black, use double quotes for strings.
+quote-style = "double"
+
+# Like Black, indent with spaces, rather than tabs.
+indent-style = "space"
+
+# Like Black, respect magic trailing commas.
+skip-magic-trailing-comma = false
+
+# Like Black, automatically detect the appropriate line ending.
+line-ending = "auto"