py-microservice/config.py
2023-10-04 21:08:37 +02:00

110 lines
2.8 KiB
Python

"""
Application configuration
values can be configured in the OS environment via KEY_NAME=...
or in a toml file using
[section]
foo = ...
bar_baz = ...
in the latter case, the section and name are uppercased and joined with `_`,
then inserted into the OS environment (in this case, resulting in
SECTION_FOO=... and SECTION_BAR_BAZ=... ).
The OS environment takes precedence - if a key is found in both env and config,
the config setting will be ignored.
"""
import tomllib
import logging
from os import environ
from functools import wraps
import consts
logger = logging.getLogger(consts.LOG_ROOT)
def load(file):
"""
Load configuration from toml and add to environment
"""
if file is None:
logger.debug('no config file provided.')
else:
logger.debug('loading config')
with open(file, 'rb') as fh:
config = tomllib.load(fh)
for k, v in config.items():
for k_inner, v_inner in v.items():
effective_key = f'{k.upper()}_{k_inner.upper()}'
if effective_key in environ:
logger.warning(
'%s already exists in os environment, skipping', effective_key)
else:
environ[effective_key] = str(v_inner)
logger.debug('raw: %s = %s', effective_key,
environ[effective_key])
def env(key, default=None):
"""
retrieve a key from the OS environment.
throws `KeyError` when the key is not found and no default fallback is provided.
"""
if default is None:
return environ[key]
return environ.get(key, default=default)
def logged(f):
"""
logging wrapper for resolved configuration settings
"""
@wraps(f)
def decorated(*args, **kwargs):
res = f(*args, **kwargs)
logger.debug('resolved: %s = %s', args[0], res)
return res
return decorated
@logged
def int_val(key, default=None):
"""
retrieve configuration value as int
"""
return int(env(key, default=default))
def strtobool(val):
"""Convert a string representation of truth to true (1) or false (0).
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
'val' is anything else.
"""
val = val.lower()
if val in ("y", "yes", "t", "true", "on", "1"):
return True
elif val in ("n", "no", "f", "false", "off", "0"):
return False
else:
raise ValueError(f'Invalid bool string: {val}')
@logged
def bool_val(key, default=None):
"""
retrieve configuration value as bool
"""
return strtobool(env(key, default=default))
@logged
def str_val(key, default=None):
"""
retrieve configuration value as bool
"""
return env(key, default=default)