diff --git a/AUTHORS.rst b/AUTHORS.rst index 5e4d75e988a..9ecbd9d5a10 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -45,6 +45,7 @@ Contributors * Daniel Eades -- improved static typing * Daniel Hahler -- testing and CI improvements * Daniel Pizetta -- inheritance diagram improvements +* Dave Hoese -- ``sphinx.ext.viewcode`` bug fix * Dave Kuhlman -- original LaTeX writer * Dimitri Papadopoulos Orfanos -- linting and spelling * Dmitry Shachnev -- modernisation and reproducibility diff --git a/CHANGES.rst b/CHANGES.rst index 5124dbb90e2..61d9d37c8e4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -19,6 +19,8 @@ Bugs fixed * #13377: Restore support for using ``sphinx.testing.path`` paths with ``sphinx.testing.fixtures``. Patch by Kazuya Takei. +* #13380: viewcode: Fix importing modules more than once. + Patch by Dave Hoese. Testing ------- diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py index 44e9aeaab75..4b1c62ad0d1 100644 --- a/sphinx/ext/viewcode.py +++ b/sphinx/ext/viewcode.py @@ -2,7 +2,7 @@ from __future__ import annotations -import importlib.util +import importlib import operator import posixpath import traceback @@ -62,16 +62,20 @@ def _get_full_modname(modname: str, attribute: str) -> str | None: num_parts = len(module_path) for i in range(num_parts, 0, -1): mod_root = '.'.join(module_path[:i]) - module_spec = importlib.util.find_spec(mod_root) - if module_spec is not None: + try: + # import_module() caches the module in sys.modules + module = importlib.import_module(mod_root) break + except ModuleNotFoundError: + continue + except BaseException as exc: + # Importing modules may cause any side effects, including + # SystemExit, so we need to catch all errors. + msg = f"viewcode failed to import '{mod_root}'." + raise ImportError(msg) from exc else: return None - # Load and execute the module - module = importlib.util.module_from_spec(module_spec) - if module_spec.loader is None: - return None - module_spec.loader.exec_module(module) + if i != num_parts: for mod in module_path[i:]: module = getattr(module, mod)