Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pydantic/v1/typing.py: 19%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

263 statements  

1import functools 

2import operator 

3import sys 

4import typing 

5from collections.abc import Callable 

6from os import PathLike 

7from typing import ( # type: ignore 

8 TYPE_CHECKING, 

9 AbstractSet, 

10 Any, 

11 Callable as TypingCallable, 

12 ClassVar, 

13 Dict, 

14 ForwardRef, 

15 Generator, 

16 Iterable, 

17 List, 

18 Mapping, 

19 NewType, 

20 Optional, 

21 Sequence, 

22 Set, 

23 Tuple, 

24 Type, 

25 TypeVar, 

26 Union, 

27 _eval_type, 

28 cast, 

29 get_type_hints, 

30) 

31 

32from typing_extensions import ( 

33 Annotated, 

34 Final, 

35 Literal, 

36 NotRequired as TypedDictNotRequired, 

37 Required as TypedDictRequired, 

38) 

39 

40try: 

41 from typing import _TypingBase as typing_base # type: ignore 

42except ImportError: 

43 from typing import _Final as typing_base # type: ignore 

44 

45try: 

46 from typing import GenericAlias as TypingGenericAlias # type: ignore 

47except ImportError: 

48 # python < 3.9 does not have GenericAlias (list[int], tuple[str, ...] and so on) 

49 TypingGenericAlias = () 

50 

51try: 

52 from types import UnionType as TypesUnionType # type: ignore 

53except ImportError: 

54 # python < 3.10 does not have UnionType (str | int, byte | bool and so on) 

55 TypesUnionType = () 

56 

57 

58if sys.version_info < (3, 9): 

59 

60 def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: 

61 return type_._evaluate(globalns, localns) 

62 

63elif sys.version_info < (3, 12, 4): 

64 

65 def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: 

66 # Even though it is the right signature for python 3.9, mypy complains with 

67 # `error: Too many arguments for "_evaluate" of "ForwardRef"` hence the cast... 

68 # Python 3.13/3.12.4+ made `recursive_guard` a kwarg, so name it explicitly to avoid: 

69 # TypeError: ForwardRef._evaluate() missing 1 required keyword-only argument: 'recursive_guard' 

70 return cast(Any, type_)._evaluate(globalns, localns, recursive_guard=set()) 

71 

72else: 

73 

74 def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: 

75 # Pydantic 1.x will not support PEP 695 syntax, but provide `type_params` to avoid 

76 # warnings: 

77 return cast(Any, type_)._evaluate(globalns, localns, type_params=(), recursive_guard=set()) 

78 

79 

80if sys.version_info < (3, 9): 

81 # Ensure we always get all the whole `Annotated` hint, not just the annotated type. 

82 # For 3.7 to 3.8, `get_type_hints` doesn't recognize `typing_extensions.Annotated`, 

83 # so it already returns the full annotation 

84 get_all_type_hints = get_type_hints 

85 

86else: 

87 

88 def get_all_type_hints(obj: Any, globalns: Any = None, localns: Any = None) -> Any: 

89 return get_type_hints(obj, globalns, localns, include_extras=True) 

90 

91 

92_T = TypeVar('_T') 

93 

94AnyCallable = TypingCallable[..., Any] 

95NoArgAnyCallable = TypingCallable[[], Any] 

96 

97# workaround for https://github.com/python/mypy/issues/9496 

98AnyArgTCallable = TypingCallable[..., _T] 

99 

100 

101# Annotated[...] is implemented by returning an instance of one of these classes, depending on 

102# python/typing_extensions version. 

103AnnotatedTypeNames = {'AnnotatedMeta', '_AnnotatedAlias'} 

104 

105 

106LITERAL_TYPES: Set[Any] = {Literal} 

107if hasattr(typing, 'Literal'): 

108 LITERAL_TYPES.add(typing.Literal) 

109 

110 

111if sys.version_info < (3, 8): 

