mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #4813 from tk0miya/4812_type_annotated_variables
Fix #4812: autodoc ignores type annotated variables
This commit is contained in:
commit
9fb2aa68b9
1
CHANGES
1
CHANGES
@ -22,6 +22,7 @@ Bugs fixed
|
|||||||
* #4789: imgconverter: confused by convert.exe of Windows
|
* #4789: imgconverter: confused by convert.exe of Windows
|
||||||
* #4783: On windows, Sphinx crashed when drives of srcdir and outdir are
|
* #4783: On windows, Sphinx crashed when drives of srcdir and outdir are
|
||||||
different
|
different
|
||||||
|
* #4812: autodoc ignores type annotated variables
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
|
@ -23,7 +23,7 @@ from sphinx.util.nodes import explicit_title_re, set_source_info, \
|
|||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
from typing import Any, Dict, List, Tuple # NOQA
|
from typing import Any, Dict, Generator, List, Tuple # NOQA
|
||||||
from sphinx.application import Sphinx # NOQA
|
from sphinx.application import Sphinx # NOQA
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import ast
|
|||||||
import inspect
|
import inspect
|
||||||
import itertools
|
import itertools
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
import tokenize
|
import tokenize
|
||||||
from token import NAME, NEWLINE, INDENT, DEDENT, NUMBER, OP, STRING
|
from token import NAME, NEWLINE, INDENT, DEDENT, NUMBER, OP, STRING
|
||||||
from tokenize import COMMENT, NL
|
from tokenize import COMMENT, NL
|
||||||
@ -27,6 +28,21 @@ indent_re = re.compile(u'^\\s*$')
|
|||||||
emptyline_re = re.compile(u'^\\s*(#.*)?$')
|
emptyline_re = re.compile(u'^\\s*(#.*)?$')
|
||||||
|
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 6):
|
||||||
|
ASSIGN_NODES = (ast.Assign, ast.AnnAssign)
|
||||||
|
else:
|
||||||
|
ASSIGN_NODES = (ast.Assign)
|
||||||
|
|
||||||
|
|
||||||
|
def get_assign_targets(node):
|
||||||
|
# type: (ast.AST) -> List[ast.expr]
|
||||||
|
"""Get list of targets from Assign and AnnAssign node."""
|
||||||
|
if isinstance(node, ast.Assign):
|
||||||
|
return node.targets
|
||||||
|
else:
|
||||||
|
return [node.target] # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def get_lvar_names(node, self=None):
|
def get_lvar_names(node, self=None):
|
||||||
# type: (ast.AST, ast.expr) -> List[unicode]
|
# type: (ast.AST, ast.expr) -> List[unicode]
|
||||||
"""Convert assignment-AST to variable names.
|
"""Convert assignment-AST to variable names.
|
||||||
@ -284,7 +300,8 @@ class VariableCommentPicker(ast.NodeVisitor):
|
|||||||
# type: (ast.Assign) -> None
|
# type: (ast.Assign) -> None
|
||||||
"""Handles Assign node and pick up a variable comment."""
|
"""Handles Assign node and pick up a variable comment."""
|
||||||
try:
|
try:
|
||||||
varnames = sum([get_lvar_names(t, self=self.get_self()) for t in node.targets], [])
|
targets = get_assign_targets(node)
|
||||||
|
varnames = sum([get_lvar_names(t, self=self.get_self()) for t in targets], [])
|
||||||
current_line = self.get_line(node.lineno)
|
current_line = self.get_line(node.lineno)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return # this assignment is not new definition!
|
return # this assignment is not new definition!
|
||||||
@ -320,12 +337,18 @@ class VariableCommentPicker(ast.NodeVisitor):
|
|||||||
for varname in varnames:
|
for varname in varnames:
|
||||||
self.add_entry(varname)
|
self.add_entry(varname)
|
||||||
|
|
||||||
|
def visit_AnnAssign(self, node):
|
||||||
|
# type: (ast.AST) -> None
|
||||||
|
"""Handles AnnAssign node and pick up a variable comment."""
|
||||||
|
self.visit_Assign(node) # type: ignore
|
||||||
|
|
||||||
def visit_Expr(self, node):
|
def visit_Expr(self, node):
|
||||||
# type: (ast.Expr) -> None
|
# type: (ast.Expr) -> None
|
||||||
"""Handles Expr node and pick up a comment if string."""
|
"""Handles Expr node and pick up a comment if string."""
|
||||||
if (isinstance(self.previous, ast.Assign) and isinstance(node.value, ast.Str)):
|
if (isinstance(self.previous, ASSIGN_NODES) and isinstance(node.value, ast.Str)):
|
||||||
try:
|
try:
|
||||||
varnames = get_lvar_names(self.previous.targets[0], self.get_self())
|
targets = get_assign_targets(self.previous)
|
||||||
|
varnames = get_lvar_names(targets[0], self.get_self())
|
||||||
for varname in varnames:
|
for varname in varnames:
|
||||||
if isinstance(node.value.s, text_type):
|
if isinstance(node.value.s, text_type):
|
||||||
docstring = node.value.s
|
docstring = node.value.s
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from six import PY2
|
from six import PY2
|
||||||
|
|
||||||
@ -94,6 +96,18 @@ def test_comment_picker_location():
|
|||||||
('Foo', 'attr3'): 'comment for attr3(3)'}
|
('Foo', 'attr3'): 'comment for attr3(3)'}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.version_info < (3, 6), reason='tests for py36+ syntax')
|
||||||
|
def test_annotated_assignment_py36():
|
||||||
|
source = ('a: str = "Sphinx" #: comment\n'
|
||||||
|
'b: int = 1\n'
|
||||||
|
'"""string on next line"""')
|
||||||
|
parser = Parser(source)
|
||||||
|
parser.parse()
|
||||||
|
assert parser.comments == {('', 'a'): 'comment',
|
||||||
|
('', 'b'): 'string on next line'}
|
||||||
|
assert parser.definitions == {}
|
||||||
|
|
||||||
|
|
||||||
def test_complex_assignment():
|
def test_complex_assignment():
|
||||||
source = ('a = 1 + 1; b = a #: compound statement\n'
|
source = ('a = 1 + 1; b = a #: compound statement\n'
|
||||||
'c, d = (1, 1) #: unpack assignment\n'
|
'c, d = (1, 1) #: unpack assignment\n'
|
||||||
|
Loading…
Reference in New Issue
Block a user