Refactor matricizer, fix some linter complaints

* Matricizer.choose_dimensions was not actually being used. It has been
  removed, and dimensions of the hash matrix are now determined
  internally by the matricizer, rather than in its constructor.
* Linter was complaining about missing documentation and some other
  minor styling things so those are fixed
* Linter had a few more stupid lints enabled ("too-many-statements"?
  really? god forbid your code is too long, I'm not a computer science
  student, come on guys)

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-06-03 12:20:07 -07:00
parent 2b30070851
commit c17326f4e7
6 changed files with 117 additions and 102 deletions

View File

@@ -438,7 +438,9 @@ disable=raw-checker-failed,
use-implicit-booleaness-not-comparison-to-zero, use-implicit-booleaness-not-comparison-to-zero,
too-many-locals, too-many-locals,
too-few-public-methods, too-few-public-methods,
line-too-long too-many-statements,
line-too-long,
redefined-builtin,
# Enable the message, report, category or checker with the given id(s). You can # Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option # either give multiple identifier separated by comma (,) or put this option

View File

@@ -7,12 +7,10 @@ import textwrap
from .color import colorize from .color import colorize
from .matricizer import Matricizer, NibbleMatricizer, RandomartMatricizer from .matricizer import Matricizer, NibbleMatricizer, RandomartMatricizer
from .palettes import Palette, DEFAULT_PALETTES, PALETTES from .palettes import Palette, PALETTES
from .writer import ANSIWriter, SVGWriter, Writer from .writer import ANSIWriter, SVGWriter, Writer
# TODO - WASM compile for embedding directly in HTML
# - this may not be an option, sadly. might have to just port it to JS
# TODO - option to add a caption based on the filename (for SVG) # TODO - option to add a caption based on the filename (for SVG)
# TODO - load palettes from a file # TODO - load palettes from a file
# TODO - PNG output # TODO - PNG output
@@ -36,11 +34,13 @@ def cli_main() -> None:
PALETTE_HELP = "\n".join( PALETTE_HELP = "\n".join(
[ [
"PALETTE CHOICES", "PALETTE CHOICES",
'\n'.join(textwrap.wrap( "\n".join(
", ".join(PALETTE_CHOICES), textwrap.wrap(
initial_indent=" ", ", ".join(PALETTE_CHOICES),
subsequent_indent=" ", initial_indent=" ",
)), subsequent_indent=" ",
)
),
] ]
) )
HASH_CHOICES = ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"] HASH_CHOICES = ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"]
@@ -59,7 +59,9 @@ def cli_main() -> None:
OUTPUT_TYPE_HELP = "OUTPUT TYPE (-y, --output-type)\n" + "\n".join( OUTPUT_TYPE_HELP = "OUTPUT TYPE (-y, --output-type)\n" + "\n".join(
[f" {choice} - {desc}" for choice, desc in OUTPUT_TYPE_CHOICES.items()] [f" {choice} - {desc}" for choice, desc in OUTPUT_TYPE_CHOICES.items()]
) )
EPILOGUE = "\n\n".join([MATRIX_HELP, PALETTE_HELP, INPUT_TYPE_HELP, OUTPUT_TYPE_HELP]) EPILOGUE = "\n\n".join(
[MATRIX_HELP, PALETTE_HELP, INPUT_TYPE_HELP, OUTPUT_TYPE_HELP]
)
progname: str = sys.argv[0] progname: str = sys.argv[0]
if progname.endswith("__main__.py"): if progname.endswith("__main__.py"):
@@ -175,13 +177,9 @@ def cli_main() -> None:
matricizer: Matricizer matricizer: Matricizer
match args.matrix: match args.matrix:
case "nibble": case "nibble":
w, h = NibbleMatricizer.DIMENSIONS[args.hash] matricizer = NibbleMatricizer()
matricizer = NibbleMatricizer(w, h)
case "randomart": case "randomart":
# 17x9 is what openssh uses matricizer = RandomartMatricizer()
# TODO - allow configuring dimensions, maybe
w, h = RandomartMatricizer.DIMENSIONS[args.hash]
matricizer = RandomartMatricizer(w, h)
case _: case _:
assert False, f"invalid args.matrix: {args.matrix}" assert False, f"invalid args.matrix: {args.matrix}"
@@ -210,4 +208,3 @@ def cli_main() -> None:
sys.stdout.write(output) sys.stdout.write(output)
else: else:
args.out.write_text(output) args.out.write_text(output)

