From 80f84260bad23b0f71b86636a987a2b9dd49b828 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 22 Nov 2021 01:48:47 +0900 Subject: [PATCH] Fix #9879: autodoc: AttributeError for object having invalid __doc__ --- CHANGES | 2 ++ sphinx/ext/autodoc/__init__.py | 2 +- sphinx/util/inspect.py | 11 +++++++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 4ee8d55cd91..ec3d22b267b 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,8 @@ Bugs fixed * #9838: autodoc: AttributeError is raised on building document for functions decorated by functools.lru_cache +* #9879: autodoc: AttributeError is raised on building document for an object + having invalid __doc__ atribute Testing -------- diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 2cdf224cb25..8fcefcc716e 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1704,7 +1704,7 @@ def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]: classdoc_from = self.options.get('class-doc-from', self.config.autoclass_content) docstrings = [] - attrdocstring = self.get_attr(self.object, '__doc__', None) + attrdocstring = getdoc(self.object, self.get_attr) if attrdocstring: docstrings.append(attrdocstring) diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 3a39bde1d6b..24ea49ae04e 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -871,6 +871,13 @@ def getdoc(obj: Any, attrgetter: Callable = safe_getattr, * inherited docstring * inherited decorated methods """ + def getdoc_internal(obj: Any, attrgetter: Callable = safe_getattr) -> Optional[str]: + doc = attrgetter(obj, '__doc__', None) + if isinstance(doc, str): + return doc + else: + return None + if cls and name and isclassmethod(obj, cls, name): for basecls in getmro(cls): meth = basecls.__dict__.get(name) @@ -879,7 +886,7 @@ def getdoc(obj: Any, attrgetter: Callable = safe_getattr, if doc is not None or not allow_inherited: return doc - doc = attrgetter(obj, '__doc__', None) + doc = getdoc_internal(obj) if ispartial(obj) and doc == obj.__class__.__doc__: return getdoc(obj.func) elif doc is None and allow_inherited: @@ -888,7 +895,7 @@ def getdoc(obj: Any, attrgetter: Callable = safe_getattr, for basecls in getmro(cls): meth = safe_getattr(basecls, name, None) if meth is not None: - doc = attrgetter(meth, '__doc__', None) + doc = getdoc_internal(meth) if doc is not None: break