Fix #9607: autodoc: Incorrect base class detection

In case of the descendants of generic class, the value of
obj.__orig_bases__ is incorrect because it returns original base
arguments for the child of the generic class instead of the target
class itself.

This uses obj.__dict__ to get the correct __orig_bases__ information.
This commit is contained in:
Takeshi KOMIYA
2021-09-20 13:20:26 +09:00
parent a05dc0b419
commit 10b7f0e252
5 changed files with 37 additions and 1 deletions

View File

@@ -21,6 +21,8 @@ Bugs fixed
* #9630: autodoc: Failed to build cross references if :confval:`primary_domain`
is not 'py'
* #9644: autodoc: Crashed on getting source info from problematic object
* #9607: autodoc: Incorrect base class detection for the subclasses of the
generic class
* #9630: autosummary: Failed to build summary table if :confval:`primary_domain`
is not 'py'

View File

@@ -1650,7 +1650,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
# add inheritance info, if wanted
if not self.doc_as_attr and self.options.show_inheritance:
if hasattr(self.object, '__orig_bases__') and len(self.object.__orig_bases__):
if inspect.getorigbases(self.object):
# A subclass of generic types
# refs: PEP-560 <https://www.python.org/dev/peps/pep-0560/>
bases = list(self.object.__orig_bases__)

View File

@@ -187,6 +187,21 @@ def getmro(obj: Any) -> Tuple[Type, ...]:
return tuple()
def getorigbases(obj: Any) -> Optional[Tuple[Any, ...]]:
"""Get __orig_bases__ from *obj* safely."""
if not inspect.isclass(obj):
return None
# Get __orig_bases__ from obj.__dict__ to avoid accessing the parent's __orig_bases__.
# refs: https://github.com/sphinx-doc/sphinx/issues/9607
__dict__ = safe_getattr(obj, '__dict__', {})
__orig_bases__ = __dict__.get('__orig_bases__')
if isinstance(__orig_bases__, tuple) and len(__orig_bases__) > 0:
return __orig_bases__
else:
return None
def getslots(obj: Any) -> Optional[Dict]:
"""Get __slots__ attribute of the class as dict.

View File

@@ -29,6 +29,10 @@ class Quux(List[Union[int, float]]):
pass
class Corge(Quux):
pass
Alias = Foo
#: docstring

View File

@@ -273,6 +273,21 @@ def test_show_inheritance_for_subclass_of_generic_type(app):
]
@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_show_inheritance_for_decendants_of_generic_type(app):
options = {'show-inheritance': None}
actual = do_autodoc(app, 'class', 'target.classes.Corge', options)
assert list(actual) == [
'',
'.. py:class:: Corge(iterable=(), /)',
' :module: target.classes',
'',
' Bases: :py:class:`target.classes.Quux`',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_process_bases(app):
def autodoc_process_bases(app, name, obj, options, bases):