pydantic-settings 2.16.1 显式 secrets_dir 为何仍读取旧目录
包装 file_secret_settings 时,2.16.1 会先采用被包装源的目录。把运行时目录统一交给 Settings(_secrets_dir=...),同时核对环境变量覆盖,避免悄悄读取旧密钥。
给 NestedSecretsSettingsSource 传了新目录,配置却仍从旧目录读取;两个目录都有同名文件时,程序甚至不会报错,只会得到旧值。pydantic-settings 2.16.1 中,包装现成的 file_secret_settings 与直接传入设置类,目录选择并不相同。
例如,模型的 secrets_dir 是目录 A,定制配置源时又写了:
NestedSecretsSettingsSource(file_secret_settings, secrets_dir="/run/secrets/B")
此时被包装的源已经从模型配置解析出 A。2.16.1 会先取它的 secrets_dir,再考虑外层显式提供的 B。因此,确认文件存在和权限正常以后,还要检查实际使用了哪一个目录。
让目录只从一个入口传入
需要在部署时选择目录的项目,可以继续包装 file_secret_settings,但不要在包装器里另传 secrets_dir。把目录交给 Settings(_secrets_dir=...),让已经解析好的路径沿原配置源传下去:
from pydantic import SecretStr
from pydantic_settings import (
BaseSettings,
NestedSecretsSettingsSource,
SettingsConfigDict,
)
class Settings(BaseSettings):
app_key: SecretStr
model_config = SettingsConfigDict(secrets_dir="/run/secrets/A")
@classmethod
def settings_customise_sources(
cls, settings_cls, init_settings, env_settings,
dotenv_settings, file_secret_settings,
):
return (
init_settings,
env_settings,
dotenv_settings,
NestedSecretsSettingsSource(file_secret_settings),
)
settings = Settings(_secrets_dir="/run/secrets/B")
这段配置假定 B 是应用有权读取的目录,其中有名为 app_key 的文件。实际项目若使用嵌套子模型,还需保留原来的 secrets_nested_delimiter 或 secrets_nested_subdir 设置;更换目录不会替你改变文件命名规则。
上面的配置源顺序仍允许构造参数、环境变量和 dotenv 覆盖文件秘密。判断目录选择时,应在隔离环境使用不敏感的标记值,并排除这些更高优先级的输入,否则读取结果相同,也可能只是环境变量一直获胜。不要把真实密钥打印到日志里证明来自哪个目录。
如果目录永远固定,也可以只在模型配置中声明,包装器不再重复声明。应避免同时在模型、源构造函数和运行时传入三个互相冲突的路径。
直接传设置类会改变运行时覆盖能力
NestedSecretsSettingsSource(settings_cls, secrets_dir=B) 可以避开现成源已带着 A 的情况,但它没有继承 file_secret_settings 上已经解析好的运行时目录。项目若依赖 Settings(_secrets_dir=C) 切换租户或部署目录,不能只换构造方式就认为这种覆盖仍然有效。
2.16.1 的正式实现仍先取被包装源的路径。后续版本若改为外层显式目录优先,原来同时传 B 和 C 的配置也可能改变结果。升级时要分别核对“只有模型目录”“运行时覆盖”“包装器另传目录”三种调用,不能把不同入口当成等价写法。
修订后,用两个隔离目录中不同的非敏感标记检查读取结果;再检查目标目录不存在、必填文件缺失以及原环境变量覆盖的行为。缺失文件有可能由默认值或其他源补上,仅仅成功创建 Settings 不足以证明目录选对了。


