import os.path import pkg_resources import yaml NAME = "phi" DEFAULT_CONFIG = { "core": {"listen": {"host": "localhost", "port": 8080}}, "ldap": { "host": "localhost", "port": 389, "encryption": "TLSv1.2", "ciphers": "HIGH", "validate": True, "ca_certs": pkg_resources.resource_filename(NAME, "openldap/cert.pem"), "username": None, "password": None, "base_dn": None, "attribute_id": "uid", "attribute_mail": "mail", }, "logging": { "version": 1, "formatters": {"default": {"format": "[%(name)s %(levelname)s] %(message)s"}}, "handlers": { "console": { "class": "logging.StreamHandler", "formatter": "default", "stream": "ext://sys.stdout", }, "file": { "class": "logging.FileHandler", "formatter": "default", "filename": "phi.log", }, }, "loggers": { "phi": {"level": "INFO", "handlers": ["console", "file"],}, "aiohttp": {"level": "INFO", "handlers": ["console", "file"],}, "ldap3": {"level": "WARNING", "handlers": ["console", "file"],}, }, }, } DUMMY_CONFIG = {"core": {}, "ldap": {}, "logging": {}} CONFIG_FILE = "config.yml" CONFIG_PATHS = [ "./", "~/.config/" + NAME + "/", "/usr/local/etc/" + NAME + "/", "/etc/" + NAME + "/", ] CONFIG_FILES = [os.path.join(p, CONFIG_FILE) for p in CONFIG_PATHS] def get_config(config_path=None): """Return the path of the found configuration file and its content :param config_path: optional path to a config file. :returns: (path, config) :rtype: (str, dict) """ if config_path: with open(config_path) as c: config = yaml.safe_load(c) return config_path, config for f in CONFIG_FILES: try: with open(f, "r") as c: config = yaml.safe_load(c) return (f, config) except FileNotFoundError: # Skip to the next file. # We only care if the file is preset but it's not # accessible or if the file is not present at all # in any of CONFIG_PATHS. pass return None, DUMMY_CONFIG def merge_config(cli_config, file_config): """ Merge the cli-provided and file-provided config. """ return recursive_merge(cli_config, file_config) def _init_with_shape_of(element): if isinstance(element, dict): return {} elif isinstance(element, list): return [] return None def recursive_merge(main_config, aux_config): def _recursive_merge(main, aux, default, key, _ret_config): if isinstance(default, dict): _sub_conf = {} for k, v in default.items(): _main = main[k] if k in main else _init_with_shape_of(v) _aux = aux[k] if k in aux else _init_with_shape_of(v) _recursive_merge(_main, _aux, v, k, _sub_conf) _ret_config[key] = _sub_conf elif isinstance(default, list): _main = main.copy() if aux is not None: _main.extend(aux) _ret_config[key] = list(set(_main)) elif isinstance(default, bool): _ret_config[key] = default and aux and main else: if main is not None: _ret_config[key] = main elif aux is not None: _ret_config[key] = aux else: _ret_config[key] = default _config = {} _recursive_merge(main_config, aux_config, DEFAULT_CONFIG, "ROOT", _config) return _config["ROOT"]