Skip to content

Commit

Permalink
wip(loader): Setting overwrites added tentatively.
Browse files Browse the repository at this point in the history
Made an issue on the ``pydantic/pydantic_settings`` github to see if I
could make this work the way I'd like. See  pydantic/pydantic-settings#346.
  • Loading branch information
acederberg committed Jul 23, 2024
1 parent 5f422b1 commit aecd9e2
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 19 deletions.
24 changes: 16 additions & 8 deletions yaml_settings_pydantic/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,21 @@ class YamlFileData(TypedDict):
envvar=None, subpath=None, required=True
)

YamlSettingsFilesInput = (
Sequence[str]
| Sequence[Path]
| str
| Path
| dict[str, YamlFileConfigDict]
| dict[Path, YamlFileConfigDict]
| set[str]
| set[Path]
)


class YamlSettingsConfigDict(SettingsConfigDict):
yaml_files: Annotated[
set[Path]
| Sequence[Path]
| dict[Path, YamlFileConfigDict]
| Path
| set[str]
| Sequence[str]
| dict[str, YamlFileConfigDict]
| str,
YamlSettingsFilesInput,
Doc(
"Files to load. This can be a ``str`` or ``Sequence`` of "
"configuration paths, or a dictionary of file names mapping to "
Expand All @@ -91,6 +95,10 @@ class YamlSettingsConfigDict(SettingsConfigDict):
]


YamlFilesData = dict[Path, YamlFileData]
YamlFilesConfigs = dict[Path, YamlFileConfigDict]


def resolve_filepaths(fp: Path, fp_config: YamlFileConfigDict) -> Path:

fp_from_env = None
Expand Down
22 changes: 13 additions & 9 deletions yaml_settings_pydantic/manifests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

from __future__ import annotations

from typing import Any, Self
from pathlib import Path
from typing import Any, Self, Unpack

from pydantic import BaseModel

Expand All @@ -13,24 +14,27 @@
class BaseYaml(BaseModel):
"""Pydantic model loadable from yaml. See :meth:`load`."""

_yaml_files_data: dict[Path, loader.YamlFileData]
_yaml_files_configs: dict[Path, loader.YamlSettingsConfigDict]

@classmethod
def load(
cls,
*,
overwrite: dict[str, Any] | None = None,
exclude: dict[str, Any] | None = None,
**yaml_settings: loader.YamlSettingsConfigDict,
init_kwargs: dict[str, Any] | None = None,
yaml_exclude: dict[str, Any] | None = None,
**yaml_settings: Unpack[loader.YamlSettingsConfigDict],
) -> Self:

if (yaml_files := yaml_settings.get("yaml_files")) is None:
raise ValueError()
# if (yaml_files := yaml_settings.get("yaml_files")) is None:
# raise ValueError()

yaml_files_config = loader.validate_yaml_settings_config_files(yaml_files)
yaml_files_config = loader.validate_yaml_settings_config_files(yaml_settings)
yaml_files_data = loader.load_yaml_data(yaml_files_config)
data = loader.validate_yaml_data(
yaml_files_data,
overwrite=overwrite,
exclude=exclude,
overwrite=init_kwargs,
exclude=yaml_exclude,
)

return cls.model_validate(data)
37 changes: 35 additions & 2 deletions yaml_settings_pydantic/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from pydantic.fields import FieldInfo
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource
from typing_extensions import Doc
from typing_extensions import Doc, override

from yaml_settings_pydantic import loader, util

Expand Down Expand Up @@ -124,11 +124,44 @@ class BaseYamlSettings(BaseSettings):
`model_config["yaml_reload"]`.
"""

__yaml_settings_cls__: ClassVar[CreateYamlSettings]
__yaml_settings_self__: CreateYamlSettings | None
__yaml_exclude__: bool

if TYPE_CHECKING:
# NOTE: pydantic>=2.7 checks at load time for annotated fields, and
# thinks that `model_config` is a model field name.
model_config: ClassVar[loader.YamlSettingsConfigDict]

def __init_subclass__(cls, **kwargs):
"""Create the default ``CreateYamlSettings`` instance."""
super().__init_subclass__(**kwargs)
cls.__yaml_settings_cls__ = CreateYamlSettings(cls)

def __init__(
self,
_yaml_files: loader.YamlSettingsFilesInput | None = None,
_yaml_reload: bool | None = None,
_yaml_exclude: bool = False,
**kwargs,
):
# NOTE: If any overwrites are added, then do not use ``__yaml_settings_cls__``.
# This is a pain because the other option is to overwrite
# ``_settings_build_values``, which likely results in having
# to overwrite ``settings_customise_sources``.
if _yaml_files is not None:
yaml_settings = self.model_config.copy()
yaml_settings.update(
yaml_files=_yaml_files,
yaml_reload=_yaml_reload,
)
self.__yaml_settings_self__ = CreateYamlSettings(self.__class__)
else:
self.__yaml_settings_self__ = self.__yaml_settings_cls__

self.__yaml_exclude__ = _yaml_exclude
super().__init__(**super()._settings_build_values(kwargs))

@classmethod
def settings_customise_sources(
cls,
Expand All @@ -142,10 +175,10 @@ def settings_customise_sources(

# Look for YAML files.
logger.debug("Creating YAML settings callable for `%s`.", cls.__name__)
yaml_settings = CreateYamlSettings(settings_cls)

# The order in which these appear determines their precendence. So a
# `.env` file could be added to # override the ``YAML`` configuration

return (
init_settings,
env_settings,
Expand Down

0 comments on commit aecd9e2

Please sign in to comment.