Coverage for pydantic/_internal/_utils.py: 99.57%

170 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-02-13 19:35 +0000

1"""Bucket of reusable internal utilities. 

2 

3This should be reduced as much as possible with functions only used in one place, moved to that place. 

4""" 

5 

6from __future__ import annotations as _annotations 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

7 

8import dataclasses 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

9import keyword 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

10import typing 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

11import warnings 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

12import weakref 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

13from collections import OrderedDict, defaultdict, deque 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

14from collections.abc import Mapping 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

15from copy import deepcopy 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

16from functools import cached_property 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

17from inspect import Parameter 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

18from itertools import zip_longest 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

19from types import BuiltinFunctionType, CodeType, FunctionType, GeneratorType, LambdaType, ModuleType 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

20from typing import Any, Callable, Generic, TypeVar, overload 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

21 

22from typing_extensions import TypeAlias, TypeGuard, deprecated 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

23 

24from pydantic import PydanticDeprecatedSince211 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

25 

26from . import _repr, _typing_extra 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

27from ._import_utils import import_cached_base_model 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

28 

29if typing.TYPE_CHECKING: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

30 MappingIntStrAny: TypeAlias = 'typing.Mapping[int, Any] | typing.Mapping[str, Any]' 

31 AbstractSetIntStr: TypeAlias = 'typing.AbstractSet[int] | typing.AbstractSet[str]' 

32 from ..main import BaseModel 

33 

34 

35# these are types that are returned unchanged by deepcopy 

36IMMUTABLE_NON_COLLECTIONS_TYPES: set[type[Any]] = { 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

37 int, 

38 float, 

39 complex, 

40 str, 

41 bool, 

42 bytes, 

43 type, 

44 _typing_extra.NoneType, 

45 FunctionType, 

46 BuiltinFunctionType, 

47 LambdaType, 

48 weakref.ref, 

49 CodeType, 

50 # note: including ModuleType will differ from behaviour of deepcopy by not producing error. 

51 # It might be not a good idea in general, but considering that this function used only internally 

52 # against default values of fields, this will allow to actually have a field with module as default value 

53 ModuleType, 

54 NotImplemented.__class__, 

55 Ellipsis.__class__, 

56} 

57 

58# these are types that if empty, might be copied with simple copy() instead of deepcopy() 

59BUILTIN_COLLECTIONS: set[type[Any]] = { 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

60 list, 

61 set, 

62 tuple, 

63 frozenset, 

64 dict, 

65 OrderedDict, 

66 defaultdict, 

67 deque, 

68} 

69 

70 

71def can_be_positional(param: Parameter) -> bool: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

72 """Return whether the parameter accepts a positional argument. 

73 

74 ```python {test="skip" lint="skip"} 

75 def func(a, /, b, *, c): 

76 pass 

77 

78 params = inspect.signature(func).parameters 

79 can_be_positional(params['a']) 

80 #> True 

81 can_be_positional(params['b']) 

82 #> True 

83 can_be_positional(params['c']) 

84 #> False 

85 ``` 

86 """ 

87 return param.kind in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD) 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

88 

89 

90def sequence_like(v: Any) -> bool: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

91 return isinstance(v, (list, tuple, set, frozenset, GeneratorType, deque)) 1abcdmnopqrstefghuvwxyzijklABCDEF

92 

93 

94def lenient_isinstance(o: Any, class_or_tuple: type[Any] | tuple[type[Any], ...] | None) -> bool: # pragma: no cover 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

95 try: 

96 return isinstance(o, class_or_tuple) # type: ignore[arg-type] 

97 except TypeError: 

98 return False 

99 

100 

101def lenient_issubclass(cls: Any, class_or_tuple: Any) -> bool: # pragma: no cover 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

102 try: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

103 return isinstance(cls, type) and issubclass(cls, class_or_tuple) 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

104 except TypeError: 1abcdefghijkl

105 if isinstance(cls, _typing_extra.WithArgsTypes): 1abcdefghijkl

106 return False 1abcdefghijkl

107 raise # pragma: no cover 

108 

109 

110def is_model_class(cls: Any) -> TypeGuard[type[BaseModel]]: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

111 """Returns true if cls is a _proper_ subclass of BaseModel, and provides proper type-checking, 

112 unlike raw calls to lenient_issubclass. 

113 """ 

114 BaseModel = import_cached_base_model() 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

115 

116 return lenient_issubclass(cls, BaseModel) and cls is not BaseModel 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

117 

118 

119def is_valid_identifier(identifier: str) -> bool: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

120 """Checks that a string is a valid identifier and not a Python keyword. 

121 :param identifier: The identifier to test. 

122 :return: True if the identifier is valid. 

123 """ 

