简体中文 English 繁體中文 日本語 Русский
本文为静态镜像,内容以交互版为准 在交互式文档中心打开 →

ErisPulse.Core.config 模块


模块概述

ErisPulse 配置中心

集中管理所有配置项,避免循环导入问题 提供自动补全缺失配置项的功能 添加内存缓存和延迟写入机制以提高性能 基于 tomlkit 实现注释保留写入:配置文件中的注释与键顺序在任何框架写入后均不丢失 配置写入采用"唯一临时文件 + fsync + 原子替换"(os.replace): 进程被杀(OOM / SIGKILL)或断电时磁盘上的配置文件要么是完整旧内容、要么是完整新内容, 不会出现空文件或半写状态;临时文件名按进程唯一,多实例共享配置目录时不再互相争抢踩踏

提示

  1. 使用 getConfig(key) / setConfig(key, value) 读写配置
  2. 配置变更可通过生命周期钩子监听: @lifecycle.on("config.set")

函数列表

parse_bool_config(value: Any)

解析配置中的布尔值

提示 支持的值:

  • True: True, 1, "true", "True", "1", "yes", "Yes", "on", "On"
  • False: False, 0, "false", "False", "0", "no", "No", "off", "Off"

json_safe(value: Any, _depth: int = 0)

递归将任意结构转换为可直接 JSON 序列化的等价结构

供 get_topology 等面向 WebUI 的聚合方法保证输出可序列化: dict / list / tuple / set 递归处理;类对象(type)取 __name__;其余不可序列化对象退化为 str() 表示。


类列表

class ConfigManager

ConfigManager 类提供相关功能。

方法列表

__init__(config_file: str = DEFAULT_CONFIG_FILE_PATH)

初始化配置管理器


_start_config_watcher()

启动后台线程定期检查配置文件变化

当用户手动编辑 config.toml 时,后台线程检测到 mtime 变化后 自动重载缓存并发射 config.updated 生命周期事件。

内部方法


_watch_config_file()

记录配置文件的当前 mtime,用于后续检测外部修改

内部方法


_migrate_config()

迁移旧配置文件到新位置

从项目根目录的 config.toml 迁移到 config/config.toml

内部方法


_load_config()

从文件加载配置到缓存

对加载失败按三种状态分别给出可操作的诊断信息:

返回值 (bool): 加载成功(含文件缺失)返回 True;解析/权限等错误返回 False

内部方法


_drop_redundant_dirty_keys()

丢弃与新配置文件内容一致的待写键

配置重载(外部修改)后,部分待写键的值可能已与文件一致, 继续保留会在下次 flush 时用陈旧快照覆盖用户热更新。 仅当待写值已反映在缓存(即文件)中时才丢弃;真正未落盘的写入仍保留。

内部方法


_log_config_error(message: str, level: str = 'error')

将配置加载诊断信息写入日志

内部方法 统一处理 logger 尚未就绪的早期场景,失败时静默忽略。


_malformed_sentinel_path()

跨进程告警冷却哨兵文件路径

位于配置文件同级目录下的隐藏文件,通过其 mtime 实现跨进程去重: 无论 epsdk run 子进程、python main.py 直跑、还是多实例场景, 所有进程共享同一文件系统,自然协调告警频率。

内部方法


_acquire_instance_lock()

尝试以独占方式锁定配置目录的实例锁文件,检测多实例共享配置目录

锁文件位于配置文件同级目录(:data:~.constants.CONFIG_LOCK_FILE_NAME), 进程持锁后全生命周期不释放,由 OS 在进程退出时自动归还—— 无需清理逻辑,进程被强杀也不会留下"幽灵锁"。 锁被占用说明另一个 ErisPulse 实例正在使用同一配置目录, 此时仅记录告警(并发写入可能互相覆盖),不阻塞框架启动。

内部方法


_atomic_write_text(text: str)

原子写入配置文件(唯一临时文件 + fsync + os.replace)

旧实现使用固定名 <config>.tmp 并在 rename 前不落盘,存在两类丢文件场景: 多实例共享配置目录时同名临时文件被对端 truncate / rename(ENOENT 争抢); 进程被杀或断电时 rename 元数据先于数据块落盘(ext4 延迟分配),留下空文件。 现改为:同目录 mkstemp 生成进程唯一临时文件 → 写毕 flush + fsync 强制数据落盘 → os.replace 原子替换目标(POSIX / Windows 均原子); POSIX 下额外 fsync 配置目录,尽力保证断电后替换结果不回退。

