diff --git a/CHANGES b/CHANGES index 6c2761c53e8..8d82ad9855b 100644 --- a/CHANGES +++ b/CHANGES @@ -49,6 +49,7 @@ Features added * #7143: autodoc: Support final classes and methods * #7384: autodoc: Support signatures defined by ``__new__()``, metaclasses and builtin base classes +* #4422: autodoc: Support GenericAlias in Python 3.7 or above * #7466: autosummary: headings in generated documents are not translated * #7490: autosummary: Add ``:caption:`` option to autosummary directive to set a caption to the toctree diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index f8e4be99972..4b96c6d0dc8 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1544,6 +1544,30 @@ def add_content(self, more_content: Any, no_docstring: bool = False) -> None: super().add_content(more_content, no_docstring=True) +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.annotation = SUPPRESS # type: ignore + super().add_directive_header(sig) + + def add_content(self, more_content: Any, no_docstring: bool = False) -> None: + name = stringify_typehint(self.object) + content = StringList([_('alias of %s') % name], source='') + super().add_content(content) + + class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: ignore """ Specialized Documenter subclass for methods (normal, static and class). @@ -1900,6 +1924,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.add_autodocumenter(ModuleDocumenter) app.add_autodocumenter(ClassDocumenter) app.add_autodocumenter(ExceptionDocumenter) + app.add_autodocumenter(GenericAliasDocumenter) app.add_autodocumenter(DataDocumenter) app.add_autodocumenter(DataDeclarationDocumenter) app.add_autodocumenter(FunctionDocumenter) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 473ac3db697..4dde73829ec 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -91,14 +91,14 @@ def setup_documenters(app: Any) -> None: ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter, FunctionDocumenter, MethodDocumenter, AttributeDocumenter, InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter, - SlotsAttributeDocumenter, DataDeclarationDocumenter, + SlotsAttributeDocumenter, DataDeclarationDocumenter, GenericAliasDocumenter, SingledispatchFunctionDocumenter, ) documenters = [ ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter, FunctionDocumenter, MethodDocumenter, AttributeDocumenter, InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter, - SlotsAttributeDocumenter, DataDeclarationDocumenter, + SlotsAttributeDocumenter, DataDeclarationDocumenter, GenericAliasDocumenter, SingledispatchFunctionDocumenter, ] # type: List[Type[Documenter]] for documenter in documenters: diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index c1799778c52..5cc1f3c2285 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -1587,6 +1587,37 @@ def test_autodoc_typed_instance_variables(app): ] +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_GenericAlias(app): + options = {"members": None, + "undoc-members": None} + actual = do_autodoc(app, 'module', 'target.genericalias', options) + if sys.version_info < (3, 7): + assert list(actual) == [ + '', + '.. py:module:: target.genericalias', + '', + '', + '.. py:attribute:: T', + ' :module: target.genericalias', + '', + ' alias of :class:`typing.List`', + ] + else: + assert list(actual) == [ + '', + '.. py:module:: target.genericalias', + '', + '', + '.. py:data:: T', + ' :module: target.genericalias', + '', + ' A list of int', + '', + ' alias of List[int]', + ] + + @pytest.mark.skipif(sys.version_info < (3, 9), reason='py39+ is required.') @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_Annotated(app):