1010from abc import ABC , abstractmethod
1111from argparse import SUPPRESS , ArgumentParser , HelpFormatter , Namespace , _SubParsersAction
1212from collections import deque
13- from dataclasses import is_dataclass
13+ from dataclasses import asdict , is_dataclass
1414from enum import Enum
1515from pathlib import Path
1616from types import FunctionType
1717from typing import (
1818 TYPE_CHECKING ,
1919 Any ,
2020 Callable ,
21+ Dict ,
2122 Generic ,
2223 Iterator ,
2324 List ,
3435
3536import typing_extensions
3637from dotenv import dotenv_values
37- from pydantic import AliasChoices , AliasPath , BaseModel , Json
38+ from pydantic import AliasChoices , AliasPath , BaseModel , Json , TypeAdapter
3839from pydantic ._internal ._repr import Representation
40+ from pydantic ._internal ._signature import _field_name_for_signature
3941from pydantic ._internal ._typing_extra import WithArgsTypes , origin_is_union , typing_base
4042from pydantic ._internal ._utils import deep_update , is_model_class , lenient_issubclass
4143from pydantic .dataclasses import is_pydantic_dataclass
@@ -248,55 +250,23 @@ def __call__(self) -> dict[str, Any]:
248250
249251class DefaultSettingsSource (PydanticBaseSettingsSource ):
250252 """
251- Source class for loading default values.
253+ Source class for loading default object values.
252254 """
253255
254- def __init__ (self , settings_cls : type [BaseSettings ]):
256+ def __init__ (self , settings_cls : type [BaseSettings ], default_objects_copy_by_value : bool | None = None ):
255257 super ().__init__ (settings_cls )
256- self .defaults = self ._get_defaults (settings_cls )
257-
258- def _get_defaults (self , settings_cls : type [BaseSettings ]) -> dict [str , Any ]:
259- defaults : dict [str , Any ] = {}
260- if self .config .get ('validate_default' ):
261- fields = (
262- settings_cls .__pydantic_fields__ if is_pydantic_dataclass (settings_cls ) else settings_cls .model_fields
263- )
264- for field_name , field_info in fields .items ():
265- if field_info .validate_default is not False :
266- resolved_name = self ._get_resolved_name (field_name , field_info )
267- if field_info .default not in (PydanticUndefined , None ):
268- defaults [resolved_name ] = field_info .default
269- elif field_info .default_factory is not None :
270- defaults [resolved_name ] = field_info .default_factory
271- return defaults
272-
273- def _get_resolved_name (self , field_name : str , field_info : FieldInfo ) -> str :
274- if not any ((field_info .alias , field_info .validation_alias )):
275- return field_name
276-
277- resolved_names : list [str ] = []
278- is_alias_path_only : bool = True
279- new_alias_paths : list [AliasPath ] = []
280- for alias in (field_info .alias , field_info .validation_alias ):
281- if alias is None :
282- continue
283- elif isinstance (alias , str ):
284- resolved_names .append (alias )
285- is_alias_path_only = False
286- elif isinstance (alias , AliasChoices ):
287- for name in alias .choices :
288- if isinstance (name , str ):
289- resolved_names .append (name )
290- is_alias_path_only = False
291- else :
292- new_alias_paths .append (name )
293- else :
294- new_alias_paths .append (alias )
295- for alias_path in new_alias_paths :
296- name = cast (str , alias_path .path [0 ])
297- if not resolved_names and is_alias_path_only :
298- resolved_names .append (name )
299- return tuple (dict .fromkeys (resolved_names ))[0 ]
258+ self .defaults : dict [str , Any ] = {}
259+ self .default_objects_copy_by_value = (
260+ default_objects_copy_by_value
261+ if default_objects_copy_by_value is not None
262+ else self .config .get ('default_objects_copy_by_value' , False )
263+ )
264+ if self .default_objects_copy_by_value :
265+ for field_name , field_info in settings_cls .model_fields .items ():
266+ if is_dataclass (type (field_info .default )):
267+ self .defaults [_field_name_for_signature (field_name , field_info )] = asdict (field_info .default )
268+ elif is_model_class (type (field_info .default )):
269+ self .defaults [_field_name_for_signature (field_name , field_info )] = field_info .default .model_dump ()
300270
301271 def get_field_value (self , field : FieldInfo , field_name : str ) -> tuple [Any , str , bool ]:
302272 # Nothing to do here. Only implement the return statement to make mypy happy
@@ -306,27 +276,44 @@ def __call__(self) -> dict[str, Any]:
306276 return self .defaults
307277
308278 def __repr__ (self ) -> str :
309- return 'DefaultSettingsSource()'
279+ return f 'DefaultSettingsSource(default_objects_copy_by_value= { self . default_objects_copy_by_value } )'
310280
311281
312282class InitSettingsSource (PydanticBaseSettingsSource ):
313283 """
314284 Source class for loading values provided during settings class initialization.
315285 """
316286
317- def __init__ (self , settings_cls : type [BaseSettings ], init_kwargs : dict [str , Any ]):
287+ def __init__ (
288+ self ,
289+ settings_cls : type [BaseSettings ],
290+ init_kwargs : dict [str , Any ],
291+ default_objects_copy_by_value : bool | None = None ,
292+ ):
318293 self .init_kwargs = init_kwargs
319294 super ().__init__ (settings_cls )
295+ self .default_objects_copy_by_value = (
296+ default_objects_copy_by_value
297+ if default_objects_copy_by_value is not None
298+ else self .config .get ('default_objects_copy_by_value' , False )
299+ )
320300
321301 def get_field_value (self , field : FieldInfo , field_name : str ) -> tuple [Any , str , bool ]:
322302 # Nothing to do here. Only implement the return statement to make mypy happy
323303 return None , '' , False
324304
325305 def __call__ (self ) -> dict [str , Any ]:
326- return self .init_kwargs
306+ return (
307+ TypeAdapter (Dict [str , Any ]).dump_python (self .init_kwargs )
308+ if self .default_objects_copy_by_value
309+ else self .init_kwargs
310+ )
327311
328312 def __repr__ (self ) -> str :
329- return f'InitSettingsSource(init_kwargs={ self .init_kwargs !r} )'
313+ return (
314+ f'InitSettingsSource(init_kwargs={ self .init_kwargs !r} , '
315+ f'default_objects_copy_by_value={ self .default_objects_copy_by_value } )'
316+ )
330317
331318
332319class PydanticBaseEnvSettingsSource (PydanticBaseSettingsSource ):
0 commit comments