Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Comment on lines +71 to +75
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AA-Turner There is an outer except Exception, does that count?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been wrong for a while, simple counterexample:

import sys
try:
    sys.exit()
except Exception:
    print('caught')

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
Loading