autodoc: Reset `sys.modules` on partial import failure (#11645)

If importing with ``TYPE_CHECKING is True`` fails, reset the state of ``sys.modules``
so that the attempt with ``TYPE_CHECKING is False`` may succeed.

Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
This commit is contained in:
Matt Wozniski 2023-08-29 15:05:41 -04:00 committed by GitHub
parent e494baa9dd
commit 8248be31db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 37 additions and 1 deletions

View File

@ -16,6 +16,11 @@ Features added
Bugs fixed
----------
* #11645: Fix a regression preventing autodoc from importing modules within
packages that make use of ``if typing.TYPE_CHECKING:`` to guard circular
imports needed by type checkers.
Patch by Matt Wozniski.
Testing
-------

View File

@ -3,6 +3,7 @@
from __future__ import annotations
import importlib
import sys
import traceback
import typing
from typing import TYPE_CHECKING, Any, Callable, NamedTuple
@ -82,13 +83,18 @@ def import_object(modname: str, objpath: list[str], objtype: str = '',
objpath = list(objpath)
while module is None:
try:
orig_modules = frozenset(sys.modules)
try:
# try importing with ``typing.TYPE_CHECKING == True``
typing.TYPE_CHECKING = True
module = import_module(modname, warningiserror=warningiserror)
except ImportError:
# if that fails (e.g. circular import), retry with
# ``typing.TYPE_CHECKING == False``
# ``typing.TYPE_CHECKING == False`` after reverting
# changes made to ``sys.modules`` by the failed try
for m in [m for m in sys.modules if m not in orig_modules]:
sys.modules.pop(m)
typing.TYPE_CHECKING = False
module = import_module(modname, warningiserror=warningiserror)
finally:

View File

@ -0,0 +1 @@
from circular_import.c import SomeClass

View File

@ -0,0 +1 @@
X = 42

View File

@ -0,0 +1,4 @@
import typing
if typing.TYPE_CHECKING:
from circular_import import SomeClass

View File

@ -0,0 +1,6 @@
import circular_import.a
import circular_import.b
class SomeClass:
X = circular_import.a.X

View File

@ -2024,6 +2024,19 @@ def test_autodoc_TYPE_CHECKING(app):
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_TYPE_CHECKING_circular_import(app):
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'module', 'circular_import', options)
assert list(actual) == [
'',
'.. py:module:: circular_import',
'',
]
assert sys.modules["circular_import"].a is sys.modules["circular_import.a"]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_singledispatch(app):
options = {"members": None}