Skip to content

Commit

Permalink
py domain: Allow to make a style for arguments of functions and metho…
Browse files Browse the repository at this point in the history
…ds (refs: sphinx-doc#6417)
  • Loading branch information
tk0miya committed Feb 14, 2020
1 parent 23b697e commit 9906a18
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Expand Up @@ -19,6 +19,8 @@ Incompatible changes
when ``:inherited-members:`` and ``:special-members:`` are given.
* #6830: py domain: ``meta`` fields in info-field-list becomes reserved. They
are not displayed on output document now
* #6417: py domain: doctree of desc_parameterlist has been changed. The
argument names, annotations and default values are wrapped with inline node
* The structure of ``sphinx.events.EventManager.listeners`` has changed
* Due to the scoping changes for :rst:dir:`productionlist` some uses of
:rst:role:`token` must be modified to include the scope which was previously
Expand All @@ -43,6 +45,7 @@ Features added
* #6558: glossary: emit a warning for duplicated glossary entry
* #6558: std domain: emit a warning for duplicated generic objects
* #6830: py domain: Add new event: :event:`object-description-transform`
* #6417: py domain: Allow to make a style for arguments of functions and methods
* Support priority of event handlers. For more detail, see
:py:meth:`.Sphinx.connect()`
* #3077: Implement the scoping for :rst:dir:`productionlist` as indicated
Expand Down
39 changes: 38 additions & 1 deletion sphinx/domains/python.py
Expand Up @@ -30,6 +30,7 @@
from sphinx.util import logging
from sphinx.util.docfields import Field, GroupedField, TypedField
from sphinx.util.docutils import SphinxDirective
from sphinx.util.inspect import signature_from_str
from sphinx.util.nodes import make_refnode
from sphinx.util.typing import TextlikeNode

Expand Down Expand Up @@ -62,6 +63,34 @@
}


def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist:
"""Parse a list of arguments using python parser"""
params = addnodes.desc_parameterlist(arglist)
sig = signature_from_str('(%s)' % arglist)
for param in sig.parameters.values():
node = addnodes.desc_parameter()
if param.kind == param.VAR_POSITIONAL:
node += nodes.inline('', '*' + param.name, classes=['argument'])
elif param.kind == param.VAR_KEYWORD:
node += nodes.inline('', '**' + param.name, classes=['argument'])
else:
node += nodes.inline('', param.name, classes=['argument'])

if param.annotation is not param.empty:
node += nodes.Text(': ')
node += nodes.inline('', param.annotation, classes=['annotation'])
if param.default is not param.empty:
if param.annotation is not param.empty:
node += nodes.Text(' = ')
else:
node += nodes.Text('=')
node += nodes.inline('', param.default, classes=['default_value'])

params += node

return params


def _pseudo_parse_arglist(signode: desc_signature, arglist: str) -> None:
""""Parse" a list of arguments separated by commas.
Expand Down Expand Up @@ -284,7 +313,15 @@ def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]

signode += addnodes.desc_name(name, name)
if arglist:
_pseudo_parse_arglist(signode, arglist)
try:
signode += _parse_arglist(arglist)
except SyntaxError:
# fallback to parse arglist original parser.
# it supports to represent optional arguments (ex. "func(foo [, bar])")
_pseudo_parse_arglist(signode, arglist)
except NotImplementedError as exc:
logger.warning(exc)
_pseudo_parse_arglist(signode, arglist)
else:
if self.needs_arglist():
# for callables, add an empty parameter list
Expand Down
2 changes: 1 addition & 1 deletion tests/test_build_html.py
Expand Up @@ -177,7 +177,7 @@ def test_html4_output(app, status, warning):
],
'autodoc.html': [
(".//dt[@id='autodoc_target.Class']", ''),
(".//dt[@id='autodoc_target.function']/em", r'\*\*kwds'),
(".//dt[@id='autodoc_target.function']/em/span[@class='argument']", r'\*\*kwds'),
(".//dd/p", r'Return spam\.'),
],
'extapi.html': [
Expand Down
38 changes: 37 additions & 1 deletion tests/test_domain_py.py
Expand Up @@ -241,7 +241,43 @@ def test_pyfunction_signature(app):
desc_content)]))
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="py", objtype="function", noindex=False)
assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, "name: str"])
assert_node(doctree[1][0][1],
[desc_parameterlist, desc_parameter, ([nodes.inline, "name"],
": ",
[nodes.inline, "str"])])


def test_pyfunction_signature_full(app):
text = (".. py:function:: hello(a: str, b = 1, *args: str, "
"c: bool = True, **kwargs: str) -> str")
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index,
[desc, ([desc_signature, ([desc_name, "hello"],
desc_parameterlist,
[desc_returns, "str"])],
desc_content)]))
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="py", objtype="function", noindex=False)
assert_node(doctree[1][0][1],
[desc_parameterlist, ([desc_parameter, ([nodes.inline, "a"],
": ",
[nodes.inline, "str"])],
[desc_parameter, ([nodes.inline, "b"],
"=",
[nodes.inline, "1"])],
[desc_parameter, ([nodes.inline, "*args"],
": ",
[nodes.inline, "str"])],
[desc_parameter, ([nodes.inline, "c"],
": ",
[nodes.inline, "bool"],
" = ",
[nodes.inline, "True"])],
[desc_parameter, ([nodes.inline, "**kwargs"],
": ",
[nodes.inline, "str"])])])




def test_optional_pyfunction_signature(app):
Expand Down

0 comments on commit 9906a18

Please sign in to comment.