From b88b78e626083d1495b90441d0cdf7dd4c2afc32 Mon Sep 17 00:00:00 2001 From: Antoine A <> Date: Sat, 24 Feb 2024 19:05:20 +0100 Subject: Simplify and document python code --- regional-currency/ask_questions.py | 92 +++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 32 deletions(-) diff --git a/regional-currency/ask_questions.py b/regional-currency/ask_questions.py index 8c36311..c8da3b1 100755 --- a/regional-currency/ask_questions.py +++ b/regional-currency/ask_questions.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +"""Python script to ask questions using an interactive prompt""" import base64 import os @@ -11,6 +12,7 @@ CONFIG_FILE = "config/user.conf" def load_conf() -> Dict[str, str]: + """Load user configuration file""" conf = {} with open(CONFIG_FILE, "r") as f: for kv in f.read().splitlines(): @@ -24,6 +26,7 @@ conf = load_conf() def add_conf(name: str, value: str): + """Update a user configuration value and update the configuration file""" conf[name] = value content = "" for key, value in conf.items(): @@ -36,6 +39,7 @@ def add_conf(name: str, value: str): def run_cmd( cmd: list[str], input: str | None = None, env: Dict[str, str] | None = None ) -> int: + """Run a command in a child process and return its exit code""" result = subprocess.run( cmd, stdout=subprocess.PIPE, @@ -53,6 +57,7 @@ def run_cmd( def try_cmd( cmd: list[str], input: str | None = None, env: Dict[str, str] | None = None ) -> bool: + """Run a command in a child process and return if successful""" return run_cmd(cmd, input, env) == 0 @@ -60,35 +65,44 @@ A = TypeVar("A") T = TypeVar("T") -def custom( +def conf_value( name: str | None, action: Callable[[], str | None], default: T | None = None, check: Callable[[str], T | None] = lambda it: it, fmt: Callable[[T], str] = lambda it: str(it), -): +) -> T: + """ + Logic to configure a value + + :param name: if present will try to fetch the current value and will store the new value + :param action: how a value will be obtained + :param default: default value to use if no value is given + :param check: check and normalize the value + :param fmt: format value for storage + :return: the configuration value + """ + value = None + + # Fetch current value if name is not None: - prev = conf.get(name, None) - if prev is not None: # If got previous value - checked = check(prev) - if checked is not None: # And previous value is valid - add_conf(name, fmt(checked)) # Add again in case normalization changed - return checked - - while True: - raw = action() - if raw is None: - if default is not None: - if name is not None: - add_conf(name, fmt(default)) - return default - continue - else: - checked = check(raw) - if checked is not None: - if name is not None: - add_conf(name, fmt(checked)) - return checked + curr = conf.get(name, None) + if curr is not None: + # Check the current value and ask again if invalid + value = check(curr) + + # Ask for a new value until we get a valid one + while value is None: + new = action() + # Use default if no value was provided else check the new value + value = check(new) if new is not None else default + + + # Store the new value + if name is not None: + add_conf(name, fmt(value)) + + return value def ask( @@ -98,9 +112,19 @@ def ask( check: Callable[[str], T | None] = lambda it: it, fmt: Callable[[T], str] = lambda it: str(it), ) -> T: - def do_ask(): - log.write(msg.encode()) - log.write("\n".encode()) + """ + Prompt the user to configurea value + :param name: if present will try to fetch the current value and will store the new value + :param msg: the message to prompt the user with + :param default: default value to use if no value is obtained + :param check: check and normalize the value + :param fmt: format value for storage + :return: the configuration value + """ + def do_ask() -> str | None: + # Log the prompt + log.write(msg.encode() + "\n".encode()) + # Actual prompt raw = input(msg).strip() if raw == "": if default is None: @@ -108,14 +132,16 @@ def ask( return None return raw - return custom(name, do_ask, default, check, fmt) + return conf_value(name, do_ask, default, check, fmt) def ask_str(name: str | None, msg: str, default: str | None = None) -> str: + "Prompt the user to configure a string" return ask(name, msg, default) def ask_currency(name: str, msg: str, default: str | None = None) -> str: + "Prompt the user to configure a currency name" def check_currency(currency: str) -> str | None: currency = currency.upper() if not currency.isascii() or not currency.isalpha(): @@ -130,6 +156,7 @@ def ask_currency(name: str, msg: str, default: str | None = None) -> str: def ask_host(name: str, msg: str, default: str | None = None) -> str: + "Prompt the user to configure the installation hostname" def check_host(host: str) -> str | None: success = True for subdomain in ["backend", "bank", "exchange"]: @@ -143,6 +170,7 @@ def ask_host(name: str, msg: str, default: str | None = None) -> str: def ask_yes_no(name: str | None, msg: str, default: bool | None = None) -> bool: + "Prompt the user to configure a boolean" def check_yes_no(raw: str) -> bool | None: raw = raw.lower() if raw == "y" or raw == "yes": @@ -187,7 +215,7 @@ if ask_yes_no("ENABLE_TLS", "5. Setup TLS using Let's Encrypt? (Y/n): ", True): else: return "y" - custom("TLS_TOS", ask_tos) + conf_value("TLS_TOS", ask_tos) add_conf("PROTO", "https") else: add_conf("PROTO", "http") @@ -201,7 +229,7 @@ if not ask_yes_no( if ask_yes_no( "DO_TELESIGN", - "7. Setup sms two-factor authentication using Telesign https://www.telesign.com? (Y/n): ", + "7. Setup SMS two-factor authentication using Telesign https://www.telesign.com? (Y/n): ", True, ): @@ -215,11 +243,11 @@ if ask_yes_no( auth_token = base64.b64encode(f"{customer_id}:{api_key}".encode()).decode() if not try_cmd( ["libeufin-tan-sms.sh", phone_number], - "12345", + "12345 is your verification code for GNU Taler setup", {**os.environ, "AUTH_TOKEN": auth_token}, ): print( - "Failed to send an sms using Telesign API, check your credentials and phone number" + "Failed to send an SMS using Telesign API, check your credentials and phone number" ) return None code = ask_str(None, f"7.4. Enter the code received by {phone_number} : ") @@ -230,7 +258,7 @@ if ask_yes_no( return None return auth_token - custom("TELESIGN_AUTH_TOKEN", ask_telesign) + conf_value("TELESIGN_AUTH_TOKEN", ask_telesign) ask_str( "BANK_ADMIN_PASSWORD", "8. Enter the admin password for the bank (or press enter to autogenerate password): ", -- cgit v1.2.3