Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 25 additions & 14 deletions sphinx/pycode/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,21 +400,32 @@ def visit_AnnAssign(self, node: ast.AnnAssign) -> None:

def visit_Expr(self, node: ast.Expr) -> None:
"""Handles Expr node and pick up a comment if string."""
if (isinstance(self.previous, (ast.Assign, ast.AnnAssign)) and
isinstance(node.value, ast.Str)):
try:
targets = get_assign_targets(self.previous)
varnames = get_lvar_names(targets[0], self.get_self())
for varname in varnames:
if isinstance(node.value.s, str):
docstring = node.value.s
else:
docstring = node.value.s.decode(self.encoding or 'utf-8')
if not isinstance(self.previous, (ast.Assign, ast.AnnAssign)):
return

self.add_variable_comment(varname, dedent_docstring(docstring))
self.add_entry(varname)
except TypeError:
pass # this assignment is not new definition!
raw_value: Optional[Any] = None
if isinstance(node.value, ast.Str):
raw_value = node.value.s
elif isinstance(node.value, ast.Constant):
if isinstance(node.value.value, (str, bytes)):
raw_value = node.value.value

if raw_value is None:
return

try:
targets = get_assign_targets(self.previous)
varnames = get_lvar_names(targets[0], self.get_self())
for varname in varnames:
if isinstance(raw_value, str):
docstring = raw_value
else:
docstring = raw_value.decode(self.encoding or 'utf-8')

self.add_variable_comment(varname, dedent_docstring(docstring))
self.add_entry(varname)
except TypeError:
pass # this assignment is not new definition!

def visit_Try(self, node: ast.Try) -> None:
"""Handles Try node and processes body and else-clause.
Expand Down
6 changes: 6 additions & 0 deletions tests/roots/test-ext-autodoc-typealias/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import os
import sys

sys.path.insert(0, os.path.abspath('.'))

extensions = ['sphinx.ext.autodoc']
4 changes: 4 additions & 0 deletions tests/roots/test-ext-autodoc-typealias/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Type Alias Docstring Fixtures
=============================

.. automodule:: type_alias_docstrings
36 changes: 36 additions & 0 deletions tests/roots/test-ext-autodoc-typealias/type_alias_docstrings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Fixtures for type alias docstring handling tests."""

from pathlib import Path
from typing import Any, Callable, Dict, Optional


ScaffoldOpts = Dict[str, Any]
"""Dictionary with PyScaffold's options, see ``pyscaffold.api.create_project``.
Should be treated as immutable (copy before mutating).

Please notice some behaviours given by the options **SHOULD** be observed. For
example, files should be overwritten when the **force** option is ``True``.
Similarly when **pretend** is ``True``, no operation should be really
performed, but any action should be logged as if realized.
"""


FileContents = Optional[str]
"""When the file content is ``None``, the file should not be written to disk.
Empty files are represented by an empty string ``""`` as content.
"""


FileOp = Callable[[Path, FileContents, ScaffoldOpts], Optional[Path]]
"""Signature of functions considered file operations::

Callable[[Path, FileContents, ScaffoldOpts], Optional[Path]]

- **path** (:class:`pathlib.Path`): file path potentially written to disk.
- **contents** (:obj:`FileContents`): usual text content. :obj:`None` skips
writing the file.
- **opts** (:obj:`ScaffoldOpts`): a dict with PyScaffold's options.

If a file is written (or permissions are changed) the implementation should
return the :class:`pathlib.Path`. Otherwise :obj:`None` should be returned.
"""
32 changes: 32 additions & 0 deletions tests/test_ext_autodoc_autodata.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,35 @@ def test_autodata_hide_value(app):
' :meta hide-value:',
'',
]


@pytest.mark.sphinx('html', testroot='ext-autodoc-typealias')
def test_autodata_type_alias_docstring(monkeypatch, app):
from sphinx.pycode import parser

class _FakeStr(parser.ast.AST):
_fields = ()

monkeypatch.setattr(parser.ast, 'Str', _FakeStr, raising=False)

actual = do_autodoc(app, 'data', 'type_alias_docstrings.ScaffoldOpts')
assert list(actual) == [
'',
'.. py:data:: ScaffoldOpts',
' :module: type_alias_docstrings',
'',
' Dictionary with PyScaffold\'s options, see '
'``pyscaffold.api.create_project``.',
' Should be treated as immutable (copy before mutating).',
'',
' Please notice some behaviours given by the options **SHOULD** be '
'observed. For',
' example, files should be overwritten when the **force** option is '
'``True``.',
' Similarly when **pretend** is ``True``, no operation should be really',
' performed, but any action should be logged as if realized.',
'',
' alias of :class:`~typing.Dict`\\ [:class:`str`, '
':class:`~typing.Any`]',
'',
]