Skip to content

Commit

Permalink
Fix #8219: autodoc: Parameters for generic base class are not shown
Browse files Browse the repository at this point in the history
  • Loading branch information
tk0miya committed Nov 3, 2020
1 parent c028fb7 commit 4032713
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 8 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Expand Up @@ -41,6 +41,9 @@ Features added
Bugs fixed
----------

* #8219: autodoc: Parameters for generic class are not shown when super class is
a generic class and show-inheritance option is given (in Python 3.7 or above)

Testing
--------

Expand Down
19 changes: 11 additions & 8 deletions sphinx/ext/autodoc/__init__.py
Expand Up @@ -37,7 +37,7 @@
from sphinx.util.inspect import (
evaluate_signature, getdoc, object_description, safe_getattr, stringify_signature
)
from sphinx.util.typing import stringify as stringify_typehint
from sphinx.util.typing import restify, stringify as stringify_typehint

if False:
# For type annotation
Expand Down Expand Up @@ -1522,13 +1522,16 @@ def add_directive_header(self, sig: str) -> None:
if not self.doc_as_attr and self.options.show_inheritance:
sourcename = self.get_sourcename()
self.add_line('', sourcename)
if hasattr(self.object, '__bases__') and len(self.object.__bases__):
bases = [':class:`%s`' % b.__name__
if b.__module__ in ('__builtin__', 'builtins')
else ':class:`%s.%s`' % (b.__module__, b.__qualname__)
for b in self.object.__bases__]
self.add_line(' ' + _('Bases: %s') % ', '.join(bases),
sourcename)

if hasattr(self.object, '__orig_bases__') and len(self.object.__orig_bases__):
# A subclass of generic types
# refs: PEP-560 <https://www.python.org/dev/peps/pep-0560/>
bases = [restify(cls) for cls in self.object.__orig_bases__]
self.add_line(' ' + _('Bases: %s') % ', '.join(bases), sourcename)
elif hasattr(self.object, '__bases__') and len(self.object.__bases__):
# A normal class
bases = [restify(cls) for cls in self.object.__bases__]
self.add_line(' ' + _('Bases: %s') % ', '.join(bases), sourcename)

def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
if encoding is not None:
Expand Down
38 changes: 38 additions & 0 deletions sphinx/util/typing.py
Expand Up @@ -30,6 +30,10 @@ def _evaluate(self, globalns: Dict, localns: Dict) -> Any:
ref = _ForwardRef(self.arg)
return ref._eval_type(globalns, localns)

if False:
# For type annotation
from typing import Type # NOQA # for python3.5.1


# An entry of Directive.option_spec
DirectiveOption = Callable[[str], Any]
Expand Down Expand Up @@ -234,3 +238,37 @@ def _stringify_py36(annotation: Any) -> str:
', '.join(param_strings))

return qualname


def restify(cls: "Type") -> str:
"""Convert python class to a reST reference."""
from sphinx.util import inspect # lazy loading

if cls.__module__ in ('__builtin__', 'builtins'):
return ':class:`%s`' % cls.__name__
elif inspect.isgenericalias(cls):
if cls._name:
if cls.__module__ == 'typing':
text = ':class:`%s`' % cls._name
else:
text = ':class:`%s.%s`' % (cls.__module__, cls._name)
else:
text = restify(cls.__origin__)

if cls.__args__:
text += r"\ [%s]" % ", ".join(restify(a) for a in cls.__args__)
return text
elif hasattr(cls, '__qualname__'):
if cls.__module__ == 'typing':
return ':class:`%s`' % cls.__qualname__
else:
return ':class:`%s.%s`' % (cls.__module__, cls.__qualname__)
elif hasattr(cls, '_name'):
# SpecialForm (ex. Union)
if cls.__module__ == 'typing':
return ':obj:`%s`' % cls._name
else:
return ':obj:`%s.%s`' % (cls.__module__, cls._name)
else:
# not a class (ex. TypeVar)
return ':obj:`%s.%s`' % (cls.__module__, cls.__name__)
8 changes: 8 additions & 0 deletions tests/roots/test-ext-autodoc/target/classes.py
@@ -1,3 +1,6 @@
from typing import List, Union


class Foo:
pass

Expand All @@ -10,3 +13,8 @@ def __init__(self, x, y):
class Baz:
def __new__(cls, x, y):
pass


class Qux(List[Union[int, float]]):
"""A subclass of List[Union[int, float]]"""
pass
33 changes: 33 additions & 0 deletions tests/test_ext_autodoc_autoclass.py
@@ -0,0 +1,33 @@
"""
test_ext_autodoc_autoclass
~~~~~~~~~~~~~~~~~~~~~~~~~~
Test the autodoc extension. This tests mainly the Documenters; the auto
directives are tested in a test source file translated by test_build.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

import sys

import pytest

from test_ext_autodoc import do_autodoc


@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_subclass_of_generic_type(app):
options = {'show-inheritance': True}
actual = do_autodoc(app, 'class', 'target.classes.Qux', options)
assert list(actual) == [
'',
'.. py:class:: Qux(iterable=(), /)',
' :module: target.classes',
'',
' Bases: :class:`List`\\ [:obj:`Union`\\ [:class:`int`, :class:`float`]]',
'',
' A subclass of List[Union[int, float]]',
'',
]

0 comments on commit 4032713

Please sign in to comment.