Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pydantic/_internal/_typing_extra.py: 58%

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

196 statements  

1"""Logic for interacting with type annotations, mostly extensions, shims and hacks to wrap Python's typing module.""" 

2 

3from __future__ import annotations 

4 

5import collections.abc 

6import re 

7import sys 

8import types 

9import typing 

10from functools import partial 

11from typing import TYPE_CHECKING, Any, Callable, cast 

12 

13import typing_extensions 

14from typing_extensions import deprecated, get_args, get_origin 

15from typing_inspection import typing_objects 

16from typing_inspection.introspection import is_union_origin 

17 

18from pydantic.version import version_short 

19 

20from ._namespace_utils import GlobalsNamespace, MappingNamespace, NsResolver, get_module_ns_of 

21 

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

23 NoneType = type(None) 

24 EllipsisType = type(Ellipsis) 

25else: 

26 from types import EllipsisType as EllipsisType 

27 from types import NoneType as NoneType 

28 

29if TYPE_CHECKING: 

30 from pydantic import BaseModel 

31 

32# As per https://typing-extensions.readthedocs.io/en/latest/#runtime-use-of-types, 

33# always check for both `typing` and `typing_extensions` variants of a typing construct. 

34# (this is implemented differently than the suggested approach in the `typing_extensions` 

35# docs for performance). 

36 

37 

38_t_annotated = typing.Annotated 

39_te_annotated = typing_extensions.Annotated 

40 

41 

42def is_annotated(tp: Any, /) -> bool: 

43 """Return whether the provided argument is a `Annotated` special form. 

44 

45 ```python {test="skip" lint="skip"} 

46 is_annotated(Annotated[int, ...]) 

47 #> True 

48 ``` 

49 """ 

50 origin = get_origin(tp) 

51 return origin is _t_annotated or origin is _te_annotated 

52 

53 

54def annotated_type(tp: Any, /) -> Any | None: 

55 """Return the type of the `Annotated` special form, or `None`.""" 

56 return tp.__origin__ if typing_objects.is_annotated(get_origin(tp)) else None 

57 

58 

59def unpack_type(tp: Any, /) -> Any | None: 

60 """Return the type wrapped by the `Unpack` special form, or `None`.""" 

61 return get_args(tp)[0] if typing_objects.is_unpack(get_origin(tp)) else None 

62 

63 

64def is_hashable(tp: Any, /) -> bool: 

65 """Return whether the provided argument is the `Hashable` class. 

66 

67 ```python {test="skip" lint="skip"} 

68 is_hashable(Hashable) 

69 #> True 

70 ``` 

71 """ 

72 # `get_origin` is documented as normalizing any typing-module aliases to `collections` classes, 

73 # hence the second check: 

74 return tp is collections.abc.Hashable or get_origin(tp) is collections.abc.Hashable 

75 

76 

77def is_callable(tp: Any, /) -> bool: 

78 """Return whether the provided argument is a `Callable`, parametrized or not. 

79 

80 ```python {test="skip" lint="skip"} 

81 is_callable(Callable[[int], str]) 

82 #> True 

83 is_callable(typing.Callable) 

84 #> True 

85 is_callable(collections.abc.Callable) 

86 #> True 

87 ``` 

88 """ 

89 # `get_origin` is documented as normalizing any typing-module aliases to `collections` classes, 

90 # hence the second check: 

91 return tp is collections.abc.Callable or get_origin(tp) is collections.abc.Callable 

92 

93 

94_classvar_re = re.compile(r'((\w+\.)?Annotated\[)?(\w+\.)?ClassVar\[') 

95 

96 

97def is_classvar_annotation(tp: Any, /) -> bool: 

98 """Return whether the provided argument represents a class variable annotation. 

99 

100 Although not explicitly stated by the typing specification, `ClassVar` can be used 

101 inside `Annotated` and as such, this function checks for this specific scenario. 

102 

103 Because this function is used to detect class variables before evaluating forward references 

104 (or because evaluation failed), we also implement a naive regex match implementation. This is 

105 required because class variables are inspected before fields are collected, so we try to be 

106 as accurate as possible. 

107 """ 

