|
1 | 1 | import os |
2 | | -from typing import Dict, List, Literal, Optional |
| 2 | +from typing import Any, Dict, List, Literal, Optional, Tuple |
3 | 3 |
|
4 | 4 | import yaml |
5 | 5 | from pydantic import Field |
| 6 | +from pydantic.fields import FieldInfo |
6 | 7 | from pydantic_settings import ( |
7 | 8 | BaseSettings, |
8 | 9 | PydanticBaseSettingsSource, |
9 | 10 | SettingsConfigDict, |
10 | 11 | ) |
11 | 12 |
|
12 | 13 |
|
| 14 | +class YamlConfigSettingsSource(PydanticBaseSettingsSource): |
| 15 | + """Custom settings source that loads configuration from a YAML file. |
| 16 | +
|
| 17 | + The file path is determined by the SHS_MCP_CONFIG environment variable, |
| 18 | + defaulting to 'config.yaml' if not set. |
| 19 | + """ |
| 20 | + |
| 21 | + def get_field_value( |
| 22 | + self, field: FieldInfo, field_name: str |
| 23 | + ) -> Tuple[Any, str, bool]: |
| 24 | + # Not used for this implementation |
| 25 | + return None, field_name, False |
| 26 | + |
| 27 | + def __call__(self) -> Dict[str, Any]: |
| 28 | + """Load and return the YAML configuration data.""" |
| 29 | + config_path = os.getenv("SHS_MCP_CONFIG", "config.yaml") |
| 30 | + is_explicitly_set = "SHS_MCP_CONFIG" in os.environ |
| 31 | + |
| 32 | + if not os.path.exists(config_path): |
| 33 | + # If the config file was explicitly specified but doesn't exist, fail fast |
| 34 | + if is_explicitly_set: |
| 35 | + raise FileNotFoundError( |
| 36 | + f"Config file not found: {config_path}\n" |
| 37 | + f"Specified via: SHS_MCP_CONFIG environment variable" |
| 38 | + ) |
| 39 | + # If using default and it doesn't exist, return empty (will use defaults) |
| 40 | + return {} |
| 41 | + |
| 42 | + with open(config_path, "r") as f: |
| 43 | + config_data = yaml.safe_load(f) |
| 44 | + |
| 45 | + return config_data or {} |
| 46 | + |
| 47 | + |
13 | 48 | class AuthConfig(BaseSettings): |
14 | 49 | """Authentication configuration for the Spark server.""" |
15 | 50 |
|
@@ -77,4 +112,16 @@ def settings_customise_sources( |
77 | 112 | dotenv_settings: PydanticBaseSettingsSource, |
78 | 113 | file_secret_settings: PydanticBaseSettingsSource, |
79 | 114 | ) -> tuple[PydanticBaseSettingsSource, ...]: |
80 | | - return env_settings, dotenv_settings, init_settings, file_secret_settings |
| 115 | + # Precedence order (highest to lowest): |
| 116 | + # 1. Environment variables |
| 117 | + # 2. .env file |
| 118 | + # 3. YAML config file (from SHS_MCP_CONFIG) |
| 119 | + # 4. Init settings (constructor arguments) |
| 120 | + # 5. File secrets |
| 121 | + return ( |
| 122 | + env_settings, |
| 123 | + dotenv_settings, |
| 124 | + YamlConfigSettingsSource(settings_cls), |
| 125 | + init_settings, |
| 126 | + file_secret_settings, |
| 127 | + ) |
0 commit comments