""" Configuration Loader - Load YAML config with environment variable substitution Compatible with Python 3.6+ """ import os import re import yaml from dotenv import load_dotenv def load_config(config_path='config/config.yaml'): """ Load configuration from YAML file with environment variable substitution Supports: - ${VAR_NAME} - Required environment variable - ${VAR_NAME:-default} - Optional with default value """ # Load environment variables from .env file load_dotenv() # Read YAML file with open(config_path, 'r') as f: config_text = f.read() # Substitute environment variables config_text = substitute_env_vars(config_text) # Parse YAML config = yaml.safe_load(config_text) # Load environment-specific overrides if specified env = config.get('environment', 'staging') env_config_path = 'config/environments/{}.yaml'.format(env) if os.path.exists(env_config_path): with open(env_config_path, 'r') as f: env_config_text = f.read() env_config_text = substitute_env_vars(env_config_text) env_config = yaml.safe_load(env_config_text) # Merge environment-specific config config = deep_merge(config, env_config) return config def substitute_env_vars(text): """ Substitute ${VAR_NAME} and ${VAR_NAME:-default} patterns with environment variables """ def replacer(match): var_expr = match.group(1) # Check for default value syntax: VAR:-default if ':-' in var_expr: var_name, default = var_expr.split(':-', 1) return os.getenv(var_name, default) else: # Required variable value = os.getenv(var_expr) if value is None: raise ValueError("Required environment variable not set: {}".format(var_expr)) return value # Pattern: ${VAR_NAME} or ${VAR_NAME:-default} pattern = r'\$\{([^}]+)\}' return re.sub(pattern, replacer, text) def deep_merge(base, override): """ Deep merge two dictionaries """ result = base.copy() for key, value in override.items(): if key in result and isinstance(result[key], dict) and isinstance(value, dict): result[key] = deep_merge(result[key], value) else: result[key] = value return result def load_field_mappings(config): """ Load field mappings configuration """ mappings_file = config['fields']['mappings_file'] with open(mappings_file, 'r') as f: return yaml.safe_load(f)