Daemon configuration manager. Loads from config file, falls back to defaults. Provides get/set methods for configuration access.
| 50 | |
| 51 | |
| 52 | class DaemonConfig: |
| 53 | """ |
| 54 | Daemon configuration manager. |
| 55 | |
| 56 | Loads from config file, falls back to defaults. |
| 57 | Provides get/set methods for configuration access. |
| 58 | """ |
| 59 | |
| 60 | def __init__(self, config_file: Path = CONFIG_FILE): |
| 61 | self.config_file = config_file |
| 62 | self._config: Dict[str, Any] = self._load_config() |
| 63 | |
| 64 | def _load_config(self) -> Dict[str, Any]: |
| 65 | """Load configuration from file or return defaults.""" |
| 66 | if self.config_file.exists(): |
| 67 | try: |
| 68 | with open(self.config_file, 'r') as f: |
| 69 | user_config = json.load(f) |
| 70 | # Merge with defaults |
| 71 | return self._merge_configs(DEFAULT_CONFIG, user_config) |
| 72 | except (json.JSONDecodeError, IOError) as e: |
| 73 | print(f"Warning: Could not load config file: {e}") |
| 74 | return DEFAULT_CONFIG.copy() |
| 75 | return DEFAULT_CONFIG.copy() |
| 76 | |
| 77 | def _merge_configs(self, base: Dict, override: Dict) -> Dict: |
| 78 | """Recursively merge override config into base config.""" |
| 79 | result = base.copy() |
| 80 | for key, value in override.items(): |
| 81 | if key in result and isinstance(result[key], dict) and isinstance(value, dict): |
| 82 | result[key] = self._merge_configs(result[key], value) |
| 83 | else: |
| 84 | result[key] = value |
| 85 | return result |
| 86 | |
| 87 | def get(self, *keys: str, default: Any = None) -> Any: |
| 88 | """ |
| 89 | Get a configuration value by nested keys. |
| 90 | |
| 91 | Example: config.get("daemon", "log_level") |
| 92 | """ |
| 93 | current = self._config |
| 94 | for key in keys: |
| 95 | if isinstance(current, dict) and key in current: |
| 96 | current = current[key] |
| 97 | else: |
| 98 | return default |
| 99 | |
| 100 | # Expand tilde paths for string values |
| 101 | if isinstance(current, str) and current.startswith("~"): |
| 102 | return str(Path(current).expanduser()) |
| 103 | return current |
| 104 | |
| 105 | def set(self, *keys: str, value: Any): |
| 106 | """ |
| 107 | Set a configuration value by nested keys. |
| 108 | |
| 109 | Example: config.set("daemon", "log_level", value="DEBUG") |