Coverage for pydantic/_internal/_config.py: 100.00%

149 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-28 10:05 +0000

1from __future__ import annotations as _annotations 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

2 

3import warnings 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

4from contextlib import contextmanager 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

5from re import Pattern 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

6from typing import ( 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

7 TYPE_CHECKING, 

8 Any, 

9 Callable, 

10 Literal, 

11 cast, 

12) 

13 

14from pydantic_core import core_schema 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

15from typing_extensions import Self 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

16 

17from ..aliases import AliasGenerator 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

18from ..config import ConfigDict, ExtraValues, JsonDict, JsonEncoder, JsonSchemaExtraCallable 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

19from ..errors import PydanticUserError 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

20from ..warnings import PydanticDeprecatedSince20, PydanticDeprecatedSince210 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

21 

22if not TYPE_CHECKING: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

23 # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915 

24 # and https://youtrack.jetbrains.com/issue/PY-51428 

25 DeprecationWarning = PydanticDeprecatedSince20 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

26 

27if TYPE_CHECKING: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

28 from .._internal._schema_generation_shared import GenerateSchema 

29 from ..fields import ComputedFieldInfo, FieldInfo 

30 

31DEPRECATION_MESSAGE = 'Support for class-based `config` is deprecated, use ConfigDict instead.' 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

32 

33 

34class ConfigWrapper: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

35 """Internal wrapper for Config which exposes ConfigDict items as attributes.""" 

36 

37 __slots__ = ('config_dict',) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

38 

39 config_dict: ConfigDict 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

40 

41 # all annotations are copied directly from ConfigDict, and should be kept up to date, a test will fail if they 

42 # stop matching 

43 title: str | None 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

44 str_to_lower: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

45 str_to_upper: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

46 str_strip_whitespace: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

47 str_min_length: int 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

48 str_max_length: int | None 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

49 extra: ExtraValues | None 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

50 frozen: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

51 populate_by_name: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

52 use_enum_values: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

53 validate_assignment: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

54 arbitrary_types_allowed: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

55 from_attributes: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

56 # whether to use the actual key provided in the data (e.g. alias or first alias for "field required" errors) instead of field_names 

57 # to construct error `loc`s, default `True` 

58 loc_by_alias: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

59 alias_generator: Callable[[str], str] | AliasGenerator | None 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

60 model_title_generator: Callable[[type], str] | None 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

61 field_title_generator: Callable[[str, FieldInfo | ComputedFieldInfo], str] | None 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

62 ignored_types: tuple[type, ...] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

63 allow_inf_nan: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

64 json_schema_extra: JsonDict | JsonSchemaExtraCallable | None 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

65 json_encoders: dict[type[object], JsonEncoder] | None 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

66 

67 # new in V2 

68 strict: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

69 # whether instances of models and dataclasses (including subclass instances) should re-validate, default 'never' 

70 revalidate_instances: Literal['always', 'never', 'subclass-instances'] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

71 ser_json_timedelta: Literal['iso8601', 'float'] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

72 ser_json_bytes: Literal['utf8', 'base64', 'hex'] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

73 val_json_bytes: Literal['utf8', 'base64', 'hex'] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

74 ser_json_inf_nan: Literal['null', 'constants', 'strings'] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

75 # whether to validate default values during validation, default False 

76 validate_default: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

77 validate_return: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

78 protected_namespaces: tuple[str | Pattern[str], ...] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

79 hide_input_in_errors: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

80 defer_build: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

81 plugin_settings: dict[str, object] | None 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

82 schema_generator: type[GenerateSchema] | None 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

83 json_schema_serialization_defaults_required: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

84 json_schema_mode_override: Literal['validation', 'serialization', None] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

85 coerce_numbers_to_str: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

86 regex_engine: Literal['rust-regex', 'python-re'] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

87 validation_error_cause: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

88 use_attribute_docstrings: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

89 cache_strings: bool | Literal['all', 'keys', 'none'] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

90 validate_by_alias: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

91 validate_by_name: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