124 return identifier.isidentifier() and not keyword.iskeyword(identifier) 1abcdmnopqrstefghuvwxyzijklABCDEF

125 

126 

127KeyType = TypeVar('KeyType') 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

128 

129 

130def deep_update(mapping: dict[KeyType, Any], *updating_mappings: dict[KeyType, Any]) -> dict[KeyType, Any]: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

131 updated_mapping = mapping.copy() 1abcdmnopqrstefghuvwxyzijklABCDEF

132 for updating_mapping in updating_mappings: 1abcdmnopqrstefghuvwxyzijklABCDEF

133 for k, v in updating_mapping.items(): 1abcdmnopqrstefghuvwxyzijklABCDEF

134 if k in updated_mapping and isinstance(updated_mapping[k], dict) and isinstance(v, dict): 1abcdmnopqrstefghuvwxyzijklABCDEF

135 updated_mapping[k] = deep_update(updated_mapping[k], v) 1abcdmnopqrstefghuvwxyzijklABCDEF

136 else: 

137 updated_mapping[k] = v 1abcdmnopqrstefghuvwxyzijklABCDEF

138 return updated_mapping 1abcdmnopqrstefghuvwxyzijklABCDEF

139 

140 

141def update_not_none(mapping: dict[Any, Any], **update: Any) -> None: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

142 mapping.update({k: v for k, v in update.items() if v is not None}) 1abcdmnopqrstefghuvwxyzijklABCDEF

143 

144 

145T = TypeVar('T') 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

146 

147 

148def unique_list( 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

149 input_list: list[T] | tuple[T, ...], 

150 *, 

151 name_factory: typing.Callable[[T], str] = str, 

152) -> list[T]: 

153 """Make a list unique while maintaining order. 

154 We update the list if another one with the same name is set 

155 (e.g. model validator overridden in subclass). 

156 """ 

157 result: list[T] = [] 1abcdmnopqrstefghuvwxyzijklABCDEF

158 result_names: list[str] = [] 1abcdmnopqrstefghuvwxyzijklABCDEF

159 for v in input_list: 1abcdmnopqrstefghuvwxyzijklABCDEF

160 v_name = name_factory(v) 1abcdmnopqrstefghuvwxyzijklABCDEF

161 if v_name not in result_names: 1abcdmnopqrstefghuvwxyzijklABCDEF

162 result_names.append(v_name) 1abcdmnopqrstefghuvwxyzijklABCDEF

163 result.append(v) 1abcdmnopqrstefghuvwxyzijklABCDEF

164 else: 

165 result[result_names.index(v_name)] = v 1abcdmnopqrstefghuvwxyzijklABCDEF

166 

167 return result 1abcdmnopqrstefghuvwxyzijklABCDEF

168 

169 

170class ValueItems(_repr.Representation): 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

171 """Class for more convenient calculation of excluded or included fields on values.""" 

172 

173 __slots__ = ('_items', '_type') 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

174 

175 def __init__(self, value: Any, items: AbstractSetIntStr | MappingIntStrAny) -> None: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

176 items = self._coerce_items(items) 1abcdmnopqrstefghuvwxyzijklABCDEF

177 

178 if isinstance(value, (list, tuple)): 1abcdmnopqrstefghuvwxyzijklABCDEF

179 items = self._normalize_indexes(items, len(value)) # type: ignore 1abcdmnopqrstefghuvwxyzijklABCDEF

180 

181 self._items: MappingIntStrAny = items # type: ignore 1abcdmnopqrstefghuvwxyzijklABCDEF

182 

183 def is_excluded(self, item: Any) -> bool: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

184 """Check if item is fully excluded. 

185 

186 :param item: key or index of a value 

187 """ 

188 return self.is_true(self._items.get(item)) 1abcdmnopqrstefghuvwxyzijklABCDEF

189 

190 def is_included(self, item: Any) -> bool: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

191 """Check if value is contained in self._items. 

192 

193 :param item: key or index of value 

194 """ 

195 return item in self._items 1abcdmnopqrstefghuvwxyzijklABCDEF

196 

197 def for_element(self, e: int | str) -> AbstractSetIntStr | MappingIntStrAny | None: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

198 """:param e: key or index of element on value 

199 :return: raw values for element if self._items is dict and contain needed element 

200 """ 

201 item = self._items.get(e) # type: ignore 1abcdmnopqrstefghuvwxyzijklABCDEF

202 return item if not self.is_true(item) else None 1abcdmnopqrstefghuvwxyzijklABCDEF

203 

