diff --git a/pydantic_settings/main.py b/pydantic_settings/main.py index e06f4831..740b05cf 100644 --- a/pydantic_settings/main.py +++ b/pydantic_settings/main.py @@ -426,6 +426,7 @@ def _settings_build_values( if sources: state: dict[str, Any] = {} + defaults: dict[str, Any] = {} states: dict[str, dict[str, Any]] = {} for source in sources: if isinstance(source, PydanticBaseSettingsSource): @@ -435,8 +436,15 @@ def _settings_build_values( source_name = source.__name__ if hasattr(source, '__name__') else type(source).__name__ source_state = source() + if isinstance(source, DefaultSettingsSource): + defaults = source_state + states[source_name] = source_state state = deep_update(source_state, state) + + # Strip any default values not explicity set before returning final state + state = {key: val for key, val in state.items() if key not in defaults or defaults[key] != val} + return state else: # no one should mean to do this, but I think returning an empty dict is marginally preferable diff --git a/tests/test_settings.py b/tests/test_settings.py index 867e83dd..7949eb38 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -639,8 +639,27 @@ class SettingsDefaultsA(BaseSettings, env_nested_delimiter='__', nested_model_de nested_d: NestedC = NestedC(v0=False, v1=True) nested_c: NestedD = NestedD() + assert SettingsDefaultsA().model_dump() == { + 'nested_a': {'v0': False, 'v1': True}, + 'nested_b': {'v0': False, 'v1': True}, + 'nested_c': {'v0': False, 'v1': True}, + 'nested_d': {'v0': False, 'v1': True}, + } + assert SettingsDefaultsA().model_dump(exclude_unset=True) == {} + env.set('NESTED_A__V0', 'True') env.set('NESTED_B__V0', 'True') + assert SettingsDefaultsA().model_dump() == { + 'nested_a': {'v0': True, 'v1': True}, + 'nested_b': {'v0': True, 'v1': True}, + 'nested_c': {'v0': False, 'v1': True}, + 'nested_d': {'v0': False, 'v1': True}, + } + assert SettingsDefaultsA().model_dump(exclude_unset=True) == { + 'nested_a': {'v0': True, 'v1': True}, + 'nested_b': {'v0': True, 'v1': True}, + } + env.set('NESTED_C__V0', 'True') env.set('NESTED_D__V0', 'True') assert SettingsDefaultsA().model_dump() == { @@ -649,6 +668,12 @@ class SettingsDefaultsA(BaseSettings, env_nested_delimiter='__', nested_model_de 'nested_c': {'v0': True, 'v1': True}, 'nested_d': {'v0': True, 'v1': True}, } + assert SettingsDefaultsA().model_dump(exclude_unset=True) == { + 'nested_a': {'v0': True, 'v1': True}, + 'nested_b': {'v0': True, 'v1': True}, + 'nested_c': {'v0': True, 'v1': True}, + 'nested_d': {'v0': True, 'v1': True}, + } def test_init_kwargs_nested_model_default_partial_update(env):