loader.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. 配置加载器
  5. 统一从 config.yaml 加载配置
  6. 支持通过环境变量覆盖
  7. """
  8. import os
  9. import yaml
  10. from typing import Any, Dict, Optional
  11. # 项目根目录
  12. BASE_DIR = os.path.dirname(
  13. os.path.dirname(os.path.abspath(__file__))
  14. )
  15. # 环境变量映射
  16. ENV_MAPPING: Dict[str, str] = {
  17. # MySQL
  18. "MYSQL_HOST": "database.mysql.host",
  19. "MYSQL_PORT": "database.mysql.port",
  20. "MYSQL_USER": "database.mysql.user",
  21. "MYSQL_PASSWORD": "database.mysql.password",
  22. "MYSQL_DATABASE": "database.mysql.database",
  23. "MYSQL_POOL_SIZE": "database.mysql.pool_size",
  24. # Doris
  25. "DORIS_HOST": "database.doris.host",
  26. "DORIS_PORT": "database.doris.port",
  27. "DORIS_USER": "database.doris.user",
  28. "DORIS_PASSWORD": "database.doris.password",
  29. "DORIS_DATABASE": "database.doris.database",
  30. "DORIS_POOL_SIZE": "database.doris.pool_size",
  31. # PostgreSQL
  32. "PGSQL_HOST": "database.pgsql.host",
  33. "PGSQL_PORT": "database.pgsql.port",
  34. "PGSQL_USER": "database.pgsql.user",
  35. "PGSQL_PASSWORD": "database.pgsql.password",
  36. "PGSQL_DATABASE": "database.pgsql.database",
  37. "PGSQL_POOL_SIZE": "database.pgsql.pool_size",
  38. }
  39. # 类型转换
  40. TYPE_CONVERTERS: Dict[str, type] = {
  41. "port": int,
  42. "pool_size": int,
  43. }
  44. def _get_base_dir() -> str:
  45. """获取项目根目录"""
  46. return BASE_DIR
  47. def _load_yaml() -> Dict[str, Any]:
  48. """加载 YAML 配置"""
  49. yaml_path = os.path.join(_get_base_dir(), "config.yaml")
  50. if not os.path.exists(yaml_path):
  51. return {}
  52. with open(yaml_path, "r", encoding="utf-8") as f:
  53. content = yaml.safe_load(f)
  54. return content if content else {}
  55. def _set_nested(config: Dict[str, Any], path: str, value: str) -> None:
  56. """设置嵌套配置"""
  57. keys = path.split(".")
  58. current = config
  59. for key in keys[:-1]:
  60. if key not in current:
  61. current[key] = {}
  62. current = current[key]
  63. last_key = keys[-1]
  64. # 类型转换
  65. for type_key, converter in TYPE_CONVERTERS.items():
  66. if type_key in last_key.lower() and isinstance(value, str):
  67. try:
  68. value = converter(value)
  69. except (ValueError, TypeError):
  70. pass
  71. current[last_key] = value
  72. def load_config() -> Dict[str, Any]:
  73. """
  74. 加载配置
  75. 优先级: 环境变量 > config.yaml
  76. Returns:
  77. 配置字典
  78. """
  79. # 1. 加载 YAML 作为基础
  80. config = _load_yaml()
  81. # 2. 环境变量覆盖
  82. for env_key, config_path in ENV_MAPPING.items():
  83. env_value = os.getenv(env_key)
  84. if env_value is not None:
  85. _set_nested(config, config_path, env_value)
  86. return config
  87. # 全局缓存
  88. _config_cache: Optional[Dict[str, Any]] = None
  89. def get_config(key: Optional[str] = None, default: Any = None) -> Any:
  90. """
  91. 获取配置值
  92. Args:
  93. key: 配置路径,如 "database.mysql.host",None 返回全部
  94. default: 默认值
  95. Returns:
  96. 配置值
  97. """
  98. global _config_cache
  99. if _config_cache is None:
  100. _config_cache = load_config()
  101. if key is None:
  102. return _config_cache
  103. keys = key.split(".")
  104. current = _config_cache
  105. for k in keys:
  106. if isinstance(current, dict) and k in current:
  107. current = current[k]
  108. else:
  109. return default
  110. return current
  111. def reload_config() -> Dict[str, Any]:
  112. """重新加载配置"""
  113. global _config_cache
  114. _config_cache = None
  115. return load_config()