| | import json |
| | import os |
| | import re |
| | from copy import deepcopy |
| | from distutils.util import strtobool |
| | from pathlib import Path |
| |
|
| | import yaml |
| |
|
| |
|
| |
|
| | def build_from_file(file_path: str): |
| | path = Path(file_path) |
| | workers_path = path / "workers" |
| | if not workers_path.exists(): |
| | return [] |
| |
|
| | |
| | worker_configs = {} |
| | other_configs = {} |
| | file_names = {} |
| |
|
| | for file in path.rglob("*"): |
| | if file.suffix not in [".json", ".yaml", ".yml"]: |
| | continue |
| |
|
| | key = file.name.split(".", 1)[0] |
| |
|
| | if key in file_names: |
| | raise Exception( |
| | f"Duplicate file name [{key}] found:\n" |
| | f"File 1: {file_names[key]}\n" |
| | f"File 2: {file}" |
| | ) |
| | file_names[key] = file |
| |
|
| | try: |
| | with open(file, "r") as f: |
| | if file.suffix == ".json": |
| | content = json.load(f) |
| | else: |
| | content = yaml.load(f, Loader=yaml.FullLoader) |
| | except Exception as e: |
| | raise Exception(f"Error loading file {file}: {str(e)}") |
| |
|
| | if workers_path in file.parents: |
| | worker_configs[key] = content |
| | else: |
| | other_configs[key] = content |
| |
|
| | for conf in worker_configs.values(): |
| | prep_config(conf, other_configs, []) |
| |
|
| | worker_configs_list = [] |
| | for worker_config in worker_configs.values(): |
| | if isinstance(worker_config, list): |
| | worker_configs_list.extend(worker_config) |
| | else: |
| | worker_configs_list.append(worker_config) |
| |
|
| | return worker_configs_list |
| |
|
| |
|
| | def prep_config(sub_config: dict | list, config: dict, forbid_keys: list): |
| | if isinstance(sub_config, dict): |
| | for key, conf in sub_config.items(): |
| | if isinstance(conf, str): |
| | if match := re.search(r"\$\{sub\|([^}]+)\}", conf): |
| | module_key = match.group(1).strip() |
| | if module_key not in config: |
| | raise Exception( |
| | "Incomplete configuration, lack module [{}]".format( |
| | module_key |
| | ) |
| | ) |
| | elif module_key in forbid_keys: |
| | raise Exception( |
| | "Can't reference submodule recursively. [{}]".format( |
| | module_key |
| | ) |
| | ) |
| | sub_config[key] = deepcopy(config[module_key]) |
| | prep_config(sub_config[key], config, forbid_keys + [module_key]) |
| | elif match := re.search(r"\$\{env\|([^,}]+)(?:,([^}]+))?\}", conf): |
| | env_key = match.group(1).strip() |
| | default_value = match.group(2) |
| | env_value = os.getenv(env_key) |
| | if env_value: |
| | sub_config[key] = env_value |
| | elif not env_value and default_value: |
| | sub_config[key] = default_value.strip() |
| | if sub_config[key] == "null" or sub_config[key] == "~": |
| | sub_config[key] = None |
| | else: |
| | raise ValueError( |
| | f"Environmental variable {env_key} need to be set." |
| | ) |
| |
|
| | elif isinstance(conf, dict): |
| | prep_config(sub_config[key], config, forbid_keys) |
| |
|
| | elif isinstance(conf, list): |
| | for i, item in enumerate(conf): |
| | if isinstance(item, dict): |
| | prep_config(sub_config[key][i], config, forbid_keys) |
| | elif isinstance(sub_config, list): |
| | for item in sub_config: |
| | prep_config(item, config, forbid_keys) |
| |
|