|
2 | 2 | from __future__ import annotations
|
3 | 3 |
|
4 | 4 | import ast
|
| 5 | +import importlib |
5 | 6 | import inspect
|
6 | 7 | import re
|
7 | 8 | import sys
|
@@ -404,32 +405,56 @@ def get_all_type_hints(autodoc_mock_imports: list[str], obj: Any, name: str) ->
|
404 | 405 | _TYPE_GUARD_IMPORTS_RESOLVED_GLOBALS_ID = set()
|
405 | 406 |
|
406 | 407 |
|
407 |
| -def _resolve_type_guarded_imports(autodoc_mock_imports: list[str], obj: Any) -> None: # noqa: C901 |
408 |
| - if hasattr(obj, "__module__") and obj.__module__ in _TYPE_GUARD_IMPORTS_RESOLVED: |
409 |
| - return # already processed module |
410 |
| - if not hasattr(obj, "__globals__"): # classes with __slots__ do not have this |
411 |
| - return # if lacks globals nothing we can do |
412 |
| - if id(obj.__globals__) in _TYPE_GUARD_IMPORTS_RESOLVED_GLOBALS_ID: |
413 |
| - return # already processed object |
414 |
| - _TYPE_GUARD_IMPORTS_RESOLVED.add(obj.__module__) |
415 |
| - if obj.__module__ not in sys.builtin_module_names: |
416 |
| - if hasattr(obj, "__globals__"): |
417 |
| - _TYPE_GUARD_IMPORTS_RESOLVED_GLOBALS_ID.add(id(obj.__globals__)) |
418 |
| - |
419 |
| - module = inspect.getmodule(obj) |
420 |
| - if module: |
| 408 | +def _should_skip_guarded_import_resolution(obj: Any) -> bool: |
| 409 | + if isinstance(obj, types.ModuleType): |
| 410 | + return False # Don't skip modules |
| 411 | + |
| 412 | + if not hasattr(obj, "__globals__"): |
| 413 | + return True # Skip objects without __globals__ |
| 414 | + |
| 415 | + if hasattr(obj, "__module__"): |
| 416 | + return obj.__module__ in _TYPE_GUARD_IMPORTS_RESOLVED or obj.__module__ in sys.builtin_module_names |
| 417 | + |
| 418 | + return id(obj.__globals__) in _TYPE_GUARD_IMPORTS_RESOLVED_GLOBALS_ID |
| 419 | + |
| 420 | + |
| 421 | +def _execute_guarded_code(autodoc_mock_imports: list[str], obj: Any, module_code: str) -> None: |
| 422 | + for _, part in _TYPE_GUARD_IMPORT_RE.findall(module_code): |
| 423 | + guarded_code = textwrap.dedent(part) |
| 424 | + try: |
421 | 425 | try:
|
422 |
| - module_code = inspect.getsource(module) |
423 |
| - except (TypeError, OSError): |
424 |
| - ... # no source code => no type guards |
425 |
| - else: |
426 |
| - for _, part in _TYPE_GUARD_IMPORT_RE.findall(module_code): |
427 |
| - guarded_code = textwrap.dedent(part) |
428 |
| - try: |
429 |
| - with mock(autodoc_mock_imports): |
430 |
| - exec(guarded_code, obj.__globals__) # noqa: S102 |
431 |
| - except Exception as exc: # noqa: BLE001 |
432 |
| - _LOGGER.warning("Failed guarded type import with %r", exc) |
| 426 | + with mock(autodoc_mock_imports): |
| 427 | + exec(guarded_code, getattr(obj, "__globals__", obj.__dict__)) # noqa: S102 |
| 428 | + except ImportError as exc: |
| 429 | + # ImportError might have occurred because the module has guarded code as well, |
| 430 | + # so we recurse on the module. |
| 431 | + if exc.name: |
| 432 | + _resolve_type_guarded_imports(autodoc_mock_imports, importlib.import_module(exc.name)) |
| 433 | + |
| 434 | + # Retry the guarded code and see if it works now after resolving all nested type guards. |
| 435 | + with mock(autodoc_mock_imports): |
| 436 | + exec(guarded_code, getattr(obj, "__globals__", obj.__dict__)) # noqa: S102 |
| 437 | + except Exception as exc: # noqa: BLE001 |
| 438 | + _LOGGER.warning("Failed guarded type import with %r", exc) |
| 439 | + |
| 440 | + |
| 441 | +def _resolve_type_guarded_imports(autodoc_mock_imports: list[str], obj: Any) -> None: |
| 442 | + if _should_skip_guarded_import_resolution(obj): |
| 443 | + return |
| 444 | + |
| 445 | + if hasattr(obj, "__globals__"): |
| 446 | + _TYPE_GUARD_IMPORTS_RESOLVED_GLOBALS_ID.add(id(obj.__globals__)) |
| 447 | + |
| 448 | + module = inspect.getmodule(obj) |
| 449 | + |
| 450 | + if module: |
| 451 | + try: |
| 452 | + module_code = inspect.getsource(module) |
| 453 | + except (TypeError, OSError): |
| 454 | + ... # no source code => no type guards |
| 455 | + else: |
| 456 | + _TYPE_GUARD_IMPORTS_RESOLVED.add(module.__name__) |
| 457 | + _execute_guarded_code(autodoc_mock_imports, obj, module_code) |
433 | 458 |
|
434 | 459 |
|
435 | 460 | def _get_type_hint(autodoc_mock_imports: list[str], name: str, obj: Any) -> dict[str, Any]:
|
|
0 commit comments