92 serialize_by_alias: bool 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

93 

94 def __init__(self, config: ConfigDict | dict[str, Any] | type[Any] | None, *, check: bool = True): 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

95 if check: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

96 self.config_dict = prepare_config(config) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

97 else: 

98 self.config_dict = cast(ConfigDict, config) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

99 

100 @classmethod 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

101 def for_model(cls, bases: tuple[type[Any], ...], namespace: dict[str, Any], kwargs: dict[str, Any]) -> Self: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

102 """Build a new `ConfigWrapper` instance for a `BaseModel`. 

103 

104 The config wrapper built based on (in descending order of priority): 

105 - options from `kwargs` 

106 - options from the `namespace` 

107 - options from the base classes (`bases`) 

108 

109 Args: 

110 bases: A tuple of base classes. 

111 namespace: The namespace of the class being created. 

112 kwargs: The kwargs passed to the class being created. 

113 

114 Returns: 

115 A `ConfigWrapper` instance for `BaseModel`. 

116 """ 

117 config_new = ConfigDict() 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

118 for base in bases: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

119 config = getattr(base, 'model_config', None) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

120 if config: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

121 config_new.update(config.copy()) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

122 

123 config_class_from_namespace = namespace.get('Config') 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

124 config_dict_from_namespace = namespace.get('model_config') 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

125 

126 raw_annotations = namespace.get('__annotations__', {}) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

127 if raw_annotations.get('model_config') and config_dict_from_namespace is None: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

128 raise PydanticUserError( 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

129 '`model_config` cannot be used as a model field name. Use `model_config` for model configuration.', 

130 code='model-config-invalid-field-name', 

131 ) 

132 

133 if config_class_from_namespace and config_dict_from_namespace: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

134 raise PydanticUserError('"Config" and "model_config" cannot be used together', code='config-both') 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

135 

136 config_from_namespace = config_dict_from_namespace or prepare_config(config_class_from_namespace) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

137 

138 config_new.update(config_from_namespace) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

139 

140 for k in list(kwargs.keys()): 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

141 if k in config_keys: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

142 config_new[k] = kwargs.pop(k) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

143 

144 return cls(config_new) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

145 

146 # we don't show `__getattr__` to type checkers so missing attributes cause errors 

147 if not TYPE_CHECKING: # pragma: no branch 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

148 

149 def __getattr__(self, name: str) -> Any: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

150 try: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

151 return self.config_dict[name] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

152 except KeyError: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

153 try: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

154 return config_defaults[name] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

155 except KeyError: 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

156 raise AttributeError(f'Config has no attribute {name!r}') from None 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

157 

158 def core_config(self, title: str | None) -> core_schema.CoreConfig: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

159 """Create a pydantic-core config. 

160 

161 We don't use getattr here since we don't want to populate with defaults. 

162 

163 Args: 

164 title: The title to use if not set in config. 

165 

166 Returns: 

167 A `CoreConfig` object created from config. 

168 """ 

169 config = self.config_dict 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

170 

171 if config.get('schema_generator') is not None: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

172 warnings.warn( 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

173 'The `schema_generator` setting has been deprecated since v2.10. This setting no longer has any effect.', 

174 PydanticDeprecatedSince210, 

175 stacklevel=2, 

176 ) 

177 

178 if (populate_by_name := config.get('populate_by_name')) is not None: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

179 # We include this patch for backwards compatibility purposes, but this config setting will be deprecated in v3.0, and likely removed in v4.0. 

180 # Thus, the above warning and this patch can be removed then as well. 

181 if config.get('validate_by_name') is None: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

182 config['validate_by_alias'] = True 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

183 config['validate_by_name'] = populate_by_name 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

184 

185 # We dynamically patch validate_by_name to be True if validate_by_alias is set to False 

186 # and validate_by_name is not explicitly set. 

187 if config.get('validate_by_alias') is False and config.get('validate_by_name') is None: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

188 config['validate_by_name'] = True 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

189 

