diff --git a/CHANGES b/CHANGES index 9848e7a4d..813a84af2 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.TypeVarDocumenter`` * ``sphinx.ext.autodoc.importer._getannotations()`` * ``sphinx.pycode.ModuleAnalyzer.parse()`` * ``sphinx.util.requests.is_ssl_error()`` @@ -53,6 +54,8 @@ Bugs fixed based uninitalized variables * #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 + information of TypeVars * #8419: html search: Do not load ``language_data.js`` in non-search pages * #8454: graphviz: The layout option for graph and digraph directives don't work * #8437: Makefile: ``make clean`` with empty BUILDDIR is dangerous diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst index 81fd82fca..6763b693c 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.TypeVarDocumenter`` + - 3.4 + - 5.0 + - ``sphinx.ext.autodoc.DataDocumenter`` + * - ``sphinx.ext.autodoc.importer._getannotations()`` - 3.4 - 4.0 diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index bdd633cbc..eaa7b898c 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1700,25 +1700,76 @@ class ExceptionDocumenter(ClassDocumenter): return isinstance(member, type) and issubclass(member, BaseException) -class NewTypeMixin: +class DataDocumenterMixinBase: + def should_suppress_directive_header(self) -> bool: + """Check directive header should be suppressed.""" + return False + + def update_content(self, more_content: StringList) -> None: + """Update docstring for the NewType object.""" + pass + + +class NewTypeMixin(DataDocumenterMixinBase): """ Mixin for DataDocumenter and AttributeDocumenter to provide the feature for supporting NewTypes. """ def should_suppress_directive_header(self) -> bool: - """Check directive header should be suppressed.""" - return inspect.isNewType(self.object) # type: ignore + return (inspect.isNewType(self.object) or # type: ignore + super().should_suppress_directive_header()) def update_content(self, more_content: StringList) -> None: - """Update docstring for the NewType object.""" if inspect.isNewType(self.object): # type: ignore supertype = restify(self.object.__supertype__) # type: ignore more_content.append(_('alias of %s') % supertype, '') more_content.append('', '') + super().update_content(more_content) -class DataDocumenter(ModuleLevelDocumenter, NewTypeMixin): + +class TypeVarMixin(DataDocumenterMixinBase): + """ + Mixin for DataDocumenter and AttributeDocumenter to provide the feature for + supporting TypeVars. + """ + + def should_suppress_directive_header(self) -> bool: + return (isinstance(self.object, TypeVar) or # type: ignore + super().should_suppress_directive_header()) + + def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]: + if ignore is not None: + warnings.warn("The 'ignore' argument to autodoc.%s.get_doc() is deprecated." + % self.__class__.__name__, + RemovedInSphinx50Warning, stacklevel=2) + + if isinstance(self.object, TypeVar): # type: ignore + if self.object.__doc__ != TypeVar.__doc__: # type: ignore + return super().get_doc() # type: ignore + else: + return [] + else: + return super().get_doc() # type: ignore + + def update_content(self, more_content: StringList) -> None: + if isinstance(self.object, TypeVar): # type: ignore + attrs = [repr(self.object.__name__)] # type: ignore + for constraint in self.object.__constraints__: # type: ignore + attrs.append(stringify_typehint(constraint)) + if self.object.__covariant__: # type: ignore + attrs.append("covariant=True") + if self.object.__contravariant__: # type: ignore + attrs.append("contravariant=True") + + more_content.append(_('alias of TypeVar(%s)') % ", ".join(attrs), '') + more_content.append('', '') + + super().update_content(more_content) + + +class DataDocumenter(NewTypeMixin, TypeVarMixin, ModuleLevelDocumenter): """ Specialized Documenter subclass for data items. """ @@ -1876,39 +1927,10 @@ class TypeVarDocumenter(DataDocumenter): directivetype = 'data' priority = DataDocumenter.priority + 1 - @classmethod - def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any - ) -> bool: - return isinstance(member, TypeVar) and isattr - - def add_directive_header(self, sig: str) -> None: - self.options = Options(self.options) - self.options['annotation'] = SUPPRESS - super().add_directive_header(sig) - - def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]: - if ignore is not None: - warnings.warn("The 'ignore' argument to autodoc.%s.get_doc() is deprecated." - % self.__class__.__name__, - RemovedInSphinx50Warning, stacklevel=2) - - if self.object.__doc__ != TypeVar.__doc__: - return super().get_doc() - else: - return [] - - def add_content(self, more_content: Optional[StringList], no_docstring: bool = False - ) -> None: - attrs = [repr(self.object.__name__)] - for constraint in self.object.__constraints__: - attrs.append(stringify_typehint(constraint)) - if self.object.__covariant__: - attrs.append("covariant=True") - if self.object.__contravariant__: - attrs.append("contravariant=True") - - content = StringList([_('alias of TypeVar(%s)') % ", ".join(attrs)], source='') - super().add_content(content) + def __init__(self, *args: Any, **kwargs: Any) -> None: + warnings.warn("%s is deprecated." % self.__class__.__name__, + RemovedInSphinx50Warning, stacklevel=2) + super().__init__(*args, **kwargs) class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: ignore @@ -2072,7 +2094,8 @@ class SingledispatchMethodDocumenter(MethodDocumenter): super().__init__(*args, **kwargs) -class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter, NewTypeMixin): # type: ignore # NOQA +class AttributeDocumenter(NewTypeMixin, TypeVarMixin, # type: ignore + DocstringStripSignatureMixin, ClassLevelDocumenter): """ Specialized Documenter subclass for attributes. """ @@ -2403,7 +2426,6 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.add_autodocumenter(DataDocumenter) app.add_autodocumenter(GenericAliasDocumenter) app.add_autodocumenter(NewTypeDataDocumenter) - app.add_autodocumenter(TypeVarDocumenter) app.add_autodocumenter(FunctionDocumenter) app.add_autodocumenter(DecoratorDocumenter) app.add_autodocumenter(MethodDocumenter) diff --git a/tests/roots/test-ext-autodoc/target/typevar.py b/tests/roots/test-ext-autodoc/target/typevar.py index ce531e8f3..864fea20c 100644 --- a/tests/roots/test-ext-autodoc/target/typevar.py +++ b/tests/roots/test-ext-autodoc/target/typevar.py @@ -19,7 +19,8 @@ T6 = NewType("T6", int) class Class: - # TODO: TypeVar + #: T1 + T1 = TypeVar("T1") #: T6 T6 = NewType("T6", int) diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 9b80d8669..4b18da862 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -1735,6 +1735,14 @@ def test_autodoc_TypeVar(app): ' :module: target.typevar', '', '', + ' .. py:attribute:: Class.T1', + ' :module: target.typevar', + '', + ' T1', + '', + " alias of TypeVar('T1')", + '', + '', ' .. py:attribute:: Class.T6', ' :module: target.typevar', '', @@ -1750,6 +1758,7 @@ def test_autodoc_TypeVar(app): '', " alias of TypeVar('T1')", '', + '', '.. py:data:: T3', ' :module: target.typevar', '', @@ -1757,6 +1766,7 @@ def test_autodoc_TypeVar(app): '', " alias of TypeVar('T3', int, str)", '', + '', '.. py:data:: T4', ' :module: target.typevar', '', @@ -1764,6 +1774,7 @@ def test_autodoc_TypeVar(app): '', " alias of TypeVar('T4', covariant=True)", '', + '', '.. py:data:: T5', ' :module: target.typevar', '', @@ -1771,6 +1782,7 @@ def test_autodoc_TypeVar(app): '', " alias of TypeVar('T5', contravariant=True)", '', + '', '.. py:data:: T6', ' :module: target.typevar', '', diff --git a/tests/test_ext_autodoc_autoattribute.py b/tests/test_ext_autodoc_autoattribute.py index 28c48d081..4787530de 100644 --- a/tests/test_ext_autodoc_autoattribute.py +++ b/tests/test_ext_autodoc_autoattribute.py @@ -85,3 +85,18 @@ def test_autoattribute_NewType(app): ' alias of :class:`int`', '', ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autoattribute_TypeVar(app): + actual = do_autodoc(app, 'attribute', 'target.typevar.Class.T1') + assert list(actual) == [ + '', + '.. py:attribute:: Class.T1', + ' :module: target.typevar', + '', + ' T1', + '', + " alias of TypeVar('T1')", + '', + ] diff --git a/tests/test_ext_autodoc_autodata.py b/tests/test_ext_autodoc_autodata.py index 9c765676f..dbf6103cb 100644 --- a/tests/test_ext_autodoc_autodata.py +++ b/tests/test_ext_autodoc_autodata.py @@ -88,3 +88,18 @@ def test_autodata_NewType(app): ' alias of :class:`int`', '', ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodata_TypeVar(app): + actual = do_autodoc(app, 'data', 'target.typevar.T1') + assert list(actual) == [ + '', + '.. py:data:: T1', + ' :module: target.typevar', + '', + ' T1', + '', + " alias of TypeVar('T1')", + '', + ]