import json import os import yaml def read_config(name): if not os.path.isfile(name): raise FileNotFoundError(f"File {name} doesn't exist") _, ext = os.path.splitext(name) ext = ext.lower() if ext == '.json': return read_json(name) elif ext == '.properties': return read_prop(name) elif ext in ('.yaml', '.yml'): return read_yaml(name) else: raise ValueError(f"Wrong file type: {ext}") def read_json(name): with open(name, 'r', encoding='utf-8') as f: return dict(json.load(f)) def read_prop(filepath, sep='=', comment_char='#'): """ Read the file passed as parameter as a properties file. """ conf = {} with open(filepath, "rt", encoding='utf-8') as f: for line in f: l = line.strip() if l and not l.startswith(comment_char): key, _, value = l.partition(sep) conf[key.strip()] = value.strip().strip('"') return conf def read_yaml(name, secrets_file='secrets.yaml'): # Load secrets first secrets = {} secrets_path = os.path.join(os.path.dirname(name), secrets_file) if os.path.exists(secrets_path): with open(secrets_path, 'r', encoding='utf-8') as f: secrets = yaml.safe_load(f) or {} def secret_constructor(loader, node): secret_key = loader.construct_scalar(node) if secret_key not in secrets: raise ValueError(f"Secret '{secret_key}' not found in {secrets_file}") return secrets[secret_key] # Own loader class, so the global yaml.SafeLoader is not modified class SecretLoader(yaml.SafeLoader): pass SecretLoader.add_constructor('!secret', secret_constructor) # Load the main configuration with open(name, 'r', encoding='utf-8') as f: y_conf = yaml.load(f, Loader=SecretLoader) if y_conf is None: return {} if not isinstance(y_conf, dict): raise ValueError(f"Wrong config structure in {name}: top level must be a mapping") return dict(y_conf)