2025, Oct 20 21:16

Как подключить YAML в pydantic-settings через YamlConfigSettingsSource

Почему yaml_file и yaml_config_section не читают YAML в pydantic-settings, и как включить YamlConfigSettingsSource: рабочее решение и загрузка конфигурации.

Чтение конфигурации из YAML в pydantic-settings может показаться обманчиво простым благодаря параметрам yaml_file и yaml_config_section в model_config. Однако простая установка этих опций сама по себе не заставляет BaseSettings загружать YAML, из-за чего нередко возникает ValidationError об отсутствующих полях. Ниже — краткое объяснение, почему так происходит, и как корректно подключить YAML через документированный механизм кастомизации.

Воспроизведение проблемы

Задача — загрузить одно целочисленное значение из секции YAML в модель настроек. В коде заданы yaml_file и yaml_config_section, а также поле, которое должно заполняться из YAML.

from pathlib import Path
from pydantic_settings import BaseSettings, SettingsConfigDict
class AppConfig(BaseSettings):
    model_config = SettingsConfigDict(
        yaml_file=Path('cfg.yaml'),
        yaml_config_section='section',
    )
    svc_port: int
cfg = AppConfig()

И YAML-файл рядом с кодом:

section:
  svc_port: 123

Запуск падает с ошибкой об отсутствующем поле. В исходном случае это выглядело так:

pydantic_core._pydantic_core.ValidationError: 1 validation error for Settings
port
  Field required [type=missing, input_value={}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.11/v/missing

Что явно указывает: YAML вообще не был прочитан.

Что на самом деле происходит

Параметры yaml_file и yaml_config_section существуют, но сами по себе ничего не делают. Из‑за ограничений Pydantic при прямом чтении YAML-файлов необходимо настроить источники конфигурации и явным образом добавить YamlConfigSettingsSource, как описано в документации. Только тогда YAML-опции начинают работать. Без этого переопределения BaseSettings не будет пытаться читать YAML, и ваши поля окажутся «отсутствующими».

Рабочее решение с моделью секции

Один из практичных способов — описать секцию YAML как вложенную BaseModel и подключить YamlConfigSettingsSource через хук кастомизации.

from pydantic import BaseModel
from pydantic_settings import BaseSettings, YamlConfigSettingsSource
class SectionConfig(BaseModel):
    svc_port: int
class AppConfig(BaseSettings):
    section: SectionConfig
    @classmethod
    def settings_customise_sources(cls, settings_cls, **kwargs):
        return (YamlConfigSettingsSource(settings_cls, "cfg.yaml"),)
conf = AppConfig()
print(conf.section.svc_port)

При таком YAML:

section:
  svc_port: 123

На выходе получаем ожидаемое 123.

Альтернатива: передавать параметры YAML через model_config

Можно оставить yaml_file и yaml_config_section в model_config и не указывать имя файла при создании YamlConfigSettingsSource. В этом случае источник автоматически берёт параметры из model_config.

from pydantic_settings import BaseSettings, YamlConfigSettingsSource, SettingsConfigDict
class RootOptions(BaseSettings):
    svc_port: int
    model_config = SettingsConfigDict(
        yaml_file='cfg.yaml',
        yaml_config_section='section',
    )
    @classmethod
    def settings_customise_sources(cls, settings_cls, **kwargs):
        return (YamlConfigSettingsSource(settings_cls),)
opts = RootOptions()
print(opts.svc_port)

С той же структурой YAML:

section:
  svc_port: 123

Результат снова — 123.

Замечание о полях «extra»

Не нужно включать extra="allow" только ради чтения значений из YAML-секции. Если ключ попадает в «extra», это обычно означает, что в вашей модели нет соответствующего объявленного поля. Как только поле объявлено, значение сопоставляется корректно. Это не связано с использованием секций.

Почему это важно

Опираться лишь на yaml_file и yaml_config_section — путь к запутанным сбоям, потому что YAML в таком случае вообще не читается. Решение — явно подключить YamlConfigSettingsSource через settings_customise_sources, как и рекомендует документация. Понимание этого небольшого, но ключевого шага экономит время и делает загрузку конфигурации предсказуемой.

Выводы

Если вы хотите загружать YAML в pydantic-settings, объявите поля, а затем включите YAML как источник настроек. Имя файла можно передать прямо в YamlConfigSettingsSource или держать все YAML-параметры в model_config и позволить источнику прочитать их оттуда. Если вы структурируете YAML по секциям, можно представить секцию как вложенную BaseModel или читать значения непосредственно в поля — по вашему выбору. Главное — YAML начнёт читаться только после настроенного переопределения источников.

Статья основана на вопросе с StackOverflow от rzlvmp и ответе от strawdog.