summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2020-02-01 13:38:26 +0900
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2020-02-03 01:16:21 +0900
commit5e4e44c19598aaeeda15422422cf5eec8136a9ea (patch)
tree4bf8dc8b05b92af019f028dc69f25238da8d4028
parent66e4897a050f3cc4e10fa993db579d0b70d5a989 (diff)
downloadsphinx-git-5e4e44c19598aaeeda15422422cf5eec8136a9ea.tar.gz
autodoc: Support type annotations for variables
-rw-r--r--CHANGES1
-rw-r--r--sphinx/ext/autodoc/__init__.py30
-rw-r--r--tests/test_autodoc.py48
3 files changed, 52 insertions, 27 deletions
diff --git a/CHANGES b/CHANGES
index a761731b8..b8b42e51c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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>`_
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index e42169684..682fa1cdd 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -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:
+ 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:
+ 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:
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index b7c645be8..c8ab55479 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -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',
' ',