From bb63f030346829d05997efc3ec4713b3c937f3d5 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 28 Nov 2020 13:47:23 +0900 Subject: [PATCH] Fix #8503: autoattribute could not create document for a GenericAlias --- CHANGES | 3 + doc/extdev/deprecated.rst | 5 ++ sphinx/ext/autodoc/__init__.py | 55 ++++++++----------- sphinx/ext/autodoc/deprecated.py | 15 +++++ sphinx/ext/autosummary/generate.py | 5 +- .../test-ext-autodoc/target/genericalias.py | 5 ++ tests/test_ext_autodoc.py | 22 ++++++++ tests/test_ext_autodoc_autoattribute.py | 26 +++++++++ tests/test_ext_autodoc_autodata.py | 26 +++++++++ 9 files changed, 128 insertions(+), 34 deletions(-) diff --git a/CHANGES b/CHANGES index 1ea53bfc5..769b29ab0 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,7 @@ Deprecated * The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()`` * ``sphinx.ext.autodoc.Documenter.get_object_members()`` * ``sphinx.ext.autodoc.DataDeclarationDocumenter`` +* ``sphinx.ext.autodoc.GenericAliasDocumenter`` * ``sphinx.ext.autodoc.InstanceAttributeDocumenter`` * ``sphinx.ext.autodoc.SlotsAttributeDocumenter`` * ``sphinx.ext.autodoc.TypeVarDocumenter`` @@ -60,6 +61,8 @@ Bugs fixed based uninitalized variables * #8480: autodoc: autoattribute could not create document for __slots__ attributes +* #8503: autodoc: autoattribute could not create document for a GenericAlias as + class attributes correctly * #8452: autodoc: autodoc_type_aliases doesn't work when autodoc_typehints is set to "description" * #8460: autodoc: autodata and autoattribute directives do not display type diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst index bbd9d283e..068bf899b 100644 --- a/doc/extdev/deprecated.rst +++ b/doc/extdev/deprecated.rst @@ -41,6 +41,11 @@ The following is a list of deprecated interfaces. - 5.0 - ``sphinx.ext.autodoc.DataDocumenter`` + * - ``sphinx.ext.autodoc.GenericAliasDocumenter`` + - 3.4 + - 5.0 + - ``sphinx.ext.autodoc.DataDocumenter`` + * - ``sphinx.ext.autodoc.InstanceAttributeDocumenter`` - 3.4 - 5.0 diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index f77852c13..ea2818800 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1701,6 +1701,25 @@ class DataDocumenterMixinBase: pass +class GenericAliasMixin(DataDocumenterMixinBase): + """ + Mixin for DataDocumenter and AttributeDocumenter to provide the feature for + supporting GenericAliases. + """ + + def should_suppress_directive_header(self) -> bool: + return (inspect.isgenericalias(self.object) or + super().should_suppress_directive_header()) + + def update_content(self, more_content: StringList) -> None: + if inspect.isgenericalias(self.object): + alias = stringify_typehint(self.object) + more_content.append(_('alias of %s') % alias, '') + more_content.append('', '') + + super().update_content(more_content) + + class NewTypeMixin(DataDocumenterMixinBase): """ Mixin for DataDocumenter and AttributeDocumenter to provide the feature for @@ -1801,8 +1820,8 @@ class UninitializedGlobalVariableMixin(DataDocumenterMixinBase): super().add_content(more_content, no_docstring=no_docstring) # type: ignore -class DataDocumenter(NewTypeMixin, TypeVarMixin, UninitializedGlobalVariableMixin, - ModuleLevelDocumenter): +class DataDocumenter(GenericAliasMixin, NewTypeMixin, TypeVarMixin, + UninitializedGlobalVariableMixin, ModuleLevelDocumenter): """ Specialized Documenter subclass for data items. """ @@ -1863,32 +1882,6 @@ class DataDocumenter(NewTypeMixin, TypeVarMixin, UninitializedGlobalVariableMixi super().add_content(more_content, no_docstring=no_docstring) -class GenericAliasDocumenter(DataDocumenter): - """ - Specialized Documenter subclass for GenericAliases. - """ - - objtype = 'genericalias' - directivetype = 'data' - priority = DataDocumenter.priority + 1 - - @classmethod - def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any - ) -> bool: - return inspect.isgenericalias(member) - - def add_directive_header(self, sig: str) -> None: - self.options = Options(self.options) - self.options['annotation'] = SUPPRESS - super().add_directive_header(sig) - - def add_content(self, more_content: Optional[StringList], no_docstring: bool = False - ) -> None: - name = stringify_typehint(self.object) - content = StringList([_('alias of %s') % name], source='') - super().add_content(content) - - class NewTypeDataDocumenter(DataDocumenter): """ Specialized Documenter subclass for NewTypes. @@ -2102,8 +2095,8 @@ class SlotsMixin(DataDocumenterMixinBase): return super().get_doc(encoding, ignore) # type: ignore -class AttributeDocumenter(NewTypeMixin, SlotsMixin, TypeVarMixin, # type: ignore - DocstringStripSignatureMixin, ClassLevelDocumenter): +class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type: ignore + TypeVarMixin, DocstringStripSignatureMixin, ClassLevelDocumenter): """ Specialized Documenter subclass for attributes. """ @@ -2324,6 +2317,7 @@ def migrate_autodoc_member_order(app: Sphinx, config: Config) -> None: # for compatibility from sphinx.ext.autodoc.deprecated import DataDeclarationDocumenter # NOQA +from sphinx.ext.autodoc.deprecated import GenericAliasDocumenter # NOQA from sphinx.ext.autodoc.deprecated import InstanceAttributeDocumenter # NOQA from sphinx.ext.autodoc.deprecated import SingledispatchFunctionDocumenter # NOQA from sphinx.ext.autodoc.deprecated import SingledispatchMethodDocumenter # NOQA @@ -2336,7 +2330,6 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.add_autodocumenter(ClassDocumenter) app.add_autodocumenter(ExceptionDocumenter) app.add_autodocumenter(DataDocumenter) - app.add_autodocumenter(GenericAliasDocumenter) app.add_autodocumenter(NewTypeDataDocumenter) app.add_autodocumenter(FunctionDocumenter) app.add_autodocumenter(DecoratorDocumenter) diff --git a/sphinx/ext/autodoc/deprecated.py b/sphinx/ext/autodoc/deprecated.py index c6ca13105..5541a7a91 100644 --- a/sphinx/ext/autodoc/deprecated.py +++ b/sphinx/ext/autodoc/deprecated.py @@ -109,3 +109,18 @@ class SlotsAttributeDocumenter(AttributeDocumenter): warnings.warn("%s is deprecated." % self.__class__.__name__, RemovedInSphinx50Warning, stacklevel=2) super().__init__(*args, **kwargs) + + +class GenericAliasDocumenter(DataDocumenter): + """ + Specialized Documenter subclass for GenericAliases. + """ + + objtype = 'genericalias' + directivetype = 'data' + priority = DataDocumenter.priority + 1 # type: ignore + + def __init__(self, *args: Any, **kwargs: Any) -> None: + warnings.warn("%s is deprecated." % self.__class__.__name__, + RemovedInSphinx50Warning, stacklevel=2) + super().__init__(*args, **kwargs) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 2dc936591..10f8ca913 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -87,15 +87,14 @@ AutosummaryEntry = NamedTuple('AutosummaryEntry', [('name', str), def setup_documenters(app: Any) -> None: from sphinx.ext.autodoc import (AttributeDocumenter, ClassDocumenter, DataDocumenter, DecoratorDocumenter, ExceptionDocumenter, - FunctionDocumenter, GenericAliasDocumenter, - MethodDocumenter, ModuleDocumenter, + FunctionDocumenter, MethodDocumenter, ModuleDocumenter, NewTypeAttributeDocumenter, NewTypeDataDocumenter, PropertyDocumenter, SingledispatchFunctionDocumenter) documenters = [ ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter, FunctionDocumenter, MethodDocumenter, NewTypeAttributeDocumenter, NewTypeDataDocumenter, AttributeDocumenter, DecoratorDocumenter, PropertyDocumenter, - GenericAliasDocumenter, SingledispatchFunctionDocumenter, + SingledispatchFunctionDocumenter, ] # type: List[Type[Documenter]] for documenter in documenters: app.registry.add_documenter(documenter.objtype, documenter) diff --git a/tests/roots/test-ext-autodoc/target/genericalias.py b/tests/roots/test-ext-autodoc/target/genericalias.py index d37bcb7fa..9909efca1 100644 --- a/tests/roots/test-ext-autodoc/target/genericalias.py +++ b/tests/roots/test-ext-autodoc/target/genericalias.py @@ -4,3 +4,8 @@ from typing import Callable, List T = List[int] C = Callable[[int], None] # a generic alias not having a doccomment + + +class Class: + #: A list of int + T = List[int] diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 000ddc0c5..02ffae549 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -1701,6 +1701,15 @@ def test_autodoc_GenericAlias(app): '.. py:module:: target.genericalias', '', '', + '.. py:class:: Class()', + ' :module: target.genericalias', + '', + '', + ' .. py:attribute:: Class.T', + ' :module: target.genericalias', + '', + ' alias of :class:`List`\\ [:class:`int`]', + '', '.. py:attribute:: T', ' :module: target.genericalias', '', @@ -1712,12 +1721,25 @@ def test_autodoc_GenericAlias(app): '.. py:module:: target.genericalias', '', '', + '.. py:class:: Class()', + ' :module: target.genericalias', + '', + '', + ' .. py:attribute:: Class.T', + ' :module: target.genericalias', + '', + ' A list of int', + '', + ' alias of List[int]', + '', + '', '.. py:data:: T', ' :module: target.genericalias', '', ' A list of int', '', ' alias of List[int]', + '', ] diff --git a/tests/test_ext_autodoc_autoattribute.py b/tests/test_ext_autodoc_autoattribute.py index a0d5e5b6f..e44395ee3 100644 --- a/tests/test_ext_autodoc_autoattribute.py +++ b/tests/test_ext_autodoc_autoattribute.py @@ -107,6 +107,32 @@ def test_autoattribute_slots_variable_str(app): ] +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autoattribute_GenericAlias(app): + actual = do_autodoc(app, 'attribute', 'target.genericalias.Class.T') + if sys.version_info < (3, 7): + assert list(actual) == [ + '', + '.. py:attribute:: Class.T', + ' :module: target.genericalias', + ' :value: typing.List[int]', + '', + ' A list of int', + '', + ] + else: + assert list(actual) == [ + '', + '.. py:attribute:: Class.T', + ' :module: target.genericalias', + '', + ' A list of int', + '', + ' alias of List[int]', + '', + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autoattribute_NewType(app): actual = do_autodoc(app, 'attribute', 'target.typevar.Class.T6') diff --git a/tests/test_ext_autodoc_autodata.py b/tests/test_ext_autodoc_autodata.py index dbf6103cb..d3f63f432 100644 --- a/tests/test_ext_autodoc_autodata.py +++ b/tests/test_ext_autodoc_autodata.py @@ -75,6 +75,32 @@ def test_autodata_type_comment(app): ] +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodata_GenericAlias(app): + actual = do_autodoc(app, 'data', 'target.genericalias.T') + if sys.version_info < (3, 7): + assert list(actual) == [ + '', + '.. py:data:: T', + ' :module: target.genericalias', + ' :value: typing.List[int]', + '', + ' A list of int', + '', + ] + else: + assert list(actual) == [ + '', + '.. py:data:: T', + ' :module: target.genericalias', + '', + ' A list of int', + '', + ' alias of List[int]', + '', + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodata_NewType(app): actual = do_autodoc(app, 'data', 'target.typevar.T6')