108 if typing_objects.is_classvar(tp): 

109 return True 

110 

111 origin = get_origin(tp) 

112 

113 if typing_objects.is_classvar(origin): 

114 return True 

115 

116 if typing_objects.is_annotated(origin): 

117 annotated_type = tp.__origin__ 

118 if typing_objects.is_classvar(annotated_type) or typing_objects.is_classvar(get_origin(annotated_type)): 

119 return True 

120 

121 str_ann: str | None = None 

122 if isinstance(tp, typing.ForwardRef): 

123 str_ann = tp.__forward_arg__ 

124 if isinstance(tp, str): 

125 str_ann = tp 

126 

127 if str_ann is not None and _classvar_re.match(str_ann): 

128 # stdlib dataclasses do something similar, although a bit more advanced 

129 # (see `dataclass._is_type`). 

130 return True 

131 

132 return False 

133 

134 

135_t_final = typing.Final 

136_te_final = typing_extensions.Final 

137 

138 

139# TODO implement `is_finalvar_annotation` as Final can be wrapped with other special forms: 

140def is_finalvar(tp: Any, /) -> bool: 

141 """Return whether the provided argument is a `Final` special form, parametrized or not. 

142 

143 ```python {test="skip" lint="skip"} 

144 is_finalvar(Final[int]) 

145 #> True 

146 is_finalvar(Final) 

147 #> True 

148 """ 

149 # Final is not necessarily parametrized: 

150 if tp is _t_final or tp is _te_final: 

151 return True 

152 origin = get_origin(tp) 

153 return origin is _t_final or origin is _te_final 

154 

155 

156_NONE_TYPES: tuple[Any, ...] = (None, NoneType, typing.Literal[None], typing_extensions.Literal[None]) 

157 

158 

159def is_none_type(tp: Any, /) -> bool: 

160 """Return whether the argument represents the `None` type as part of an annotation. 

161 

162 ```python {test="skip" lint="skip"} 

163 is_none_type(None) 

164 #> True 

165 is_none_type(NoneType) 

166 #> True 

167 is_none_type(Literal[None]) 

168 #> True 

169 is_none_type(type[None]) 

170 #> False 

171 """ 

172 return tp in _NONE_TYPES 

173 

174 

175def is_namedtuple(tp: Any, /) -> bool: 

176 """Return whether the provided argument is a named tuple class. 

177 

178 The class can be created using `typing.NamedTuple` or `collections.namedtuple`. 

179 Parametrized generic classes are *not* assumed to be named tuples. 

180 """ 

181 from ._utils import lenient_issubclass # circ. import 

182 

183 return lenient_issubclass(tp, tuple) and hasattr(tp, '_fields') 

184 

185 

186# TODO In 2.12, delete this export. It is currently defined only to not break 

187# pydantic-settings which relies on it: 

188origin_is_union = is_union_origin 

189 

190 

191def is_generic_alias(tp: Any, /) -> bool: 

192 return isinstance(tp, (types.GenericAlias, typing._GenericAlias)) # pyright: ignore[reportAttributeAccessIssue] 

193 

194 

195# TODO: Ideally, we should avoid relying on the private `typing` constructs: 

196 

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

198 WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias) # pyright: ignore[reportAttributeAccessIssue] 

199else: 

200 WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias, types.UnionType) # pyright: ignore[reportAttributeAccessIssue] 

201 

202 

203# Similarly, we shouldn't rely on this `_Final` class, which is even more private than `_GenericAlias`: 

204typing_base: Any = typing._Final # pyright: ignore[reportAttributeAccessIssue] 

205 

206 

207### Annotation evaluations functions: 

208 

209 

210def parent_frame_namespace(*, parent_depth: int = 2, force: bool = False) -> dict[str, Any] | None: 

