Skip to content

Commit 43d6975

Browse files
godlygeekAA-Turner
andauthored
Leverage importlib.reload for reloading modules (#11679)
Co-authored-by: Adam Turner <[email protected]>
1 parent 13da5d7 commit 43d6975

File tree

4 files changed

+41
-17
lines changed

4 files changed

+41
-17
lines changed

CHANGES

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Release 7.2.6 (in development)
44
Bugs fixed
55
----------
66

7+
* #11679: Add the :envvar:`!SPHINX_AUTODOC_RELOAD_MODULES` environment variable,
8+
which if set reloads modules when using autodoc with ``TYPE_CHECKING = True``.
9+
Patch by Matt Wozniski and Adam Turner.
10+
* #11679: Use :py:func:`importlib.reload` to reload modules in autodoc.
11+
Patch by Matt Wozniski and Adam Turner.
712

813
Release 7.2.5 (released Aug 30, 2023)
914
=====================================

doc/conf.py

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
import sphinx
88

9+
os.environ['SPHINX_AUTODOC_RELOAD_MODULES'] = '1'
10+
911
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo',
1012
'sphinx.ext.autosummary', 'sphinx.ext.extlinks',
1113
'sphinx.ext.intersphinx',

sphinx/ext/autodoc/importer.py

+31-17
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
from __future__ import annotations
44

5+
import contextlib
56
import importlib
7+
import os
68
import sys
79
import traceback
810
import typing
@@ -21,6 +23,8 @@
2123
)
2224

2325
if TYPE_CHECKING:
26+
from types import ModuleType
27+
2428
from sphinx.ext.autodoc import ObjectMember
2529

2630
logger = logging.getLogger(__name__)
@@ -69,6 +73,19 @@ def import_module(modname: str, warningiserror: bool = False) -> Any:
6973
raise ImportError(exc, traceback.format_exc()) from exc
7074

7175

76+
def _reload_module(module: ModuleType, warningiserror: bool = False) -> Any:
77+
"""
78+
Call importlib.reload(module), convert exceptions to ImportError
79+
"""
80+
try:
81+
with logging.skip_warningiserror(not warningiserror):
82+
return importlib.reload(module)
83+
except BaseException as exc:
84+
# Importing modules may cause any side effects, including
85+
# SystemExit, so we need to catch all errors.
86+
raise ImportError(exc, traceback.format_exc()) from exc
87+
88+
7289
def import_object(modname: str, objpath: list[str], objtype: str = '',
7390
attrgetter: Callable[[Any, str], Any] = safe_getattr,
7491
warningiserror: bool = False) -> Any:
@@ -83,23 +100,20 @@ def import_object(modname: str, objpath: list[str], objtype: str = '',
83100
objpath = list(objpath)
84101
while module is None:
85102
try:
86-
orig_modules = frozenset(sys.modules)
87-
try:
88-
# try importing with ``typing.TYPE_CHECKING == True``
89-
typing.TYPE_CHECKING = True
90-
module = import_module(modname, warningiserror=warningiserror)
91-
except ImportError:
92-
# if that fails (e.g. circular import), retry with
93-
# ``typing.TYPE_CHECKING == False`` after reverting
94-
# changes made to ``sys.modules`` by the failed try
95-
for m in [m for m in sys.modules if m not in orig_modules]:
96-
sys.modules.pop(m)
97-
98-
typing.TYPE_CHECKING = False
99-
module = import_module(modname, warningiserror=warningiserror)
100-
finally:
101-
# ensure ``typing.TYPE_CHECKING == False``
102-
typing.TYPE_CHECKING = False
103+
original_module_names = frozenset(sys.modules)
104+
module = import_module(modname, warningiserror=warningiserror)
105+
if os.environ.get('SPHINX_AUTODOC_RELOAD_MODULES'):
106+
new_modules = [m for m in sys.modules if m not in original_module_names]
107+
# Try reloading modules with ``typing.TYPE_CHECKING == True``.
108+
try:
109+
typing.TYPE_CHECKING = True
110+
# Ignore failures; we've already successfully loaded these modules
111+
with contextlib.suppress(ImportError, KeyError):
112+
for m in new_modules:
113+
_reload_module(sys.modules[m])
114+
finally:
115+
typing.TYPE_CHECKING = False
116+
module = sys.modules[modname]
103117
logger.debug('[autodoc] import %s => %r', modname, module)
104118
except ImportError as exc:
105119
logger.debug('[autodoc] import %s => failed', modname)

tests/conftest.py

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
from pathlib import Path
23

34
import docutils
@@ -24,6 +25,8 @@ def _init_console(locale_dir=sphinx.locale._LOCALE_DIR, catalog='sphinx'):
2425
# Exclude 'roots' dirs for pytest test collector
2526
collect_ignore = ['roots']
2627

28+
os.environ['SPHINX_AUTODOC_RELOAD_MODULES'] = '1'
29+
2730

2831
@pytest.fixture(scope='session')
2932
def rootdir():

0 commit comments

Comments
 (0)