Skip to content

Commit

Permalink
Clean sys.modules if TYPE_CHECKING=True import fails
Browse files Browse the repository at this point in the history
autodoc now attempts to import with `typing.TYPE_CHECKING = True`
first, and then falls back to `typing.TYPE_CHECKING = False` if that
fails. Unfortunately, the first import can leave behind some
partially-imported modules in `sys.modules` such that the retry fails.

Attempt to work around this by detecting what modules were added to
`sys.modules` by the first attempt and removing them before retrying.

Signed-off-by: Matt Wozniski <mwozniski@bloomberg.net>
  • Loading branch information
godlygeek committed Aug 28, 2023
1 parent 2f6ea14 commit 8528c31
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 1 deletion.
8 changes: 7 additions & 1 deletion sphinx/ext/autodoc/importer.py
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import importlib
import sys
import traceback
import typing
from typing import TYPE_CHECKING, Any, Callable, NamedTuple
Expand Down Expand Up @@ -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 frozenset(sys.modules) - orig_modules:
sys.modules.pop(m)

typing.TYPE_CHECKING = False
module = import_module(modname, warningiserror=warningiserror)
finally:
Expand Down
1 change: 1 addition & 0 deletions tests/roots/test-ext-autodoc/circular_import/__init__.py
@@ -0,0 +1 @@
from circular_import.c import SomeClass
1 change: 1 addition & 0 deletions tests/roots/test-ext-autodoc/circular_import/a.py
@@ -0,0 +1 @@
X = 42
4 changes: 4 additions & 0 deletions tests/roots/test-ext-autodoc/circular_import/b.py
@@ -0,0 +1,4 @@
import typing

if typing.TYPE_CHECKING:
from circular_import import SomeClass
6 changes: 6 additions & 0 deletions tests/roots/test-ext-autodoc/circular_import/c.py
@@ -0,0 +1,6 @@
import circular_import.a
import circular_import.b


class SomeClass:
X = circular_import.a.X
13 changes: 13 additions & 0 deletions tests/test_ext_autodoc.py
Expand Up @@ -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}
Expand Down

0 comments on commit 8528c31

Please sign in to comment.