autodoc: Support type annotations for variables

This commit is contained in:
Takeshi KOMIYA 2020-02-01 13:38:26 +09:00
parent 66e4897a05
commit 5e4e44c195
3 changed files with 54 additions and 29 deletions

View File

@ -47,6 +47,7 @@ Features added
images (imagesize-1.2.0 or above is required)
* #6994: imgconverter: Support illustrator file (.ai) to .png conversion
* autodoc: Support Positional-Only Argument separator (PEP-570 compliant)
* autodoc: Support type annotations for variables
* #2755: autodoc: Add new event: :event:`autodoc-before-process-signature`
* #2755: autodoc: Support type_comment style (ex. ``# type: (str) -> str``)
annotation (python3.8+ or `typed_ast <https://github.com/python/typed_ast>`_

View File

@ -10,6 +10,7 @@
:license: BSD, see LICENSE for details.
"""
import importlib
import re
import warnings
from types import ModuleType
@ -33,6 +34,7 @@ from sphinx.util import logging
from sphinx.util import rpartition
from sphinx.util.docstrings import prepare_docstring
from sphinx.util.inspect import getdoc, object_description, safe_getattr, stringify_signature
from sphinx.util.typing import stringify as stringify_typehint
if False:
# For type annotation
@ -1233,11 +1235,18 @@ class DataDocumenter(ModuleLevelDocumenter):
sourcename = self.get_sourcename()
if not self.options.annotation:
try:
objrepr = object_description(self.object)
annotations = getattr(self.parent, '__annotations__', {})
if self.objpath[-1] in annotations:
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
self.add_line(' :type: ' + objrepr, sourcename)
except ValueError:
pass
try:
objrepr = object_description(self.object)
self.add_line(' :value: ' + objrepr, sourcename)
except ValueError:
pass
else:
self.add_line(' :annotation: = ' + objrepr, sourcename)
elif self.options.annotation is SUPPRESS:
pass
else:
@ -1276,6 +1285,12 @@ class DataDeclarationDocumenter(DataDocumenter):
"""Never import anything."""
# disguise as a data
self.objtype = 'data'
try:
# import module to obtain type annotation
self.parent = importlib.import_module(self.modname)
except ImportError:
pass
return True
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
@ -1405,11 +1420,18 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
if not self.options.annotation:
if not self._datadescriptor:
try:
objrepr = object_description(self.object)
annotations = getattr(self.parent, '__annotations__', {})
if self.objpath[-1] in annotations:
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
self.add_line(' :type: ' + objrepr, sourcename)
except ValueError:
pass
try:
objrepr = object_description(self.object)
self.add_line(' :value: ' + objrepr, sourcename)
except ValueError:
pass
else:
self.add_line(' :annotation: = ' + objrepr, sourcename)
elif self.options.annotation is SUPPRESS:
pass
else:

View File

@ -906,7 +906,7 @@ def test_autodoc_module_scope(app):
'',
'.. py:attribute:: Class.mdocattr',
' :module: target',
' :annotation: = <_io.StringIO object>',
' :value: <_io.StringIO object>',
'',
' should be documented as well - süß',
' '
@ -922,7 +922,7 @@ def test_autodoc_class_scope(app):
'',
'.. py:attribute:: Class.mdocattr',
' :module: target',
' :annotation: = <_io.StringIO object>',
' :value: <_io.StringIO object>',
'',
' should be documented as well - süß',
' '
@ -942,12 +942,12 @@ def test_class_attributes(app):
' ',
' .. py:attribute:: AttCls.a1',
' :module: target',
' :annotation: = hello world',
' :value: hello world',
' ',
' ',
' .. py:attribute:: AttCls.a2',
' :module: target',
' :annotation: = None',
' :value: None',
' '
]
@ -966,7 +966,7 @@ def test_instance_attributes(app):
' ',
' .. py:attribute:: InstAttCls.ca1',
' :module: target',
" :annotation: = 'a'",
" :value: 'a'",
' ',
' Doc comment for class attribute InstAttCls.ca1.',
' It can have multiple lines.',
@ -974,28 +974,28 @@ def test_instance_attributes(app):
' ',
' .. py:attribute:: InstAttCls.ca2',
' :module: target',
" :annotation: = 'b'",
" :value: 'b'",
' ',
' Doc comment for InstAttCls.ca2. One line only.',
' ',
' ',
' .. py:attribute:: InstAttCls.ca3',
' :module: target',
" :annotation: = 'c'",
" :value: 'c'",
' ',
' Docstring for class attribute InstAttCls.ca3.',
' ',
' ',
' .. py:attribute:: InstAttCls.ia1',
' :module: target',
' :annotation: = None',
' :value: None',
' ',
' Doc comment for instance attribute InstAttCls.ia1',
' ',
' ',
' .. py:attribute:: InstAttCls.ia2',
' :module: target',
' :annotation: = None',
' :value: None',
' ',
' Docstring for instance attribute InstAttCls.ia2.',
' '
@ -1014,7 +1014,7 @@ def test_instance_attributes(app):
' ',
' .. py:attribute:: InstAttCls.ca1',
' :module: target',
" :annotation: = 'a'",
" :value: 'a'",
' ',
' Doc comment for class attribute InstAttCls.ca1.',
' It can have multiple lines.',
@ -1022,7 +1022,7 @@ def test_instance_attributes(app):
' ',
' .. py:attribute:: InstAttCls.ia1',
' :module: target',
' :annotation: = None',
' :value: None',
' ',
' Doc comment for instance attribute InstAttCls.ia1',
' '
@ -1090,28 +1090,28 @@ def test_enum_class(app):
' ',
' .. py:attribute:: EnumCls.val1',
' :module: target.enum',
' :annotation: = 12',
' :value: 12',
' ',
' doc for val1',
' ',
' ',
' .. py:attribute:: EnumCls.val2',
' :module: target.enum',
' :annotation: = 23',
' :value: 23',
' ',
' doc for val2',
' ',
' ',
' .. py:attribute:: EnumCls.val3',
' :module: target.enum',
' :annotation: = 34',
' :value: 34',
' ',
' doc for val3',
' ',
' ',
' .. py:attribute:: EnumCls.val4',
' :module: target.enum',
' :annotation: = 34',
' :value: 34',
' '
]
@ -1121,7 +1121,7 @@ def test_enum_class(app):
'',
'.. py:attribute:: EnumCls.val1',
' :module: target.enum',
' :annotation: = 12',
' :value: 12',
'',
' doc for val1',
' '
@ -1405,38 +1405,40 @@ def test_autodoc_typed_instance_variables(app):
' ',
' .. py:attribute:: Class.attr1',
' :module: target.typed_vars',
' :annotation: = 0',
' :type: int',
' :value: 0',
' ',
' ',
' .. py:attribute:: Class.attr2',
' :module: target.typed_vars',
' :annotation: = None',
' :value: None',
' ',
' ',
' .. py:attribute:: Class.attr3',
' :module: target.typed_vars',
' :annotation: = None',
' :value: None',
' ',
' attr3',
' ',
' ',
' .. py:attribute:: Class.attr4',
' :module: target.typed_vars',
' :annotation: = None',
' :value: None',
' ',
' attr4',
' ',
'',
'.. py:data:: attr1',
' :module: target.typed_vars',
" :annotation: = ''",
' :type: str',
" :value: ''",
'',
' attr1',
' ',
'',
'.. py:data:: attr2',
' :module: target.typed_vars',
" :annotation: = None",
" :value: None",
'',
' attr2',
' '
@ -1455,7 +1457,7 @@ def test_autodoc_for_egged_code(app):
'',
'.. py:data:: CONSTANT',
' :module: sample',
' :annotation: = 1',
' :value: 1',
'',
' constant on sample.py',
' ',