Skip to main content

yaml-config-loader

轻量级 Python 配置加载工具包,提供类似 Spring Boot 的配置管理能力。

功能特性

  • YAML 配置加载:自动递归查找 application.yml,全局缓存配置
  • 环境变量解析:yml 中的 ${ENV_VAR} / ${ENV_VAR:-default} 自动替换为环境变量值
  • @configuration_properties:类似 Spring Boot 的配置属性绑定,支持嵌套 dataclass 自动转换
  • 标量类型强转:按字段注解将配置值强转为 str/int/float/bool
  • List[X] 泛型转换:Optional[List[SkillConfig]] 递归转换为 dataclass 实例列表
  • @Value:类似 Spring Boot 的 @Value("${...}"),单个配置值注入,支持嵌套路径和默认值
  • aliases 别名映射:yml 中的数字 key(如 "1002")、关键字等映射到合法的 Python 属性名
  • aliases 嵌套路径:aliases 值含 .(如 "otherMath.100.a.100")时视为完整层级路径,严格按 yml 层级钻取,解决不同层级同名 key 的歧义
  • 字段级 yml_key 显式声明:嵌套 dataclass 字段用 field(metadata={"yml_key": "100"}) 精确声明映射,变量名任意,不依赖命名约定
  • 自动 kebab-case → snake_case 转换:yml 中的 api-key 自动映射到 Python 的 api_key,无需手动配置
  • @component 单例容器:轻量级 IoC 容器,线程安全单例,自动注册和获取 Bean
  • DotDict 点访问字典:支持 config.user.name 方式访问嵌套字典
  • 零配置:开箱即用,无需复杂设置

安装

pip install yaml-config-loader

v1.1.0 包名变更说明:import 包名已从 config_loader 更名为 yaml_config_loader,与 PyPI 包名保持一致,这样 PyCharm 等 IDE 能自动识别并提示"安装软件包"。

旧代码 from config_loader import ... 仍然可用(兼容别名包),但推荐新代码使用 from yaml_config_loader import ...。

更新日志

v1.3.0(最新)

新增功能:

  1. @Value 单值注入:类似 Spring Boot @Value("${...}"),支持两种用法
    • Annotated + @value_config(推荐,保留类型注解)
    • 描述符方式(简洁,无需类型注解)
  2. aliases 别名映射:解决 yml 中数字 key(如 "1002")、Python 关键字(如 class)无法作为属性名的问题
  3. 自动 kebab-case → snake_case 转换:yml 中的 api-key 自动映射到 Python 的 api_key,嵌套对象也自动转换,无需手动写 aliases
  4. aliases 嵌套路径:aliases 值含 .(如 "b_a": "otherMath.100.a.100")视为相对前缀的完整层级路径,严格按 yml 层级钻取绑定,不同层级的同名数字 key 不会混淆
  5. 字段级 yml_key 显式声明:嵌套 dataclass 字段用 field(metadata={"yml_key": "100"}) 精确声明对应的 yml key,变量名任意命名,不依赖 X_100 命名约定

