Skip to content

Commit

Permalink
Fix viewcode extension importing modules more than once (#13380)
Browse files Browse the repository at this point in the history
Co-authored-by: Adam Turner <[email protected]>
  • Loading branch information
djhoese and AA-Turner authored Feb 21, 2025
1 parent 7e1bf28 commit 8ef0708
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 8 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------
Expand Down
20 changes: 12 additions & 8 deletions sphinx/ext/viewcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

import importlib.util
import importlib
import operator
import posixpath
import traceback
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 8ef0708

Please sign in to comment.