211 """Fetch the local namespace of the parent frame where this function is called. 

212 

213 Using this function is mostly useful to resolve forward annotations pointing to members defined in a local namespace, 

214 such as assignments inside a function. Using the standard library tools, it is currently not possible to resolve 

215 such annotations: 

216 

217 ```python {lint="skip" test="skip"} 

218 from typing import get_type_hints 

219 

220 def func() -> None: 

221 Alias = int 

222 

223 class C: 

224 a: 'Alias' 

225 

226 # Raises a `NameError: 'Alias' is not defined` 

227 get_type_hints(C) 

228 ``` 

229 

230 Pydantic uses this function when a Pydantic model is being defined to fetch the parent frame locals. However, 

231 this only allows us to fetch the parent frame namespace and not other parents (e.g. a model defined in a function, 

232 itself defined in another function). Inspecting the next outer frames (using `f_back`) is not reliable enough 

233 (see https://discuss.python.org/t/20659). 

234 

235 Because this function is mostly used to better resolve forward annotations, nothing is returned if the parent frame's 

236 code object is defined at the module level. In this case, the locals of the frame will be the same as the module 

237 globals where the class is defined (see `_namespace_utils.get_module_ns_of`). However, if you still want to fetch 

238 the module globals (e.g. when rebuilding a model, where the frame where the rebuild call is performed might contain 

239 members that you want to use for forward annotations evaluation), you can use the `force` parameter. 

240 

241 Args: 

242 parent_depth: The depth at which to get the frame. Defaults to 2, meaning the parent frame where this function 

243 is called will be used. 

244 force: Whether to always return the frame locals, even if the frame's code object is defined at the module level. 

245 

246 Returns: 

247 The locals of the namespace, or `None` if it was skipped as per the described logic. 

248 """ 

249 frame = sys._getframe(parent_depth) 

250 

251 if frame.f_code.co_name.startswith('<generic parameters of'): 

252 # As `parent_frame_namespace` is mostly called in `ModelMetaclass.__new__`, 

253 # the parent frame can be the annotation scope if the PEP 695 generic syntax is used. 

254 # (see https://docs.python.org/3/reference/executionmodel.html#annotation-scopes, 

255 # https://docs.python.org/3/reference/compound_stmts.html#generic-classes). 

256 # In this case, the code name is set to `<generic parameters of MyClass>`, 

257 # and we need to skip this frame as it is irrelevant. 

258 frame = cast(types.FrameType, frame.f_back) # guaranteed to not be `None` 

259 

260 # note, we don't copy frame.f_locals here (or during the last return call), because we don't expect the namespace to be 

261 # modified down the line if this becomes a problem, we could implement some sort of frozen mapping structure to enforce this. 

262 if force: 

263 return frame.f_locals 

264 

265 # If either of the following conditions are true, the class is defined at the top module level. 

266 # To better understand why we need both of these checks, see 

267 # https://github.com/pydantic/pydantic/pull/10113#discussion_r1714981531. 

268 if frame.f_back is None or frame.f_code.co_name == '<module>': 

269 return None 

270 

271 return frame.f_locals 

272 

273 

274def _type_convert(arg: Any) -> Any: 

275 """Convert `None` to `NoneType` and strings to `ForwardRef` instances. 

276 

277 This is a backport of the private `typing._type_convert` function. When 

278 evaluating a type, `ForwardRef._evaluate` ends up being called, and is 

279 responsible for making this conversion. However, we still have to apply 

280 it for the first argument passed to our type evaluation functions, similarly 

281 to the `typing.get_type_hints` function. 

282 """ 

283 if arg is None: 

284 return NoneType 

285 if isinstance(arg, str): 

286 # Like `typing.get_type_hints`, assume the arg can be in any context, 

287 # hence the proper `is_argument` and `is_class` args: 

288 return _make_forward_ref(arg, is_argument=False, is_class=True) 

289 return arg 

290 

291 

292def get_model_type_hints( 

293 obj: type[BaseModel], 

294 *, 

295 ns_resolver: NsResolver | None = None, 

296) -> dict[str, tuple[Any, bool]]: 

297 """Collect annotations from a Pydantic model class, including those from parent classes. 

298 

299 Args: 

300 obj: The Pydantic model to inspect. 

301 ns_resolver: A namespace resolver instance to use. Defaults to an empty instance. 

302 

303 Returns: 

304 A dictionary mapping annotation names to a two-tuple: the first element is the evaluated 

305 type or the original annotation if a `NameError` occurred, the second element is a boolean 

306 indicating if whether the evaluation succeeded. 

307 """ 

