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
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-13 19:35 +0000
1"""Bucket of reusable internal utilities.
3This should be reduced as much as possible with functions only used in one place, moved to that place.
4"""
6from __future__ import annotations as _annotations 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
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
22from typing_extensions import TypeAlias, TypeGuard, deprecated 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
24from pydantic import PydanticDeprecatedSince211 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
26from . import _repr, _typing_extra 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
27from ._import_utils import import_cached_base_model 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
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
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}
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}
71def can_be_positional(param: Parameter) -> bool: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
72 """Return whether the parameter accepts a positional argument.
74 ```python {test="skip" lint="skip"}
75 def func(a, /, b, *, c):
76 pass
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
90def sequence_like(v: Any) -> bool: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
91 return isinstance(v, (list, tuple, set, frozenset, GeneratorType, deque)) 1abcdmnopqrstefghuvwxyzijklABCDEF
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
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
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
116 return lenient_issubclass(cls, BaseModel) and cls is not BaseModel 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
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
127KeyType = TypeVar('KeyType') 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
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
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
145T = TypeVar('T') 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
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
167 return result 1abcdmnopqrstefghuvwxyzijklABCDEF
170class ValueItems(_repr.Representation): 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
171 """Class for more convenient calculation of excluded or included fields on values."""
173 __slots__ = ('_items', '_type') 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
175 def __init__(self, value: Any, items: AbstractSetIntStr | MappingIntStrAny) -> None: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
176 items = self._coerce_items(items) 1abcdmnopqrstefghuvwxyzijklABCDEF
178 if isinstance(value, (list, tuple)): 1abcdmnopqrstefghuvwxyzijklABCDEF
179 items = self._normalize_indexes(items, len(value)) # type: ignore 1abcdmnopqrstefghuvwxyzijklABCDEF
181 self._items: MappingIntStrAny = items # type: ignore 1abcdmnopqrstefghuvwxyzijklABCDEF
183 def is_excluded(self, item: Any) -> bool: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
184 """Check if item is fully excluded.
186 :param item: key or index of a value
187 """
188 return self.is_true(self._items.get(item)) 1abcdmnopqrstefghuvwxyzijklABCDEF
190 def is_included(self, item: Any) -> bool: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
191 """Check if value is contained in self._items.
193 :param item: key or index of value
194 """
195 return item in self._items 1abcdmnopqrstefghuvwxyzijklABCDEF
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
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
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
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
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.
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.
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.
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
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
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
277 return merged 1abcdmnopqrstefghuvwxyzijklABCDEF
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
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
296 @staticmethod 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
297 def is_true(v: Any) -> bool: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
298 return v is True or v is ... 1abcdmnopqrstefghuvwxyzijklABCDEF
300 def __repr_args__(self) -> _repr.ReprArgs: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
301 return [(None, self._items)] 1abcdmnopqrstefghuvwxyzijklABCDEF
304if typing.TYPE_CHECKING: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
306 def LazyClassAttribute(name: str, get_value: Callable[[], T]) -> T: ...
308else:
310 class LazyClassAttribute: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
311 """A descriptor exposing an attribute only accessible on a class (hidden from instances).
313 The attribute is lazily computed and cached during the first access.
314 """
316 def __init__(self, name: str, get_value: Callable[[], Any]) -> None: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
317 self.name = name 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
318 self.get_value = get_value 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
320 @cached_property 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
321 def value(self) -> Any: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
322 return self.get_value() 1abcdmnopqrstefghuvwxyzijklABCDEF
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
330Obj = TypeVar('Obj') 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
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
349 return deepcopy(obj) # slowest way when we actually might need a deepcopy 1abcdmnopqrstefghuvwxyzijklABCDEF
352_SENTINEL = object() 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
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`.
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
370@dataclasses.dataclass(frozen=True) 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
371class SafeGetItemProxy: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
372 """Wrapper redirecting `__getitem__` to `get` with a sentinel value as default
374 This makes is safe to use in `operator.itemgetter` when some keys may be missing
375 """
377 # Define __slots__manually for performances
378 # @dataclasses.dataclass() only support slots=True in python>=3.10
379 __slots__ = ('wrapped',) 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
381 wrapped: Mapping[str, Any] 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
383 def __getitem__(self, key: str, /) -> Any: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
384 return self.wrapped.get(key, _SENTINEL) 1abcdmnopqrstefghuvwxyzijklABCDEF
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
392 def __contains__(self, key: str, /) -> bool:
393 return self.wrapped.__contains__(key)
396_ModelT = TypeVar('_ModelT', bound='BaseModel') 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
397_RT = TypeVar('_RT') 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
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.
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 """
408 def __init__(self, fget: Callable[[type[_ModelT]], _RT], /) -> None: 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
409 # Note: fget should be a classmethod:
410 self.fget = fget 1abcdmnopqrstefghuvwxyzGHIJKLMijklABCDEF
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