read_conf.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. # -*- coding: utf-8 -*-
  2. # @Time : 2024/6/7
  3. # @Author : 魏志亮
  4. import os
  5. import yaml
  6. from typing import Any, Optional, Dict
  7. def load_yaml_config(file_path: str, encoding: str = 'utf-8') -> Dict[str, Any]:
  8. """
  9. 加载YAML配置文件
  10. Args:
  11. file_path: YAML文件路径
  12. encoding: 文件编码,默认为utf-8
  13. Returns:
  14. 解析后的配置字典
  15. Raises:
  16. FileNotFoundError: 文件不存在时抛出
  17. yaml.YAMLError: YAML解析错误时抛出
  18. """
  19. try:
  20. with open(file_path, 'r', encoding=encoding) as f:
  21. data = yaml.safe_load(f)
  22. # 确保返回字典类型,防止YAML文件为空时返回None
  23. return data if isinstance(data, dict) else {}
  24. except FileNotFoundError:
  25. raise FileNotFoundError(f"配置文件不存在: {file_path}")
  26. except yaml.YAMLError as e:
  27. raise yaml.YAMLError(f"YAML解析错误: {e}")
  28. def get_config_value(config: Dict[str, Any], key: str, default: Optional[Any] = None) -> Any:
  29. """
  30. 从配置字典中安全地获取值
  31. Args:
  32. config: 配置字典
  33. key: 配置键名
  34. default: 默认值,当键不存在或值为None时返回
  35. Returns:
  36. 配置值或默认值
  37. """
  38. # 处理config为None的情况
  39. if config is None:
  40. return default
  41. # 支持嵌套键,如 "database.host"
  42. keys = key.split('.')
  43. value = config
  44. for k in keys:
  45. if isinstance(value, dict) and k in value:
  46. value = value[k]
  47. else:
  48. value = None
  49. break
  50. # 如果值为None且提供了默认值,返回默认值
  51. if value is None and default is not None:
  52. return default
  53. return value
  54. def merge_configs(base_config: Dict[str, Any], override_config: Dict[str, Any]) -> Dict[str, Any]:
  55. """
  56. 合并配置字典
  57. Args:
  58. base_config: 基础配置
  59. override_config: 覆盖配置
  60. Returns:
  61. 合并后的配置
  62. """
  63. result = base_config.copy()
  64. for key, value in override_config.items():
  65. if key in result and isinstance(result[key], dict) and isinstance(value, dict):
  66. # 递归合并嵌套字典
  67. result[key] = merge_configs(result[key], value)
  68. else:
  69. # 直接覆盖
  70. result[key] = value
  71. return result
  72. def load_config_with_env(file_path: str, encoding: str = 'utf-8') -> Dict[str, Any]:
  73. """
  74. 加载配置文件并支持环境变量覆盖
  75. Args:
  76. file_path: YAML文件路径
  77. encoding: 文件编码,默认为utf-8
  78. Returns:
  79. 解析后的配置字典
  80. """
  81. # 加载基础配置
  82. base_config = load_yaml_config(file_path, encoding)
  83. # 检查是否有环境变量覆盖
  84. env_prefix = "ETL_"
  85. override_config = {}
  86. for key, value in os.environ.items():
  87. if key.startswith(env_prefix):
  88. # 转换环境变量名到配置键名
  89. config_key = key[len(env_prefix):].lower().replace('_', '.')
  90. # 解析值
  91. if value.lower() == 'true':
  92. parsed_value = True
  93. elif value.lower() == 'false':
  94. parsed_value = False
  95. elif value.isdigit():
  96. parsed_value = int(value)
  97. elif '.' in value and all(part.isdigit() for part in value.split('.')):
  98. parsed_value = float(value)
  99. else:
  100. parsed_value = value
  101. # 构建嵌套配置
  102. keys = config_key.split('.')
  103. current = override_config
  104. for k in keys[:-1]:
  105. if k not in current:
  106. current[k] = {}
  107. current = current[k]
  108. current[keys[-1]] = parsed_value
  109. # 合并配置
  110. if override_config:
  111. base_config = merge_configs(base_config, override_config)
  112. return base_config
  113. # 为了保持向后兼容,保留原函数名(可选)
  114. yaml_conf = load_yaml_config
  115. read_conf = get_config_value