修复的问题:

  1. ${ENV_VAR} 环境变量不解析:yaml_loader 新增 _resolve_env_vars 递归解析 ${ENV_VAR} 与 ${ENV_VAR:-default},加载与回退读取均生效,不再把字面量传给下游
  2. 缓存为空时回退读文件依赖相对路径 + 静默吞错:回退改为 find_yaml() 项目级搜索(绝对路径),不依赖 CWD;读取失败/文件未找到均输出 logger.warning,不再静默绑成 {}
  3. 绑定失败完全静默:前缀不存在、字段缺 key、文件未找到、aliases 目标属性未声明、嵌套路径不存在,五种情况均输出 logger.warning 告警
  4. 不做标量类型强转:新增 _coerce_scalar,按字段注解将配置值强转为 str/int/float/bool(bool 支持 "false"/"0"/"no"/"off" 等字符串),注解为 float 时 0.8 不再变字符串
  5. List[dataclass] 泛型不转换:新增 List[X] 递归转换,Optional[List[SkillConfig]] 实际拿到的是 List[SkillConfig] 实例列表,注解与运行时类型一致
  6. @component 返回工厂函数:改为返回类本身 + __new__ 单例,isinstance、继承、get_type_hints、反射全部正常,get_bean() 直接返回实例
  7. functools.wraps 污染类属性:移除 @component 中的 functools.wraps(cls);@configuration_properties 使用 functools.wraps(cls, updated=[]) 避免类 __dict__ 复制
  8. init_config import 即执行 + verbose 打印:init_config(verbose=False) 默认静默(print 仅在 verbose=True),日志统一走 logging 体系,不污染控制台
  9. 单例非线程安全 / 注册表无锁:__new__ 双检锁 + 注册表锁保证线程安全;同名类覆盖注册表时输出 logger.warning 提示
  10. _get_caller_dir 依赖 inspect.stack:支持 set_config_search_dir() 显式指定搜索起点 + 调用方目录缓存(同进程只计算一次),inspect.stack 仅兜底且失败回退 cwd,兼容 PyInstaller 等场景
  11. get_session 直接崩:非标识符 yml key(纯数字 "1003" 等)绑定到实例 __dict__,getattr(self, "1003") 动态访问可用,get_session 不再崩溃

优化:

  • get_bean() 支持直接传类对象 get_bean(MyService),不再只能传类名字符串
  • DotDict 构造函数递归转换嵌套 dict,DotDict(config) 直接支持点访问

v1.1.0

  • import 包名从 config_loader 更名为 yaml_config_loader(与 PyPI 包名一致)
  • 保留 config_loader 兼容别名包,老用户代码无需修改

v1.0.0

  • 初始版本:YAML 配置加载、@configuration_properties、@component、DotDict

快速开始

1. 创建 application.yml

user:
  name: "张三"
  age: 25
  email: "zhangsan@example.com"
  address:
    city: "北京"
    street: "长安街"
  # kebab-case(连字符)自动转换为 snake_case(下划线)
  api-key: "abc123"
  server-timeout: 30
  # 纯数字 key 自动后缀匹配(如 _1002 / item_1002),也可用 aliases 显式声明
  "1002": "编号1002的值"

server:
  port: 8080
  host: "localhost"

2. 初始化配置

from yaml_config_loader import init_config

# 一行初始化配置(类似 load_dotenv(find_dotenv()))
# 自动递归查找 application.yml 并加载到全局缓存
init_config()

3. 使用 @configuration_properties 绑定配置

from dataclasses import dataclass
from typing import Optional
from yaml_config_loader import configuration_properties, component, DotDict

@component
@configuration_properties(prefix="user")
@dataclass
class UserConfig:
    name: str = None
    age: int = None
    email: str = None
    address: Optional[DotDict] = None  # 嵌套配置自动转为 DotDict

    def show(self):
        print(f"姓名: {self.name}")
        print(f"年龄: {self.age}")
        print(f"城市: {self.address.city}")  # 点访问嵌套配置

user = UserConfig()
user.show()

4. 嵌套 dataclass 自动转换

from dataclasses import dataclass
from typing import Optional
from yaml_config_loader import configuration_properties

@dataclass
class Address:
    city: str = None
    street: str = None

@configuration_properties(prefix="user")
@dataclass
class UserConfig:
    name: str = None
    address: Optional[Address] = None  # 自动转为 Address 实例

user = UserConfig()
print(user.address.city)  # 北京

5. 自动 kebab-case → snake_case 转换

yml 中的 api-key(连字符)自动映射到 Python 的 api_key(下划线),不需要写 aliases,嵌套对象也自动转换。

yml 配置:

user:
  api-key: "abc123"
  server-timeout: 30
  nested:
    my-name: "嵌套配置"

Python 代码:

from dataclasses import dataclass
from yaml_config_loader import configuration_properties