204 def _normalize_indexes(self, items: MappingIntStrAny, v_length: int) -> dict[int | str, Any]: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

205 """:param items: dict or set of indexes which will be normalized 

206 :param v_length: length of sequence indexes of which will be 

207 

208 >>> self._normalize_indexes({0: True, -2: True, -1: True}, 4) 

209 {0: True, 2: True, 3: True} 

210 >>> self._normalize_indexes({'__all__': True}, 4) 

211 {0: True, 1: True, 2: True, 3: True} 

212 """ 

213 normalized_items: dict[int | str, Any] = {} 1abcdmnopqrstefghuvwxyzijklABCDEF

214 all_items = None 1abcdmnopqrstefghuvwxyzijklABCDEF

215 for i, v in items.items(): 1abcdmnopqrstefghuvwxyzijklABCDEF

216 if not (isinstance(v, typing.Mapping) or isinstance(v, typing.AbstractSet) or self.is_true(v)): 1abcdmnopqrstefghuvwxyzijklABCDEF

217 raise TypeError(f'Unexpected type of exclude value for index "{i}" {v.__class__}') 1abcdmnopqrstefghuvwxyzijklABCDEF

218 if i == '__all__': 1abcdmnopqrstefghuvwxyzijklABCDEF

219 all_items = self._coerce_value(v) 1abcdmnopqrstefghuvwxyzijklABCDEF

220 continue 1abcdmnopqrstefghuvwxyzijklABCDEF

221 if not isinstance(i, int): 1abcdmnopqrstefghuvwxyzijklABCDEF

222 raise TypeError( 1abcdmnopqrstefghuvwxyzijklABCDEF

223 'Excluding fields from a sequence of sub-models or dicts must be performed index-wise: ' 

224 'expected integer keys or keyword "__all__"' 

225 ) 

226 normalized_i = v_length + i if i < 0 else i 1abcdmnopqrstefghuvwxyzijklABCDEF

227 normalized_items[normalized_i] = self.merge(v, normalized_items.get(normalized_i)) 1abcdmnopqrstefghuvwxyzijklABCDEF

228 

229 if not all_items: 1abcdmnopqrstefghuvwxyzijklABCDEF

230 return normalized_items 1abcdmnopqrstefghuvwxyzijklABCDEF

231 if self.is_true(all_items): 1abcdmnopqrstefghuvwxyzijklABCDEF

232 for i in range(v_length): 1abcdmnopqrstefghuvwxyzijklABCDEF

233 normalized_items.setdefault(i, ...) 1abcdmnopqrstefghuvwxyzijklABCDEF

234 return normalized_items 1abcdmnopqrstefghuvwxyzijklABCDEF

235 for i in range(v_length): 1abcdmnopqrstefghuvwxyzijklABCDEF

236 normalized_item = normalized_items.setdefault(i, {}) 1abcdmnopqrstefghuvwxyzijklABCDEF

237 if not self.is_true(normalized_item): 237 ↛ 235line 237 didn't jump to line 235 because the condition on line 237 was always true1abcdmnopqrstefghuvwxyzijklABCDEF

238 normalized_items[i] = self.merge(all_items, normalized_item) 1abcdmnopqrstefghuvwxyzijklABCDEF

239 return normalized_items 1abcdmnopqrstefghuvwxyzijklABCDEF

240 

241 @classmethod 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

242 def merge(cls, base: Any, override: Any, intersect: bool = False) -> Any: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

243 """Merge a `base` item with an `override` item. 

244 

245 Both `base` and `override` are converted to dictionaries if possible. 

246 Sets are converted to dictionaries with the sets entries as keys and 

247 Ellipsis as values. 

248 

249 Each key-value pair existing in `base` is merged with `override`, 

250 while the rest of the key-value pairs are updated recursively with this function. 

251 

252 Merging takes place based on the "union" of keys if `intersect` is 

253 set to `False` (default) and on the intersection of keys if 

254 `intersect` is set to `True`. 

255 """ 

256 override = cls._coerce_value(override) 1abcdmnopqrstefghuvwxyzijklABCDEF

257 base = cls._coerce_value(base) 1abcdmnopqrstefghuvwxyzijklABCDEF

258 if override is None: 1abcdmnopqrstefghuvwxyzijklABCDEF

259 return base 1abcdmnopqrstefghuvwxyzijklABCDEF

260 if cls.is_true(base) or base is None: 1abcdmnopqrstefghuvwxyzijklABCDEF

261 return override 1abcdmnopqrstefghuvwxyzijklABCDEF

262 if cls.is_true(override): 1abcdmnopqrstefghuvwxyzijklABCDEF