308 hints: dict[str, Any] | dict[str, tuple[Any, bool]] = {} 

309 ns_resolver = ns_resolver or NsResolver() 

310 

311 for base in reversed(obj.__mro__): 

312 ann: dict[str, Any] | None = base.__dict__.get('__annotations__') 

313 if not ann or isinstance(ann, types.GetSetDescriptorType): 

314 continue 

315 with ns_resolver.push(base): 

316 globalns, localns = ns_resolver.types_namespace 

317 for name, value in ann.items(): 

318 if name.startswith('_'): 

319 # For private attributes, we only need the annotation to detect the `ClassVar` special form. 

320 # For this reason, we still try to evaluate it, but we also catch any possible exception (on 

321 # top of the `NameError`s caught in `try_eval_type`) that could happen so that users are free 

322 # to use any kind of forward annotation for private fields (e.g. circular imports, new typing 

323 # syntax, etc). 

324 try: 

325 hints[name] = try_eval_type(value, globalns, localns) 

326 except Exception: 

327 hints[name] = (value, False) 

328 else: 

329 hints[name] = try_eval_type(value, globalns, localns) 

330 return hints 

331 

332 

333def get_cls_type_hints( 

334 obj: type[Any], 

335 *, 

336 ns_resolver: NsResolver | None = None, 

337) -> dict[str, Any]: 

338 """Collect annotations from a class, including those from parent classes. 

339 

340 Args: 

341 obj: The class to inspect. 

342 ns_resolver: A namespace resolver instance to use. Defaults to an empty instance. 

343 """ 

344 hints: dict[str, Any] | dict[str, tuple[Any, bool]] = {} 

345 ns_resolver = ns_resolver or NsResolver() 

346 

347 for base in reversed(obj.__mro__): 

348 ann: dict[str, Any] | None = base.__dict__.get('__annotations__') 

349 if not ann or isinstance(ann, types.GetSetDescriptorType): 

350 continue 

351 with ns_resolver.push(base): 

352 globalns, localns = ns_resolver.types_namespace 

353 for name, value in ann.items(): 

354 hints[name] = eval_type(value, globalns, localns) 

355 return hints 

356 

357 

358def try_eval_type( 

359 value: Any, 

360 globalns: GlobalsNamespace | None = None, 

361 localns: MappingNamespace | None = None, 

362) -> tuple[Any, bool]: 

363 """Try evaluating the annotation using the provided namespaces. 

364 

365 Args: 

366 value: The value to evaluate. If `None`, it will be replaced by `type[None]`. If an instance 

367 of `str`, it will be converted to a `ForwardRef`. 

368 localns: The global namespace to use during annotation evaluation. 

369 globalns: The local namespace to use during annotation evaluation. 

370 

371 Returns: 

372 A two-tuple containing the possibly evaluated type and a boolean indicating 

373 whether the evaluation succeeded or not. 

374 """ 

375 value = _type_convert(value) 

376 

377 try: 

378 return eval_type_backport(value, globalns, localns), True 

379 except NameError: 

380 return value, False 

381 

382 

383def eval_type( 

384 value: Any, 

385 globalns: GlobalsNamespace | None = None, 

386 localns: MappingNamespace | None = None, 

387) -> Any: 

388 """Evaluate the annotation using the provided namespaces. 

389 

390 Args: 

391 value: The value to evaluate. If `None`, it will be replaced by `type[None]`. If an instance 

392 of `str`, it will be converted to a `ForwardRef`. 

393 localns: The global namespace to use during annotation evaluation. 

394 globalns: The local namespace to use during annotation evaluation. 

395 """ 

396 value = _type_convert(value) 

397 return eval_type_backport(value, globalns, localns) 

398 

399 

400@deprecated( 

401 '`eval_type_lenient` is deprecated, use `try_eval_type` instead.', 

402 category=None, 

403) 

404def eval_type_lenient( 

405 value: Any, 

406 globalns: GlobalsNamespace | None = None, 

407 localns: MappingNamespace | None = None, 

408) -> Any: 