@configuration_properties(prefix="user")
@dataclass
class UserConfig:
    # yml 中的 api-key → 自动映射到 api_key
    api_key: str = None
    # yml 中的 server-timeout → 自动映射到 server_timeout
    server_timeout: int = None
    # 嵌套对象中的 my-name → 自动映射到 my_name
    nested: dict = None

user = UserConfig()
print(user.api_key)          # abc123
print(user.server_timeout)   # 30
print(user.nested.my_name)   # 嵌套配置

转换规则:

yml 中的 key Python 属性名 是否需要 aliases
api-key api_key ❌ 自动转换
server-timeout server_timeout ❌ 自动转换
my-name my_name ❌ 自动转换
"1002"(纯数字) _1002 / item_1002 等任意 X_1002 形式 ❌ 自动后缀匹配;也可用 aliases / metadata 显式声明
"class"(关键字) class_type ✅ 需要 aliases

6. aliases 别名映射

当 yml 中的 key 是纯数字(如 "1002")或 Python 关键字(如 class)时,无法作为 Python 属性名,用 aliases 参数做映射。

yml 配置:

user:
  "1002": "编号1002的值"
  "1003": 1003
  "class": "用户类"

Python 代码:

from dataclasses import dataclass
from yaml_config_loader import configuration_properties

@configuration_properties(
    prefix="user",
    aliases={
        "item_1002": "1002",   # Python 属性名 item_1002 ← yml key "1002"
        "item_1003": "1003",   # Python 属性名 item_1003 ← yml key "1003"
        "class_type": "class",  # Python 属性名 class_type ← yml key "class"
    },
)
@dataclass
class UserConfig:
    item_1002: str = None
    item_1003: int = None
    class_type: str = None

user = UserConfig()
print(user.item_1002)   # 编号1002的值
print(user.item_1003)   # 1003
print(user.class_type)   # 用户类

aliases 格式:

aliases={
    "Python属性名": "yml中的key名",
}

优先级:

  1. aliases 显式映射(优先级最高,同层 key)
  2. aliases 嵌套路径(值含 .,从前缀向下钻取完整层级)
  3. 自动 kebab-case → snake_case 转换
  4. 纯数字 key 后缀匹配("100" 匹配任意 X_100 形式字段)
  5. 直接用 yml key

6.1 aliases 嵌套路径(完整层级映射)

当 yml 中不同层级出现同名 key(如 otherMath.100 与 otherMath.100.a.100 都有 "100")时,同层 key 别名无法区分。aliases 的值含 . 时视为相对前缀的完整层级路径,严格按 yml 层级钻取;变量名(键)可任意命名,被映射者(值)必须写清 yml 层级。

yml 配置:

ai:
  otherMath:
    "100":
      model: qwen3-max
      a:
        "100":
          b: qwen3-max-plus

Python 代码:

from dataclasses import dataclass
from typing import Optional
from yaml_config_loader import configuration_properties

@dataclass
class BConfig:
    b: str = None

@configuration_properties(prefix="ai", aliases={
    # 变量名任意;值写清 yml 完整层级(相对 ai 前缀),精确指向 a 层下的 "100"
    "b_a": "otherMath.100.a.100",
})
class AiConfig:
    b_a: Optional[BConfig] = None

conf = AiConfig()
print(conf.b_a.b)   # qwen3-max-plus(精确取到 a 层下的 "100",不会与 otherMath 层的 "100" 混淆)

6.2 字段级 yml_key 显式声明(嵌套 dataclass)

嵌套 dataclass 内部也可以用 field(metadata={"yml_key": "..."}) 显式声明字段对应的 yml key:变量名任意命名,映射精确锁定当前层级,不依赖 X_100 之类的命名约定,不同层级出现同名 key 也不会错乱。

yml 配置:

ai:
  otherMath:
    "100":
      model: qwen3-max
      a:
        "100":
          b: qwen3-max-plus

Python 代码:

from dataclasses import dataclass, field
from typing import Optional
from yaml_config_loader import configuration_properties

@dataclass
class BConfig:
    b: str = None

