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

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

266 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 

72elif sys.version_info < (3, 14): 

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 

79else: 

80 

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

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

83 # warnings: 

84 return typing.evaluate_forward_ref( 

85 type_, 

86 globals=globalns, 

87 locals=localns, 

88 type_params=(), 

89 _recursive_guard=set(), 

90 ) 

91 

92 

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

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

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

96 # so it already returns the full annotation 

97 get_all_type_hints = get_type_hints 

98 

99else: 

100 

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

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

103 

104 

105_T = TypeVar('_T') 

106 

107AnyCallable = TypingCallable[..., Any] 

108NoArgAnyCallable = TypingCallable[[], Any] 

109 

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

111AnyArgTCallable = TypingCallable[..., _T] 

112 

113 

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

115# python/typing_extensions version. 

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

117 

118 

119LITERAL_TYPES: Set[Any] = {Literal} 

120if hasattr(typing, 'Literal'): 

121 LITERAL_TYPES.add(typing.Literal) 

122 

123 

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

125 

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

127 if type(t).__name__ in AnnotatedTypeNames: 

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

129 return cast(Type[Any], Annotated) 

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

131 

132else: 

133 from typing import get_origin as _typing_get_origin 

134 

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

136 """ 

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

138 custom generic classes like `ConstrainedList` 

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

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

141 """ 

142 if type(tp).__name__ in AnnotatedTypeNames: 

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

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

145 

146 

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

148 from typing import _GenericAlias 

149 

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

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

152 

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

154 and able to handle almost all use cases. 

155 """ 

156 if type(t).__name__ in AnnotatedTypeNames: 

157 return t.__args__ + t.__metadata__ 

158 if isinstance(t, _GenericAlias): 

159 res = t.__args__ 

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

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

162 return res 

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

164 

165else: 

166 from typing import get_args as _typing_get_args 

167 

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

169 """ 

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

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

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

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

174 """ 

175 if hasattr(tp, '_nparams'): 

176 return (Any,) * tp._nparams 

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

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

179 # This will probably be clarified in pydantic v2 

180 try: 

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

182 return ((),) 

183 # there is a TypeError when compiled with cython 

184 except TypeError: # pragma: no cover 

185 pass 

186 return () 

187 

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

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

190 

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

192 Examples:: 

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

194 get_args(int) == () 

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

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

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

198 """ 

199 if type(tp).__name__ in AnnotatedTypeNames: 

200 return tp.__args__ + tp.__metadata__ 

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

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

203 

204 

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

206 

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

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

209 They convert strings to ForwardRef automatically. 

210 

211 Examples:: 

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

213 """ 

214 return tp 

215 

216else: 

217 

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

219 """ 

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

221 

222 Examples:: 

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

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

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

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

227 """ 

228 origin = get_origin(tp) 

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

230 return tp 

231 

232 args = get_args(tp) 

233 

234 # typing.Annotated needs special treatment 

235 if origin is Annotated: 

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

237 

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

239 converted = tuple( 

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

241 for arg in args 

242 ) 

243 

244 if converted == args: 

245 return tp 

246 elif isinstance(tp, TypingGenericAlias): 

247 return TypingGenericAlias(origin, converted) 

248 elif isinstance(tp, TypesUnionType): 

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

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

251 else: 

252 try: 

253 setattr(tp, '__args__', converted) 

254 except AttributeError: 

255 pass 

256 return tp 

257 

258 

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

260 

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

262 return tp is Union 

263 

264 WithArgsTypes = (TypingGenericAlias,) 

265 

266else: 

267 import types 

268 import typing 

269 

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

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

272 

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

274 

275 

276StrPath = Union[str, PathLike] 

277 

278 

279if TYPE_CHECKING: 

280 from pydantic.v1.fields import ModelField 

281 

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

283 DictStrAny = Dict[str, Any] 

284 DictAny = Dict[Any, Any] 

285 SetStr = Set[str] 

286 ListStr = List[str] 

287 IntStr = Union[int, str] 

288 AbstractSetIntStr = AbstractSet[IntStr] 

289 DictIntStrAny = Dict[IntStr, Any] 

290 MappingIntStrAny = Mapping[IntStr, Any] 

291 CallableGenerator = Generator[AnyCallable, None, None] 

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

293 

294 MYPY = False 

295 if MYPY: 

296 AnyClassMethod = classmethod[Any] 

297 else: 

298 # classmethod[TargetType, CallableParamSpecType, CallableReturnType] 

299 AnyClassMethod = classmethod[Any, Any, Any] 

300 

301__all__ = ( 

302 'AnyCallable', 

303 'NoArgAnyCallable', 

304 'NoneType', 

305 'is_none_type', 

306 'display_as_type', 

307 'resolve_annotations', 

308 'is_callable_type', 

309 'is_literal_type', 

310 'all_literal_values', 

311 'is_namedtuple', 

312 'is_typeddict', 

313 'is_typeddict_special', 

314 'is_new_type', 

315 'new_type_supertype', 

316 'is_classvar', 

317 'is_finalvar', 

318 'update_field_forward_refs', 

319 'update_model_forward_refs', 

320 'TupleGenerator', 

321 'DictStrAny', 

322 'DictAny', 

323 'SetStr', 

324 'ListStr', 

325 'IntStr', 

326 'AbstractSetIntStr', 

327 'DictIntStrAny', 

328 'CallableGenerator', 

329 'ReprArgs', 

330 'AnyClassMethod', 

331 'CallableGenerator', 

332 'WithArgsTypes', 

333 'get_args', 

334 'get_origin', 

335 'get_sub_types', 

336 'typing_base', 

337 'get_all_type_hints', 

338 'is_union', 

339 'StrPath', 

340 'MappingIntStrAny', 

341) 

342 

343 

344NoneType = None.__class__ 

345 

346 

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

348 

349 

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

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

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

353 # mechanism. 

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

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

356 

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

358 return type_ in NONE_TYPES 

359 

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

361 

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

363 for none_type in NONE_TYPES: 

364 if type_ is none_type: 

365 return True 

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

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

368 # hopefully this check avoids that issue. 

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

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

371 return False 

372 

373else: 

374 

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

376 return type_ in NONE_TYPES 

377 

378 

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

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

381 v = v.__class__ 

382 

383 if is_union(get_origin(v)): 

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

385 

386 if isinstance(v, WithArgsTypes): 

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

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

389 

390 try: 

391 return v.__name__ 

392 except AttributeError: 

393 # happens with typing objects 

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

395 

396 

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

398 """ 

399 Partially taken from typing.get_type_hints. 

400 

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

402 """ 

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

404 if module_name: 

405 try: 

406 module = sys.modules[module_name] 

407 except KeyError: 

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

409 pass 

410 else: 

411 base_globals = module.__dict__ 

412 

413 annotations = {} 

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

415 if isinstance(value, str): 

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

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

418 else: 

419 value = ForwardRef(value, is_argument=False) 

420 try: 

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

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

423 else: 

424 value = _eval_type(value, base_globals, None) 

425 except NameError: 

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

427 pass 

428 annotations[name] = value 

429 return annotations 

430 

431 

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

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

434 

435 

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

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

438 

439 

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

441 return get_args(type_) 

442 

443 

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

445 """ 

446 This method is used to retrieve all Literal values as 

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

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

449 """ 

450 if not is_literal_type(type_): 

451 return (type_,) 

452 

453 values = literal_values(type_) 

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

455 

456 

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

458 """ 

459 Check if a given class is a named tuple. 

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

461 """ 

462 from pydantic.v1.utils import lenient_issubclass 

463 

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

465 

466 

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

468 """ 

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

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

471 """ 

472 from pydantic.v1.utils import lenient_issubclass 

473 

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

475 

476 

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

478 return type_ is TypedDictRequired or type_ is TypedDictNotRequired 

479 

480 

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

482 """ 

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

484 """ 

485 return _check_typeddict_special(type_) or _check_typeddict_special(get_origin(type_)) 

486 

487 

488test_type = NewType('test_type', str) 

489 

490 

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

492 """ 

493 Check whether type_ was created using typing.NewType 

494 """ 

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

496 

497 

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

499 while hasattr(type_, '__supertype__'): 

500 type_ = type_.__supertype__ 

501 return type_ 

502 

503 

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

505 if v is None: 

506 return False 

507 

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

509 

510 

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

512 """ 

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

514 """ 

515 if v is None: 

516 return False 

517 

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

519 

520 

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

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

523 return True 

524 

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

526 # forward references, see #3679 

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

528 return True 

529 

530 return False 

531 

532 

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

534 return _check_finalvar(ann_type) or _check_finalvar(get_origin(ann_type)) 

535 

536 

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

538 """ 

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

540 """ 

541 prepare = False 

542 if field.type_.__class__ == ForwardRef: 

543 prepare = True 

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

545 if field.outer_type_.__class__ == ForwardRef: 

546 prepare = True 

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

548 if prepare: 

549 field.prepare() 

550 

551 if field.sub_fields: 

552 for sub_f in field.sub_fields: 

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

554 

555 if field.discriminator_key is not None: 

556 field.prepare_discriminated_union_sub_fields() 

557 

558 

559def update_model_forward_refs( 

560 model: Type[Any], 

561 fields: Iterable['ModelField'], 

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

563 localns: 'DictStrAny', 

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

565) -> None: 

566 """ 

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

568 """ 

569 if model.__module__ in sys.modules: 

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

571 else: 

572 globalns = {} 

573 

574 globalns.setdefault(model.__name__, model) 

575 

576 for f in fields: 

577 try: 

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

579 except exc_to_suppress: 

580 pass 

581 

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

583 if isinstance(key, str): 

584 fr: ForwardRef = ForwardRef(key) 

585 elif isinstance(key, ForwardRef): 

586 fr = key 

587 else: 

588 continue 

589 

590 try: 

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

592 except exc_to_suppress: # pragma: no cover 

593 continue 

594 

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

596 

597 

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

599 """ 

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

601 without brackets. Otherwise returns None. 

602 """ 

603 if type_ is type: 

604 return True 

605 

606 if get_origin(type_) is None: 

607 return None 

608 

609 args = get_args(type_) 

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

611 return True 

612 else: 

613 return args[0] 

614 

615 

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

617 """ 

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

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

620 """ 

621 origin = get_origin(tp) 

622 if origin is Annotated: 

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

624 elif is_union(origin): 

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

626 else: 

627 return [tp]