409 ev, _ = try_eval_type(value, globalns, localns) 

410 return ev 

411 

412 

413def eval_type_backport( 

414 value: Any, 

415 globalns: GlobalsNamespace | None = None, 

416 localns: MappingNamespace | None = None, 

417 type_params: tuple[Any, ...] | None = None, 

418) -> Any: 

419 """An enhanced version of `typing._eval_type` which will fall back to using the `eval_type_backport` 

420 package if it's installed to let older Python versions use newer typing constructs. 

421 

422 Specifically, this transforms `X | Y` into `typing.Union[X, Y]` and `list[X]` into `typing.List[X]` 

423 (as well as all the types made generic in PEP 585) if the original syntax is not supported in the 

424 current Python version. 

425 

426 This function will also display a helpful error if the value passed fails to evaluate. 

427 """ 

428 try: 

429 return _eval_type_backport(value, globalns, localns, type_params) 

430 except TypeError as e: 

431 if 'Unable to evaluate type annotation' in str(e): 

432 raise 

433 

434 # If it is a `TypeError` and value isn't a `ForwardRef`, it would have failed during annotation definition. 

435 # Thus we assert here for type checking purposes: 

436 assert isinstance(value, typing.ForwardRef) 

437 

438 message = f'Unable to evaluate type annotation {value.__forward_arg__!r}.' 

439 if sys.version_info >= (3, 11): 

440 e.add_note(message) 

441 raise 

442 else: 

443 raise TypeError(message) from e 

444 except RecursionError as e: 

445 # TODO ideally recursion errors should be checked in `eval_type` above, but `eval_type_backport` 

446 # is used directly in some places. 

447 message = ( 

448 "If you made use of an implicit recursive type alias (e.g. `MyType = list['MyType']), " 

449 'consider using PEP 695 type aliases instead. For more details, refer to the documentation: ' 

450 f'https://docs.pydantic.dev/{version_short()}/concepts/types/#named-recursive-types' 

451 ) 

452 if sys.version_info >= (3, 11): 

453 e.add_note(message) 

454 raise 

455 else: 

456 raise RecursionError(f'{e.args[0]}\n{message}') 

457 

458 

459def _eval_type_backport( 

460 value: Any, 

461 globalns: GlobalsNamespace | None = None, 

462 localns: MappingNamespace | None = None, 

463 type_params: tuple[Any, ...] | None = None, 

464) -> Any: 

465 try: 

466 return _eval_type(value, globalns, localns, type_params) 

467 except TypeError as e: 

468 if not (isinstance(value, typing.ForwardRef) and is_backport_fixable_error(e)): 

469 raise 

470 

471 try: 

472 from eval_type_backport import eval_type_backport 

473 except ImportError: 

474 raise TypeError( 

475 f'Unable to evaluate type annotation {value.__forward_arg__!r}. If you are making use ' 

476 'of the new typing syntax (unions using `|` since Python 3.10 or builtins subscripting ' 

477 'since Python 3.9), you should either replace the use of new syntax with the existing ' 

478 '`typing` constructs or install the `eval_type_backport` package.' 

479 ) from e 

480 

481 return eval_type_backport( 

482 value, 

483 globalns, 

484 localns, # pyright: ignore[reportArgumentType], waiting on a new `eval_type_backport` release. 

485 try_default=False, 

486 ) 

487 

488 

489def _eval_type( 

490 value: Any, 

491 globalns: GlobalsNamespace | None = None, 

492 localns: MappingNamespace | None = None, 

493 type_params: tuple[Any, ...] | None = None, 

494) -> Any: 

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

496 return typing._eval_type( # type: ignore 

497 value, globalns, localns, type_params=type_params 

498 ) 

499 else: 

500 return typing._eval_type( # type: ignore 

501 value, globalns, localns 

502 ) 

503 

504 

505def is_backport_fixable_error(e: TypeError) -> bool: 

506 msg = str(e) 

507 

508 return sys.version_info < (3, 10) and msg.startswith('unsupported operand type(s) for |: ') 

509 

510 