@dataclass
class AConfig:
    # 变量名任意;yml_key 精确指向当前层级(a 层)的 "100"
    b_a: Optional[BConfig] = field(default=None, metadata={"yml_key": "100"})

@dataclass
class ModelConfig:
    model: str = None
    a: Optional[AConfig] = None

@dataclass
class OtherMathConfig:
    item_100: Optional[ModelConfig] = None   # 数字 key "100" 自动映射到 item_100

@configuration_properties(prefix="ai")
class AiConfig:
    otherMath: Optional[OtherMathConfig] = None

conf = AiConfig()
print(conf.otherMath.item_100.a.b_a.b)   # qwen3-max-plus

嵌套 dataclass 内字段的匹配优先级:

  1. metadata={"yml_key": "..."} 显式声明(最高,精确匹配当前层级的 key,含 int 型数字 key)
  2. 自动 kebab-case → snake_case 转换
  3. 纯数字 key 后缀匹配("100" 匹配任意 X_100 形式字段)
  4. 直接用 yml key

7. 使用 @Value 注入单个配置值

类似 Spring Boot 的 @Value("${...}"),只读取单个配置值,更轻量灵活。

推荐用法(Annotated + @value_config,保留类型注解):

from typing import Annotated
from yaml_config_loader import init_config, Value, value_config

init_config()

@value_config
class AppConfig:
    # 基本用法:读取 user.name
    name: Annotated[str, Value(key="user.name", default="未知用户")] = None

    # 带默认值:配置不存在时返回 18
    age: Annotated[int, Value(key="user.age", default=18)] = None

    # 嵌套路径:支持点分隔,如 user.address.city
    city: Annotated[str, Value(key="user.address.city", default="未知城市")] = None

    # 读取其他前缀的配置
    server_port: Annotated[int, Value(key="server.port", default=8080)] = None

app = AppConfig()
print(app.name)         # 张三
print(app.age)          # 25
print(app.city)         # 北京
print(app.server_port)  # 8080

简洁用法(描述符方式,无需类型注解):

from yaml_config_loader import init_config, Value

init_config()

class AppConfig:
    name = Value("user.name")
    age = Value("user.age", default=18)
    city = Value("user.address.city", default="未知城市")

说明:Python 语法不支持 @Value 直接写在类属性上(@装饰器 只能用于 def 和 class),因此用 Annotated + @value_config 的方式实现等价效果,同时保留类型提示。

@Value 与 @configuration_properties 的区别:

特性 @Value @configuration_properties
适用场景 只需要几个配置值 需要绑定一整组配置
用法 Annotated[str, Value(key="...")] + @value_config 类装饰器 @configuration_properties(prefix="user")
默认值 支持 default=xxx 字段默认值
嵌套路径 支持点分隔 user.address.city 自动绑定嵌套 dataclass
类型注解 ✅ 保留 ✅ 配合 @dataclass
只读 ✅ 禁止直接赋值 ❌ 可修改
kebab-case 自动转换 ❌ 需手动写完整路径 ✅ 自动转换
aliases 支持 ❌ 需手动写完整路径 ✅ 支持

8. 使用单例容器

from yaml_config_loader import component, get_bean

@component
class MyService:
    def hello(self):
        return "Hello, World!"

# 获取单例实例(支持直接传类对象)
service = get_bean(MyService)
print(service.hello())  # Hello, World!

# 也支持传类名字符串
service2 = get_bean("MyService")
print(service is service2)  # True(单例)

9. 直接使用 YAML 加载工具

from yaml_config_loader import load_yaml, get_yaml_config, find_yaml

# 自动查找并加载 application.yml
load_yaml()

# 获取全局配置
config = get_yaml_config()
print(config["user"]["name"])  # 张三

# 查找配置文件路径
path = find_yaml()
print(path)

API 参考

装饰器/类