112 

113 def get_origin(t: Type[Any]) -> Optional[Type[Any]]: 

114 if type(t).__name__ in AnnotatedTypeNames: 

115 # weirdly this is a runtime requirement, as well as for mypy 

116 return cast(Type[Any], Annotated) 

117 return getattr(t, '__origin__', None) 

118 

119else: 

120 from typing import get_origin as _typing_get_origin 

121 

122 def get_origin(tp: Type[Any]) -> Optional[Type[Any]]: 

123 """ 

124 We can't directly use `typing.get_origin` since we need a fallback to support 

125 custom generic classes like `ConstrainedList` 

126 It should be useless once https://github.com/cython/cython/issues/3537 is 

127 solved and https://github.com/pydantic/pydantic/pull/1753 is merged. 

128 """ 

129 if type(tp).__name__ in AnnotatedTypeNames: 

130 return cast(Type[Any], Annotated) # mypy complains about _SpecialForm 

131 return _typing_get_origin(tp) or getattr(tp, '__origin__', None) 

132 

133 

134if sys.version_info < (3, 8): 

135 from typing import _GenericAlias 

136 

137 def get_args(t: Type[Any]) -> Tuple[Any, ...]: 

138 """Compatibility version of get_args for python 3.7. 

139 

140 Mostly compatible with the python 3.8 `typing` module version 

141 and able to handle almost all use cases. 

142 """ 

143 if type(t).__name__ in AnnotatedTypeNames: 

144 return t.__args__ + t.__metadata__ 

145 if isinstance(t, _GenericAlias): 

146 res = t.__args__ 

147 if t.__origin__ is Callable and res and res[0] is not Ellipsis: 

148 res = (list(res[:-1]), res[-1]) 

149 return res 

150 return getattr(t, '__args__', ()) 

151 

152else: 

153 from typing import get_args as _typing_get_args 

154 

155 def _generic_get_args(tp: Type[Any]) -> Tuple[Any, ...]: 

156 """ 

157 In python 3.9, `typing.Dict`, `typing.List`, ... 

158 do have an empty `__args__` by default (instead of the generic ~T for example). 

159 In order to still support `Dict` for example and consider it as `Dict[Any, Any]`, 

160 we retrieve the `_nparams` value that tells us how many parameters it needs. 

161 """ 

162 if hasattr(tp, '_nparams'): 

163 return (Any,) * tp._nparams 

164 # Special case for `tuple[()]`, which used to return ((),) with `typing.Tuple` 

165 # in python 3.10- but now returns () for `tuple` and `Tuple`. 

166 # This will probably be clarified in pydantic v2 

167 try: 

168 if tp == Tuple[()] or sys.version_info >= (3, 9) and tp == tuple[()]: # type: ignore[misc] 

169 return ((),) 

170 # there is a TypeError when compiled with cython 

171 except TypeError: # pragma: no cover 

172 pass 

173 return () 

174 

175 def get_args(tp: Type[Any]) -> Tuple[Any, ...]: 

176 """Get type arguments with all substitutions performed. 

177 

178 For unions, basic simplifications used by Union constructor are performed. 

179 Examples:: 

180 get_args(Dict[str, int]) == (str, int) 

181 get_args(int) == () 

182 get_args(Union[int, Union[T, int], str][int]) == (int, str) 

183 get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) 

184 get_args(Callable[[], T][int]) == ([], int) 

185 """ 

186 if type(tp).__name__ in AnnotatedTypeNames: 

187 return tp.__args__ + tp.__metadata__ 

188 # the fallback is needed for the same reasons as `get_origin` (see above) 

189 return _typing_get_args(tp) or getattr(tp, '__args__', ()) or _generic_get_args(tp) 

190 

191 

192if sys.version_info < (3, 9): 

193 

194 def convert_generics(tp: Type[Any]) -> Type[Any]: 

195 """Python 3.9 and older only supports generics from `typing` module. 

196 They convert strings to ForwardRef automatically. 

197 

198 Examples:: 

199 typing.List['Hero'] == typing.List[ForwardRef('Hero')] 

200 """ 

201 return tp 

202 

203else: 

204 

205 def convert_generics(tp: Type[Any]) -> Type[Any]: 

206 """ 

207 Recursively searches for `str` type hints and replaces them with ForwardRef. 

208 

209 Examples:: 

210 convert_generics(list['Hero']) == list[ForwardRef('Hero')] 

211 convert_generics(dict['Hero', 'Team']) == dict[ForwardRef('Hero'), ForwardRef('Team')] 

212 convert_generics(typing.Dict['Hero', 'Team']) == typing.Dict[ForwardRef('Hero'), ForwardRef('Team')] 

213 convert_generics(list[str | 'Hero'] | int) == list[str | ForwardRef('Hero')] | int 

214 """ 

215 origin = get_origin(tp) 

216 if not origin or not hasattr(tp, '__args__'): 

217 return tp 

218 

219 args = get_args(tp) 

220 

221 # typing.Annotated needs special treatment 

222 if origin is Annotated: 

223 return Annotated[(convert_generics(args[0]), *args[1:])] # type: ignore 

224 

225 # recursively replace `str` instances inside of `GenericAlias` with `ForwardRef(arg)` 

226 converted = tuple( 

227 ForwardRef(arg) if isinstance(arg, str) and isinstance(tp, TypingGenericAlias) else convert_generics(arg) 

228 for arg in args 

229 ) 

230 

231 if converted == args: 

232 return tp 

233 elif isinstance(tp, TypingGenericAlias): 

234 return TypingGenericAlias(origin, converted) 

235 elif isinstance(tp, TypesUnionType): 

236 # recreate types.UnionType (PEP604, Python >= 3.10) 

237 return functools.reduce(operator.or_, converted) # type: ignore 

238 else: 

239 try: 

240 setattr(tp, '__args__', converted) 

241 except AttributeError: 

242 pass 

243 return tp 

244 

245 

246if sys.version_info < (3, 10): 

247 

248 def is_union(tp: Optional[Type[Any]]) -> bool: 

249 return tp is Union 

250 

251 WithArgsTypes = (TypingGenericAlias,) 

252 

253else: 

254 import types 

255 import typing 

256 

257 def is_union(tp: Optional[Type[Any]]) -> bool: 

258 return tp is Union or tp is types.UnionType # noqa: E721 

259 

260 WithArgsTypes = (typing._GenericAlias, types.GenericAlias, types.UnionType) 

261 

262 

263StrPath = Union[str, PathLike] 

264 

265 

266if TYPE_CHECKING: 

267 from pydantic.v1.fields import ModelField 

268 

269 TupleGenerator = Generator[Tuple[str, Any], None, None] 

270 DictStrAny = Dict[str, Any] 

271 DictAny = Dict[Any, Any] 

272 SetStr = Set[str] 

273 ListStr = List[str] 

274 IntStr = Union[int, str] 

275 AbstractSetIntStr = AbstractSet[IntStr] 

276 DictIntStrAny = Dict[IntStr, Any] 

277 MappingIntStrAny = Mapping[IntStr, Any] 

278 CallableGenerator = Generator[AnyCallable, None, None] 

279 ReprArgs = Sequence[Tuple[Optional[str], Any]] 

280 

281 MYPY = False 

282 if MYPY: 

283 AnyClassMethod = classmethod[Any] 

284 else: 

285 # classmethod[TargetType, CallableParamSpecType, CallableReturnType] 

286 AnyClassMethod = classmethod[Any, Any, Any] 

287 

288__all__ = ( 

289 'AnyCallable', 

290 'NoArgAnyCallable', 

291 'NoneType', 

292 'is_none_type', 

293 'display_as_type', 

294 'resolve_annotations', 

295 'is_callable_type', 

296 'is_literal_type', 

297 'all_literal_values', 

298 'is_namedtuple', 

299 'is_typeddict', 

300 'is_typeddict_special', 

301 'is_new_type', 

302 'new_type_supertype', 

303 'is_classvar', 

304 'is_finalvar', 

305 'update_field_forward_refs', 

306 'update_model_forward_refs', 

307 'TupleGenerator', 

308 'DictStrAny', 

309 'DictAny', 

310 'SetStr', 

311 'ListStr', 

312 'IntStr', 

313 'AbstractSetIntStr', 

314 'DictIntStrAny', 

315 'CallableGenerator', 

316 'ReprArgs', 

317 'AnyClassMethod', 

318 'CallableGenerator', 

319 'WithArgsTypes', 

320 'get_args', 

321 'get_origin', 

322 'get_sub_types', 

323 'typing_base', 

324 'get_all_type_hints', 

325 'is_union', 

326 'StrPath', 

327 'MappingIntStrAny', 

328) 

329 

330 

331NoneType = None.__class__ 

332 

333 

334NONE_TYPES: Tuple[Any, Any, Any] = (None, NoneType, Literal[None]) 

335 

336 

337if sys.version_info < (3, 8): 

338 # Even though this implementation is slower, we need it for python 3.7: 

339 # In python 3.7 "Literal" is not a builtin type and uses a different 

340 # mechanism. 

341 # for this reason `Literal[None] is Literal[None]` evaluates to `False`, 

342 # breaking the faster implementation used for the other python versions. 

343 

344 def is_none_type(type_: Any) -> bool: 

345 return type_ in NONE_TYPES 

346 

347elif sys.version_info[:2] == (3, 8): 

348 

349 def is_none_type(type_: Any) -> bool: 

350 for none_type in NONE_TYPES: 

351 if type_ is none_type: 

352 return True 

353 # With python 3.8, specifically 3.8.10, Literal "is" check sare very flakey 

354 # can change on very subtle changes like use of types in other modules, 

355 # hopefully this check avoids that issue. 

356 if is_literal_type(type_): # pragma: no cover 

357 return all_literal_values(type_) == (None,) 

358 return False 

359 

360else: 

361 

362 def is_none_type(type_: Any) -> bool: 

363 return type_ in NONE_TYPES 

364 

365 

366def display_as_type(v: Type[Any]) -> str: 

367 if not isinstance(v, typing_base) and not isinstance(v, WithArgsTypes) and not isinstance(v, type): 

368 v = v.__class__ 

369 

370 if is_union(get_origin(v)): 

371 return f'Union[{", ".join(map(display_as_type, get_args(v)))}]' 

372 

373 if isinstance(v, WithArgsTypes): 

374 # Generic alias are constructs like `list[int]` 

375 return str(v).replace('typing.', '') 

376 

377 try: 

378 return v.__name__ 

379 except AttributeError: 

380 # happens with typing objects 

381 return str(v).replace('typing.', '') 

382 

383 

384def resolve_annotations(raw_annotations: Dict[str, Type[Any]], module_name: Optional[str]) -> Dict[str, Type[Any]]: 

385 """ 

386 Partially taken from typing.get_type_hints. 

387 

388 Resolve string or ForwardRef annotations into type objects if possible. 

389 """ 

390 base_globals: Optional[Dict[str, Any]] = None 

391 if module_name: 

392 try: 

393 module = sys.modules[module_name] 

394 except KeyError: 

395 # happens occasionally, see https://github.com/pydantic/pydantic/issues/2363 

396 pass 

397 else: 

398 base_globals = module.__dict__ 

399 

400 annotations = {} 

401 for name, value in raw_annotations.items(): 

402 if isinstance(value, str): 

403 if (3, 10) > sys.version_info >= (3, 9, 8) or sys.version_info >= (3, 10, 1): 

404 value = ForwardRef(value, is_argument=False, is_class=True) 

405 else: 

406 value = ForwardRef(value, is_argument=False) 

407 try: 

408 if sys.version_info >= (3, 13): 

409 value = _eval_type(value, base_globals, None, type_params=()) 

410 else: 

411 value = _eval_type(value, base_globals, None) 

412 except NameError: 

413 # this is ok, it can be fixed with update_forward_refs 

414 pass 

415 annotations[name] = value 

416 return annotations 

417 

418 

419def is_callable_type(type_: Type[Any]) -> bool: 

420 return type_ is Callable or get_origin(type_) is Callable 

421 

422 

423def is_literal_type(type_: Type[Any]) -> bool: 

424 return Literal is not None and get_origin(type_) in LITERAL_TYPES 

425 

426 

427def literal_values(type_: Type[Any]) -> Tuple[Any, ...]: 

428 return get_args(type_) 

429 

430 

431def all_literal_values(type_: Type[Any]) -> Tuple[Any, ...]: 

432 """ 

433 This method is used to retrieve all Literal values as 

434 Literal can be used recursively (see https://www.python.org/dev/peps/pep-0586) 

435 e.g. `Literal[Literal[Literal[1, 2, 3], "foo"], 5, None]` 

436 """ 

437 if not is_literal_type(type_): 

438 return (type_,) 

439 

440 values = literal_values(type_) 

441 return tuple(x for value in values for x in all_literal_values(value)) 

442 

443 

444def is_namedtuple(type_: Type[Any]) -> bool: 

445 """ 

446 Check if a given class is a named tuple. 

447 It can be either a `typing.NamedTuple` or `collections.namedtuple` 

448 """ 

449 from pydantic.v1.utils import lenient_issubclass 

450 

451 return lenient_issubclass(type_, tuple) and hasattr(type_, '_fields') 

452 

453 

454def is_typeddict(type_: Type[Any]) -> bool: 

455 """ 

456 Check if a given class is a typed dict (from `typing` or `typing_extensions`) 

457 In 3.10, there will be a public method (https://docs.python.org/3.10/library/typing.html#typing.is_typeddict) 

458 """ 

459 from pydantic.v1.utils import lenient_issubclass 

460 

461 return lenient_issubclass(type_, dict) and hasattr(type_, '__total__') 

462 

463 

464def _check_typeddict_special(type_: Any) -> bool: 

465 return type_ is TypedDictRequired or type_ is TypedDictNotRequired 

466 

467 

468def is_typeddict_special(type_: Any) -> bool: 

469 """ 

470 Check if type is a TypedDict special form (Required or NotRequired). 

471 """ 

472 return _check_typeddict_special(type_) or _check_typeddict_special(get_origin(type_)) 

473 

474 

475test_type = NewType('test_type', str) 

476 

477 

478def is_new_type(type_: Type[Any]) -> bool: 

479 """ 

480 Check whether type_ was created using typing.NewType 

481 """ 

482 return isinstance(type_, test_type.__class__) and hasattr(type_, '__supertype__') # type: ignore 

483 

484 

485def new_type_supertype(type_: Type[Any]) -> Type[Any]: 

486 while hasattr(type_, '__supertype__'): 

487 type_ = type_.__supertype__ 

488 return type_ 

489 

490 

491def _check_classvar(v: Optional[Type[Any]]) -> bool: 

492 if v is None: 

493 return False 

494 

495 return v.__class__ == ClassVar.__class__ and getattr(v, '_name', None) == 'ClassVar' 

496 

497 

498def _check_finalvar(v: Optional[Type[Any]]) -> bool: 

499 """ 

500 Check if a given type is a `typing.Final` type. 

501 """ 

502 if v is None: 

503 return False 

504 

505 return v.__class__ == Final.__class__ and (sys.version_info < (3, 8) or getattr(v, '_name', None) == 'Final') 

506 

507 

508def is_classvar(ann_type: Type[Any]) -> bool: 

509 if _check_classvar(ann_type) or _check_classvar(get_origin(ann_type)): 

510 return True 

511 

512 # this is an ugly workaround for class vars that contain forward references and are therefore themselves 

513 # forward references, see #3679 

514 if ann_type.__class__ == ForwardRef and ann_type.__forward_arg__.startswith('ClassVar['): 

515 return True 

516 

517 return False 

518 

519 

520def is_finalvar(ann_type: Type[Any]) -> bool: 

521 return _check_finalvar(ann_type) or _check_finalvar(get_origin(ann_type)) 

522 

523 

524def update_field_forward_refs(field: 'ModelField', globalns: Any, localns: Any) -> None: 

525 """ 

526 Try to update ForwardRefs on fields based on this ModelField, globalns and localns. 

527 """ 

528 prepare = False 

529 if field.type_.__class__ == ForwardRef: 

530 prepare = True 

531 field.type_ = evaluate_forwardref(field.type_, globalns, localns or None) 

532 if field.outer_type_.__class__ == ForwardRef: 

533 prepare = True 

534 field.outer_type_ = evaluate_forwardref(field.outer_type_, globalns, localns or None) 

535 if prepare: 

536 field.prepare() 

537 

538 if field.sub_fields: 

539 for sub_f in field.sub_fields: 

540 update_field_forward_refs(sub_f, globalns=globalns, localns=localns) 

541 

542 if field.discriminator_key is not None: 

543 field.prepare_discriminated_union_sub_fields() 

544 

545 

546def update_model_forward_refs( 

547 model: Type[Any], 

548 fields: Iterable['ModelField'], 

549 json_encoders: Dict[Union[Type[Any], str, ForwardRef], AnyCallable], 

550 localns: 'DictStrAny', 

551 exc_to_suppress: Tuple[Type[BaseException], ...] = (), 

552) -> None: 

553 """ 

554 Try to update model fields ForwardRefs based on model and localns. 

555 """ 

556 if model.__module__ in sys.modules: 

557 globalns = sys.modules[model.__module__].__dict__.copy() 

558 else: 

559 globalns = {} 

560 

561 globalns.setdefault(model.__name__, model) 

562 

563 for f in fields: 

564 try: 

565 update_field_forward_refs(f, globalns=globalns, localns=localns) 

566 except exc_to_suppress: 

567 pass 

568 

569 for key in set(json_encoders.keys()): 

570 if isinstance(key, str): 

571 fr: ForwardRef = ForwardRef(key) 

572 elif isinstance(key, ForwardRef): 

573 fr = key 

574 else: 

575 continue 

576 

577 try: 

578 new_key = evaluate_forwardref(fr, globalns, localns or None) 

579 except exc_to_suppress: # pragma: no cover 

580 continue 

581 

582 json_encoders[new_key] = json_encoders.pop(key) 

583 

584 

585def get_class(type_: Type[Any]) -> Union[None, bool, Type[Any]]: 

586 """ 

587 Tries to get the class of a Type[T] annotation. Returns True if Type is used 

588 without brackets. Otherwise returns None. 

589 """ 

590 if type_ is type: 

591 return True 

592 

593 if get_origin(type_) is None: 

594 return None 

595 

596 args = get_args(type_) 

597 if not args or not isinstance(args[0], type): 

598 return True 

599 else: 

600 return args[0] 

601 

602 

603def get_sub_types(tp: Any) -> List[Any]: 

604 """ 

605 Return all the types that are allowed by type `tp` 

606 `tp` can be a `Union` of allowed types or an `Annotated` type 

607 """ 

608 origin = get_origin(tp) 

609 if origin is Annotated: 

610 return get_sub_types(get_args(tp)[0]) 

611 elif is_union(origin): 

612 return [x for t in get_args(tp) for x in get_sub_types(t)] 

613 else: 

614 return [tp]