From eefd56e16f022b86c7bcb0eb1f5b4e5dfcd249b7 Mon Sep 17 00:00:00 2001 From: CordlessCoder <42666308+CordlessCoder@users.noreply.github.com> Date: Tue, 5 Jul 2022 16:18:17 +0000 Subject: [PATCH] Add files via upload --- utils/console.py | 91 +++++++++------ utils/tomlchecker.py | 262 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 321 insertions(+), 32 deletions(-) create mode 100644 utils/tomlchecker.py diff --git a/utils/console.py b/utils/console.py index 5b91fef..8a68280 100644 --- a/utils/console.py +++ b/utils/console.py @@ -4,7 +4,6 @@ from rich.markdown import Markdown from rich.padding import Padding from rich.panel import Panel from rich.text import Text -from rich.columns import Columns import re console = Console() @@ -29,12 +28,6 @@ def print_substep(text, style=""): console.print(text, style=style) -def print_table(items): - """Prints items in a table.""" - - console.print(Columns([Panel(f"[yellow]{item}", expand=True) for item in items])) - - def handle_input( message: str = "", check_type=False, @@ -44,33 +37,67 @@ def handle_input( nmax=None, oob_error="", extra_info="", + options: list = None, + default=NotImplemented, ): - match = re.compile(match + "$") + if default is not NotImplemented: + console.print( + "[green]" + + message + + '\n[blue bold]The default value is "' + + str(default) + + '"\nDo you want to use it?(y/n)' + ) + if input().casefold().startswith("y"): + return default + if options is None: + match = re.compile(match) + console.print("[green bold]" + extra_info, no_wrap=True) + while True: + console.print(message, end="") + user_input = input("").strip() + if check_type is not False: + try: + user_input = check_type(user_input) + if (nmin is not None and user_input < nmin) or ( + nmax is not None and user_input > nmax + ): + # FAILSTATE Input out of bounds + console.print("[red]" + oob_error) + continue + break # Successful type conversion and number in bounds + except ValueError: + # Type conversion failed + console.print("[red]" + err_message) + continue + elif match != "" and re.match(match, user_input) is None: + console.print( + "[red]" + err_message + + "\nAre you absolutely sure it's correct?(y/n)" + ) + if input().casefold().startswith("y"): + break + continue + else: + # FAILSTATE Input STRING out of bounds + if (nmin is not None and len(user_input) < nmin) or ( + nmax is not None and len(user_input) > nmax + ): + console.print("[red bold]" + oob_error) + continue + break # SUCCESS Input STRING in bounds + return user_input console.print(extra_info, no_wrap=True) while True: console.print(message, end="") user_input = input("").strip() - if re.match(match, user_input) is not None: - if check_type is not False: - try: - user_input = check_type(user_input) # this line is fine - if nmin is not None and user_input < nmin: - console.print("[red]" + oob_error) # Input too low failstate - continue - if nmax is not None and user_input > nmax: - console.print("[red]" + oob_error) # Input too high - continue - break # Successful type conversion and number in bounds - except ValueError: - console.print("[red]" + err_message) # Type conversion failed - continue - if nmin is not None and len(user_input) < nmin: # Check if string is long enough - console.print("[red]" + oob_error) - continue - if nmax is not None and len(user_input) > nmax: # Check if string is not too long - console.print("[red]" + oob_error) - continue - break - console.print("[red]" + err_message) - - return user_input + if user_input not in options: + console.print( + "[red bold]" + + err_message + + "\nValid options are: " + + ", ".join(map(str, options)) + + "." + ) + continue + return user_input diff --git a/utils/tomlchecker.py b/utils/tomlchecker.py new file mode 100644 index 0000000..c44108f --- /dev/null +++ b/utils/tomlchecker.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +# import os +import toml +from rich import pretty +from rich.console import Console +import re + +# from utils.console import handle_input +from console import handle_input + + +console = Console() + + +printed = False + + +def crawl(obj: dict, func=lambda x, y: print(x, y, end="\n"), path: list = []): + for key in obj.keys(): + if type(obj[key]) is dict: + crawl(obj[key], func, path + [key]) + continue + func(path + [key], obj[key]) + + +def check(value, checks, name): + global printed + if printed is False: + console.print( + """\ +[blue bold]############################### +# # +# Checking TOML configuration # +# # +############################### +If you see any prompts, that means that you have unset/incorrectly set variables, please input the correct values.\ +""" + ) + printed = True + if "type" in checks: + try: + value = eval(checks["type"])(value) + except: + value = handle_input( + message=( + ( + ("[blue]Example: " + str(checks["example"]) + "\n") + if "example" in checks + else "" + ) + + "[red]" + + ("Non-optional ", "Optional ")[ + "optional" in checks and checks["optional"] is True + ] + ) + + " [#C0CAF5 bold]" + + str(name) + + "[#F7768E bold]=", + check_type=eval(checks["type"]), + extra_info=checks["explanation"] if "explanation" in checks else "", + default=checks["default"] if "default" in checks else NotImplemented, + match=checks["regex"] if "regex" in checks else "", + err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", + nmin=checks["nmin"] if "nmin" in checks else None, + nmax=checks["nmax"] if "nmax" in checks else None, + oob_error=checks["oob_error"] + if "oob_error" in checks + else "Input out of bounds(Value too high/low/long/short)", + ) + + if ( + "options" in checks and value not in checks["options"] + ): # FAILSTATE Value is not one of the options + value = handle_input( + message=( + (("[blue]Example: " + str(checks["example"]) + "\n") + if "example" in checks else "") + + "[red]" + + ("Non-optional ", "Optional ")[ + "optional" in checks and checks["optional"] is True + ] + ) + + "[#C0CAF5 bold]" + + str(name) + + "[#F7768E bold]=", + extra_info=checks["explanation"] if "explanation" in checks else "", + err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", + default=checks["default"] if "default" in checks else NotImplemented, + options=checks["options"], + ) + if "regex" in checks and ( + (isinstance(value, str) and re.match(checks["regex"], value) is None) + or not isinstance(value, str) + ): # FAILSTATE Value doesn't match regex, or has regex but is not a string. + value = handle_input( + message=( + (("[blue]Example: " + str(checks["example"]) + "\n") + if "example" in checks else "") + + "[red]" + + ("Non-optional ", "Optional ")[ + "optional" in checks and checks["optional"] is True + ] + ) + + "[#C0CAF5 bold]" + + str(name) + + "[#F7768E bold]=", + extra_info=checks["explanation"] if "explanation" in checks else "", + match=checks["regex"], + err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", + default=checks["default"] if "default" in checks else NotImplemented, + nmin=checks["nmin"] if "nmin" in checks else None, + nmax=checks["nmax"] if "nmax" in checks else None, + oob_error=checks["oob_error"] + if "oob_error" in checks + else "Input out of bounds(Value too high/low/long/short)", + ) + + if not hasattr(value, "__iter__") and ( + ("nmin" in checks and checks["nmin"] + is not None and value < checks["nmin"]) + or ("nmax" in checks and checks["nmax"] is not None and value > checks["nmax"]) + ): + value = handle_input( + message=( + (("[blue]Example: " + str(checks["example"]) + "\n") + if "example" in checks else "") + + "[red]" + + ("Non-optional ", "Optional ")[ + "optional" in checks and checks["optional"] is True + ] + ) + + "[#C0CAF5 bold]" + + str(name) + + "[#F7768E bold]=", + extra_info=checks["explanation"] if "explanation" in checks else "", + default=checks["default"] if "default" in checks else NotImplemented, + match=checks["regex"] if "regex" in checks else "", + err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", + nmin=checks["nmin"] if "nmin" in checks else None, + nmax=checks["nmax"] if "nmax" in checks else None, + oob_error=checks["oob_error"] + if "oob_error" in checks + else "Input out of bounds(Value too high/low/long/short)", + ) + if hasattr(value, "__iter__") and ( + ("nmin" in checks and checks["nmin"] + is not None and len(value) < checks["nmin"]) + or ("nmax" in checks and checks["nmax"] is not None and len(value) > checks["nmax"]) + ): + value = handle_input( + message=( + (("[blue]Example: " + str(checks["example"]) + "\n") + if "example" in checks else "") + + "[red]" + + ("Non-optional ", "Optional ")[ + "optional" in checks and checks["optional"] is True + ] + ) + + "[#C0CAF5 bold]" + + str(name) + + "[#F7768E bold]=", + extra_info=checks["explanation"] if "explanation" in checks else "", + default=checks["default"] if "default" in checks else NotImplemented, + match=checks["regex"] if "regex" in checks else "", + err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", + nmin=checks["nmin"] if "nmin" in checks else None, + nmax=checks["nmax"] if "nmax" in checks else None, + oob_error=checks["oob_error"] + if "oob_error" in checks + else "Input out of bounds(Value too high/low/long/short)", + ) + if value == {}: + handle_input( + message=( + (("[blue]Example: " + str(checks["example"]) + "\n") + if "example" in checks else "") + + "[red]" + + ("Non-optional ", "Optional ")[ + "optional" in checks and checks["optional"] is True + ] + ) + + "[#C0CAF5 bold]" + + str(name) + + "[#F7768E bold]=", + extra_info=checks["explanation"] if "explanation" in checks else "", + default=checks["default"] if "default" in checks else NotImplemented, + match=checks["regex"] if "regex" in checks else "", + err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", + nmin=checks["nmin"] if "nmin" in checks else None, + nmax=checks["nmax"] if "nmax" in checks else None, + oob_error=checks["oob_error"] + if "oob_error" in checks + else "Input out of bounds(Value too high/low/long/short)", + ) + return value + + +def crawl_and_check(obj: dict, path: list, checks: dict = {}, name=""): + if len(path) == 0: + return check(obj, checks, name) + if path[0] not in obj.keys(): + obj[path[0]] = {} + obj[path[0]] = crawl_and_check(obj[path[0]], path[1:], checks, path[0]) + return obj + + +def check_vars(path, checks): + global config + crawl_and_check(config, path, checks) + + +def check_toml(template_file, config_file) -> bool: + try: + template = toml.load(template_file) + except Exception as error: + console.print( + f"[red bold]Encountered error when trying to to load {template_file}: {error}" + ) + return False + try: + global config + config = toml.load(config_file) + except (toml.TomlDecodeError): + console.print( + f"""[blue]Couldn't read {config_file}. +Overwrite it?(y/n)""" + ) + if not input().startswith("y"): + print("Unable to read config, and not allowed to overwrite it. Giving up.") + return False + else: + try: + with open(config_file, "w") as f: + f.write("") + except: + console.print( + f"[red bold]Failed to overwrite {config_file}. Giving up.\nSuggestion: check {config_file} permissions for the user." + ) + return False + except (FileNotFoundError): + console.print( + f"""[blue]Couldn't find {config_file} +Creating it now.""" + ) + try: + with open(config_file, "x") as f: + f.write("") + config = {} + except: + console.print( + f"[red bold]Failed to write to {config_file}. Giving up.\nSuggestion: check the folder's permissions for the user." + ) + return False + crawl(template, check_vars) + pretty.pprint(config) + with open(config_file, "w") as f: + toml.dump(config, f) + return True + + +if __name__ == "__main__": + check_toml(".config.template.toml", "config.toml")