装饰器/类 说明
@configuration_properties(prefix="xxx", aliases={...}) 绑定 YAML 配置到类属性,支持 aliases 别名映射(含嵌套路径)和自动 kebab-case 转换
@value_config 类装饰器,扫描 Annotated 中的 Value 元数据,自动创建描述符
@component 注册为单例 Bean
Value(key="...", default=xxx) 单个配置值注入描述符(类似 Spring Boot @Value)

函数

函数 说明
init_config(filename="application.yml", verbose=False) 一行初始化配置(查找 + 加载,默认静默,print 仅在 verbose=True 时输出)
set_config_search_dir(dir_path) 显式指定配置文件搜索起点目录(适用于 PyInstaller 打包等 inspect.stack 不可靠场景)
load_yaml(path=None) 加载 YAML 配置到全局缓存
find_yaml(filename="application.yml") 递归查找 application.yml 文件路径
get_yaml_config() 获取全局配置字典
get_loaded_yaml_path() 获取已加载的配置文件路径
get_bean(cls_or_name) 获取单例 Bean 实例(支持类对象或类名)
list_beans() 列出所有已注册的 Bean
to_dot_dict(d, kebab_to_snake=False) 将普通字典转为 DotDict,可选 kebab-case 转换

类

类 说明
DotDict 支持点访问的字典,构造时递归转换嵌套 dict
Value 单个配置值注入描述符,支持嵌套路径和默认值,只读

配置文件查找规则

find_yaml() 会按以下顺序递归查找:

  1. 用户显式指定的目录(调用 set_config_search_dir() 时,优先级最高)
  2. 当前脚本所在目录
  3. 当前目录下的常见配置子目录(config/、conf/、resources/ 等)
  4. 定位项目根目录(通过 .git、pyproject.toml 等标记)
  5. 在整个项目范围内递归搜索(自动排除 .venv、__pycache__、.git 等目录)

支持的文件名:application.yml、application.yaml

搜索起点说明:调用方脚本目录会缓存(同进程只计算一次),inspect.stack() 仅作兜底(失败时回退 cwd)。若运行环境特殊(PyInstaller 打包、C 扩展调用栈),建议显式调用 set_config_search_dir() 指定搜索起点。

环境变量解析

加载配置时自动递归解析 ${ENV_VAR} 环境变量引用,支持默认值语法 ${ENV_VAR:-default}。

ai:
  openai:
    api_key: ${ALIYUN_API_KEY}          # 读取环境变量 ALIYUN_API_KEY
    timeout: ${AI_TIMEOUT:-30}          # 环境变量不存在时使用默认值 30
  • 环境变量存在:替换为环境变量值
  • 环境变量不存在但有默认值:使用默认值
  • 环境变量不存在且无默认值:保留原字面量(与 Spring Boot 行为一致)

许可证

MIT License

Metadata

Release files for yaml-config-loader 1.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for yaml-config-loader 1.3.0
File Size Uploaded
yaml_config_loader-1.3.0.tar.gz 27.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for yaml-config-loader 1.3.0
File Interpreter ABI Platform
yaml_config_loader-1.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 53.2 kB

Release files / yaml_config_loader-1.3.0.tar.gz

Download URL yaml_config_loader-1.3.0.tar.gz
Size 27.9 kB
Tags Source
SHA-256 checksum
How to use checksums
1514d1660f1ffe2f98393ceff2078bf90580f749b3e56dfac240aa04b8b8dafd
BLAKE2b-256 checksum
How to use checksums
54cb09f0baf79ee3110eef8a06262f8eb1ccbb3cea285cc9465a0bf1076b1012
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.7

Release files / yaml_config_loader-1.3.0-py3-none-any.whl

Download URL yaml_config_loader-1.3.0-py3-none-any.whl
Size 25.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ac0a47cdf956c91d5275137e22eeb314e6b3cd08bd21783d4a275e816a0c8746
BLAKE2b-256 checksum
How to use checksums
fdc34fc67b9a0761cd75f796cfc3334b0e0734a3fc46bdb7f9731a059da4b7cf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.7

Release history Release notifications | RSS feed

This release

1.3.0 This release

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page