import os
from typing import Dict, Any, Optional, List
import yaml
CONFIG_ENV_VAR = "CONCH_SDK_CONFIG"
CONFIG_FILE_NAME = "sdk-config.yaml"
SYSTEM_CONFIG_DIR = "/etc/conch"
SYSTEM_CONFIG_PATH = os.path.join(SYSTEM_CONFIG_DIR, CONFIG_FILE_NAME)
REPO_CONFIG_DIR_NAME = "config"
REPO_CONFIG_REL_PATH = os.path.join(REPO_CONFIG_DIR_NAME, CONFIG_FILE_NAME)
def get_package_template_path() -> str:
here = os.path.dirname(__file__)
repo_root = os.path.abspath(os.path.join(here, ".."))
return os.path.join(repo_root, REPO_CONFIG_REL_PATH)
def _candidate_paths_from_env() -> List[str]:
paths: List[str] = []
env_path = os.getenv(CONFIG_ENV_VAR)
if env_path:
paths.append(env_path)
return paths
def _candidate_paths_from_user() -> List[str]:
paths: List[str] = []
home = os.path.expanduser("~")
xdg_config_home = os.getenv("XDG_CONFIG_HOME", os.path.join(home, ".config"))
user_path = os.path.join(xdg_config_home, "conch", CONFIG_FILE_NAME)
paths.append(user_path)
return paths
def _candidate_paths_from_system() -> List[str]:
return [SYSTEM_CONFIG_PATH]
def _candidate_paths_from_repo_config() -> List[str]:
return [get_package_template_path()]
def _find_default_config_path() -> str:
"""
find the default config path in the order of priority:
1. environment variable CONCH_SDK_CONFIG
2. user-level config ~/.config/conch/sdk-config.yaml (or $XDG_CONFIG_HOME)
3. system-level config /etc/conch/sdk-config.yaml
4. repo-level default config config/sdk-config.yaml
"""
search_order = (
_candidate_paths_from_env()
+ _candidate_paths_from_user()
+ _candidate_paths_from_system()
+ _candidate_paths_from_repo_config()
)
for path in search_order:
if path and os.path.exists(path):
return path
raise FileNotFoundError(
"Conch SDK configuration file not found. "
"Tried (in order):\n"
" 1) $CONCH_SDK_CONFIG\n"
" 2) $XDG_CONFIG_HOME/conch/sdk-config.yaml (or ~/.config/conch/sdk-config.yaml)\n"
" 3) /etc/conch/sdk-config.yaml\n"
" 4) <repo>/config/sdk-config.yaml\n"
"Please create one based on the default template (config/sdk-config.yaml) "
"or run the CLI to initialize /etc configuration."
)
def warn_onproxy():
if os.environ.get('http_proxy') or os.environ.get('HTTP_PROXY'):
print('warning: http proxy enabled')
if os.environ.get('https_proxy') or os.environ.get('HTTPS_PROXY'):
print('warning: https proxy enabled')
def load_config(config_path: Optional[str] = None) -> Dict[str, Any]:
"""
load config:
- when config_path is explicitly provided, use it;
- otherwise, load the config from the default path in the order of priority.
"""
resolved_path = config_path or _find_default_config_path()
if not os.path.exists(resolved_path):
raise FileNotFoundError(f"Configuration file not found: {resolved_path}")
with open(resolved_path, "r", encoding="utf-8") as f:
try:
cfg = yaml.safe_load(f)
except yaml.YAMLError as e:
raise ValueError(f"Failed to parse configuration file '{resolved_path}': {e}")
if not isinstance(cfg, dict):
raise ValueError(
f"Configuration file '{resolved_path}' must contain a YAML mapping at the top level."
)
warn_onproxy()
return cfg