Add input-type

Input can now either be a path, raw data, or a hash itself (path is
default)

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-05-24 11:45:42 -07:00
parent e14c796888
commit 1277c2db8b
2 changed files with 58 additions and 18 deletions

View File

@@ -4,6 +4,7 @@ import argparse
import hashlib import hashlib
from pathlib import Path from pathlib import Path
import sys import sys
import textwrap
from .colorizer import PaletteColorizer from .colorizer import PaletteColorizer
from .matricizer import Matricizer, NibbleMatricizer, RandomartMatricizer from .matricizer import Matricizer, NibbleMatricizer, RandomartMatricizer
@@ -46,11 +47,19 @@ def main() -> None:
] ]
) )
HASH_CHOICES = ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"] HASH_CHOICES = ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"]
INPUT_TYPE_CHOICES = {
"path": "the input should be treated as a path and data is read from the path",
"hash": "the input should be treated as a hexadecimal hash (requires -a or --hash to be supplied)",
"data": "the input should be treated as raw data",
}
INPUT_TYPE_HELP = "INPUT TYPE (-x, --input-type)\n" + "\n".join(
[f" {choice} - {desc}" for choice, desc in INPUT_TYPE_CHOICES.items()]
)
EPILOGUE = "\n\n".join([MATRIX_HELP, PALETTE_HELP]) EPILOGUE = "\n\n".join([MATRIX_HELP, PALETTE_HELP])
progname: str = sys.argv[0] progname: str = sys.argv[0]
if progname.endswith('__main__.py'): if progname.endswith("__main__.py"):
progname = 'colorhash' progname = "colorhash"
ap = argparse.ArgumentParser( ap = argparse.ArgumentParser(
prog=progname, prog=progname,
@@ -60,10 +69,10 @@ def main() -> None:
) )
ap.add_argument( ap.add_argument(
"infile", "input",
type=argparse.FileType("rb"), type=str,
default=sys.stdin, default="-",
help="The input file to use. Set to '-' or blank for STDIN. default: STDIN", help="The input to use. When acting as a path, set to '-' or blank for STDIN. Use -x or --input-type to control how input is treated. default: -",
) )
ap.add_argument( ap.add_argument(
"-o", "-o",
@@ -94,7 +103,8 @@ def main() -> None:
"--hash", "--hash",
metavar="ALGORITHM", metavar="ALGORITHM",
choices=HASH_CHOICES, choices=HASH_CHOICES,
default="sha512", # default="sha512",
required=False,
help="Choose the hash algorithm. default: sha512", help="Choose the hash algorithm. default: sha512",
) )
ap.add_argument( ap.add_argument(
@@ -105,30 +115,63 @@ def main() -> None:
default=32, default=32,
help="Decide how big the output squares are, in pixels. default: 32", help="Decide how big the output squares are, in pixels. default: 32",
) )
ap.add_argument(
"-x",
"--input-type",
default="path",
choices=INPUT_TYPE_CHOICES.keys(),
help="Determines how the input should be treated. default: path",
)
args = ap.parse_args() args = ap.parse_args()
############################################################################ ############################################################################
# End arg parsing # End arg parsing
############################################################################ ############################################################################
# -a/--hash arg is not required when we're using file and data input types. only required for
# hash input type
if args.input_type in ("data", "path") and args.hash is None:
args.hash = "sha512"
# Get the hash # Get the hash
match args.input_type:
case "path":
if args.input == "-":
infile = sys.stdin.buffer
else:
# TODO - pretty error message for when the file doesn't exist
infile = open(args.input, "rb")
# file_digest (I hope) will not load too much into memory # file_digest (I hope) will not load too much into memory
hashdata = hashlib.file_digest(args.infile, args.hash).digest() hashdata = hashlib.file_digest(infile, args.hash).digest()
case "hash":
# TODO - maybe a better error message?
if args.hash is None:
print(
"ERROR: -a or --hash should be supplied on the command line when using the hash input type",
file=sys.stderr,
)
raise SystemExit(1)
# TODO - pretty error message for malformed input
hashdata = bytes([int(byte, 16) for byte in textwrap.wrap(args.input, 2)])
case "data":
hashdata = hashlib.new(args.hash, args.input.encode()).digest()
case _:
assert False, f"unknown input type {args.input_type}"
# Choose the palette # Choose the palette
palette: list[str] palette: list[str]
if args.palette == 'auto': if args.palette == "auto":
palette = list(PALETTES.values())[sum(hashdata) % 8] palette = list(DEFAULT_PALETTES.values())[sum(hashdata) % 8]
else: else:
palette = PALETTES[args.palette] palette = PALETTES[args.palette]
# Choose the dimensions and the matricizer # Choose the dimensions and the matricizer
matricizer: Matricizer matricizer: Matricizer
match args.matrix: match args.matrix:
case 'nibble': case "nibble":
w, h = NibbleMatricizer.DIMENSIONS[args.hash] w, h = NibbleMatricizer.DIMENSIONS[args.hash]
matricizer = NibbleMatricizer(w, h) matricizer = NibbleMatricizer(w, h)
case 'randomart': case "randomart":
# 17x9 is what openssh uses # 17x9 is what openssh uses
# TODO - allow configuring dimensions, maybe # TODO - allow configuring dimensions, maybe
matricizer = RandomartMatricizer(17, 9) matricizer = RandomartMatricizer(17, 9)
@@ -142,7 +185,7 @@ def main() -> None:
matrix = matricizer.matricize(hashdata) matrix = matricizer.matricize(hashdata)
colors = colorizer.colorize(matrix) colors = colorizer.colorize(matrix)
svg = gensvg(colors, args.square_size) svg = gensvg(colors, args.square_size)
if str(args.out) == '-': if str(args.out) == "-":
sys.stdout.write(svg) sys.stdout.write(svg)
else: else:
args.out.write_text(svg) args.out.write_text(svg)

View File

@@ -3,6 +3,7 @@ import abc
from typing import Sequence from typing import Sequence
from .matricizer import Matrix from .matricizer import Matrix
from .palettes import Palette
StrMatrix = Sequence[Sequence[str]] StrMatrix = Sequence[Sequence[str]]
@@ -26,9 +27,6 @@ class Colorizer(metaclass=abc.ABCMeta):
""" """
Palette = Sequence[str]
class PaletteColorizer(Colorizer): class PaletteColorizer(Colorizer):
""" """
A palette colorizer. A palette colorizer.
@@ -41,7 +39,6 @@ class PaletteColorizer(Colorizer):
:param palette: the palette to use for this colorizer. :param palette: the palette to use for this colorizer.
""" """
assert len(palette) == 16, "palette must contain exactly 16 colors"
self.palette = palette self.palette = palette
def colorize(self, matrix: Matrix) -> StrMatrix: def colorize(self, matrix: Matrix) -> StrMatrix: