Coverage for pydantic/generics.py: 99.28%

176 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-08-15 13:26 +0000

1import sys 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

2import types 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

3import typing 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

4from typing import ( 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

5 TYPE_CHECKING, 

6 Any, 

7 ClassVar, 

8 Dict, 

9 ForwardRef, 

10 Generic, 

11 Iterator, 

12 List, 

13 Mapping, 

14 Optional, 

15 Tuple, 

16 Type, 

17 TypeVar, 

18 Union, 

19 cast, 

20) 

21from weakref import WeakKeyDictionary, WeakValueDictionary 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

22 

23from typing_extensions import Annotated, Literal as ExtLiteral 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

24 

25from pydantic.class_validators import gather_all_validators 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

26from pydantic.fields import DeferredType 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

27from pydantic.main import BaseModel, create_model 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

28from pydantic.types import JsonWrapper 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

29from pydantic.typing import display_as_type, get_all_type_hints, get_args, get_origin, typing_base 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

30from pydantic.utils import all_identical, lenient_issubclass 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

31 

32if sys.version_info >= (3, 10): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

33 from typing import _UnionGenericAlias 1abcdefghijklmPQRSTUnopqrst

34if sys.version_info >= (3, 8): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

35 from typing import Literal 1uvwxabcdefyzABghijklCDmPQRSTUnEFGHopqrst

36 

37GenericModelT = TypeVar('GenericModelT', bound='GenericModel') 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

38TypeVarType = Any # since mypy doesn't allow the use of TypeVar as a type 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

39 

40CacheKey = Tuple[Type[Any], Any, Tuple[Any, ...]] 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

41Parametrization = Mapping[TypeVarType, Type[Any]] 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

42 

43# weak dictionaries allow the dynamically created parametrized versions of generic models to get collected 

44# once they are no longer referenced by the caller. 

45if sys.version_info >= (3, 9): # Typing for weak dictionaries available at 3.9 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

46 GenericTypesCache = WeakValueDictionary[CacheKey, Type[BaseModel]] 1wxabcdefABghijklDmPQRSTUnGHopqrst

47 AssignedParameters = WeakKeyDictionary[Type[BaseModel], Parametrization] 1wxabcdefABghijklDmPQRSTUnGHopqrst

48else: 

49 GenericTypesCache = WeakValueDictionary 1IJuvKLyzMCNOEF

50 AssignedParameters = WeakKeyDictionary 1IJuvKLyzMCNOEF

51 

52# _generic_types_cache is a Mapping from __class_getitem__ arguments to the parametrized version of generic models. 

53# This ensures multiple calls of e.g. A[B] return always the same class. 

54_generic_types_cache = GenericTypesCache() 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

55 

56# _assigned_parameters is a Mapping from parametrized version of generic models to assigned types of parametrizations 

57# as captured during construction of the class (not instances). 

58# E.g., for generic model `Model[A, B]`, when parametrized model `Model[int, str]` is created, 

59# `Model[int, str]`: {A: int, B: str}` will be stored in `_assigned_parameters`. 

60# (This information is only otherwise available after creation from the class name string). 

61_assigned_parameters = AssignedParameters() 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

62 

63 

64class GenericModel(BaseModel): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

65 __slots__ = () 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

66 __concrete__: ClassVar[bool] = False 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

67 

68 if TYPE_CHECKING: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

69 # Putting this in a TYPE_CHECKING block allows us to replace `if Generic not in cls.__bases__` with 

70 # `not hasattr(cls, "__parameters__")`. This means we don't need to force non-concrete subclasses of 

71 # `GenericModel` to also inherit from `Generic`, which would require changes to the use of `create_model` below. 

72 __parameters__: ClassVar[Tuple[TypeVarType, ...]] 

73 

74 # Setting the return type as Type[Any] instead of Type[BaseModel] prevents PyCharm warnings 

75 def __class_getitem__(cls: Type[GenericModelT], params: Union[Type[Any], Tuple[Type[Any], ...]]) -> Type[Any]: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

76 """Instantiates a new class from a generic class `cls` and type variables `params`. 

77 

78 :param params: Tuple of types the class . Given a generic class 

79 `Model` with 2 type variables and a concrete model `Model[str, int]`, 

80 the value `(str, int)` would be passed to `params`. 

81 :return: New model class inheriting from `cls` with instantiated 

82 types described by `params`. If no parameters are given, `cls` is 

83 returned as is. 

84 

85 """ 

86 

87 def _cache_key(_params: Any) -> CacheKey: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

88 args = get_args(_params) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

89 # python returns a list for Callables, which is not hashable 

90 if len(args) == 2 and isinstance(args[0], list): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

91 args = (tuple(args[0]), args[1]) 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

92 return cls, _params, args 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

93 

94 cached = _generic_types_cache.get(_cache_key(params)) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

95 if cached is not None: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

96 return cached 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

97 if cls.__concrete__ and Generic not in cls.__bases__: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

98 raise TypeError('Cannot parameterize a concrete instantiation of a generic model') 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

99 if not isinstance(params, tuple): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

100 params = (params,) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

101 if cls is GenericModel and any(isinstance(param, TypeVar) for param in params): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

102 raise TypeError('Type parameters should be placed on typing.Generic, not GenericModel') 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

103 if not hasattr(cls, '__parameters__'): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

104 raise TypeError(f'Type {cls.__name__} must inherit from typing.Generic before being parameterized') 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

105 

106 check_parameters_count(cls, params) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

107 # Build map from generic typevars to passed params 

108 typevars_map: Dict[TypeVarType, Type[Any]] = dict(zip(cls.__parameters__, params)) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

109 if all_identical(typevars_map.keys(), typevars_map.values()) and typevars_map: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

110 return cls # if arguments are equal to parameters it's the same object 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

111 

112 # Create new model with original model as parent inserting fields with DeferredType. 

113 model_name = cls.__concrete_name__(params) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

114 validators = gather_all_validators(cls) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

115 

116 type_hints = get_all_type_hints(cls).items() 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

117 instance_type_hints = {k: v for k, v in type_hints if get_origin(v) is not ClassVar} 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

118 

119 fields = {k: (DeferredType(), cls.__fields__[k].field_info) for k in instance_type_hints if k in cls.__fields__} 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

120 

121 model_module, called_globally = get_caller_frame_info() 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

122 created_model = cast( 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

123 Type[GenericModel], # casting ensures mypy is aware of the __concrete__ and __parameters__ attributes 

124 create_model( 

125 model_name, 

126 __module__=model_module or cls.__module__, 

127 __base__=(cls,) + tuple(cls.__parameterized_bases__(typevars_map)), 

128 __config__=None, 

129 __validators__=validators, 

130 __cls_kwargs__=None, 

131 **fields, 

132 ), 

133 ) 

134 

135 _assigned_parameters[created_model] = typevars_map 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

136 

137 if called_globally: # create global reference and therefore allow pickling 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

138 object_by_reference = None 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

139 reference_name = model_name 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

140 reference_module_globals = sys.modules[created_model.__module__].__dict__ 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

141 while object_by_reference is not created_model: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

142 object_by_reference = reference_module_globals.setdefault(reference_name, created_model) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

143 reference_name += '_' 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

144 

145 created_model.Config = cls.Config 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

146 

147 # Find any typevars that are still present in the model. 

148 # If none are left, the model is fully "concrete", otherwise the new 

149 # class is a generic class as well taking the found typevars as 

150 # parameters. 

151 new_params = tuple( 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

152 {param: None for param in iter_contained_typevars(typevars_map.values())} 

153 ) # use dict as ordered set 

154 created_model.__concrete__ = not new_params 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

155 if new_params: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

156 created_model.__parameters__ = new_params 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

157 

158 # Save created model in cache so we don't end up creating duplicate 

159 # models that should be identical. 

160 _generic_types_cache[_cache_key(params)] = created_model 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

161 if len(params) == 1: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

162 _generic_types_cache[_cache_key(params[0])] = created_model 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

163 

164 # Recursively walk class type hints and replace generic typevars 

165 # with concrete types that were passed. 

166 _prepare_model_fields(created_model, fields, instance_type_hints, typevars_map) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

167 

168 return created_model 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

169 

170 @classmethod 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

171 def __concrete_name__(cls: Type[Any], params: Tuple[Type[Any], ...]) -> str: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

172 """Compute class name for child classes. 

173 

174 :param params: Tuple of types the class . Given a generic class 

175 `Model` with 2 type variables and a concrete model `Model[str, int]`, 

176 the value `(str, int)` would be passed to `params`. 

177 :return: String representing a the new class where `params` are 

178 passed to `cls` as type variables. 

179 

180 This method can be overridden to achieve a custom naming scheme for GenericModels. 

181 """ 

182 param_names = [display_as_type(param) for param in params] 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

183 params_component = ', '.join(param_names) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

184 return f'{cls.__name__}[{params_component}]' 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

185 

186 @classmethod 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

187 def __parameterized_bases__(cls, typevars_map: Parametrization) -> Iterator[Type[Any]]: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

188 """ 

189 Returns unbound bases of cls parameterised to given type variables 

190 

191 :param typevars_map: Dictionary of type applications for binding subclasses. 

192 Given a generic class `Model` with 2 type variables [S, T] 

193 and a concrete model `Model[str, int]`, 

194 the value `{S: str, T: int}` would be passed to `typevars_map`. 

195 :return: an iterator of generic sub classes, parameterised by `typevars_map` 

196 and other assigned parameters of `cls` 

197 

198 e.g.: 

199 ``` 

200 class A(GenericModel, Generic[T]): 

201 ... 

202 

203 class B(A[V], Generic[V]): 

204 ... 

205 

206 assert A[int] in B.__parameterized_bases__({V: int}) 

207 ``` 

208 """ 