190 if (not config.get('validate_by_alias', True)) and (not config.get('validate_by_name', False)): 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

191 raise PydanticUserError( 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

192 'At least one of `validate_by_alias` or `validate_by_name` must be set to True.', 

193 code='validate-by-alias-and-name-false', 

194 ) 

195 

196 return core_schema.CoreConfig( 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

197 **{ # pyright: ignore[reportArgumentType] 

198 k: v 

199 for k, v in ( 

200 ('title', config.get('title') or title or None), 

201 ('extra_fields_behavior', config.get('extra')), 

202 ('allow_inf_nan', config.get('allow_inf_nan')), 

203 ('str_strip_whitespace', config.get('str_strip_whitespace')), 

204 ('str_to_lower', config.get('str_to_lower')), 

205 ('str_to_upper', config.get('str_to_upper')), 

206 ('strict', config.get('strict')), 

207 ('ser_json_timedelta', config.get('ser_json_timedelta')), 

208 ('ser_json_bytes', config.get('ser_json_bytes')), 

209 ('val_json_bytes', config.get('val_json_bytes')), 

210 ('ser_json_inf_nan', config.get('ser_json_inf_nan')), 

211 ('from_attributes', config.get('from_attributes')), 

212 ('loc_by_alias', config.get('loc_by_alias')), 

213 ('revalidate_instances', config.get('revalidate_instances')), 

214 ('validate_default', config.get('validate_default')), 

215 ('str_max_length', config.get('str_max_length')), 

216 ('str_min_length', config.get('str_min_length')), 

217 ('hide_input_in_errors', config.get('hide_input_in_errors')), 

218 ('coerce_numbers_to_str', config.get('coerce_numbers_to_str')), 

219 ('regex_engine', config.get('regex_engine')), 

220 ('validation_error_cause', config.get('validation_error_cause')), 

221 ('cache_strings', config.get('cache_strings')), 

222 ('validate_by_alias', config.get('validate_by_alias')), 

223 ('validate_by_name', config.get('validate_by_name')), 

224 ('serialize_by_alias', config.get('serialize_by_alias')), 

225 ) 

226 if v is not None 

227 } 

228 ) 

229 

230 def __repr__(self): 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

231 c = ', '.join(f'{k}={v!r}' for k, v in self.config_dict.items()) 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

232 return f'ConfigWrapper({c})' 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

233 

234 

235class ConfigWrapperStack: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

236 """A stack of `ConfigWrapper` instances.""" 

237 

238 def __init__(self, config_wrapper: ConfigWrapper): 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

239 self._config_wrapper_stack: list[ConfigWrapper] = [config_wrapper] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

240 

241 @property 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

242 def tail(self) -> ConfigWrapper: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

243 return self._config_wrapper_stack[-1] 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

244 

245 @contextmanager 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

246 def push(self, config_wrapper: ConfigWrapper | ConfigDict | None): 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

247 if config_wrapper is None: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

248 yield 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

249 return 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

250 

251 if not isinstance(config_wrapper, ConfigWrapper): 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

252 config_wrapper = ConfigWrapper(config_wrapper, check=False) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

253 

254 self._config_wrapper_stack.append(config_wrapper) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

255 try: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

256 yield 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

257 finally: 

258 self._config_wrapper_stack.pop() 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

259 

260 