View File

@@ -1,5 +1,6 @@
"All things that turn a hash into a matrix." "All things that turn a hash into a matrix."
import abc import abc
import re
from typing import Mapping, Sequence from typing import Mapping, Sequence
from .palettes import Palette, DEFAULT_PALETTES, GRADIENT_PALETTES, MULTICOLOR_PALETTES from .palettes import Palette, DEFAULT_PALETTES, GRADIENT_PALETTES, MULTICOLOR_PALETTES
@@ -8,6 +9,27 @@ from .palettes import Palette, DEFAULT_PALETTES, GRADIENT_PALETTES, MULTICOLOR_P
Matrix = Sequence[Sequence[int]] Matrix = Sequence[Sequence[int]]
def detect_hash_algorithm(hash_or_algo: str | bytes) -> str | None:
dimensions: Mapping[int, str] = {
32: "md5",
40: "sha1",
56: "sha224",
64: "sha256",
96: "sha384",
128: "sha512",
}
if isinstance(hash_or_algo, bytes):
return dimensions.get(len(hash_or_algo) * 2)
hoa = hash_or_algo.lower()
if re.match(r"^([0-9a-fA-F]{2})+$", hoa):
return dimensions.get(len(hoa))
elif hoa in list(dimensions.values()):
return hoa
else:
return None
class Matricizer(metaclass=abc.ABCMeta): class Matricizer(metaclass=abc.ABCMeta):
""" """
The base Matricizer class. The base Matricizer class.
@@ -16,16 +38,6 @@ class Matricizer(metaclass=abc.ABCMeta):
(inclusive). The method by which this is done is up to the matricizer. (inclusive). The method by which this is done is up to the matricizer.
""" """
def __init__(self, w: int, h: int) -> None:
"""
Create a new matricizer for the given dimensions.
:param w: the width of the output matrix.
:param h: the height of hte output matrix.
"""
self.w = w
self.h = h
@abc.abstractmethod @abc.abstractmethod
def matricize(self, data: bytes) -> Matrix: def matricize(self, data: bytes) -> Matrix:
""" """
@@ -35,16 +47,6 @@ class Matricizer(metaclass=abc.ABCMeta):
:returns: the matrix converted from the hash data. :returns: the matrix converted from the hash data.
""" """
@staticmethod
@abc.abstractmethod
def choose_dimensions(hash: str) -> tuple[int, int]:
"""
Choose the dimensions for this matrix based on the hash algorithm.
:param hash: the hash algorithm being used.
:returns: a width and height as a tuple.
"""
def choose_palette( def choose_palette(
self, data: bytes, palettes: Mapping[str, Palette] | None = None self, data: bytes, palettes: Mapping[str, Palette] | None = None
) -> Palette: ) -> Palette:
@@ -84,32 +86,31 @@ class NibbleMatricizer(Matricizer):
:returns: the matrix converted from the hash data. :returns: the matrix converted from the hash data.
""" """
algo = detect_hash_algorithm(data)
w, h = self.DIMENSIONS[algo]
nibbles = [] nibbles = []
for b in data: for b in data:
top = (b & 0xF0) >> 4 top = (b & 0xF0) >> 4
bottom = b & 0x0F bottom = b & 0x0F
nibbles += [top, bottom] nibbles += [top, bottom]
if len(nibbles) != self.w * self.h: if len(nibbles) != w * h:
raise ValueError( raise ValueError(
f"input data length ({len(nibbles)}) must match matrix dimensions " f"input data length ({len(nibbles)}) must match matrix dimensions "
f"({self.w}x{self.h} = {self.w * self.h})" f"({w}x{h} = {w * h})"
) )
cols = [] cols = []
row = [] row = []
for b in nibbles: for b in nibbles:
row += [b] row += [b]
if len(row) == self.w: if len(row) == w:
cols += [row] cols += [row]
row = [] row = []
return cols return cols
@staticmethod
def choose_dimensions(hash: str) -> tuple[int, int]:
return NibbleMatricizer.DIMENSIONS[hash]
def choose_palette( def choose_palette(
self, data: bytes, palettes: Mapping[str, Palette] | None = None self, data: bytes, palettes: Mapping[str, Palette] | None = None
) -> Palette: ) -> Palette:
@@ -150,10 +151,12 @@ class RandomartMatricizer(Matricizer):
:param data: the hash data to turn into a matrix. :param data: the hash data to turn into a matrix.
:returns: the matrix converted from the hash data. :returns: the matrix converted from the hash data.
""" """
algo = detect_hash_algorithm(data)
w, h = self.DIMENSIONS[algo]
rows = [[0] * self.w for _ in range(self.h)] rows = [[0] * w for _ in range(h)]
c = self.w // 2 c = w // 2
r = self.h // 2 r = h // 2
for value in data: for value in data:
for _ in range(4): for _ in range(4):
if value & 0x1: if value & 0x1:
@@ -164,18 +167,14 @@ class RandomartMatricizer(Matricizer):
r += 1 r += 1
else: else:
r -= 1 r -= 1
c = min(max(c, 0), self.w - 1) c = min(max(c, 0), w - 1)
r = min(max(r, 0), self.h - 1) r = min(max(r, 0), h - 1)
# max value is 0xf # max value is 0xf
if rows[r][c] < 0xF: if rows[r][c] < 0xF:
rows[r][c] += 1 rows[r][c] += 1
value >>= 2; value >>= 2
return rows return rows
@staticmethod
def choose_dimensions(hash: str) -> tuple[int, int]:
return RandomartMatricizer.DIMENSIONS[hash]
def choose_palette( def choose_palette(
self, data: bytes, palettes: Mapping[str, Palette] | None = None self, data: bytes, palettes: Mapping[str, Palette] | None = None
) -> Palette: ) -> Palette:

