Load configuration, merging user file over built-in defaults.
Parameters
path:
Explicit path to a config.yaml file. When None the function
searches :data:_CONFIG_SEARCH_PATHS in order and uses the first file
it finds. If no file is found the built-in defaults are used as-is.
Source code in src/config.py
| def load(path: str | Path | None = None) -> Config:
"""Load configuration, merging user file over built-in defaults.
Parameters
----------
path:
Explicit path to a ``config.yaml`` file. When *None* the function
searches :data:`_CONFIG_SEARCH_PATHS` in order and uses the first file
it finds. If no file is found the built-in defaults are used as-is.
"""
data = dict(_DEFAULTS)
# Resolve the file to load
config_file: Path | None = None
if path is not None:
config_file = Path(path)
else:
for candidate in _CONFIG_SEARCH_PATHS:
if candidate.exists():
config_file = candidate
break
if config_file is not None and config_file.exists():
with config_file.open("r", encoding="utf-8") as fh:
user_data = yaml.safe_load(fh) or {}
data = _deep_merge(data, user_data)
# Allow environment variable to override API key (local servers may not
# need one; only look it up when api_key_env is explicitly set).
openai_key_env = data["openai"].get("api_key_env", "")
if openai_key_env and os.environ.get(openai_key_env):
data["openai"]["api_key"] = os.environ[openai_key_env]
else:
data["openai"].setdefault("api_key", "")
cfg = Config(data)
# Check config file permissions after building the Config so we can read
# the security.check_file_permissions setting from merged data.
if cfg.security.get("check_file_permissions", True) and config_file is not None:
from src.security import check_path_permissions
check_path_permissions(config_file, label="config file")
return cfg
|