263 return base if intersect else override 1abcdmnopqrstefghuvwxyzijklABCDEF

264 

265 # intersection or union of keys while preserving ordering: 

266 if intersect: 1abcdmnopqrstefghuvwxyzijklABCDEF

267 merge_keys = [k for k in base if k in override] + [k for k in override if k in base] 1abcdmnopqrstefghuvwxyzijklABCDEF

268 else: 

269 merge_keys = list(base) + [k for k in override if k not in base] 1abcdmnopqrstefghuvwxyzijklABCDEF

270 

271 merged: dict[int | str, Any] = {} 1abcdmnopqrstefghuvwxyzijklABCDEF

272 for k in merge_keys: 1abcdmnopqrstefghuvwxyzijklABCDEF

273 merged_item = cls.merge(base.get(k), override.get(k), intersect=intersect) 1abcdmnopqrstefghuvwxyzijklABCDEF

274 if merged_item is not None: 1abcdmnopqrstefghuvwxyzijklABCDEF

275 merged[k] = merged_item 1abcdmnopqrstefghuvwxyzijklABCDEF

276 

277 return merged 1abcdmnopqrstefghuvwxyzijklABCDEF

278 

279 @staticmethod 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

280 def _coerce_items(items: AbstractSetIntStr | MappingIntStrAny) -> MappingIntStrAny: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

281 if isinstance(items, typing.Mapping): 1abcdmnopqrstefghuvwxyzijklABCDEF

282 pass 1abcdmnopqrstefghuvwxyzijklABCDEF

283 elif isinstance(items, typing.AbstractSet): 1abcdmnopqrstefghuvwxyzijklABCDEF

284 items = dict.fromkeys(items, ...) # type: ignore 1abcdmnopqrstefghuvwxyzijklABCDEF

285 else: 

286 class_name = getattr(items, '__class__', '???') 1abcdmnopqrstefghuvwxyzijklABCDEF

287 raise TypeError(f'Unexpected type of exclude value {class_name}') 1abcdmnopqrstefghuvwxyzijklABCDEF

288 return items # type: ignore 1abcdmnopqrstefghuvwxyzijklABCDEF

289 

290 @classmethod 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

291 def _coerce_value(cls, value: Any) -> Any: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

292 if value is None or cls.is_true(value): 1abcdmnopqrstefghuvwxyzijklABCDEF

293 return value 1abcdmnopqrstefghuvwxyzijklABCDEF

294 return cls._coerce_items(value) 1abcdmnopqrstefghuvwxyzijklABCDEF

295 

296 @staticmethod 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

297 def is_true(v: Any) -> bool: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

298 return v is True or v is ... 1abcdmnopqrstefghuvwxyzijklABCDEF

299 

300 def __repr_args__(self) -> _repr.ReprArgs: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

301 return [(None, self._items)] 1abcdmnopqrstefghuvwxyzijklABCDEF

302 

303 

304if typing.TYPE_CHECKING: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

305 

306 def LazyClassAttribute(name: str, get_value: Callable[[], T]) -> T: ... 

307 

308else: 

309 

310 class LazyClassAttribute: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

311 """A descriptor exposing an attribute only accessible on a class (hidden from instances). 

312 

313 The attribute is lazily computed and cached during the first access. 

314 """ 

315 

316 def __init__(self, name: str, get_value: Callable[[], Any]) -> None: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

317 self.name = name 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

318 self.get_value = get_value 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

319 

320 @cached_property 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

321 def value(self) -> Any: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

322 return self.get_value() 1abcdmnopqrstefghuvwxyzijklABCDEF

323 

324 def __get__(self, instance: Any, owner: type[Any]) -> None: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

325 if instance is None: 1abcdmnopqrstefghuvwxyzijklABCDEF

326 return self.value 1abcdmnopqrstefghuvwxyzijklABCDEF

327 raise AttributeError(f'{self.name!r} attribute of {owner.__name__!r} is class-only') 1abcdmnopqrstefghuvwxyzijklABCDEF

328 

329 

330Obj = TypeVar('Obj') 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

331 

332 

333def smart_deepcopy(obj: Obj) -> Obj: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

334 """Return type as is for immutable built-in types 

335 Use obj.copy() for built-in empty collections 

336 Use copy.deepcopy() for non-empty collections and unknown objects. 

337 """ 

338 obj_type = obj.__class__ 1abcdmnopqrstefghuvwxyzijklABCDEF

339 if obj_type in IMMUTABLE_NON_COLLECTIONS_TYPES: 1abcdmnopqrstefghuvwxyzijklABCDEF

340 return obj # fastest case: obj is immutable and not collection therefore will not be copied anyway 1abcdmnopqrstefghuvwxyzijklABCDEF

341 try: 1abcdmnopqrstefghuvwxyzijklABCDEF

342 if not obj and obj_type in BUILTIN_COLLECTIONS: 1abcdmnopqrstefghuvwxyzijklABCDEF

343 # faster way for empty collections, no need to copy its members 

344 return obj if obj_type is tuple else obj.copy() # tuple doesn't have copy method # type: ignore 1abcdmnopqrstefghuvwxyzijklABCDEF

345 except (TypeError, ValueError, RuntimeError): 1abcdmnopqrstefghuvwxyzijklABCDEF

346 # do we really dare to catch ALL errors? Seems a bit risky 

347 pass 1abcdmnopqrstefghuvwxyzijklABCDEF

348 

349 return deepcopy(obj) # slowest way when we actually might need a deepcopy 1abcdmnopqrstefghuvwxyzijklABCDEF

350 

351 

352_SENTINEL = object() 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

353 

354 

355def all_identical(left: typing.Iterable[Any], right: typing.Iterable[Any]) -> bool: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

356 """Check that the items of `left` are the same objects as those in `right`. 

357 

358 >>> a, b = object(), object() 

359 >>> all_identical([a, b, a], [a, b, a]) 

360 True 

361 >>> all_identical([a, b, [a]], [a, b, [a]]) # new list object, while "equal" is not "identical" 

362 False 

363 """ 

364 for left_item, right_item in zip_longest(left, right, fillvalue=_SENTINEL): 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

365 if left_item is not right_item: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

366 return False 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

367 return True 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

368 

369 

370@dataclasses.dataclass(frozen=True) 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

371class SafeGetItemProxy: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

372 """Wrapper redirecting `__getitem__` to `get` with a sentinel value as default 

373 

374 This makes is safe to use in `operator.itemgetter` when some keys may be missing 

375 """ 

376 

377 # Define __slots__manually for performances 

378 # @dataclasses.dataclass() only support slots=True in python>=3.10 

379 __slots__ = ('wrapped',) 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

380 

381 wrapped: Mapping[str, Any] 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

382 

383 def __getitem__(self, key: str, /) -> Any: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

384 return self.wrapped.get(key, _SENTINEL) 1abcdmnopqrstefghuvwxyzijklABCDEF

385 

386 # required to pass the object to operator.itemgetter() instances due to a quirk of typeshed 

387 # https://github.com/python/mypy/issues/13713 

388 # https://github.com/python/typeshed/pull/8785 

389 # Since this is typing-only, hide it in a typing.TYPE_CHECKING block 

390 if typing.TYPE_CHECKING: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

391 

392 def __contains__(self, key: str, /) -> bool: 

393 return self.wrapped.__contains__(key) 

394 

395 

396_ModelT = TypeVar('_ModelT', bound='BaseModel') 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

397_RT = TypeVar('_RT') 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

398 

399 

400class deprecated_instance_property(Generic[_ModelT, _RT]): 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

401 """A decorator exposing the decorated class method as a property, with a warning on instance access. 

402 

403 This decorator takes a class method defined on the `BaseModel` class and transforms it into 

404 an attribute. The attribute can be accessed on both the class and instances of the class. If accessed 

405 via an instance, a deprecation warning is emitted stating that instance access will be removed in V3. 

406 """ 

407 

408 def __init__(self, fget: Callable[[type[_ModelT]], _RT], /) -> None: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

409 # Note: fget should be a classmethod: 

410 self.fget = fget 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

411 

412 @overload 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

413 def __get__(self, instance: None, objtype: type[_ModelT]) -> _RT: ... 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

414 @overload 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

415 @deprecated( 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

416 'Accessing this attribute on the instance is deprecated, and will be removed in Pydantic V3. ' 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

417 'Instead, you should access this attribute from the model class.', 

418 category=None, 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

419 ) 

420 def __get__(self, instance: _ModelT, objtype: type[_ModelT]) -> _RT: ... 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

421 def __get__(self, instance: _ModelT | None, objtype: type[_ModelT]) -> _RT: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF

422 if instance is not None: 1abcdmnopqrstefghuvwxyzijklABCDEF

423 warnings.warn( 1abcdmnopqrstefghuvwxyzijklABCDEF

424 'Accessing this attribute on the instance is deprecated, and will be removed in Pydantic V3. ' 

425 'Instead, you should access this attribute from the model class.', 

426 category=PydanticDeprecatedSince211, 

427 stacklevel=2, 

428 ) 

429 return self.fget.__get__(instance, objtype)() 1abcdmnopqrstefghuvwxyzijklABCDEF