511def get_function_type_hints( 

512 function: Callable[..., Any], 

513 *, 

514 include_keys: set[str] | None = None, 

515 globalns: GlobalsNamespace | None = None, 

516 localns: MappingNamespace | None = None, 

517) -> dict[str, Any]: 

518 """Return type hints for a function. 

519 

520 This is similar to the `typing.get_type_hints` function, with a few differences: 

521 - Support `functools.partial` by using the underlying `func` attribute. 

522 - Do not wrap type annotation of a parameter with `Optional` if it has a default value of `None` 

523 (related bug: https://github.com/python/cpython/issues/90353, only fixed in 3.11+). 

524 """ 

525 try: 

526 if isinstance(function, partial): 

527 annotations = function.func.__annotations__ 

528 else: 

529 annotations = function.__annotations__ 

530 except AttributeError: 

531 # Some functions (e.g. builtins) don't have annotations: 

532 return {} 

533 

534 if globalns is None: 

535 globalns = get_module_ns_of(function) 

536 type_params: tuple[Any, ...] | None = None 

537 if localns is None: 

538 # If localns was specified, it is assumed to already contain type params. This is because 

539 # Pydantic has more advanced logic to do so (see `_namespace_utils.ns_for_function`). 

540 type_params = getattr(function, '__type_params__', ()) 

541 

542 type_hints = {} 

543 for name, value in annotations.items(): 

544 if include_keys is not None and name not in include_keys: 

545 continue 

546 if value is None: 

547 value = NoneType 

548 elif isinstance(value, str): 

549 value = _make_forward_ref(value) 

550 

551 type_hints[name] = eval_type_backport(value, globalns, localns, type_params) 

552 

553 return type_hints 

554 

555 

556if sys.version_info < (3, 9, 8) or (3, 10) <= sys.version_info < (3, 10, 1): 

557 

558 def _make_forward_ref( 

559 arg: Any, 

560 is_argument: bool = True, 

561 *, 

562 is_class: bool = False, 

563 ) -> typing.ForwardRef: 

564 """Wrapper for ForwardRef that accounts for the `is_class` argument missing in older versions. 

565 The `module` argument is omitted as it breaks <3.9.8, =3.10.0 and isn't used in the calls below. 

566 

567 See https://github.com/python/cpython/pull/28560 for some background. 

568 The backport happened on 3.9.8, see: 

569 https://github.com/pydantic/pydantic/discussions/6244#discussioncomment-6275458, 

570 and on 3.10.1 for the 3.10 branch, see: 

571 https://github.com/pydantic/pydantic/issues/6912 

572 

573 Implemented as EAFP with memory. 

574 """ 

575 return typing.ForwardRef(arg, is_argument) 

576 

577else: 

578 _make_forward_ref = typing.ForwardRef 

579 

580 

581if sys.version_info >= (3, 10): 

582 get_type_hints = typing.get_type_hints 

583 

584else: 

585 """ 

586 For older versions of python, we have a custom implementation of `get_type_hints` which is a close as possible to 

587 the implementation in CPython 3.10.8. 

588 """ 

589 

590 @typing.no_type_check 

591 def get_type_hints( # noqa: C901 

592 obj: Any, 

593 globalns: dict[str, Any] | None = None, 

594 localns: dict[str, Any] | None = None, 

595 include_extras: bool = False, 

596 ) -> dict[str, Any]: # pragma: no cover 

