From aa4d213692cb27a903fe1593e2dd3387e638b350 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Thu, 15 Jun 2023 14:40:37 +0300 Subject: Configs: Introduce Blueprints. Refactor configs in webqtlConfig. * Introduce flask Blueprints to help with decoupling the various modules from the `wqflask/__init__.py` module * Refactor settings: Create a function `base.webqtlConfig.init_app(...)` to handle setting up the configurations on the app correctly. Call this function at app creation time. * Move configuration utility functions from `utility.tools` module to `utility.configuration` module. * Use the `get_setting(...)` function to retrieve configuration settings from the application. --- wqflask/utility/authentication_tools.py | 11 ++- wqflask/utility/configuration.py | 165 ++++++++++++++++++++++++++++++++ wqflask/utility/hmac.py | 8 +- wqflask/utility/startup_config.py | 23 +++-- wqflask/utility/tools.py | 93 ++---------------- 5 files changed, 193 insertions(+), 107 deletions(-) create mode 100644 wqflask/utility/configuration.py (limited to 'wqflask/utility') diff --git a/wqflask/utility/authentication_tools.py b/wqflask/utility/authentication_tools.py index 7d80b3fb..3d732228 100644 --- a/wqflask/utility/authentication_tools.py +++ b/wqflask/utility/authentication_tools.py @@ -1,7 +1,7 @@ import json import requests -from flask import g +from flask import g, current_app as app from wqflask.database import database_connection from base import webqtlConfig @@ -9,7 +9,7 @@ from utility.redis_tools import (get_redis_conn, get_resource_info, get_resource_id, add_resource) -from utility.tools import GN_PROXY_URL +from utility.configuration import get_setting Redis = get_redis_conn() @@ -37,7 +37,7 @@ def check_resource_availability(dataset, user_id, trait_id=None): return webqtlConfig.SUPER_PRIVILEGES response = None - the_url = f"{GN_PROXY_URL}available?resource={resource_id}&user={user_id}" + the_url = f"{get_setting('GN_PROXY_URL')}available?resource={resource_id}&user={user_id}" try: response = json.loads(requests.get(the_url).content) except: @@ -93,8 +93,9 @@ def get_group_code(dataset): def check_admin(resource_id=None): - the_url = GN_PROXY_URL + "available?resource={}&user={}".format( - resource_id, g.user_session.user_id) + the_url = ( + f"{get_setting('GN_PROXY_URL')}available?resource={resource_id}" + f"&user={g.user_session.user_id}") try: response = json.loads(requests.get(the_url).content)['admin'] except: diff --git a/wqflask/utility/configuration.py b/wqflask/utility/configuration.py new file mode 100644 index 00000000..933d9626 --- /dev/null +++ b/wqflask/utility/configuration.py @@ -0,0 +1,165 @@ +"""Functions used in setting up configurations.""" +import os # replace os.path with pathlib.Path +import sys +import logging + +from flask import current_app + +logger = logging.getLogger(__name__) + +def override_from_envvars(app): + """ + Override `app` configuration values with those in the enviroment variables with the same names. + """ + configs = dict((key, value.strip()) for key,value in + ((key, os.environ.get(key)) for key in app.config.keys()) + if value is not None and value != "") + app.config.update(**configs) + return app + +def get_setting(app, setting_id, guess=None): + """Resolve a setting from the `app`.""" + setting = app.config.get(setting_id, guess or "") + if setting is None or setting == "": + raise Exception( + f"{setting_id} setting unknown or faulty " + "(update default_settings.py?).") + return setting + +def get_setting_bool(app, setting_id): + v = get_setting(app, setting_id) + if v not in [0, False, 'False', 'FALSE', None]: + return True + return False + + +def get_setting_int(app, setting_id): + val = get_setting(app, setting_id) + if isinstance(val, str): + return int(val) + if val is None: + return 0 + return val + +def valid_bin(path): + if os.path.islink(path) or valid_file(path): + return path + return None + +def valid_file(path): + if os.path.isfile(path): + return path + return None + +def valid_path(path): + if os.path.isdir(path): + return path + return None + +def flat_file_exists(app, subdir): + base = get_setting(app, "GENENETWORK_FILES") + return valid_path(base + "/" + subdir) + +def flat_files(app, subdir=None): + base = get_setting(app, "GENENETWORK_FILES") + if subdir: + return assert_dir(base + "/" + subdir) + return assert_dir(base) + +def assert_bin(fn): + if not valid_bin(fn): + raise Exception("ERROR: can not find binary " + fn) + return fn + + +def assert_dir(the_dir): + if not valid_path(the_dir): + raise FileNotFoundError(f"ERROR: can not find directory '{the_dir}'") + return the_dir + +def assert_writable_dir(path): + try: + fn = path + "/test.txt" + fh = open(fn, 'w') + fh.write("I am writing this text to the file\n") + fh.close() + os.remove(fn) + except IOError: + raise Exception(f"Unable to write test.txt to directory {path}") + return path + +def assert_file(fn): + if not valid_file(fn): + raise FileNotFoundError(f"Unable to find file '{fn}'") + return fn + +def mk_dir(path): + if not valid_path(path): + os.makedirs(path) + return assert_dir(path) + +def locate(app, name, subdir=None): + """ + Locate a static flat file in the GENENETWORK_FILES environment. + + This function throws an error when the file is not found. + """ + base = get_setting(app, "GENENETWORK_FILES") + if subdir: + base = base + "/" + subdir + if valid_path(base): + lookfor = base + "/" + name + if valid_file(lookfor): + return lookfor + else: + raise Exception("Can not locate " + lookfor) + if subdir: + sys.stderr.write(subdir) + raise Exception("Can not locate " + name + " in " + base) + +def locate_ignore_error(app, name, subdir=None): + """ + Locate a static flat file in the GENENETWORK_FILES environment. + + This function does not throw an error when the file is not found + but returns None. + """ + base = get_setting(app, "GENENETWORK_FILES") + if subdir: + base = base + "/" + subdir + if valid_path(base): + lookfor = base + "/" + name + if valid_file(lookfor): + return lookfor + return None + +def tempdir(app): + """Retrieve the configured temporary directory or `/tmp`.""" + return valid_path(get_setting(app, "TMPDIR", "/tmp")) + +def show_settings(app): + """Print out the application configurations.""" + BLUE = '\033[94m' + GREEN = '\033[92m' + BOLD = '\033[1m' + ENDC = '\033[0m' + app = app or current_app + LOG_LEVEL = app.config.get("LOG_LEVEL") + + print(("Set global log level to " + BLUE + LOG_LEVEL + ENDC), + file=sys.stderr) + log_level = getattr(logging, LOG_LEVEL.upper()) + logging.basicConfig(level=log_level) + + logger.info(BLUE + "Mr. Mojo Risin 2" + ENDC) + keylist = list(app.config.keys()) + print("runserver.py: ****** Webserver configuration - k,v pairs from app.config ******", + file=sys.stderr) + keylist.sort() + for k in keylist: + try: + print(("%s: %s%s%s%s" % (k, BLUE, BOLD, get_setting(app, k), ENDC)), + file=sys.stderr) + except: + print(("%s: %s%s%s%s" % (k, GREEN, BOLD, app.config[k], ENDC)), + file=sys.stderr) diff --git a/wqflask/utility/hmac.py b/wqflask/utility/hmac.py index 29891677..2fb8b106 100644 --- a/wqflask/utility/hmac.py +++ b/wqflask/utility/hmac.py @@ -1,9 +1,7 @@ import hmac import hashlib -from flask import url_for - -from wqflask import app +from flask import url_for, current_app as app def hmac_creation(stringy): @@ -37,7 +35,3 @@ def url_for_hmac(endpoint, **values): else: combiner = "?" return url + combiner + "hm=" + hm - - -app.jinja_env.globals.update(url_for_hmac=url_for_hmac, - data_hmac=data_hmac) diff --git a/wqflask/utility/startup_config.py b/wqflask/utility/startup_config.py index 69cac124..5ab43b1a 100644 --- a/wqflask/utility/startup_config.py +++ b/wqflask/utility/startup_config.py @@ -1,11 +1,10 @@ from wqflask import app -from utility.tools import WEBSERVER_MODE -from utility.tools import show_settings -from utility.tools import get_setting_int -from utility.tools import get_setting -from utility.tools import get_setting_bool +from utility.configuration import show_settings +from utility.configuration import get_setting_int +from utility.configuration import get_setting +from utility.configuration import get_setting_bool BLUE = '\033[94m' @@ -14,28 +13,28 @@ BOLD = '\033[1m' ENDC = '\033[0m' -def app_config(): +def app_config(app): app.config['SESSION_TYPE'] = app.config.get('SESSION_TYPE', 'filesystem') if not app.config.get('SECRET_KEY'): import os app.config['SECRET_KEY'] = str(os.urandom(24)) - mode = WEBSERVER_MODE + mode = get_setting(app, "WEBSERVER_MODE") if mode in ["DEV", "DEBUG"]: app.config['TEMPLATES_AUTO_RELOAD'] = True if mode == "DEBUG": app.debug = True print("==========================================") - show_settings() + show_settings(app) - port = get_setting_int("SERVER_PORT") + port = get_setting_int(app, "SERVER_PORT") - if get_setting_bool("USE_GN_SERVER"): + if get_setting_bool(app, "USE_GN_SERVER"): print(f"GN2 API server URL is [{BLUE}GN_SERVER_URL{ENDC}]") import requests - page = requests.get(get_setting("GN_SERVER_URL")) + page = requests.get(get_setting(app, "GN_SERVER_URL")) if page.status_code != 200: raise Exception("API server not found!") print(f"GN2 is running. Visit {BLUE}" f"[http://localhost:{str(port)}/{ENDC}]" - f"({get_setting('WEBSERVER_URL')})") + f"({get_setting(app, 'WEBSERVER_URL')})") diff --git a/wqflask/utility/tools.py b/wqflask/utility/tools.py index 5b3e9413..894bef76 100644 --- a/wqflask/utility/tools.py +++ b/wqflask/utility/tools.py @@ -7,6 +7,16 @@ import json from wqflask import app +from .configuration import ( + mk_dir, + valid_bin, + valid_file, + valid_path, + assert_bin, + assert_dir, + assert_file, + assert_writable_dir) + # Use the standard logger here to avoid a circular dependency import logging logger = logging.getLogger(__name__) @@ -86,24 +96,6 @@ def get_setting_int(id): return v -def valid_bin(bin): - if os.path.islink(bin) or valid_file(bin): - return bin - return None - - -def valid_file(fn): - if os.path.isfile(fn): - return fn - return None - - -def valid_path(dir): - if os.path.isdir(dir): - return dir - return None - - def js_path(module=None): """ Find the JS module in the two paths @@ -146,42 +138,6 @@ def flat_files(subdir=None): return assert_dir(base) -def assert_bin(fn): - if not valid_bin(fn): - raise Exception("ERROR: can not find binary " + fn) - return fn - - -def assert_dir(the_dir): - if not valid_path(the_dir): - raise FileNotFoundError(f"ERROR: can not find directory '{the_dir}'") - return the_dir - - -def assert_writable_dir(dir): - try: - fn = dir + "/test.txt" - fh = open(fn, 'w') - fh.write("I am writing this text to the file\n") - fh.close() - os.remove(fn) - except IOError: - raise Exception('Unable to write test.txt to directory ' + dir) - return dir - - -def assert_file(fn): - if not valid_file(fn): - raise FileNotFoundError(f"Unable to find file '{fn}'") - return fn - - -def mk_dir(dir): - if not valid_path(dir): - os.makedirs(dir) - return assert_dir(dir) - - def locate(name, subdir=None): """ Locate a static flat file in the GENENETWORK_FILES environment. @@ -230,35 +186,6 @@ def tempdir(): return valid_path(get_setting("TMPDIR", "/tmp")) -BLUE = '\033[94m' -GREEN = '\033[92m' -BOLD = '\033[1m' -ENDC = '\033[0m' - - -def show_settings(): - from utility.tools import LOG_LEVEL - - print(("Set global log level to " + BLUE + LOG_LEVEL + ENDC), - file=sys.stderr) - log_level = getattr(logging, LOG_LEVEL.upper()) - logging.basicConfig(level=log_level) - - logger.info(OVERRIDES) - logger.info(BLUE + "Mr. Mojo Risin 2" + ENDC) - keylist = list(app.config.keys()) - print("runserver.py: ****** Webserver configuration - k,v pairs from app.config ******", - file=sys.stderr) - keylist.sort() - for k in keylist: - try: - print(("%s: %s%s%s%s" % (k, BLUE, BOLD, get_setting(k), ENDC)), - file=sys.stderr) - except: - print(("%s: %s%s%s%s" % (k, GREEN, BOLD, app.config[k], ENDC)), - file=sys.stderr) - - # Cached values GN_VERSION = get_setting('GN_VERSION') HOME = get_setting('HOME') -- cgit v1.2.3