Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow nested env var source to override nested init source. #204

Merged
merged 2 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pydantic_settings/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
from collections import deque
from dataclasses import is_dataclass
from pathlib import Path
from typing import TYPE_CHECKING, Any, List, Mapping, Sequence, Tuple, Union, cast
from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Sequence, Tuple, Union, cast

from dotenv import dotenv_values
from pydantic import AliasChoices, AliasPath, BaseModel, Json
from pydantic import AliasChoices, AliasPath, BaseModel, Json, TypeAdapter
from pydantic._internal._typing_extra import origin_is_union
from pydantic._internal._utils import deep_update, lenient_issubclass
from pydantic.fields import FieldInfo
Expand Down Expand Up @@ -121,7 +121,7 @@ def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str,
return None, '', False

def __call__(self) -> dict[str, Any]:
return self.init_kwargs
return TypeAdapter(Dict[str, Any]).dump_python(self.init_kwargs)

def __repr__(self) -> str:
return f'InitSettingsSource(init_kwargs={self.init_kwargs!r})'
Expand Down
37 changes: 37 additions & 0 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,43 @@ def settings_customise_sources(
assert s.bar == 'env setting'


def test_env_deep_override(env):
class DeepSubModel(BaseModel):
v4: str

class SubModel(BaseModel):
v1: str
v2: bytes
v3: int
deep: DeepSubModel

class Settings(BaseSettings, env_nested_delimiter='__'):
v0: str
sub_model: SubModel

@classmethod
def settings_customise_sources(
cls, settings_cls, init_settings, env_settings, dotenv_settings, file_secret_settings
):
return env_settings, dotenv_settings, init_settings, file_secret_settings

env.set('SUB_MODEL__DEEP__V4', 'override-v4')

s_final = {'v0': '0', 'sub_model': {'v1': 'init-v1', 'v2': b'init-v2', 'v3': 3, 'deep': {'v4': 'override-v4'}}}

s = Settings(v0='0', sub_model={'v1': 'init-v1', 'v2': b'init-v2', 'v3': 3, 'deep': {'v4': 'init-v4'}})
assert s.model_dump() == s_final

s = Settings(v0='0', sub_model=SubModel(v1='init-v1', v2=b'init-v2', v3=3, deep=DeepSubModel(v4='init-v4')))
assert s.model_dump() == s_final

s = Settings(v0='0', sub_model=SubModel(v1='init-v1', v2=b'init-v2', v3=3, deep={'v4': 'init-v4'}))
assert s.model_dump() == s_final

s = Settings(v0='0', sub_model={'v1': 'init-v1', 'v2': b'init-v2', 'v3': 3, 'deep': DeepSubModel(v4='init-v4')})
assert s.model_dump() == s_final


def test_config_file_settings_nornir(env):
"""
See https://github.com/pydantic/pydantic/pull/341#issuecomment-450378771
Expand Down
Loading