diff --git a/CHANGES b/CHANGES index aaf2ac743ae..27e8599d6c1 100644 --- a/CHANGES +++ b/CHANGES @@ -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 @@ -67,6 +69,7 @@ Features added * #6830: py domain: Add new event: :event:`object-description-transform` * #6895: py domain: Do not emit nitpicky warnings for built-in types * py domain: Support lambda functions in function signature +* #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 diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index cd0a59b917e..72bde77a3b3 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -75,35 +75,42 @@ def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist: for param in sig.parameters.values(): if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY: # PEP-570: Separator for Positional Only Parameter: / - params += addnodes.desc_parameter('', nodes.Text('/')) + params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/')) if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD, param.POSITIONAL_ONLY, None): # PEP-3102: Separator for Keyword Only Parameter: * - params += addnodes.desc_parameter('', nodes.Text('*')) + params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '*')) node = addnodes.desc_parameter() if param.kind == param.VAR_POSITIONAL: - node += nodes.Text('*' + param.name) + node += addnodes.desc_sig_operator('', '*') + node += addnodes.desc_sig_name('', param.name) elif param.kind == param.VAR_KEYWORD: - node += nodes.Text('**' + param.name) + node += addnodes.desc_sig_operator('', '**') + node += addnodes.desc_sig_name('', param.name) else: - node += nodes.Text(param.name) + node += addnodes.desc_sig_name('', param.name) if param.annotation is not param.empty: - node += nodes.Text(': ' + param.annotation) + node += addnodes.desc_sig_punctuation('', ':') + node += nodes.Text(' ') + node += addnodes.desc_sig_name('', param.annotation) if param.default is not param.empty: if param.annotation is not param.empty: - node += nodes.Text(' = ' + str(param.default)) + node += nodes.Text(' ') + node += addnodes.desc_sig_operator('', '=') + node += nodes.Text(' ') else: - node += nodes.Text('=' + str(param.default)) + node += addnodes.desc_sig_operator('', '=') + node += nodes.inline('', param.default, classes=['default_value']) params += node last_kind = param.kind if last_kind == Parameter.POSITIONAL_ONLY: # PEP-570: Separator for Positional Only Parameter: / - params += addnodes.desc_parameter('', nodes.Text('/')) + params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/')) return params diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 39cb3bf714f..a89cc8a2578 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -177,7 +177,8 @@ def test_html4_output(app, status, warning): ], 'autodoc.html': [ (".//dl[@class='py class']/dt[@id='autodoc-target-class']", ''), - (".//dl[@class='py function']/dt[@id='autodoc-target-function']/em", r'\*\*kwds'), + (".//dl[@class='py function']/dt[@id='autodoc-target-function']/em", r'\*\*'), + (".//dl[@class='py function']/dt[@id='autodoc-target-function']/em", r'kwds'), (".//dd/p", r'Return spam\.'), ], 'extapi.html': [ diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 27819af6b4f..b3a510a8bd7 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -17,7 +17,8 @@ from sphinx import addnodes from sphinx.addnodes import ( desc, desc_addname, desc_annotation, desc_content, desc_name, desc_optional, - desc_parameter, desc_parameterlist, desc_returns, desc_signature + desc_parameter, desc_parameterlist, desc_returns, desc_signature, + desc_sig_name, desc_sig_operator, desc_sig_punctuation, ) from sphinx.domains import IndexEntry from sphinx.domains.python import ( @@ -246,8 +247,10 @@ def test_pyfunction_signature(app): 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")]) + [desc_parameterlist, desc_parameter, ([desc_sig_name, "name"], + [desc_sig_punctuation, ":"], + " ", + [nodes.inline, "str"])]) def test_pyfunction_signature_full(app): @@ -262,17 +265,31 @@ def test_pyfunction_signature_full(app): assert_node(doctree[1], addnodes.desc, desctype="function", domain="py", objtype="function", noindex=False) assert_node(doctree[1][0][1], - [desc_parameterlist, ([desc_parameter, ("a", - ": str")], - [desc_parameter, ("b", - "=1")], - [desc_parameter, ("*args", - ": str")], - [desc_parameter, ("c", - ": bool", - " = True")], - [desc_parameter, ("**kwargs", - ": str")])]) + [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "a"], + [desc_sig_punctuation, ":"], + " ", + [desc_sig_name, "str"])], + [desc_parameter, ([desc_sig_name, "b"], + [desc_sig_operator, "="], + [nodes.inline, "1"])], + [desc_parameter, ([desc_sig_operator, "*"], + [desc_sig_name, "args"], + [desc_sig_punctuation, ":"], + " ", + [desc_sig_name, "str"])], + [desc_parameter, ([desc_sig_name, "c"], + [desc_sig_punctuation, ":"], + " ", + [desc_sig_name, "bool"], + " ", + [desc_sig_operator, "="], + " ", + [nodes.inline, "True"])], + [desc_parameter, ([desc_sig_operator, "**"], + [desc_sig_name, "kwargs"], + [desc_sig_punctuation, ":"], + " ", + [desc_sig_name, "str"])])]) @pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.') @@ -281,37 +298,40 @@ def test_pyfunction_signature_full_py38(app): text = ".. py:function:: hello(*, a)" doctree = restructuredtext.parse(app, text) assert_node(doctree[1][0][1], - [desc_parameterlist, ([desc_parameter, "*"], - [desc_parameter, ("a", - "=None")])]) + [desc_parameterlist, ([desc_parameter, nodes.inline, "*"], + [desc_parameter, ([desc_sig_name, "a"], + [desc_sig_operator, "="], + [nodes.inline, "None"])])]) # case: separator in the middle text = ".. py:function:: hello(a, /, b, *, c)" doctree = restructuredtext.parse(app, text) assert_node(doctree[1][0][1], - [desc_parameterlist, ([desc_parameter, "a"], - [desc_parameter, "/"], - [desc_parameter, "b"], - [desc_parameter, "*"], - [desc_parameter, ("c", - "=None")])]) + [desc_parameterlist, ([desc_parameter, desc_sig_name, "a"], + [desc_parameter, desc_sig_operator, "/"], + [desc_parameter, desc_sig_name, "b"], + [desc_parameter, desc_sig_operator, "*"], + [desc_parameter, ([desc_sig_name, "c"], + [desc_sig_operator, "="], + [nodes.inline, "None"])])]) # case: separator in the middle (2) text = ".. py:function:: hello(a, /, *, b)" doctree = restructuredtext.parse(app, text) assert_node(doctree[1][0][1], - [desc_parameterlist, ([desc_parameter, "a"], - [desc_parameter, "/"], - [desc_parameter, "*"], - [desc_parameter, ("b", - "=None")])]) + [desc_parameterlist, ([desc_parameter, desc_sig_name, "a"], + [desc_parameter, desc_sig_operator, "/"], + [desc_parameter, desc_sig_operator, "*"], + [desc_parameter, ([desc_sig_name, "b"], + [desc_sig_operator, "="], + [nodes.inline, "None"])])]) # case: separator at tail text = ".. py:function:: hello(a, /)" doctree = restructuredtext.parse(app, text) assert_node(doctree[1][0][1], - [desc_parameterlist, ([desc_parameter, "a"], - [desc_parameter, "/"])]) + [desc_parameterlist, ([desc_parameter, desc_sig_name, "a"], + [desc_parameter, desc_sig_operator, "/"])]) def test_optional_pyfunction_signature(app):