209 

210 def build_base_model( 1uvwxabcdefyzABghijklCDmPQRSTUnEFGHopqrst

211 base_model: Type[GenericModel], mapped_types: Parametrization 

212 ) -> Iterator[Type[GenericModel]]: 

213 base_parameters = tuple(mapped_types[param] for param in base_model.__parameters__) 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

214 parameterized_base = base_model.__class_getitem__(base_parameters) 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

215 if parameterized_base is base_model or parameterized_base is cls: 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

216 # Avoid duplication in MRO 

217 return 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

218 yield parameterized_base 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

219 

220 for base_model in cls.__bases__: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

221 if not issubclass(base_model, GenericModel): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

222 # not a class that can be meaningfully parameterized 

223 continue 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

224 elif not getattr(base_model, '__parameters__', None): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

225 # base_model is "GenericModel" (and has no __parameters__) 

226 # or 

227 # base_model is already concrete, and will be included transitively via cls. 

228 continue 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

229 elif cls in _assigned_parameters: 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

230 if base_model in _assigned_parameters: 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

231 # cls is partially parameterised but not from base_model 

232 # e.g. cls = B[S], base_model = A[S] 

233 # B[S][int] should subclass A[int], (and will be transitively via B[int]) 

234 # but it's not viable to consistently subclass types with arbitrary construction 

235 # So don't attempt to include A[S][int] 

236 continue 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

237 else: # base_model not in _assigned_parameters: 

238 # cls is partially parameterized, base_model is original generic 

239 # e.g. cls = B[str, T], base_model = B[S, T] 

240 # Need to determine the mapping for the base_model parameters 

241 mapped_types: Parametrization = { 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

242 key: typevars_map.get(value, value) for key, value in _assigned_parameters[cls].items() 

243 } 

244 yield from build_base_model(base_model, mapped_types) 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

245 else: 

246 # cls is base generic, so base_class has a distinct base 

247 # can construct the Parameterised base model using typevars_map directly 

248 yield from build_base_model(base_model, typevars_map) 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

249 

250 

251def replace_types(type_: Any, type_map: Mapping[Any, Any]) -> Any: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

252 """Return type with all occurrences of `type_map` keys recursively replaced with their values. 

253 

254 :param type_: Any type, class or generic alias 

255 :param type_map: Mapping from `TypeVar` instance to concrete types. 

256 :return: New type representing the basic structure of `type_` with all 

257 `typevar_map` keys recursively replaced. 

258 

259 >>> replace_types(Tuple[str, Union[List[str], float]], {str: int}) 

260 Tuple[int, Union[List[int], float]] 

261 

262 """ 

263 if not type_map: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

264 return type_ 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

265 

266 type_args = get_args(type_) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

267 origin_type = get_origin(type_) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

268 

269 if origin_type is Annotated: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

270 annotated_type, *annotations = type_args 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

271 return Annotated[replace_types(annotated_type, type_map), tuple(annotations)] 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

272 

273 if (origin_type is ExtLiteral) or (sys.version_info >= (3, 8) and origin_type is Literal): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

274 return type_map.get(type_, type_) 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

275 # Having type args is a good indicator that this is a typing module 

276 # class instantiation or a generic alias of some sort. 

277 if type_args: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

278 resolved_type_args = tuple(replace_types(arg, type_map) for arg in type_args) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

279 if all_identical(type_args, resolved_type_args): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

280 # If all arguments are the same, there is no need to modify the 

281 # type or create a new object at all 

282 return type_ 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

283 if ( 1uvwxyzABCDEFGH

284 origin_type is not None 

285 and isinstance(type_, typing_base) 

286 and not isinstance(origin_type, typing_base) 

287 and getattr(type_, '_name', None) is not None 

288 ): 

289 # In python < 3.9 generic aliases don't exist so any of these like `list`, 

290 # `type` or `collections.abc.Callable` need to be translated. 

291 # See: https://www.python.org/dev/peps/pep-0585 

292 origin_type = getattr(typing, type_._name) 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

293 assert origin_type is not None 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

294 # PEP-604 syntax (Ex.: list | str) is represented with a types.UnionType object that does not have __getitem__. 

295 # We also cannot use isinstance() since we have to compare types. 

296 if sys.version_info >= (3, 10) and origin_type is types.UnionType: # noqa: E721 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

297 return _UnionGenericAlias(origin_type, resolved_type_args) 1abcdefghijklmnopqrst

298 return origin_type[resolved_type_args] 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