261config_defaults = ConfigDict( 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

262 title=None, 

263 str_to_lower=False, 

264 str_to_upper=False, 

265 str_strip_whitespace=False, 

266 str_min_length=0, 

267 str_max_length=None, 

268 # let the model / dataclass decide how to handle it 

269 extra=None, 

270 frozen=False, 

271 populate_by_name=False, 

272 use_enum_values=False, 

273 validate_assignment=False, 

274 arbitrary_types_allowed=False, 

275 from_attributes=False, 

276 loc_by_alias=True, 

277 alias_generator=None, 

278 model_title_generator=None, 

279 field_title_generator=None, 

280 ignored_types=(), 

281 allow_inf_nan=True, 

282 json_schema_extra=None, 

283 strict=False, 

284 revalidate_instances='never', 

285 ser_json_timedelta='iso8601', 

286 ser_json_bytes='utf8', 

287 val_json_bytes='utf8', 

288 ser_json_inf_nan='null', 

289 validate_default=False, 

290 validate_return=False, 

291 protected_namespaces=('model_validate', 'model_dump'), 

292 hide_input_in_errors=False, 

293 json_encoders=None, 

294 defer_build=False, 

295 schema_generator=None, 

296 plugin_settings=None, 

297 json_schema_serialization_defaults_required=False, 

298 json_schema_mode_override=None, 

299 coerce_numbers_to_str=False, 

300 regex_engine='rust-regex', 

301 validation_error_cause=False, 

302 use_attribute_docstrings=False, 

303 cache_strings=True, 

304 validate_by_alias=True, 

305 validate_by_name=False, 

306 serialize_by_alias=False, 

307) 

308 

309 

310def prepare_config(config: ConfigDict | dict[str, Any] | type[Any] | None) -> ConfigDict: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

311 """Create a `ConfigDict` instance from an existing dict, a class (e.g. old class-based config) or None. 

312 

313 Args: 

314 config: The input config. 

315 

316 Returns: 

317 A ConfigDict object created from config. 

318 """ 

319 if config is None: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

320 return ConfigDict() 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

321 

322 if not isinstance(config, dict): 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

323 warnings.warn(DEPRECATION_MESSAGE, DeprecationWarning) 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

324 config = {k: getattr(config, k) for k in dir(config) if not k.startswith('__')} 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

325 

326 config_dict = cast(ConfigDict, config) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

327 check_deprecated(config_dict) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

328 return config_dict 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

329 

330 

331config_keys = set(ConfigDict.__annotations__.keys()) 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

332 

333 

334V2_REMOVED_KEYS = { 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

335 'allow_mutation', 

336 'error_msg_templates', 

337 'fields', 

338 'getter_dict', 

339 'smart_union', 

340 'underscore_attrs_are_private', 

341 'json_loads', 

342 'json_dumps', 

343 'copy_on_model_validation', 

344 'post_init_call', 

345} 

346V2_RENAMED_KEYS = { 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

347 'allow_population_by_field_name': 'validate_by_name', 

348 'anystr_lower': 'str_to_lower', 

349 'anystr_strip_whitespace': 'str_strip_whitespace', 

350 'anystr_upper': 'str_to_upper', 

351 'keep_untouched': 'ignored_types', 

352 'max_anystr_length': 'str_max_length', 

353 'min_anystr_length': 'str_min_length', 

354 'orm_mode': 'from_attributes', 

355 'schema_extra': 'json_schema_extra', 

356 'validate_all': 'validate_default', 

357} 

358 

359 

360def check_deprecated(config_dict: ConfigDict) -> None: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

361 """Check for deprecated config keys and warn the user. 

362 

363 Args: 

364 config_dict: The input config. 

365 """ 

366 deprecated_removed_keys = V2_REMOVED_KEYS & config_dict.keys() 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

367 deprecated_renamed_keys = V2_RENAMED_KEYS.keys() & config_dict.keys() 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

368 if deprecated_removed_keys or deprecated_renamed_keys: 1abcdefghijklmnopqrstuvwxJKLMNOPyzABCDEFGHI

369 renamings = {k: V2_RENAMED_KEYS[k] for k in sorted(deprecated_renamed_keys)} 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

370 renamed_bullets = [f'* {k!r} has been renamed to {v!r}' for k, v in renamings.items()] 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

371 removed_bullets = [f'* {k!r} has been removed' for k in sorted(deprecated_removed_keys)] 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

372 message = '\n'.join(['Valid config keys have changed in V2:'] + renamed_bullets + removed_bullets) 1abcdefghijklmnopqrstuvwxyzABCDEFGHI

373 warnings.warn(message, UserWarning) 1abcdefghijklmnopqrstuvwxyzABCDEFGHI