View File

@@ -1,5 +1,4 @@
"Base color palette definitions." "Base color palette definitions."
import abc
from typing import Sequence from typing import Sequence
from .color import Color, HSLColor from .color import Color, HSLColor

View File

@@ -1,3 +1,4 @@
"Colorhash writer classes"
import abc import abc
from .color import Color, ColorMatrix from .color import Color, ColorMatrix
@@ -13,30 +14,47 @@ class Writer(metaclass=abc.ABCMeta):
@abc.abstractmethod @abc.abstractmethod
def write(self, matrix: ColorMatrix) -> str: def write(self, matrix: ColorMatrix) -> str:
"Write the color matrix to a string." """
Write the color matrix to a string.
:param matrix: the color matrix to generate the SVG for.
:returns: the full generated SVG as a string.
"""
class ANSIWriter(Writer): class ANSIWriter(Writer):
"""
ANSI terminal writer. This will output a 24-bit true color string.
"""
def write(self, matrix: ColorMatrix) -> str: def write(self, matrix: ColorMatrix) -> str:
ESC = "\x1b" """
RESET = f"{ESC}[0m" Write the color matrix to an ANSI string.
C = "██"
:param matrix: the color matrix to generate the SVG for.
:returns: the full generated SVG as a string.
"""
esc = "\x1b"
reset = f"{esc}[0m"
c = "██"
def ansi_color(c: Color) -> str: def ansi_color(c: Color) -> str:
c = c.to_rgb() c = c.to_rgb()
return f"{ESC}[38;2;{round(c.r)};{round(c.g)};{round(c.b)}m" return f"{esc}[38;2;{round(c.r)};{round(c.g)};{round(c.b)}m"
out = "" out = ""
for row in matrix: for row in matrix:
for col in row: for col in row:
out += ansi_color(col) out += ansi_color(col)
out += C out += c
out += "\n" out += "\n"
out += RESET out += reset
return out return out
class SVGWriter(Writer): class SVGWriter(Writer):
"""
SVG string writer.
"""
def __init__(self, square_size: int) -> None: def __init__(self, square_size: int) -> None:
""" """
Create a new SVG writer that uses the given square size. Create a new SVG writer that uses the given square size.

View File

