From 16188a3f3a79c3dcc0d1adf00c719bb84e5a045a Mon Sep 17 00:00:00 2001 From: Tim Van Baak Date: Mon, 28 Dec 2020 14:46:26 -0800 Subject: [PATCH] Refactor configuration to use a file exclusively --- inquisitor/configs.py | 24 ------- inquisitor/configs/__init__.py | 4 ++ inquisitor/configs/resolver.py | 125 +++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 24 deletions(-) delete mode 100644 inquisitor/configs.py create mode 100644 inquisitor/configs/__init__.py create mode 100644 inquisitor/configs/resolver.py diff --git a/inquisitor/configs.py b/inquisitor/configs.py deleted file mode 100644 index bbb9e91..0000000 --- a/inquisitor/configs.py +++ /dev/null @@ -1,24 +0,0 @@ -import os -import logging - -DUNGEON_PATH = os.path.abspath(os.environ.get("INQUISITOR_DUNGEON") or "./dungeon") -SOURCES_PATH = os.path.abspath(os.environ.get("INQUISITOR_SOURCES") or "./sources") -CACHE_PATH = os.path.abspath(os.environ.get("INQUISITOR_CACHE") or "./cache") - -logger = logging.getLogger("inquisitor") -handler = logging.StreamHandler() -logger.addHandler(handler) - -def log_normal(): - logger.setLevel(logging.INFO) - handler.setLevel(logging.INFO) - formatter = logging.Formatter('[{levelname}] {message}', style="{") - handler.setFormatter(formatter) - -def log_verbose(): - logger.setLevel(logging.DEBUG) - handler.setLevel(logging.DEBUG) - formatter = logging.Formatter('[{asctime}] [{levelname}:{filename}:{lineno}] {message}', style="{") - handler.setFormatter(formatter) - -log_normal() diff --git a/inquisitor/configs/__init__.py b/inquisitor/configs/__init__.py new file mode 100644 index 0000000..f261c22 --- /dev/null +++ b/inquisitor/configs/__init__.py @@ -0,0 +1,4 @@ +from .resolver import data_path as DUNGEON_PATH +from .resolver import source_path as SOURCES_PATH +from .resolver import cache_path as CACHE_PATH +from .resolver import logger, add_logging_handler, init_default_logging \ No newline at end of file diff --git a/inquisitor/configs/resolver.py b/inquisitor/configs/resolver.py new file mode 100644 index 0000000..923d6e4 --- /dev/null +++ b/inquisitor/configs/resolver.py @@ -0,0 +1,125 @@ +import os +import logging + + +# Constants governing config resolution: +# Path to the config file, containing key-value pairs of the other settings +CONFIG_ENVVAR = 'INQUISITOR_CONFIG' +DEFAULT_CONFIG_PATH = '/etc/inquisitor.conf' + +# Path to the folder where items are stored +CONFIG_DATA = 'DataPath' +DEFAULT_DATA_PATH = '/var/inquisitor/data/' + +# Path to the folder where source modules are stored +CONFIG_SOURCES = 'SourcePath' +DEFAULT_SOURCES_PATH = '/var/inquisitor/sources/' + +# Path to the folder where cached files are stored +CONFIG_CACHE = 'CachePath' +DEFAULT_CACHE_PATH = '/var/inquisitor/cache/' + +# Path to a log file where logging will be redirected +CONFIG_LOGFILE = 'LogFile' +DEFAULT_LOG_FILE = None + +# Whether logging is verbose +CONFIG_VERBOSE = 'Verbose' +DEFAULT_VERBOSITY = False + + +def read_config_file(config_path): + """ + Reads a config file of key-value pairs, where non-blank lines are + either comments beginning with the character '#' or keys and values + separated by the character '='. + """ + # Parse the config file into key-value pairs + if not os.path.isfile(config_path): + raise FileNotFoundError(f'No config file found at {config_path}') + accumulated_configs = {} + with open(config_path, 'r', encoding='utf8') as cfg: + line_no = 0 + for line in cfg: + line_no += 1 + # Skip blank lines + if not line.strip(): + continue + # Skip comments + if line.lstrip().startswith('#'): + continue + # Accumulate config keyvalue pairs + if '=' not in line: + raise ValueError(f'Invalid config format on line {line_no}') + key, value = line.split('=', maxsplit=1) + accumulated_configs[key.strip()] = value.strip() + + return accumulated_configs + + +# Read envvar for config file location, with fallback to default +config_path = os.path.abspath( + os.environ.get(CONFIG_ENVVAR) or + DEFAULT_CONFIG_PATH +) + +configs = read_config_file(config_path) + +# Extract and validate config values +data_path = configs.get(CONFIG_DATA) or DEFAULT_DATA_PATH +if not os.path.isabs(data_path): + raise ValueError(f'Non-absolute data path: {data_path}') +if not os.path.isdir(data_path): + raise FileNotFoundError(f'Cannot find directory {data_path}') + +source_path = configs.get(CONFIG_SOURCES) or DEFAULT_SOURCES_PATH +if not os.path.isabs(source_path): + raise ValueError(f'Non-absolute source path: {source_path}') +if not os.path.isdir(source_path): + raise FileNotFoundError(f'Cannot find directory {source_path}') + +cache_path = configs.get(CONFIG_CACHE) or DEFAULT_CACHE_PATH +if not os.path.isabs(cache_path): + raise ValueError(f'Non-absolute cache path: {cache_path}') +if not os.path.isdir(cache_path): + raise FileNotFoundError(f'Cannot find directory {cache_path}') + +log_file = configs.get(CONFIG_LOGFILE) or DEFAULT_LOG_FILE +if log_file and not os.path.isabs(log_file): + raise ValueError(f'Non-absolute log file path: {log_file}') + +is_verbose = (configs.get(CONFIG_VERBOSE) == 'true') or DEFAULT_VERBOSITY + + +# Set up logging +logger = logging.getLogger("inquisitor") +logger.setLevel(logging.DEBUG) + +def add_logging_handler(verbose, log_filename): + """ + Adds a logging handler according to the given settings + """ + log_format = ( + '[{asctime}] [{levelname}:{filename}:{lineno}] {message}' + if verbose else + '[{levelname}] {message}' + ) + formatter = logging.Formatter(log_format, style='{') + + log_level = ( + logging.DEBUG + if verbose else + logging.INFO + ) + handler = ( + logging.FileHandler(log_filename, encoding='utf8') + if log_filename else + logging.StreamHandler() + ) + handler.setFormatter(formatter) + handler.setLevel(log_level) + + logger.addHandler(handler) + +def init_default_logging(): + add_logging_handler(is_verbose, log_file)