diff --git a/CHANGES b/CHANGES index d0acd0d93..8b4996c10 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,8 @@ Bugs fixed ---------- * #7808: autodoc: Warnings raised on variable and attribute type annotations +* #7812: autosummary: generates broken stub files if the target code contains + an attribute and module that are same name * #7811: sphinx.util.inspect causes circular import problem Testing diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index ca9bb028d..b28c62fc3 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -615,13 +615,28 @@ def split_full_qualified_name(name: str) -> Tuple[str, str]: Therefore you need to mock 3rd party modules if needed before calling this function. """ + from sphinx.util import inspect + parts = name.split('.') for i, part in enumerate(parts, 1): try: modname = ".".join(parts[:i]) - import_module(modname) + module = import_module(modname) + + # check the module has a member named as attrname + # + # Note: This is needed to detect the attribute having the same name + # as the module. + # ref: https://github.com/sphinx-doc/sphinx/issues/7812 + attrname = parts[i] + if hasattr(module, attrname): + value = inspect.safe_getattr(module, attrname) + if not inspect.ismodule(value): + return ".".join(parts[:i]), ".".join(parts[i:]) except ImportError: return ".".join(parts[:i - 1]), ".".join(parts[i - 1:]) + except IndexError: + pass return name, "" diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index d4928c847..206be1f9b 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -18,7 +18,7 @@ import typing import warnings from functools import partial, partialmethod from inspect import ( # NOQA - Parameter, isclass, ismethod, ismethoddescriptor + Parameter, isclass, ismethod, ismethoddescriptor, ismodule ) from io import StringIO from typing import Any, Callable, Mapping, List, Optional, Tuple