66
77CONFIG_MAP : dict [str , list [type [BaseSettings ]]] = {}
88ROOT = ""
9+ MISSING = object ()
910
1011_global_config : dict [str , list [BaseSettings ]] = {}
1112_config_data : dict [str , Any ] = {}
@@ -95,6 +96,22 @@ def ensure_config[C: BaseSettings](config_cls: type[C]) -> C:
9596 return instance
9697
9798
99+ def get_value (path : str , default : Any = MISSING ) -> Any :
100+ """Get a loaded config value by dotted path, preserving registered settings behavior."""
101+
102+ parts = [part for part in path .split ("." ) if part ]
103+ if not parts :
104+ raise ValueError ("config path must not be empty" )
105+
106+ value = _lookup_registered_config (parts )
107+ if value is not MISSING :
108+ return value
109+
110+ if default is not MISSING :
111+ return default
112+ raise KeyError (path )
113+
114+
98115def _copy_dict (data : dict [str , Any ]) -> dict [str , Any ]:
99116 copied : dict [str , Any ] = {}
100117 for key , value in data .items ():
@@ -105,6 +122,36 @@ def _copy_dict(data: dict[str, Any]) -> dict[str, Any]:
105122 return copied
106123
107124
125+ def _lookup_registered_config (parts : list [str ]) -> Any :
126+ section , * subpath = parts
127+ if section in CONFIG_MAP and section != ROOT :
128+ for config_cls in CONFIG_MAP [section ]:
129+ value = _lookup_path (ensure_config (config_cls ), subpath )
130+ if value is not MISSING :
131+ return value
132+
133+ for config_cls in CONFIG_MAP .get (ROOT , []):
134+ value = _lookup_path (ensure_config (config_cls ), parts )
135+ if value is not MISSING :
136+ return value
137+
138+ return MISSING
139+
140+
141+ def _lookup_path (value : Any , parts : list [str ]) -> Any :
142+ current = value
143+ for part in parts :
144+ if isinstance (current , dict ):
145+ if part not in current :
146+ return MISSING
147+ current = current [part ]
148+ continue
149+ if not hasattr (current , part ):
150+ return MISSING
151+ current = getattr (current , part )
152+ return current
153+
154+
108155def _merge_into (target : dict [str , Any ], incoming : dict [str , Any ], path : tuple [str , ...]) -> None :
109156 for key , value in incoming .items ():
110157 existing = target .get (key )
0 commit comments