597 """Taken verbatim from python 3.10.8 unchanged, except: 

598 * type annotations of the function definition above. 

599 * prefixing `typing.` where appropriate 

600 * Use `_make_forward_ref` instead of `typing.ForwardRef` to handle the `is_class` argument. 

601 

602 https://github.com/python/cpython/blob/aaaf5174241496afca7ce4d4584570190ff972fe/Lib/typing.py#L1773-L1875 

603 

604 DO NOT CHANGE THIS METHOD UNLESS ABSOLUTELY NECESSARY. 

605 ====================================================== 

606 

607 Return type hints for an object. 

608 

609 This is often the same as obj.__annotations__, but it handles 

610 forward references encoded as string literals, adds Optional[t] if a 

611 default value equal to None is set and recursively replaces all 

612 'Annotated[T, ...]' with 'T' (unless 'include_extras=True'). 

613 

614 The argument may be a module, class, method, or function. The annotations 

615 are returned as a dictionary. For classes, annotations include also 

616 inherited members. 

617 

618 TypeError is raised if the argument is not of a type that can contain 

619 annotations, and an empty dictionary is returned if no annotations are 

620 present. 

621 

622 BEWARE -- the behavior of globalns and localns is counterintuitive 

623 (unless you are familiar with how eval() and exec() work). The 

624 search order is locals first, then globals. 

625 

626 - If no dict arguments are passed, an attempt is made to use the 

627 globals from obj (or the respective module's globals for classes), 

628 and these are also used as the locals. If the object does not appear 

629 to have globals, an empty dictionary is used. For classes, the search 

630 order is globals first then locals. 

631 

632 - If one dict argument is passed, it is used for both globals and 

633 locals. 

634 

635 - If two dict arguments are passed, they specify globals and 

636 locals, respectively. 

637 """ 

638 if getattr(obj, '__no_type_check__', None): 

639 return {} 

640 # Classes require a special treatment. 

641 if isinstance(obj, type): 

642 hints = {} 

643 for base in reversed(obj.__mro__): 

644 if globalns is None: 

645 base_globals = getattr(sys.modules.get(base.__module__, None), '__dict__', {}) 

646 else: 

647 base_globals = globalns 

648 ann = base.__dict__.get('__annotations__', {}) 

649 if isinstance(ann, types.GetSetDescriptorType): 

650 ann = {} 

651 base_locals = dict(vars(base)) if localns is None else localns 

652 if localns is None and globalns is None: 

653 # This is surprising, but required. Before Python 3.10, 

654 # get_type_hints only evaluated the globalns of 

655 # a class. To maintain backwards compatibility, we reverse 

656 # the globalns and localns order so that eval() looks into 

657 # *base_globals* first rather than *base_locals*. 

658 # This only affects ForwardRefs. 

659 base_globals, base_locals = base_locals, base_globals 

660 for name, value in ann.items(): 

661 if value is None: 

662 value = type(None) 

663 if isinstance(value, str): 

664 value = _make_forward_ref(value, is_argument=False, is_class=True) 

665 

666 value = eval_type_backport(value, base_globals, base_locals) 

667 hints[name] = value 

668 if not include_extras and hasattr(typing, '_strip_annotations'): 

669 return { 

670 k: typing._strip_annotations(t) # type: ignore 

671 for k, t in hints.items() 

672 } 

673 else: 

674 return hints 

675 

676 if globalns is None: 

677 if isinstance(obj, types.ModuleType): 

678 globalns = obj.__dict__ 

679 else: 

680 nsobj = obj 

681 # Find globalns for the unwrapped object. 

682 while hasattr(nsobj, '__wrapped__'): 

683 nsobj = nsobj.__wrapped__ 

684 globalns = getattr(nsobj, '__globals__', {}) 

685 if localns is None: 

686 localns = globalns 

687 elif localns is None: 

688 localns = globalns 

689 hints = getattr(obj, '__annotations__', None) 

690 if hints is None: 

691 # Return empty annotations for something that _could_ have them. 

692 if isinstance(obj, typing._allowed_types): # type: ignore 

693 return {} 

694 else: 

695 raise TypeError(f'{obj!r} is not a module, class, method, or function.') 

696 defaults = typing._get_defaults(obj) # type: ignore 

697 hints = dict(hints) 

698 for name, value in hints.items(): 

699 if value is None: 

700 value = type(None) 

701 if isinstance(value, str): 

702 # class-level forward refs were handled above, this must be either 

703 # a module-level annotation or a function argument annotation 

704 

705 value = _make_forward_ref( 

706 value, 

707 is_argument=not isinstance(obj, types.ModuleType), 

708 is_class=False, 

709 ) 

710 value = eval_type_backport(value, globalns, localns) 

711 if name in defaults and defaults[name] is None: 

712 value = typing.Optional[value] 

713 hints[name] = value 

714 return hints if include_extras else {k: typing._strip_annotations(t) for k, t in hints.items()} # type: ignore