CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
172 lines
5.5 KiB
Python
172 lines
5.5 KiB
Python
# Copyright Advanced Micro Devices, Inc.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""YAML configuration parser with schema validation and environment variable expansion."""
|
|
|
|
import yaml
|
|
import os
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Any, Dict
|
|
from .config_validator import ConfigValidator, ConfigurationError
|
|
|
|
|
|
class ConfigParser:
|
|
"""YAML configuration parser with validation and environment variable expansion."""
|
|
|
|
# Pattern for environment variable substitution: ${VAR_NAME} or ${VAR_NAME:-default}
|
|
ENV_VAR_PATTERN = re.compile(r"\$\{([^}]+)\}")
|
|
|
|
def __init__(
|
|
self, config_file: str, validate: bool = True, expand_env_vars: bool = True
|
|
):
|
|
"""Initialize config parser.
|
|
|
|
Args:
|
|
config_file: Path to YAML configuration file
|
|
validate: Whether to validate configuration against schema
|
|
expand_env_vars: Whether to expand environment variables
|
|
|
|
Raises:
|
|
FileNotFoundError: If config file doesn't exist
|
|
ConfigurationError: If configuration is invalid
|
|
"""
|
|
self.config_file = Path(config_file)
|
|
self.config_data = {}
|
|
self.root = {} # Alias for compatibility
|
|
self.validate_on_load = validate
|
|
self.expand_env_vars = expand_env_vars
|
|
|
|
if not self.config_file.exists():
|
|
raise FileNotFoundError(f"Configuration file not found: {config_file}")
|
|
|
|
self._load()
|
|
|
|
def _load(self):
|
|
"""Load configuration from file with environment variable expansion and validation."""
|
|
try:
|
|
with open(self.config_file, "r") as f:
|
|
raw_config = yaml.safe_load(f)
|
|
|
|
# Check if config is empty
|
|
if not raw_config:
|
|
raise ConfigurationError(
|
|
f"Configuration file is empty: {self.config_file}\n"
|
|
"Please ensure the file contains valid YAML configuration."
|
|
)
|
|
|
|
# Expand environment variables if enabled
|
|
if self.expand_env_vars:
|
|
self.config_data = self._expand_env_vars(raw_config)
|
|
else:
|
|
self.config_data = raw_config
|
|
|
|
self.root = self.config_data # Alias for compatibility
|
|
|
|
# Validate configuration if enabled
|
|
if self.validate_on_load:
|
|
try:
|
|
ConfigValidator.validate_config(self.config_data)
|
|
except ValueError as e:
|
|
raise ConfigurationError(f"Configuration validation failed: {e}")
|
|
|
|
except yaml.YAMLError as e:
|
|
raise ConfigurationError(f"Invalid YAML syntax: {e}")
|
|
except ConfigurationError:
|
|
raise
|
|
except Exception as e:
|
|
if isinstance(e, FileNotFoundError):
|
|
raise
|
|
raise ConfigurationError(f"Failed to load configuration: {e}")
|
|
|
|
def get_config(self) -> Dict:
|
|
"""Get full configuration dictionary.
|
|
|
|
Returns:
|
|
Configuration dictionary
|
|
"""
|
|
return self.config_data
|
|
|
|
def getConfig(self) -> Dict:
|
|
"""Get full configuration dictionary (camelCase alias).
|
|
|
|
Returns:
|
|
Configuration dictionary
|
|
"""
|
|
return self.get_config()
|
|
|
|
def get(self, key_path: str, default: Any = None) -> Any:
|
|
"""Get configuration value by dot-separated path.
|
|
|
|
Args:
|
|
key_path: Dot-separated key path (e.g., 'Config.Core.LogLevel')
|
|
default: Default value if key not found
|
|
|
|
Returns:
|
|
Configuration value or default
|
|
"""
|
|
keys = key_path.split(".")
|
|
value = self.config_data
|
|
|
|
for key in keys:
|
|
if isinstance(value, dict) and key in value:
|
|
value = value[key]
|
|
else:
|
|
return default
|
|
|
|
return value
|
|
|
|
def _expand_env_vars(self, obj: Any) -> Any:
|
|
"""Recursively expand environment variables in configuration object.
|
|
|
|
Args:
|
|
obj: Configuration object (dict, list, str, etc.)
|
|
|
|
Returns:
|
|
Configuration with expanded environment variables
|
|
"""
|
|
if isinstance(obj, dict):
|
|
return {k: self._expand_env_vars(v) for k, v in obj.items()}
|
|
|
|
elif isinstance(obj, list):
|
|
return [self._expand_env_vars(item) for item in obj]
|
|
|
|
elif isinstance(obj, str):
|
|
return self._expand_string(obj)
|
|
|
|
else:
|
|
return obj
|
|
|
|
def _expand_string(self, value: str) -> str:
|
|
"""Expand environment variables in string (${VAR} or ${VAR:-default}).
|
|
|
|
Args:
|
|
value: String potentially containing environment variables
|
|
|
|
Returns:
|
|
String with expanded environment variables
|
|
|
|
Raises:
|
|
ConfigurationError: If required environment variable is not set
|
|
"""
|
|
|
|
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)
|
|
var_name = var_name.strip()
|
|
default = default.strip()
|
|
return os.environ.get(var_name, default)
|
|
else:
|
|
var_name = var_expr.strip()
|
|
if var_name not in os.environ:
|
|
raise ConfigurationError(
|
|
f"Required environment variable not set: {var_name}\n"
|
|
f"Set it with: export {var_name}=<value>"
|
|
)
|
|
return os.environ[var_name]
|
|
|
|
return self.ENV_VAR_PATTERN.sub(replacer, value)
|