内部方法


_set_doc_path(doc: Any, keys: list[str], value: Any)

在 tomlkit 文档树中按点分路径写入值

中间层节点缺失或非表时以空表替换(与 dict 语义一致); 叶子写入保留既有注释与顺序,新键追加至所在节末尾。

内部方法


_doc_to_plain_dict(doc: Any)

将 tomlkit 文档转为 plain dict 缓存

经 body 低层插入的条目不进入容器索引,直接 unwrap() 会丢失; 渲染后重新解析可保证缓存与文件内容严格一致。

内部方法


_flush_config()

将待写入的配置刷新到文件

使用文件锁确保多线程环境下的原子性操作。 基于 tomlkit 在解析出的文档树上做增量修改后整体回写, 文件中已有的注释与键顺序不因框架写入而丢失或重排。

内部方法


_register_atexit()

注册 atexit 钩子,确保进程退出时未持久化的配置被 flush

内部方法


_flush_on_exit()

atexit 回调:进程退出时强制刷新所有脏配置,并清理哨兵文件

内部方法 哨兵文件(.flush_malformed_cooldown)是运行时跨进程去重的临时标记,


_schedule_write()

安排延迟写入

内部方法


_check_cache_validity()

检查缓存有效性,必要时重新加载

同时检测配置文件是否被外部修改(手动编辑磁盘文件), 若文件 mtime 变化则自动重载。更新内容会在下一次 getConfig 调用时生效,无需重启程序。

内部方法


_check_file_change()

检测配置文件是否被外部程序或用户手动编辑

对比记录的 mtime 与当前文件 mtime,若不一致说明文件已被外部修改。 若变化后的 mtime 与框架自身最后一次刷盘的 mtime(_last_self_write_mtime) 一致,则判定为框架自身的写入而非外部修改,返回 False。

返回值 (bool): 文件是否被外部修改

内部方法


_emit_config_updated(old_config: dict[str, Any])

发射 config.updated 生命周期事件,通知适配器/模块配置已变更

用户手动编辑 config.toml 后,下一次 getConfig 调用会自动检测 到文件变更并触发此事件。适配器通过 on_config_update(old, new) 响应。


getConfig(key: str, default: Any = None)

获取配置项

支持点分隔符路径(如 "module.sub.key")。当存在待写入队列 (延迟刷盘未落盘的 setConfig)时,读取结果会叠加待写值, 保证"写后立读"一致性:

示例:

>>> value = sdk.config.getConfig("ErisPulse.server.port", 8000)

_walk_cache(key: str, default: Any)

内部方法 在缓存树中按点分路径取值;路径缺失或中间节点非字典时返回 default


_dirty_overlay(keys: list[str])

内部方法 收集以待查键为前缀的待写键,构建叠加子树


_deep_merge(base: dict[str, Any], override: dict[str, Any])

内部方法 深合并字典(override 优先,仅 dict 值递归合并)


setConfig(key: str, value: Any, immediate: bool = False)

设置配置项

示例:

>>> sdk.config.setConfig("ErisPulse.server.port", 9000)
>>> sdk.config.setConfig("ErisPulse.server.port", 9000, immediate=True)

force_save()

强制立即保存所有待写入的配置到磁盘

提示 注意!除非您知道您在干什么,否则请勿直接强制保存!


reload()

重新从磁盘加载配置,丢弃所有未保存的更改

提示 reload 时,未持久化的配置项会被丢弃,并重新从配置文件中加载


async agetConfig(key: str, default: Any = None)

异步获取配置项


async asetConfig(key: str, value: Any, immediate: bool = False)

异步设置配置项


setConfigTemplate(key: str, toml_text: str, immediate: bool = True)

以带注释的 TOML 模板文本写入指定配置节

用于适配器/模块首次生成配置模板:模板中的字段注释原样落盘。 目标节已存在时不覆盖(由调用方保证仅在配置缺失时调用); 文件其余内容与注释不受影响。

示例:

>>> sdk.config.setConfigTemplate("MyAdapter", '# API 令牌\ntoken = ""')

async aforce_save()

异步强制保存所有待写入的配置到磁盘


async areload()

异步重新从磁盘加载配置