Last active
February 2, 2023 00:20
-
-
Save iuriguilherme/27eb85a5750c299a8b935b14b6cc7942 to your computer and use it in GitHub Desktop.
Finds the best contrast for each color on the 8bit RGB scope
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Find contrast ratio for colors | |
TODO: Use scrollbars for color list | |
pip install numpy""" | |
import ast | |
import csv | |
import functools | |
import inspect | |
import logging | |
import queue | |
import numpy | |
import sys | |
import threading | |
import tkinter | |
log_level: int | str | None = logging.INFO | |
try: | |
log_level: int | str | None = sys.argv[1].upper() | |
logging.basicConfig(level = log_level) | |
except: | |
logging.basicConfig(level = log_level) | |
## Produces a number between 0 and 255 proportional to i relative to m | |
rgb: int = lambda i, m: round(i * (255 / max(1, m))) | |
## Returns the oposite RGB tuple in the 0-255 range | |
irgb: tuple[int] = lambda r: tuple(abs(255 - c) for c in r) | |
## Root Mean Square | |
rms: float = lambda t: (sum(i * i for i in t) / len(t)) ** 0.5 | |
nrms: float = lambda a: numpy.sqrt(numpy.mean(a ** 2)) | |
## Returns RGB subtracted by its RMS | |
rrgb: tuple[int] = lambda rgb: tuple(abs(round(rms(rgb)) - c) \ | |
for c in rgb) | |
nrgb: tuple[int] = lambda rgb: tuple(abs(round(nrms(rgb)) - c) \ | |
for c in rgb) | |
## https://www.w3.org/TR/WCAG20/#relativeluminancedef | |
srgb: tuple[float] = lambda rgb: tuple(max(1, c) / 255 for c in rgb) | |
rel: float = lambda srgb: tuple(c / 12.92 if c <= 0.04045 else \ | |
((c + 0.055) / 1.055) ** 2.4 for c in srgb) | |
lum: float = lambda r, g, b: r * 0.2126 + g * 0.7152 + b * 0.0722 | |
rl: float = lambda rgb: lum(*(c for c in rel(srgb(rgb)))) | |
## https://www.w3.org/TR/WCAG20/#contrast-ratiodef | |
cr: float = lambda l1, l2: (max(l1, l2) * 5e-2) / (min(l1, l2) * 5e-2) | |
## https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast | |
crl: float = lambda a, b: cr(rl(a), rl(b)) | |
## https://stackoverflow.com/questions/57651030/ | |
## take-hex-code-rgb-and-display-the-color-in-tkinter | |
h2r: tuple[int] = lambda h: tuple(int(h.lstrip('#')[n:n + 2], 16) \ | |
for n in range(0, 6, 2)) | |
r2h: str = lambda r: f"#{''.join([f'{c:02X}' for c in r])}" | |
@functools.cache | |
def check_contrast(rgb1, rgb2, *args, **kwargs) -> float: | |
"""Compare contrast of two colors""" | |
return crl(rgb1, rgb2) | |
def main(*args, **kwargs) -> None: | |
"""main""" | |
logger: logging.Logger = logging.getLogger( | |
inspect.currentframe().f_code.co_name) | |
logger.setLevel(kwargs.get('log_level', log_level)) | |
speed: int = kwargs.get('speed', 1) | |
root_queue: queue.Queue = queue.Queue() | |
color_left_queue: queue.Queue = queue.Queue() | |
color_right_queue: queue.Queue = queue.Queue() | |
color_done_queue: queue.Queue = queue.Queue() | |
speed_queue: queue.Queue = queue.Queue() | |
def tkinter_thread_callback( | |
color_thread: threading.Thread, | |
*args, | |
speed: int = speed, | |
root_queue: queue.Queue = root_queue, | |
color_left_queue: queue.Queue = color_left_queue, | |
color_right_queue: queue.Queue = color_right_queue, | |
color_done_queue: queue.Queue = color_done_queue, | |
speed_queue: queue.Queue = speed_queue, | |
contrasts: dict[str, object] = {}, | |
filename: str = 'contrasts.csv', | |
**kwargs, | |
) -> None: | |
"""tkinter main thread""" | |
logger: logging.Logger = logging.getLogger( | |
inspect.currentframe().f_code.co_name) | |
logger.setLevel(kwargs.get('log_level', log_level)) | |
try: | |
color_left: tuple[int] = (0, 0, 0) | |
color_right: tuple[int] = (0, 0, 0) | |
def save(*args, destroy: bool = False, **kwargs) -> None: | |
"""Save current state to CSV file""" | |
try: | |
with open(filename, 'w', newline = '') as csvfile: | |
writer: csv.DictWriter = csv.DictWriter( | |
csvfile, | |
fieldnames = [ | |
'color', | |
'contrast', | |
'last_tested', | |
'ratio', | |
'tested', | |
]) | |
writer.writeheader() | |
for key, value in contrasts.items(): | |
writer.writerow({'color': key, **{k: v for \ | |
k, v in value.items() if k not in [ | |
'frame', | |
'label_contrast', | |
'label_left', | |
'label_ratio', | |
'label_right', | |
'label_tested', | |
]}}) | |
if destroy: | |
root.destroy() | |
except Exception as e: | |
logger.exception(e) | |
def new_color( | |
*args, | |
color: tuple[int] = (0, 0, 0), | |
contrast: tuple[int] = (0, 0, 0), | |
last_tested: float = (0, 0, 0), | |
ratio: float = 0.0, | |
tested: float = 0.0, | |
**kwargs, | |
) -> None: | |
"""Add new color to the list""" | |
hexcode: str = r2h(color) | |
contrasts[color]: dict = { | |
'contrast': contrast, | |
'ratio': ratio, | |
'tested': tested, | |
'last_tested': last_tested, | |
'frame': tkinter.Frame(color_list_scroll_frame), | |
} | |
contrasts[color].update( | |
label_left = tkinter.Label( | |
contrasts[color].get('frame'), | |
text = f"{hexcode} {color}", | |
bg = hexcode, | |
fg = r2h(irgb(color)), | |
), | |
label_contrast = tkinter.Label( | |
contrasts[color].get('frame'), | |
text = f"{r2h(contrast)} {contrast}", | |
bg = r2h(contrast), | |
fg = r2h(irgb(contrast)), | |
), | |
label_right = tkinter.Label( | |
contrasts[color].get('frame'), | |
text = f"{r2h(last_tested)} {last_tested}", | |
bg = r2h(last_tested), | |
fg = r2h(irgb(last_tested)), | |
), | |
label_ratio = tkinter.Label( | |
contrasts[color].get('frame'), | |
text = f"{ratio}", | |
), | |
label_tested = tkinter.Label( | |
contrasts[color].get('frame'), | |
text = f"{tested:%}", | |
) | |
) | |
contrasts[color].get('frame').pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.TOP, | |
) | |
tkinter.Label( | |
contrasts[color].get('frame'), | |
text = "Color:", | |
).pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
contrasts[color].get('label_left').pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
tkinter.Label( | |
contrasts[color].get('frame'), | |
text = "Best contrast:", | |
).pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
contrasts[color].get('label_contrast').pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
tkinter.Label( | |
contrasts[color].get('frame'), | |
text = "Ratio:", | |
).pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
contrasts[color].get('label_ratio').pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
tkinter.Label( | |
contrasts[color].get('frame'), | |
text = "Last tested:", | |
).pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
contrasts[color].get('label_right').pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
tkinter.Label( | |
contrasts[color].get('frame'), | |
text = "Tested:", | |
).pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
contrasts[color].get('label_tested').pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
def change_speed( | |
event: tkinter.Event, | |
speed: int, | |
*args, | |
**kwargs, | |
) -> None: | |
"""Change current speed""" | |
logger: logging.Logger = logging.getLogger( | |
inspect.currentframe().f_code.co_name) | |
logger.setLevel(kwargs.get('log_level', log_level)) | |
try: | |
try: | |
speed: int = speed_listbox.curselection()[0] | |
except: | |
pass | |
speed_label.config( | |
text = f"Current speed: {speed} Change:") | |
speed_queue.put(speed) | |
except Exception as e: | |
logger.exception(e) | |
def change_color( | |
event: tkinter.Event | None = None, | |
target: tkinter.Label | None = None, | |
sources: list[tkinter.Listbox] | None = None, | |
rgb: tuple[int] | None = None, | |
update: bool = False, | |
*args, | |
**kwargs, | |
) -> None: | |
"""Change current left color""" | |
logger: logging.Logger = logging.getLogger( | |
inspect.currentframe().f_code.co_name) | |
logger.setLevel(kwargs.get('log_level', log_level)) | |
try: | |
if not target: | |
return | |
hexcode: list | str = '#000000' | |
if sources: | |
hexcode: list | str = ['#'] | |
for n in range(len(sources)): | |
try: | |
hexcode.append( | |
f"{sources[n].curselection()[0]:X}") | |
except: | |
hexcode.append(f"{0:X}") | |
hexcode: list | str = ''.join(hexcode) | |
color: tuple[int] = h2r(hexcode) | |
elif rgb: | |
color: tuple[int] = rgb | |
hexcode: str = r2h(rgb) | |
target.config( | |
text = f"{hexcode} {color}", | |
bg = hexcode, | |
fg = r2h(irgb(color)), | |
) | |
if update: | |
color_left: tuple[int] = color | |
if color_left not in contrasts: | |
new_color(color = color) | |
color_left_queue.put(( | |
color_left, | |
contrasts[color_left].get('tested'), | |
contrasts[color_left].get('last_tested'), | |
speed | |
)) | |
except Exception as e: | |
logger.exception(e) | |
root: tkinter.Tk = tkinter.Tk() | |
root.title("Contrast Ratio Test") | |
if all(x in kwargs for x in ('height', 'width')): | |
root.geometry("x".join([ | |
str(kwargs['root_width']), | |
str(kwargs['root_height']), | |
])) | |
color_chooser_frame: tkinter.Frame = tkinter.Frame(root) | |
color_left_right_frame: tkinter.Frame = tkinter.Frame(root) | |
color_left_frame: tkinter.Frame = tkinter.Frame( | |
color_left_right_frame) | |
color_right_frame: tkinter.Frame = tkinter.Frame( | |
color_left_right_frame) | |
contrast_current_frame: tkinter.Frame = tkinter.Frame(root) | |
contrast_best_frame: tkinter.Frame = tkinter.Frame(root) | |
color_list_frame: tkinter.Frame = tkinter.Frame(root) | |
color_list_canvas: tkinter.Canvas = tkinter.Canvas( | |
color_list_frame) | |
color_list_scrollbar: tkinter.Scrollbar = tkinter.Scrollbar( | |
color_list_frame, | |
orient = "vertical", | |
command = color_list_canvas.yview, | |
) | |
color_list_scroll_frame: tkinter.Frame = tkinter.Frame( | |
color_list_canvas) | |
# ~ color_list_scroll_frame.bind( | |
# ~ "<Configure>", | |
# ~ lambda event: color_list_canvas.configure( | |
# ~ scrollregion = color_list_canvas.bbox("all") | |
# ~ ) | |
# ~ ) | |
color_list_canvas.create_window( | |
(0, 0), | |
window = color_list_scroll_frame, | |
anchor = "center", | |
) | |
color_list_canvas.configure( | |
yscrollcommand = color_list_scrollbar.set) | |
color_chooser_label: tkinter.Label = tkinter.Label( | |
color_chooser_frame, | |
text = "Select color to calculate ratio:", | |
) | |
color_left_label: tkinter.Label = tkinter.Label( | |
color_left_frame) | |
color_right_label: tkinter.Label = tkinter.Label( | |
color_right_frame, | |
justify = "left", | |
) | |
color_list_label_frame: tkinter.Frame = tkinter.Frame(root) | |
contrast_current_label: tkinter.Label = tkinter.Label( | |
contrast_current_frame) | |
contrast_best_label: tkinter.Label = tkinter.Label( | |
contrast_best_frame) | |
color_list_label: tkinter.Label = tkinter.Label( | |
color_list_label_frame, | |
text = "Bests contrasts:", | |
) | |
save_button: tkinter.Button = tkinter.Button( | |
color_chooser_frame, | |
text = "Save to CSV", | |
command = save, | |
) | |
save_exit_button: tkinter.Button = tkinter.Button( | |
color_chooser_frame, | |
text = "Save & Exit", | |
command = lambda destroy = True: save( | |
destroy = destroy), | |
) | |
exit_button: tkinter.Button = tkinter.Button( | |
color_chooser_frame, | |
text = "Exit", | |
command = lambda: root.destroy(), | |
) | |
speed_label: tkinter.Label = tkinter.Label( | |
color_chooser_frame, | |
text = f"Current speed: {speed} Change:", | |
) | |
speed_listbox: tkinter.Listbox = tkinter.Listbox( | |
color_chooser_frame, | |
listvariable = tkinter.Variable( | |
value = list(range(speed_limit + 1))), | |
width = 2, | |
height = 2, | |
) | |
speed_listbox.bind("<<ListboxSelect>>", | |
lambda event: change_speed(event, speed)) | |
color_chooser_frame.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.TOP, | |
) | |
color_chooser_label.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
color_chooser_hex_listboxes: list[tkinter.Listbox] = [] | |
for n in range(6): | |
color_chooser_hex_listboxes.append( | |
tkinter.Listbox( | |
color_chooser_frame, | |
listvariable = tkinter.Variable( | |
value = [f'{h:X}' for h in range(16)]), | |
exportselection = False, | |
width = 2, | |
height = 2, | |
) | |
) | |
color_chooser_hex_listboxes[n].pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
color_chooser_button: tkinter.Button = tkinter.Button( | |
color_chooser_frame, | |
text = "Update", | |
command = lambda \ | |
sources = color_chooser_hex_listboxes, | |
target = color_left_label, | |
update = True: \ | |
change_color( | |
sources = sources, | |
target = target, | |
update = update, | |
), | |
) | |
color_left_right_frame.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.TOP, | |
) | |
color_left_frame.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.TOP, | |
) | |
color_left_label.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.TOP, | |
) | |
color_chooser_button.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
speed_label.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
speed_listbox.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
save_button.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
save_exit_button.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
exit_button.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
color_right_frame.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.TOP, | |
) | |
color_right_label.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.TOP, | |
) | |
contrast_current_frame.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.TOP, | |
) | |
tkinter.Label( | |
contrast_current_frame, | |
text = "Contrast ratio:", | |
).pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
contrast_current_label.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
contrast_best_frame.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.TOP, | |
) | |
tkinter.Label( | |
contrast_best_frame, | |
text = "Best:", | |
).pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
contrast_best_label.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
) | |
color_list_label_frame.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.TOP, | |
) | |
color_list_label.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.TOP, | |
) | |
color_list_frame.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.TOP, | |
) | |
color_list_canvas.pack( | |
fill = tkinter.BOTH, | |
side = tkinter.LEFT, | |
expand = True, | |
) | |
color_list_scrollbar.pack( | |
fill = tkinter.Y, | |
side = tkinter.RIGHT, | |
) | |
color_list_scroll_frame.pack( | |
anchor = tkinter.CENTER, | |
fill = tkinter.BOTH, | |
side = tkinter.TOP, | |
) | |
# ~ color_list_frame.config( | |
# ~ yscrollcommand = color_list_scrollbar.set) | |
for k, v in contrasts.items(): | |
new_color(color = k, **v) | |
change_color( | |
target = color_left_label, | |
rgb = (0, 0, 0), | |
update = True, | |
) | |
contrast_ratio: float = 0.0 | |
tested: float = 0.0 | |
while root.winfo_exists(): | |
try: | |
color_left, tested, color_right, speed = \ | |
color_right_queue.get( | |
timeout = float(f"1e-{speed}")) | |
except queue.Empty: | |
continue | |
change_color( | |
target = color_right_label, | |
rgb = color_right, | |
) | |
contrasts[color_left]['tested'] = tested | |
contrasts[color_left]['label_tested'].config( | |
text = f"{tested:%}") | |
contrasts[color_left]['last_tested'] = color_right | |
change_color(target = contrasts[color_left].get( | |
'label_right'), rgb = color_right) | |
contrast_ratio: float = check_contrast(color_left, | |
color_right) | |
if contrast_ratio > contrasts[color_left].get('ratio'): | |
contrasts[color_left]['ratio'] = contrast_ratio | |
contrasts[color_left]['contrast'] = color_right | |
change_color(target = contrasts[color_left].get( | |
'label_contrast'), rgb = color_right) | |
contrasts[color_left].get('label_ratio').config( | |
text = f"{contrasts[color_left].get('ratio')}") | |
contrast_current_label.config( | |
text = f"{contrast_ratio}") | |
contrast_best_label.config( | |
text = f"{contrasts[color_left].get('ratio')}") | |
root.update() | |
except tkinter.TclError: | |
while color_thread.is_alive(): | |
root_queue.put(False) | |
except Exception as e: | |
logger.exception(e) | |
def color_thread_callback( | |
*args, | |
speed: int = speed, | |
root_queue: queue.Queue = root_queue, | |
color_left_queue: queue.Queue = color_left_queue, | |
color_right_queue: queue.Queue = color_right_queue, | |
color_done_queue: queue.Queue = color_done_queue, | |
speed_queue: queue.Queue = speed_queue, | |
**kwargs, | |
) -> None: | |
"""Threaded checking for color contrast""" | |
logger: logging.Logger = logging.getLogger( | |
inspect.currentframe().f_code.co_name) | |
logger.setLevel(kwargs.get('log_level', log_level)) | |
try: | |
root_alive: bool = True | |
current_color: tuple[int] = (0, 0, 0) | |
def check_color( | |
color: tuple[int], | |
tested: float = 0.0, | |
start: tuple[int] = (0, 0, 0), | |
speed: int = speed, | |
f1: int = 255, | |
*args, | |
**kwargs, | |
) -> tuple[tuple[int], float]: | |
"""Loop all 8bit RGB space""" | |
f2 = (f1 * 1 + f1 * 2 + f1 * 3) | |
r, g, b = start | |
for r in range(start[0], f1 + 1): | |
for g in range(start[1], f1 + 1): | |
for b in range(start[2], f1 + 1): | |
tested: float = max( | |
tested, | |
( | |
( | |
(r * (f1 * 3)) + \ | |
(g * (f1 * 2)) + \ | |
(b * (f1 * 1)) | |
) / f2 | |
) / f1 | |
) | |
try: | |
if not root_queue.get( | |
timeout = float(f"1e-{speed}") | |
): | |
return ( | |
(0, 0, 0), | |
1.0, | |
(255, 255, 255), | |
0 | |
) | |
except queue.Empty: | |
pass | |
try: | |
speed = speed_queue.get( | |
timeout = float(f"1e-{speed}")) | |
except queue.Empty: | |
pass | |
try: | |
return color_left_queue.get( | |
timeout = float(f"1e-{speed}")) | |
except queue.Empty: | |
pass | |
color_right_queue.put(( | |
color, | |
tested, | |
(r, g, b), | |
speed | |
)) | |
return ( | |
color, | |
tested, | |
(r, g, b), | |
speed | |
) | |
while root_alive: | |
try: | |
root_alive: bool = root_queue.get( | |
timeout = float(f"1e-{speed}")) | |
except queue.Empty: | |
pass | |
try: | |
speed: int = speed_queue.get( | |
timeout = float(f"1e-{speed}")) | |
except queue.Empty: | |
pass | |
tested: float = 0.0 | |
start: tuple[int] = (0, 0, 0) | |
while tested < 1.0: | |
current_color, tested, start, speed = check_color( | |
current_color, | |
tested, | |
start, | |
speed, | |
) | |
except Exception as e: | |
logger.exception(e) | |
contrasts: dict = {} | |
try: | |
with open(filename, newline = '') as csvfile: | |
reader: csv.DictReader = csv.DictReader(csvfile) | |
for row in reader: | |
contrasts[ast.literal_eval(row['color'])] = {k: \ | |
ast.literal_eval(v) for k, v in row.items() if k \ | |
!= 'color'} | |
except FileNotFoundError: | |
pass | |
except Exception as e: | |
logger.exception(e) | |
color_thread: threading.Thread = threading.Thread( | |
target = color_thread_callback, | |
) | |
tkinter_thread: threading.Thread = threading.Thread( | |
target = tkinter_thread_callback, | |
args = ( | |
color_thread, | |
), | |
kwargs = dict( | |
contrasts = contrasts, | |
filename = filename, | |
), | |
) | |
tkinter_thread.start() | |
color_thread.start() | |
tkinter_thread.join() | |
print('END') | |
if __name__ == '__main__': | |
try: | |
speed_limit: int = 2 | |
try: | |
log_level: str = sys.argv[1].upper() | |
except (IndexError, AttributeError): | |
log_level: str = 'INFO' | |
try: | |
speed: int = int(sys.argv[2]) | |
except (IndexError, TypeError): | |
speed: int = 1 | |
try: | |
filename: str = sys.argv[3] | |
except IndexError: | |
filename: str = 'contrasts.csv' | |
print(f"Usage: {sys.argv[0]} {log_level} {speed} {filename}") | |
if speed > speed_limit: | |
logging.warning(f"""WARNING: Speed over {speed_limit} will \ | |
break queues. Rounding down to {speed_limit}.""") | |
speed: int = speed_limit | |
main( | |
log_level = log_level, | |
filename = filename, | |
speed = speed, | |
speed_limit = speed_limit, | |
) | |
except Exception as e: | |
logging.exception(e) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment