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

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 sys 

2import typing 

3from collections.abc import Callable 

4from os import PathLike 

5from typing import ( # type: ignore 

6 TYPE_CHECKING, 

7 AbstractSet, 

8 Any, 

9 Callable as TypingCallable, 

10 ClassVar, 

11 Dict, 

12 ForwardRef, 

13 Generator, 

14 Iterable, 

15 List, 

16 Mapping, 

17 NewType, 

18 Optional, 

19 Sequence, 

20 Set, 

21 Tuple, 

22 Type, 

23 TypeVar, 

24 Union, 

25 _eval_type, 

26 cast, 

27 get_type_hints, 

28) 

29 

30from typing_extensions import ( 

31 Annotated, 

32 Final, 

33 Literal, 

34 NotRequired as TypedDictNotRequired, 

35 Required as TypedDictRequired, 

36) 

37 

38try: 

39 from typing import _TypingBase as typing_base # type: ignore 

40except ImportError: 

41 from typing import _Final as typing_base # type: ignore 

42 

43try: 

44 from typing import GenericAlias as TypingGenericAlias # type: ignore 

45except ImportError: 

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

47 TypingGenericAlias = () 

48 

49try: 

50 from types import UnionType as TypesUnionType # type: ignore 

51except ImportError: 

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

53 TypesUnionType = () 

54 

55 

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

57 

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

59 return type_._evaluate(globalns, localns) 

60 

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

62 

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

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

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

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

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

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

69 

70else: 

71 

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

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

74 # warnings: 

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

76 

77 

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

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

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

81 # so it already returns the full annotation 

82 get_all_type_hints = get_type_hints 

83 

84else: 

85 

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

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

88 

89 

90_T = TypeVar('_T') 

91 

92AnyCallable = TypingCallable[..., Any] 

93NoArgAnyCallable = TypingCallable[[], Any] 

94 

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

96AnyArgTCallable = TypingCallable[..., _T] 

97 

98 

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

100# python/typing_extensions version. 

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

102 

103 

104LITERAL_TYPES: Set[Any] = {Literal} 

105if hasattr(typing, 'Literal'): 

106 LITERAL_TYPES.add(typing.Literal) 

107 

108 

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

110 

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

112 if type(t).__name__ in AnnotatedTypeNames: 

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

114 return cast(Type[Any], Annotated) 

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

116 

117else: 

118 from typing import get_origin as _typing_get_origin 

119 

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

121 """ 

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

123 custom generic classes like `ConstrainedList` 

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

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

126 """ 

127 if type(tp).__name__ in AnnotatedTypeNames: 

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

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

130 

131 

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

133 from typing import _GenericAlias 

134 

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

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

137 

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

139 and able to handle almost all use cases. 

140 """ 

141 if type(t).__name__ in AnnotatedTypeNames: 

142 return t.__args__ + t.__metadata__ 

143 if isinstance(t, _GenericAlias): 

144 res = t.__args__ 

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

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

147 return res 

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

149 

150else: 

151 from typing import get_args as _typing_get_args 

152 

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

154 """ 

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

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

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

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

159 """ 

160 if hasattr(tp, '_nparams'): 

161 return (Any,) * tp._nparams 

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

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

164 # This will probably be clarified in pydantic v2 

165 try: 

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

167 return ((),) 

168 # there is a TypeError when compiled with cython 

169 except TypeError: # pragma: no cover 

170 pass 

171 return () 

172 

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

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

175 

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

177 Examples:: 

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

179 get_args(int) == () 

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

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

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

183 """ 

184 if type(tp).__name__ in AnnotatedTypeNames: 

185 return tp.__args__ + tp.__metadata__ 

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

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

188 

189 

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

191 

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

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

194 They convert strings to ForwardRef automatically. 

195 

196 Examples:: 

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

198 """ 

199 return tp 

200 

201else: 

202 from typing import _UnionGenericAlias # type: ignore 

203 

204 from typing_extensions import _AnnotatedAlias 

205 

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

207 """ 

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

209 

210 Examples:: 

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

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

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

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

215 """ 

216 origin = get_origin(tp) 

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

218 return tp 

219 

220 args = get_args(tp) 

221 

222 # typing.Annotated needs special treatment 

223 if origin is Annotated: 

224 return _AnnotatedAlias(convert_generics(args[0]), args[1:]) 

225 

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

227 converted = tuple( 

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

229 for arg in args 

230 ) 

231 

232 if converted == args: 

233 return tp 

234 elif isinstance(tp, TypingGenericAlias): 

235 return TypingGenericAlias(origin, converted) 

236 elif isinstance(tp, TypesUnionType): 

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

238 return _UnionGenericAlias(origin, converted) 

239 else: 

240 try: 

241 setattr(tp, '__args__', converted) 

242 except AttributeError: 

243 pass 

244 return tp 

245 

246 

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

248 

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

250 return tp is Union 

251 

252 WithArgsTypes = (TypingGenericAlias,) 

253 

254else: 

255 import types 

256 import typing 

257 

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

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

260 

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

262 

263 

264StrPath = Union[str, PathLike] 

265 

266 

267if TYPE_CHECKING: 

268 from pydantic.v1.fields import ModelField 

269 

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

271 DictStrAny = Dict[str, Any] 

272 DictAny = Dict[Any, Any] 

273 SetStr = Set[str] 

274 ListStr = List[str] 

275 IntStr = Union[int, str] 

276 AbstractSetIntStr = AbstractSet[IntStr] 

277 DictIntStrAny = Dict[IntStr, Any] 

278 MappingIntStrAny = Mapping[IntStr, Any] 

279 CallableGenerator = Generator[AnyCallable, None, None] 

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

281 

282 MYPY = False 

283 if MYPY: 

284 AnyClassMethod = classmethod[Any] 

285 else: 

286 # classmethod[TargetType, CallableParamSpecType, CallableReturnType] 

287 AnyClassMethod = classmethod[Any, Any, Any] 

288 

289__all__ = ( 

290 'AnyCallable', 

291 'NoArgAnyCallable', 

292 'NoneType', 

293 'is_none_type', 

294 'display_as_type', 

295 'resolve_annotations', 

296 'is_callable_type', 

297 'is_literal_type', 

298 'all_literal_values', 

299 'is_namedtuple', 

300 'is_typeddict', 

301 'is_typeddict_special', 

302 'is_new_type', 

303 'new_type_supertype', 

304 'is_classvar', 

305 'is_finalvar', 

306 'update_field_forward_refs', 

307 'update_model_forward_refs', 

308 'TupleGenerator', 

309 'DictStrAny', 

310 'DictAny', 

311 'SetStr', 

312 'ListStr', 

313 'IntStr', 

314 'AbstractSetIntStr', 

315 'DictIntStrAny', 

316 'CallableGenerator', 

317 'ReprArgs', 

318 'AnyClassMethod', 

319 'CallableGenerator', 

320 'WithArgsTypes', 

321 'get_args', 

322 'get_origin', 

323 'get_sub_types', 

324 'typing_base', 

325 'get_all_type_hints', 

326 'is_union', 

327 'StrPath', 

328 'MappingIntStrAny', 

329) 

330 

331 

332NoneType = None.__class__ 

333 

334 

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

336 

337 

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

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

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

341 # mechanism. 

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

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

344 

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

346 return type_ in NONE_TYPES 

347 

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

349 

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

351 for none_type in NONE_TYPES: 

352 if type_ is none_type: 

353 return True 

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

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

356 # hopefully this check avoids that issue. 

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

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

359 return False 

360 

361else: 

362 

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

364 return type_ in NONE_TYPES 

365 

366 

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

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

369 v = v.__class__ 

370 

371 if is_union(get_origin(v)): 

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

373 

374 if isinstance(v, WithArgsTypes): 

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

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

377 

378 try: 

379 return v.__name__ 

380 except AttributeError: 

381 # happens with typing objects 

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

383 

384 

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

386 """ 

387 Partially taken from typing.get_type_hints. 

388 

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

390 """ 

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

392 if module_name: 

393 try: 

394 module = sys.modules[module_name] 

395 except KeyError: 

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

397 pass 

398 else: 

399 base_globals = module.__dict__ 

400 

401 annotations = {} 

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

403 if isinstance(value, str): 

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

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

406 else: 

407 value = ForwardRef(value, is_argument=False) 

408 try: 

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

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

411 else: 

412 value = _eval_type(value, base_globals, None) 

413 except NameError: 

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

415 pass 

416 annotations[name] = value 

417 return annotations 

418 

419 

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

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

422 

423 

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

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

426 

427 

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

429 return get_args(type_) 

430 

431 

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

433 """ 

434 This method is used to retrieve all Literal values as 

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

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

437 """ 

438 if not is_literal_type(type_): 

439 return (type_,) 

440 

441 values = literal_values(type_) 

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

443 

444 

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

446 """ 

447 Check if a given class is a named tuple. 

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

449 """ 

450 from pydantic.v1.utils import lenient_issubclass 

451 

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

453 

454 

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

456 """ 

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

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

459 """ 

460 from pydantic.v1.utils import lenient_issubclass 

461 

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

463 

464 

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

466 return type_ is TypedDictRequired or type_ is TypedDictNotRequired 

467 

468 

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

470 """ 

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

472 """ 

473 return _check_typeddict_special(type_) or _check_typeddict_special(get_origin(type_)) 

474 

475 

476test_type = NewType('test_type', str) 

477 

478 

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

480 """ 

481 Check whether type_ was created using typing.NewType 

482 """ 

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

484 

485 

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

487 while hasattr(type_, '__supertype__'): 

488 type_ = type_.__supertype__ 

489 return type_ 

490 

491 

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

493 if v is None: 

494 return False 

495 

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

497 

498 

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

500 """ 

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

502 """ 

503 if v is None: 

504 return False 

505 

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

507 

508 

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

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

511 return True 

512 

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

514 # forward references, see #3679 

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

516 return True 

517 

518 return False 

519 

520 

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

522 return _check_finalvar(ann_type) or _check_finalvar(get_origin(ann_type)) 

523 

524 

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

526 """ 

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

528 """ 

529 prepare = False 

530 if field.type_.__class__ == ForwardRef: 

531 prepare = True 

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

533 if field.outer_type_.__class__ == ForwardRef: 

534 prepare = True 

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

536 if prepare: 

537 field.prepare() 

538 

539 if field.sub_fields: 

540 for sub_f in field.sub_fields: 

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

542 

543 if field.discriminator_key is not None: 

544 field.prepare_discriminated_union_sub_fields() 

545 

546 

547def update_model_forward_refs( 

548 model: Type[Any], 

549 fields: Iterable['ModelField'], 

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

551 localns: 'DictStrAny', 

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

553) -> None: 

554 """ 

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

556 """ 

557 if model.__module__ in sys.modules: 

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

559 else: 

560 globalns = {} 

561 

562 globalns.setdefault(model.__name__, model) 

563 

564 for f in fields: 

565 try: 

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

567 except exc_to_suppress: 

568 pass 

569 

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

571 if isinstance(key, str): 

572 fr: ForwardRef = ForwardRef(key) 

573 elif isinstance(key, ForwardRef): 

574 fr = key 

575 else: 

576 continue 

577 

578 try: 

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

580 except exc_to_suppress: # pragma: no cover 

581 continue 

582 

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

584 

585 

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

587 """ 

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

589 without brackets. Otherwise returns None. 

590 """ 

591 if type_ is type: 

592 return True 

593 

594 if get_origin(type_) is None: 

595 return None 

596 

597 args = get_args(type_) 

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

599 return True 

600 else: 

601 return args[0] 

602 

603 

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

605 """ 

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

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

608 """ 

609 origin = get_origin(tp) 

610 if origin is Annotated: 

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

612 elif is_union(origin): 

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

614 else: 

615 return [tp]