Skip to content

Commit

Permalink
Merge pull request #8504 from tk0miya/deprecate_GenericAliasDocumenter
Browse files Browse the repository at this point in the history
Fix #8503: autoattribute could not create document for a GenericAlias
  • Loading branch information
tk0miya committed Nov 29, 2020
2 parents c15c61e + bb63f03 commit aa84eea
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 34 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Expand Up @@ -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``
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions doc/extdev/deprecated.rst
Expand Up @@ -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
Expand Down
55 changes: 24 additions & 31 deletions sphinx/ext/autodoc/__init__.py
Expand Up @@ -1701,6 +1701,25 @@ def update_content(self, more_content: StringList) -> None:
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
Expand Down Expand Up @@ -1801,8 +1820,8 @@ def add_content(self, more_content: Optional[StringList], no_docstring: bool = F
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.
"""
Expand Down Expand Up @@ -1863,32 +1882,6 @@ def add_content(self, more_content: Optional[StringList], no_docstring: bool = F
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.
Expand Down Expand Up @@ -2102,8 +2095,8 @@ def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
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.
"""
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
15 changes: 15 additions & 0 deletions sphinx/ext/autodoc/deprecated.py
Expand Up @@ -109,3 +109,18 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
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)
5 changes: 2 additions & 3 deletions sphinx/ext/autosummary/generate.py
Expand Up @@ -87,15 +87,14 @@ def emit_firstresult(self, *args: Any) -> None:
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)
Expand Down
5 changes: 5 additions & 0 deletions tests/roots/test-ext-autodoc/target/genericalias.py
Expand Up @@ -4,3 +4,8 @@
T = List[int]

C = Callable[[int], None] # a generic alias not having a doccomment


class Class:
#: A list of int
T = List[int]
22 changes: 22 additions & 0 deletions tests/test_ext_autodoc.py
Expand Up @@ -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',
'',
Expand All @@ -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]',
'',
]


Expand Down
26 changes: 26 additions & 0 deletions tests/test_ext_autodoc_autoattribute.py
Expand Up @@ -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')
Expand Down
26 changes: 26 additions & 0 deletions tests/test_ext_autodoc_autodata.py
Expand Up @@ -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')
Expand Down

0 comments on commit aa84eea

Please sign in to comment.