299 

300 # We handle pydantic generic models separately as they don't have the same 

301 # semantics as "typing" classes or generic aliases 

302 if not origin_type and lenient_issubclass(type_, GenericModel) and not type_.__concrete__: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

303 type_args = type_.__parameters__ 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

304 resolved_type_args = tuple(replace_types(t, type_map) for t in type_args) 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

305 if all_identical(type_args, resolved_type_args): 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

306 return type_ 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

307 return type_[resolved_type_args] 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

308 

309 # Handle special case for typehints that can have lists as arguments. 

310 # `typing.Callable[[int, str], int]` is an example for this. 

311 if isinstance(type_, (List, list)): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

312 resolved_list = list(replace_types(element, type_map) for element in type_) 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

313 if all_identical(type_, resolved_list): 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

314 return type_ 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

315 return resolved_list 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

316 

317 # For JsonWrapperValue, need to handle its inner type to allow correct parsing 

318 # of generic Json arguments like Json[T] 

319 if not origin_type and lenient_issubclass(type_, JsonWrapper): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

320 type_.inner_type = replace_types(type_.inner_type, type_map) 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

321 return type_ 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

322 

323 # If all else fails, we try to resolve the type directly and otherwise just 

324 # return the input with no modifications. 

325 new_type = type_map.get(type_, type_) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

326 # Convert string to ForwardRef 

327 if isinstance(new_type, str): 327 ↛ 328line 327 didn't jump to line 328 because the condition on line 327 was never true1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

328 return ForwardRef(new_type) 

329 else: 

330 return new_type 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

331 

332 

333def check_parameters_count(cls: Type[GenericModel], parameters: Tuple[Any, ...]) -> None: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

334 actual = len(parameters) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

335 expected = len(cls.__parameters__) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

336 if actual != expected: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

337 description = 'many' if actual > expected else 'few' 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

338 raise TypeError(f'Too {description} parameters for {cls.__name__}; actual {actual}, expected {expected}') 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

339 

340 

341DictValues: Type[Any] = {}.values().__class__ 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

342 

343 

344def iter_contained_typevars(v: Any) -> Iterator[TypeVarType]: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

345 """Recursively iterate through all subtypes and type args of `v` and yield any typevars that are found.""" 

346 if isinstance(v, TypeVar): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

347 yield v 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

348 elif hasattr(v, '__parameters__') and not get_origin(v) and lenient_issubclass(v, GenericModel): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

349 yield from v.__parameters__ 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

350 elif isinstance(v, (DictValues, list)): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

351 for var in v: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

352 yield from iter_contained_typevars(var) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

353 else: 

354 args = get_args(v) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

355 for arg in args: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

356 yield from iter_contained_typevars(arg) 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

357 

358 

359def get_caller_frame_info() -> Tuple[Optional[str], bool]: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

360 """ 

361 Used inside a function to check whether it was called globally 

362 

363 Will only work against non-compiled code, therefore used only in pydantic.generics 

364 

365 :returns Tuple[module_name, called_globally] 

366 """ 

367 try: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

368 previous_caller_frame = sys._getframe(2) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

369 except ValueError as e: 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

370 raise RuntimeError('This function must be used inside another function') from e 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

371 except AttributeError: # sys module does not have _getframe function, so there's nothing we can do about it 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

372 return None, False 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

373 frame_globals = previous_caller_frame.f_globals 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

374 return frame_globals.get('__name__'), previous_caller_frame.f_locals is frame_globals 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

375 

376 

377def _prepare_model_fields( 1uvwxabcdefyzABghijklCDmPQRSTUnEFGHopqrst

378 created_model: Type[GenericModel], 

379 fields: Mapping[str, Any], 

380 instance_type_hints: Mapping[str, type], 

381 typevars_map: Mapping[Any, type], 

382) -> None: 

383 """ 

384 Replace DeferredType fields with concrete type hints and prepare them. 

385 """ 

386 

387 for key, field in created_model.__fields__.items(): 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

388 if key not in fields: 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

389 assert field.type_.__class__ is not DeferredType 1IJuvwxabcdefKLyzABghijklMCDmnNOEFGHopqrst

390 # https://github.com/nedbat/coveragepy/issues/198 

391 continue # pragma: no cover 1abcdefghijklmnopqrst

392 

393 assert field.type_.__class__ is DeferredType, field.type_.__class__ 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

394 

395 field_type_hint = instance_type_hints[key] 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

396 concrete_type = replace_types(field_type_hint, typevars_map) 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

397 field.type_ = concrete_type 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

398 field.outer_type_ = concrete_type 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

399 field.prepare() 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst

400 created_model.__annotations__[key] = concrete_type 1IJuvwxabcdefKLyzABghijklMCDmPQRSTUnNOEFGHopqrst