@@ -1,42 +1,42 @@
<svg width="256" height="160" xmlns="http://www.w3.org/2000/svg"> <svg width="256" height="160" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="32" height="32" fill="hsl(60.00,100.00%,40.00%)" /> <rect x="0" y="0" width="32" height="32" fill="hsl(30.00,100.00%,6.67%)" />
<rect x="32" y="0" width="32" height="32" fill="hsl(60.00,100.00%,33.33%)" /> <rect x="32" y="0" width="32" height="32" fill="hsl(30.00,100.00%,36.67%)" />
<rect x="64" y="0" width="32" height="32" fill="hsl(60.00,100.00%,6.67%)" /> <rect x="64" y="0" width="32" height="32" fill="hsl(30.00,100.00%,10.00%)" />
<rect x="96" y="0" width="32" height="32" fill="hsl(60.00,100.00%,33.33%)" /> <rect x="96" y="0" width="32" height="32" fill="hsl(30.00,100.00%,0.00%)" />
<rect x="128" y="0" width="32" height="32" fill="hsl(60.00,100.00%,33.33%)" /> <rect x="128" y="0" width="32" height="32" fill="hsl(30.00,100.00%,0.00%)" />
<rect x="160" y="0" width="32" height="32" fill="hsl(60.00,100.00%,13.33%)" /> <rect x="160" y="0" width="32" height="32" fill="hsl(30.00,100.00%,23.33%)" />
<rect x="192" y="0" width="32" height="32" fill="hsl(60.00,100.00%,40.00%)" /> <rect x="192" y="0" width="32" height="32" fill="hsl(30.00,100.00%,0.00%)" />
<rect x="224" y="0" width="32" height="32" fill="hsl(60.00,100.00%,33.33%)" /> <rect x="224" y="0" width="32" height="32" fill="hsl(30.00,100.00%,26.67%)" />
<rect x="0" y="32" width="32" height="32" fill="hsl(60.00,100.00%,10.00%)" /> <rect x="0" y="32" width="32" height="32" fill="hsl(30.00,100.00%,16.67%)" />
<rect x="32" y="32" width="32" height="32" fill="hsl(60.00,100.00%,36.67%)" /> <rect x="32" y="32" width="32" height="32" fill="hsl(30.00,100.00%,3.33%)" />
<rect x="64" y="32" width="32" height="32" fill="hsl(60.00,100.00%,16.67%)" /> <rect x="64" y="32" width="32" height="32" fill="hsl(30.00,100.00%,30.00%)" />
<rect x="96" y="32" width="32" height="32" fill="hsl(60.00,100.00%,6.67%)" /> <rect x="96" y="32" width="32" height="32" fill="hsl(30.00,100.00%,36.67%)" />
<rect x="128" y="32" width="32" height="32" fill="hsl(60.00,100.00%,3.33%)" /> <rect x="128" y="32" width="32" height="32" fill="hsl(30.00,100.00%,10.00%)" />
<rect x="160" y="32" width="32" height="32" fill="hsl(60.00,100.00%,36.67%)" /> <rect x="160" y="32" width="32" height="32" fill="hsl(30.00,100.00%,36.67%)" />
<rect x="192" y="32" width="32" height="32" fill="hsl(60.00,100.00%,43.33%)" /> <rect x="192" y="32" width="32" height="32" fill="hsl(30.00,100.00%,33.33%)" />
<rect x="224" y="32" width="32" height="32" fill="hsl(60.00,100.00%,33.33%)" /> <rect x="224" y="32" width="32" height="32" fill="hsl(30.00,100.00%,3.33%)" />
<rect x="0" y="64" width="32" height="32" fill="hsl(60.00,100.00%,23.33%)" /> <rect x="0" y="64" width="32" height="32" fill="hsl(30.00,100.00%,26.67%)" />
<rect x="32" y="64" width="32" height="32" fill="hsl(60.00,100.00%,33.33%)" /> <rect x="32" y="64" width="32" height="32" fill="hsl(30.00,100.00%,20.00%)" />
<rect x="64" y="64" width="32" height="32" fill="hsl(60.00,100.00%,3.33%)" /> <rect x="64" y="64" width="32" height="32" fill="hsl(30.00,100.00%,43.33%)" />
<rect x="96" y="64" width="32" height="32" fill="hsl(60.00,100.00%,43.33%)" /> <rect x="96" y="64" width="32" height="32" fill="hsl(30.00,100.00%,6.67%)" />
<rect x="128" y="64" width="32" height="32" fill="hsl(60.00,100.00%,33.33%)" /> <rect x="128" y="64" width="32" height="32" fill="hsl(30.00,100.00%,3.33%)" />
<rect x="160" y="64" width="32" height="32" fill="hsl(60.00,100.00%,30.00%)" /> <rect x="160" y="64" width="32" height="32" fill="hsl(30.00,100.00%,13.33%)" />
<rect x="192" y="64" width="32" height="32" fill="hsl(60.00,100.00%,36.67%)" /> <rect x="192" y="64" width="32" height="32" fill="hsl(30.00,100.00%,3.33%)" />
<rect x="224" y="64" width="32" height="32" fill="hsl(60.00,100.00%,36.67%)" /> <rect x="224" y="64" width="32" height="32" fill="hsl(30.00,100.00%,50.00%)" />
<rect x="0" y="96" width="32" height="32" fill="hsl(60.00,100.00%,50.00%)" /> <rect x="0" y="96" width="32" height="32" fill="hsl(30.00,100.00%,10.00%)" />
<rect x="32" y="96" width="32" height="32" fill="hsl(60.00,100.00%,10.00%)" /> <rect x="32" y="96" width="32" height="32" fill="hsl(30.00,100.00%,23.33%)" />
<rect x="64" y="96" width="32" height="32" fill="hsl(60.00,100.00%,36.67%)" /> <rect x="64" y="96" width="32" height="32" fill="hsl(30.00,100.00%,0.00%)" />
<rect x="96" y="96" width="32" height="32" fill="hsl(60.00,100.00%,40.00%)" /> <rect x="96" y="96" width="32" height="32" fill="hsl(30.00,100.00%,23.33%)" />
<rect x="128" y="96" width="32" height="32" fill="hsl(60.00,100.00%,40.00%)" /> <rect x="128" y="96" width="32" height="32" fill="hsl(30.00,100.00%,43.33%)" />
<rect x="160" y="96" width="32" height="32" fill="hsl(60.00,100.00%,3.33%)" /> <rect x="160" y="96" width="32" height="32" fill="hsl(30.00,100.00%,13.33%)" />
<rect x="192" y="96" width="32" height="32" fill="hsl(60.00,100.00%,36.67%)" /> <rect x="192" y="96" width="32" height="32" fill="hsl(30.00,100.00%,16.67%)" />
<rect x="224" y="96" width="32" height="32" fill="hsl(60.00,100.00%,30.00%)" /> <rect x="224" y="96" width="32" height="32" fill="hsl(30.00,100.00%,6.67%)" />
<rect x="0" y="128" width="32" height="32" fill="hsl(60.00,100.00%,36.67%)" /> <rect x="0" y="128" width="32" height="32" fill="hsl(30.00,100.00%,3.33%)" />
<rect x="32" y="128" width="32" height="32" fill="hsl(60.00,100.00%,16.67%)" /> <rect x="32" y="128" width="32" height="32" fill="hsl(30.00,100.00%,50.00%)" />
<rect x="64" y="128" width="32" height="32" fill="hsl(60.00,100.00%,26.67%)" /> <rect x="64" y="128" width="32" height="32" fill="hsl(30.00,100.00%,16.67%)" />
<rect x="96" y="128" width="32" height="32" fill="hsl(60.00,100.00%,20.00%)" /> <rect x="96" y="128" width="32" height="32" fill="hsl(30.00,100.00%,30.00%)" />
<rect x="128" y="128" width="32" height="32" fill="hsl(60.00,100.00%,16.67%)" /> <rect x="128" y="128" width="32" height="32" fill="hsl(30.00,100.00%,10.00%)" />
<rect x="160" y="128" width="32" height="32" fill="hsl(60.00,100.00%,50.00%)" /> <rect x="160" y="128" width="32" height="32" fill="hsl(30.00,100.00%,16.67%)" />
<rect x="192" y="128" width="32" height="32" fill="hsl(60.00,100.00%,43.33%)" /> <rect x="192" y="128" width="32" height="32" fill="hsl(30.00,100.00%,13.33%)" />
<rect x="224" y="128" width="32" height="32" fill="hsl(60.00,100.00%,0.00%)" /> <rect x="224" y="128" width="32" height="32" fill="hsl(30.00,100.00%,36.67%)" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB