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

151 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-07-03 19:29 +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 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

7 

8import dataclasses 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

9import keyword 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

10import typing 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

11import weakref 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

12from collections import OrderedDict, defaultdict, deque 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

13from copy import deepcopy 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

14from itertools import zip_longest 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

15from types import BuiltinFunctionType, CodeType, FunctionType, GeneratorType, LambdaType, ModuleType 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

16from typing import Any, Mapping, TypeVar 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

17 

18from typing_extensions import TypeAlias, TypeGuard 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

19 

20from . import _repr, _typing_extra 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

21 

22if typing.TYPE_CHECKING: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

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

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

25 from ..main import BaseModel 

26 

27 

28# these are types that are returned unchanged by deepcopy 

29IMMUTABLE_NON_COLLECTIONS_TYPES: set[type[Any]] = { 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

30 int, 

31 float, 

32 complex, 

33 str, 

34 bool, 

35 bytes, 

36 type, 

37 _typing_extra.NoneType, 

38 FunctionType, 

39 BuiltinFunctionType, 

40 LambdaType, 

41 weakref.ref, 

42 CodeType, 

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

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

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

46 ModuleType, 

47 NotImplemented.__class__, 

48 Ellipsis.__class__, 

49} 

50 

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

52BUILTIN_COLLECTIONS: set[type[Any]] = { 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

53 list, 

54 set, 

55 tuple, 

56 frozenset, 

57 dict, 

58 OrderedDict, 

59 defaultdict, 

60 deque, 

61} 

62 

63 

64def sequence_like(v: Any) -> bool: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

65 return isinstance(v, (list, tuple, set, frozenset, GeneratorType, deque)) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

66 

67 

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

69 try: 

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

71 except TypeError: 

72 return False 

73 

74 

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

76 try: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

77 return isinstance(cls, type) and issubclass(cls, class_or_tuple) 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

78 except TypeError: 1abcdefghijkl

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

80 return False 1abcdefghijkl

81 raise # pragma: no cover 

82 

83 

84def is_model_class(cls: Any) -> TypeGuard[type[BaseModel]]: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

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

86 unlike raw calls to lenient_issubclass. 

87 """ 

88 from ..main import BaseModel 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

89 

90 return lenient_issubclass(cls, BaseModel) and cls is not BaseModel 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

91 

92 

93def is_valid_identifier(identifier: str) -> bool: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

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

95 :param identifier: The identifier to test. 

96 :return: True if the identifier is valid. 

97 """ 

98 return identifier.isidentifier() and not keyword.iskeyword(identifier) 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

99 

100 

101KeyType = TypeVar('KeyType') 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

102 

103 

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

105 updated_mapping = mapping.copy() 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

106 for updating_mapping in updating_mappings: 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

107 for k, v in updating_mapping.items(): 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

108 if k in updated_mapping and isinstance(updated_mapping[k], dict) and isinstance(v, dict): 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

109 updated_mapping[k] = deep_update(updated_mapping[k], v) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

110 else: 

111 updated_mapping[k] = v 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

112 return updated_mapping 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

113 

114 

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

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

117 

118 

119T = TypeVar('T') 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

120 

121 

122def unique_list( 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

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

124 *, 

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

126) -> list[T]: 

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

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

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

130 """ 

131 result: list[T] = [] 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

132 result_names: list[str] = [] 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

133 for v in input_list: 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

134 v_name = name_factory(v) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

135 if v_name not in result_names: 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

136 result_names.append(v_name) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

137 result.append(v) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

138 else: 

139 result[result_names.index(v_name)] = v 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

140 

141 return result 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

142 

143 

144class ValueItems(_repr.Representation): 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

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

146 

147 __slots__ = ('_items', '_type') 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

148 

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

150 items = self._coerce_items(items) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

151 

152 if isinstance(value, (list, tuple)): 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

153 items = self._normalize_indexes(items, len(value)) # type: ignore 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

154 

155 self._items: MappingIntStrAny = items # type: ignore 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

156 

157 def is_excluded(self, item: Any) -> bool: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

158 """Check if item is fully excluded. 

159 

160 :param item: key or index of a value 

161 """ 

162 return self.is_true(self._items.get(item)) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

163 

164 def is_included(self, item: Any) -> bool: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

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

166 

167 :param item: key or index of value 

168 """ 

169 return item in self._items 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

170 

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

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

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

174 """ 

175 item = self._items.get(e) # type: ignore 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

176 return item if not self.is_true(item) else None 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

177 

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

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

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

181 

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

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

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

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

186 """ 

187 normalized_items: dict[int | str, Any] = {} 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

188 all_items = None 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

189 for i, v in items.items(): 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

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

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

192 if i == '__all__': 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

193 all_items = self._coerce_value(v) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

194 continue 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

195 if not isinstance(i, int): 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

196 raise TypeError( 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

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

198 'expected integer keys or keyword "__all__"' 

199 ) 

200 normalized_i = v_length + i if i < 0 else i 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

201 normalized_items[normalized_i] = self.merge(v, normalized_items.get(normalized_i)) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

202 

203 if not all_items: 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

204 return normalized_items 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

205 if self.is_true(all_items): 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

206 for i in range(v_length): 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

207 normalized_items.setdefault(i, ...) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

208 return normalized_items 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

209 for i in range(v_length): 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

210 normalized_item = normalized_items.setdefault(i, {}) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

211 if not self.is_true(normalized_item): 211 ↛ 209line 211 didn't jump to line 209 because the condition on line 211 was always true1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

212 normalized_items[i] = self.merge(all_items, normalized_item) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

213 return normalized_items 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

214 

215 @classmethod 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

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

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

218 

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

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

221 Ellipsis as values. 

222 

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

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

225 

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

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

228 `intersect` is set to `True`. 

229 """ 

230 override = cls._coerce_value(override) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

231 base = cls._coerce_value(base) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

232 if override is None: 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

233 return base 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

234 if cls.is_true(base) or base is None: 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

235 return override 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

236 if cls.is_true(override): 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

237 return base if intersect else override 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

238 

239 # intersection or union of keys while preserving ordering: 

240 if intersect: 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

241 merge_keys = [k for k in base if k in override] + [k for k in override if k in base] 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

242 else: 

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

244 

245 merged: dict[int | str, Any] = {} 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

246 for k in merge_keys: 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

247 merged_item = cls.merge(base.get(k), override.get(k), intersect=intersect) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

248 if merged_item is not None: 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

249 merged[k] = merged_item 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

250 

251 return merged 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

252 

253 @staticmethod 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

254 def _coerce_items(items: AbstractSetIntStr | MappingIntStrAny) -> MappingIntStrAny: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

255 if isinstance(items, typing.Mapping): 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

256 pass 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

257 elif isinstance(items, typing.AbstractSet): 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

258 items = dict.fromkeys(items, ...) # type: ignore 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

259 else: 

260 class_name = getattr(items, '__class__', '???') 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

261 raise TypeError(f'Unexpected type of exclude value {class_name}') 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

262 return items # type: ignore 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

263 

264 @classmethod 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

265 def _coerce_value(cls, value: Any) -> Any: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

266 if value is None or cls.is_true(value): 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

267 return value 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

268 return cls._coerce_items(value) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

269 

270 @staticmethod 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

271 def is_true(v: Any) -> bool: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

272 return v is True or v is ... 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

273 

274 def __repr_args__(self) -> _repr.ReprArgs: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

275 return [(None, self._items)] 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

276 

277 

278if typing.TYPE_CHECKING: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

279 

280 def ClassAttribute(name: str, value: T) -> T: ... 

281 

282else: 

283 

284 class ClassAttribute: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

285 """Hide class attribute from its instances.""" 

286 

287 __slots__ = 'name', 'value' 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

288 

289 def __init__(self, name: str, value: Any) -> None: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

290 self.name = name 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

291 self.value = value 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

292 

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

294 if instance is None: 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

295 return self.value 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

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

297 

298 

299Obj = TypeVar('Obj') 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

300 

301 

302def smart_deepcopy(obj: Obj) -> Obj: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

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

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

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

306 """ 

307 obj_type = obj.__class__ 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

308 if obj_type in IMMUTABLE_NON_COLLECTIONS_TYPES: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

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

310 try: 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

311 if not obj and obj_type in BUILTIN_COLLECTIONS: 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

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

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

314 except (TypeError, ValueError, RuntimeError): 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

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

316 pass 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

317 

318 return deepcopy(obj) # slowest way when we actually might need a deepcopy 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

319 

320 

321_SENTINEL = object() 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

322 

323 

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

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

326 

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

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

329 True 

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

331 False 

332 """ 

333 for left_item, right_item in zip_longest(left, right, fillvalue=_SENTINEL): 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

334 if left_item is not right_item: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

335 return False 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

336 return True 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

337 

338 

339@dataclasses.dataclass(frozen=True) 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

340class SafeGetItemProxy: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

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

342 

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

344 """ 

345 

346 # Define __slots__manually for performances 

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

348 __slots__ = ('wrapped',) 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

349 

350 wrapped: Mapping[str, Any] 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

351 

352 def __getitem__(self, key: str, /) -> Any: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

353 return self.wrapped.get(key, _SENTINEL) 1mnabcdopqrstuvwxefghyzABCDEFijklGHIJKL

354 

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

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

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

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

359 if typing.TYPE_CHECKING: 1mnabcdopqrstuvwxefghyzABCDMNOPQRSTUVEFijklGHIJKL

360 

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

362 return self.wrapped.__contains__(key)