From f3a25f2f26e2bfba74e65bab820001dd462d8fa8 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Fri, 27 Sep 2024 09:13:07 +0200 Subject: [PATCH] Fix a regression in dotenv optional nested field (#423) --- pydantic_settings/sources.py | 11 ++++++++++- tests/test_settings.py | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/pydantic_settings/sources.py b/pydantic_settings/sources.py index 2384b03..91e004a 100644 --- a/pydantic_settings/sources.py +++ b/pydantic_settings/sources.py @@ -986,10 +986,19 @@ def __call__(self) -> dict[str, Any]: for field_name, field in self.settings_cls.model_fields.items(): for _, field_env_name, _ in self._extract_field_info(field, field_name): if env_name == field_env_name or ( - lenient_issubclass(field.annotation, BaseModel) and env_name.startswith(field_env_name) + ( + _annotation_is_complex(field.annotation, field.metadata) + or ( + origin_is_union(get_origin(field.annotation)) + and _union_is_complex(field.annotation, field.metadata) + ) + ) + and env_name.startswith(field_env_name) ): env_used = True break + if env_used: + break if not env_used: if is_extra_allowed and env_name.startswith(self.env_prefix): # env_prefix should be respected and removed from the env_name diff --git a/tests/test_settings.py b/tests/test_settings.py index eb0fecc..93067d9 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -5218,3 +5218,25 @@ class Settings(BaseSettings): s = Settings() assert s.model_dump() == {'nested': {'foo': ['one', 'two']}} + + +def test_dotenv_optional_nested(tmp_path): + p = tmp_path / '.env' + p.write_text('not_nested=works\nNESTED__A=fails\nNESTED__b=2') + + class NestedSettings(BaseModel): + A: str + b: int + + class Settings(BaseSettings): + model_config = SettingsConfigDict( + env_file=p, + env_nested_delimiter='__', + extra='forbid', + ) + + not_nested: str + NESTED: Optional[NestedSettings] + + s = Settings() + assert s.model_dump() == {'not_nested': 'works', 'NESTED': {'A': 'fails', 'b': 2}}