Allow configuration of trailing commas in multi-line signatures (#12975)

Stop outputting trailing commas for C and C++, as it is invalid syntax.

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Co-authored-by: Jakob Lykke Andersen <jakobandersen@users.noreply.github.com>
This commit is contained in:
Thomas Louf 2025-01-21 21:08:18 +01:00 committed by GitHub
parent fe06909e32
commit 1418339eb9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 451 additions and 28 deletions

View File

@ -55,6 +55,11 @@ Features added
* #7630, #4824: autodoc: Use :file:`.pyi` type stub files
to auto-document native modules.
Patch by Adam Turner, partially based on work by Allie Fitter.
* #12975: Enable configuration of trailing commas in multi-line signatures
in the Python and Javascript domains, via the new
:confval:`python_trailing_comma_in_multi_line_signatures` and
:confval:`javascript_trailing_comma_in_multi_line_signatures`
configuration options.
Bugs fixed
----------
@ -86,6 +91,7 @@ Bugs fixed
before static methods, which themselves are rendered before regular
methods and attributes.
Patch by Bénédikt Tran.
* #12975: Avoid rendering a trailing comma in C and C++ multi-line signatures.
Testing
-------

View File

@ -4092,6 +4092,14 @@ Options for the Javascript domain
.. versionadded:: 7.1
.. confval:: javascript_trailing_comma_in_multi_line_signatures
:type: :code-py:`bool`
:default: :code-py:`True`
Use a trailing comma in parameter lists spanning multiple lines, if true.
.. versionadded:: 8.2
Options for the Python domain
-----------------------------
@ -4181,6 +4189,14 @@ Options for the Python domain
.. versionadded:: 7.1
.. confval:: python_trailing_comma_in_multi_line_signatures
:type: :code-py:`bool`
:default: :code-py:`True`
Use a trailing comma in parameter lists spanning multiple lines, if true.
.. versionadded:: 8.2
.. confval:: python_use_unqualified_type_names
:type: :code-py:`bool`
:default: :code-py:`False`

View File

@ -259,6 +259,8 @@ class desc_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement):
As default the parameter list is written in line with the rest of the signature.
Set ``multi_line_parameter_list = True`` to describe a multi-line parameter list.
In that case each parameter will then be written on its own, indented line.
A trailing comma will be added on the last line
if ``multi_line_trailing_comma`` is True.
"""
child_text_separator = ', '
@ -273,6 +275,8 @@ class desc_type_parameter_list(nodes.Part, nodes.Inline, nodes.FixedTextElement)
As default the type parameters list is written in line with the rest of the signature.
Set ``multi_line_parameter_list = True`` to describe a multi-line type parameters list.
In that case each type parameter will then be written on its own, indented line.
A trailing comma will be added on the last line
if ``multi_line_trailing_comma`` is True.
"""
child_text_separator = ', '

View File

@ -109,6 +109,10 @@ class JSObject(ObjectDescription[tuple[str, str]]):
and (len(sig) > max_len > 0)
)
trailing_comma = (
self.env.config.javascript_trailing_comma_in_multi_line_signatures
)
display_prefix = self.get_display_prefix()
if display_prefix:
signode += addnodes.desc_annotation('', '', *display_prefix)
@ -129,7 +133,12 @@ class JSObject(ObjectDescription[tuple[str, str]]):
if not arglist:
signode += addnodes.desc_parameterlist()
else:
_pseudo_parse_arglist(signode, arglist, multi_line_parameter_list)
_pseudo_parse_arglist(
signode,
arglist,
multi_line_parameter_list,
trailing_comma,
)
return fullname, prefix
def _object_hierarchy_parts(self, sig_node: desc_signature) -> tuple[str, ...]:
@ -564,6 +573,12 @@ def setup(app: Sphinx) -> ExtensionMetadata:
'env',
types=frozenset({int, type(None)}),
)
app.add_config_value(
'javascript_trailing_comma_in_multi_line_signatures',
True,
'env',
types=frozenset({bool}),
)
return {
'version': 'builtin',
'env_version': 3,

View File

@ -1078,6 +1078,12 @@ def setup(app: Sphinx) -> ExtensionMetadata:
'env',
types=frozenset({int, type(None)}),
)
app.add_config_value(
'python_trailing_comma_in_multi_line_signatures',
True,
'env',
types=frozenset({bool}),
)
app.add_config_value('python_display_short_literal_types', False, 'env')
app.connect('object-description-transform', filter_meta_fields)
app.connect('missing-reference', builtin_resolver, priority=900)

View File

@ -400,11 +400,15 @@ class _TypeParameterListParser(TokenProcessor):
def _parse_type_list(
tp_list: str, env: BuildEnvironment, multi_line_parameter_list: bool = False
tp_list: str,
env: BuildEnvironment,
multi_line_parameter_list: bool = False,
trailing_comma: bool = True,
) -> addnodes.desc_type_parameter_list:
"""Parse a list of type parameters according to PEP 695."""
type_params = addnodes.desc_type_parameter_list(tp_list)
type_params['multi_line_parameter_list'] = multi_line_parameter_list
type_params['multi_line_trailing_comma'] = trailing_comma
# formal parameter names are interpreted as type parameter names and
# type annotations are interpreted as type parameter bound or constraints
parser = _TypeParameterListParser(tp_list)
@ -462,11 +466,15 @@ def _parse_type_list(
def _parse_arglist(
arglist: str, env: BuildEnvironment, multi_line_parameter_list: bool = False
arglist: str,
env: BuildEnvironment,
multi_line_parameter_list: bool = False,
trailing_comma: bool = True,
) -> addnodes.desc_parameterlist:
"""Parse a list of arguments using AST parser"""
params = addnodes.desc_parameterlist(arglist)
params['multi_line_parameter_list'] = multi_line_parameter_list
params['multi_line_trailing_comma'] = trailing_comma
sig = signature_from_str('(%s)' % arglist)
last_kind = None
for param in sig.parameters.values():
@ -522,7 +530,10 @@ def _parse_arglist(
def _pseudo_parse_arglist(
signode: desc_signature, arglist: str, multi_line_parameter_list: bool = False
signode: desc_signature,
arglist: str,
multi_line_parameter_list: bool = False,
trailing_comma: bool = True,
) -> None:
"""'Parse' a list of arguments separated by commas.
@ -532,6 +543,7 @@ def _pseudo_parse_arglist(
"""
paramlist = addnodes.desc_parameterlist()
paramlist['multi_line_parameter_list'] = multi_line_parameter_list
paramlist['multi_line_trailing_comma'] = trailing_comma
stack: list[Element] = [paramlist]
try:
for argument in arglist.split(','):

View File

@ -310,6 +310,7 @@ class PyObject(ObjectDescription[tuple[str, str]]):
and (sig_len - (arglist_span[1] - arglist_span[0])) > max_len > 0
)
trailing_comma = self.env.config.python_trailing_comma_in_multi_line_signatures
sig_prefix = self.get_signature_prefix(sig)
if sig_prefix:
if type(sig_prefix) is str:
@ -332,7 +333,10 @@ class PyObject(ObjectDescription[tuple[str, str]]):
if tp_list:
try:
signode += _parse_type_list(
tp_list, self.env, multi_line_type_parameter_list
tp_list,
self.env,
multi_line_type_parameter_list,
trailing_comma,
)
except Exception as exc:
logger.warning(
@ -341,19 +345,34 @@ class PyObject(ObjectDescription[tuple[str, str]]):
if arglist:
try:
signode += _parse_arglist(arglist, self.env, multi_line_parameter_list)
signode += _parse_arglist(
arglist,
self.env,
multi_line_parameter_list,
trailing_comma,
)
except SyntaxError:
# fallback to parse arglist original parser
# (this may happen if the argument list is incorrectly used
# as a list of bases when documenting a class)
# it supports to represent optional arguments (ex. "func(foo [, bar])")
_pseudo_parse_arglist(signode, arglist, multi_line_parameter_list)
_pseudo_parse_arglist(
signode,
arglist,
multi_line_parameter_list,
trailing_comma,
)
except (NotImplementedError, ValueError) as exc:
# duplicated parameter names raise ValueError and not a SyntaxError
logger.warning(
'could not parse arglist (%r): %s', arglist, exc, location=signode
)
_pseudo_parse_arglist(signode, arglist, multi_line_parameter_list)
_pseudo_parse_arglist(
signode,
arglist,
multi_line_parameter_list,
trailing_comma,
)
else:
if self.needs_arglist():
# for callables, add an empty parameter list

View File

@ -174,6 +174,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): # type: ignore[misc]
self.required_params_left = sum(self.list_is_required_param)
self.param_separator = node.child_text_separator
self.multi_line_parameter_list = node.get('multi_line_parameter_list', False)
self.trailing_comma = node.get('multi_line_trailing_comma', False)
if self.multi_line_parameter_list:
self.body.append('\n\n')
self.body.append(self.starttag(node, 'dl'))
@ -238,7 +239,8 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): # type: ignore[misc]
or is_required
and (is_last_group or next_is_required)
):
self.body.append(self.param_separator)
if not is_last_group or opt_param_left_at_level or self.trailing_comma:
self.body.append(self.param_separator)
self.body.append('</dd>\n')
elif self.required_params_left:
@ -281,19 +283,26 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): # type: ignore[misc]
def depart_desc_optional(self, node: Element) -> None:
self.optional_param_level -= 1
level = self.optional_param_level
if self.multi_line_parameter_list:
# If it's the first time we go down one level, add the separator
# before the bracket.
if self.optional_param_level == self.max_optional_param_level - 1:
max_level = self.max_optional_param_level
len_lirp = len(self.list_is_required_param)
is_last_group = self.param_group_index + 1 == len_lirp
# If it's the first time we go down one level, add the separator before the
# bracket, except if this is the last parameter and the parameter list
# should not feature a trailing comma.
if level == max_level - 1 and (
not is_last_group or level > 0 or self.trailing_comma
):
self.body.append(self.param_separator)
self.body.append('<span class="optional">]</span>')
# End the line if we have just closed the last bracket of this
# optional parameter group.
if self.optional_param_level == 0:
if level == 0:
self.body.append('</dd>\n')
else:
self.body.append('<span class="optional">]</span>')
if self.optional_param_level == 0:
if level == 0:
self.param_group_index += 1
def visit_desc_annotation(self, node: Element) -> None:

View File

@ -954,6 +954,7 @@ class LaTeXTranslator(SphinxTranslator):
self.required_params_left = sum(self.list_is_required_param)
self.param_separator = r'\sphinxparamcomma '
self.multi_line_parameter_list = node.get('multi_line_parameter_list', False)
self.trailing_comma = node.get('multi_line_trailing_comma', False)
def visit_desc_parameterlist(self, node: Element) -> None:
if self.has_tp_list:
@ -1013,7 +1014,7 @@ class LaTeXTranslator(SphinxTranslator):
if (
opt_param_left_at_level
or is_required
and (is_last_group or next_is_required)
and (next_is_required or self.trailing_comma)
):
self.body.append(self.param_separator)
@ -1055,13 +1056,20 @@ class LaTeXTranslator(SphinxTranslator):
def depart_desc_optional(self, node: Element) -> None:
self.optional_param_level -= 1
level = self.optional_param_level
if self.multi_line_parameter_list:
max_level = self.max_optional_param_level
len_lirp = len(self.list_is_required_param)
is_last_group = self.param_group_index + 1 == len_lirp
# If it's the first time we go down one level, add the separator before the
# bracket.
if self.optional_param_level == self.max_optional_param_level - 1:
# bracket, except if this is the last parameter and the parameter list
# should not feature a trailing comma.
if level == max_level - 1 and (
not is_last_group or level > 0 or self.trailing_comma
):
self.body.append(self.param_separator)
self.body.append('}')
if self.optional_param_level == 0:
if level == 0:
self.param_group_index += 1
def visit_desc_annotation(self, node: Element) -> None:

View File

@ -648,6 +648,7 @@ class TextTranslator(SphinxTranslator):
self.required_params_left = sum(self.list_is_required_param)
self.param_separator = ', '
self.multi_line_parameter_list = node.get('multi_line_parameter_list', False)
self.trailing_comma = node.get('multi_line_trailing_comma', False)
if self.multi_line_parameter_list:
self.param_separator = self.param_separator.rstrip()
self.context.append(sig_close_paren)
@ -699,7 +700,8 @@ class TextTranslator(SphinxTranslator):
or is_required
and (is_last_group or next_is_required)
):
self.add_text(self.param_separator)
if not is_last_group or opt_param_left_at_level or self.trailing_comma:
self.add_text(self.param_separator)
self.end_state(wrap=False, end=None)
elif self.required_params_left:
@ -740,20 +742,27 @@ class TextTranslator(SphinxTranslator):
def depart_desc_optional(self, node: Element) -> None:
self.optional_param_level -= 1
level = self.optional_param_level
if self.multi_line_parameter_list:
max_level = self.max_optional_param_level
len_lirp = len(self.list_is_required_param)
is_last_group = self.param_group_index + 1 == len_lirp
# If it's the first time we go down one level, add the separator before the
# bracket.
if self.optional_param_level == self.max_optional_param_level - 1:
# bracket, except if this is the last parameter and the parameter list
# should not feature a trailing comma.
if level == max_level - 1 and (
not is_last_group or level > 0 or self.trailing_comma
):
self.add_text(self.param_separator)
self.add_text(']')
# End the line if we have just closed the last bracket of this group of
# optional parameters.
if self.optional_param_level == 0:
if level == 0:
self.end_state(wrap=False, end=None)
else:
self.add_text(']')
if self.optional_param_level == 0:
if level == 0:
self.param_group_index += 1
def visit_desc_annotation(self, node: Element) -> None:

View File

@ -12,6 +12,7 @@ import pytest
from sphinx._cli.util.errors import strip_escape_sequences
from sphinx.builders.html import validate_html_extra_path, validate_html_static_path
from sphinx.errors import ConfigError
from sphinx.testing.util import etree_parse
from sphinx.util.inventory import InventoryFile, _InventoryItem
from tests.test_builders.xpath_data import FIGURE_CAPTION
@ -657,3 +658,60 @@ def test_html_pep_695_one_type_per_line(app, cached_etree_parse):
r'.//dt[@id="MyList"][1]',
chk('class MyList[\nT,\n](list[T])'),
)
@pytest.mark.sphinx(
'html',
testroot='domain-py-python_maximum_signature_line_length',
confoverrides={
'python_maximum_signature_line_length': 1,
'python_trailing_comma_in_multi_line_signatures': False,
},
)
def test_html_pep_695_trailing_comma_in_multi_line_signatures(app):
app.build()
fname = app.outdir / 'index.html'
etree = etree_parse(fname)
class chk:
def __init__(self, expect: str) -> None:
self.expect = expect
def __call__(self, nodes):
assert len(nodes) == 1, nodes
objnode = ''.join(nodes[0].itertext()).replace('\n\n', '')
objnode = objnode.rstrip(chr(182)) # remove '¶' symbol
objnode = objnode.strip('\n') # remove surrounding new lines
assert objnode == self.expect
# each signature has a dangling ',' at the end of its parameters lists
check_xpath(
etree,
fname,
r'.//dt[@id="generic_foo"][1]',
chk('generic_foo[\nT\n]()'),
)
check_xpath(
etree,
fname,
r'.//dt[@id="generic_bar"][1]',
chk('generic_bar[\nT\n](\nx: list[T]\n)'),
)
check_xpath(
etree,
fname,
r'.//dt[@id="generic_ret"][1]',
chk('generic_ret[\nR\n]() → R'),
)
check_xpath(
etree,
fname,
r'.//dt[@id="MyGenericClass"][1]',
chk('class MyGenericClass[\nX\n]'),
)
check_xpath(
etree,
fname,
r'.//dt[@id="MyList"][1]',
chk('class MyList[\nT\n](list[T])'),
)

View File

@ -2214,7 +2214,6 @@ def test_one_parameter_per_line(app):
app.build(force_all=True)
result = (app.outdir / 'projectnamenotset.tex').read_text(encoding='utf8')
# TODO: should these asserts check presence or absence of a final \sphinxparamcomma?
# signature of 23 characters is too short to trigger one-param-per-line mark-up
assert (
'\\pysiglinewithargsret\n'
@ -2227,6 +2226,7 @@ def test_one_parameter_per_line(app):
'{\\sphinxbfcode{\\sphinxupquote{foo}}}\n'
'{\\sphinxoptional{\\sphinxparam{' in result
)
assert r'\sphinxparam{\DUrole{n}{f}}\sphinxparamcomma' in result
# generic_arg[T]
assert (
@ -2283,6 +2283,22 @@ def test_one_parameter_per_line(app):
)
@pytest.mark.sphinx(
'latex',
testroot='domain-py-python_maximum_signature_line_length',
confoverrides={
'python_maximum_signature_line_length': 23,
'python_trailing_comma_in_multi_line_signatures': False,
},
)
def test_one_parameter_per_line_without_trailing_comma(app):
app.build(force_all=True)
result = (app.outdir / 'projectnamenotset.tex').read_text(encoding='utf8')
assert r'\sphinxparam{\DUrole{n}{f}}\sphinxparamcomma' not in result
assert r'\sphinxparam{\DUrole{n}{f}}}}' in result
@pytest.mark.sphinx('latex', testroot='markup-rubric')
def test_latex_rubric(app):
app.build()

View File

@ -1374,7 +1374,7 @@ def test_domain_c_c_maximum_signature_line_length_in_html(app):
<dd>\
<span class="n"><span class="pre">str</span></span>\
<span class="w"> </span>\
<span class="n"><span class="pre">name</span></span>,\
<span class="n"><span class="pre">name</span></span>\
</dd>
</dl>
@ -1395,6 +1395,6 @@ def test_domain_c_c_maximum_signature_line_length_in_text(app):
content = (app.outdir / 'index.txt').read_text(encoding='utf8')
param_line_fmt = STDINDENT * ' ' + '{}\n'
expected_parameter_list_hello = '(\n{})'.format(param_line_fmt.format('str name,'))
expected_parameter_list_hello = '(\n{})'.format(param_line_fmt.format('str name'))
assert expected_parameter_list_hello in content

View File

@ -2426,7 +2426,7 @@ def test_domain_cpp_cpp_maximum_signature_line_length_in_html(app):
<dd>\
<span class="n"><span class="pre">str</span></span>\
<span class="w"> </span>\
<span class="n sig-param"><span class="pre">name</span></span>,\
<span class="n sig-param"><span class="pre">name</span></span>\
</dd>
</dl>
@ -2445,6 +2445,6 @@ def test_domain_cpp_cpp_maximum_signature_line_length_in_text(app):
content = (app.outdir / 'index.txt').read_text(encoding='utf8')
param_line_fmt = STDINDENT * ' ' + '{}\n'
expected_parameter_list_hello = '(\n{})'.format(param_line_fmt.format('str name,'))
expected_parameter_list_hello = '(\n{})'.format(param_line_fmt.format('str name'))
assert expected_parameter_list_hello in content

View File

@ -755,3 +755,121 @@ def test_domain_js_javascript_maximum_signature_line_length_in_text(app):
expected_f,
)
assert expected_parameter_list_foo in content
@pytest.mark.sphinx(
'html',
testroot='domain-js-javascript_maximum_signature_line_length',
confoverrides={'javascript_trailing_comma_in_multi_line_signatures': False},
)
def test_domain_js_javascript_trailing_comma_in_multi_line_signatures_in_html(app):
app.build()
content = (app.outdir / 'index.html').read_text(encoding='utf8')
expected_parameter_list_hello = """\
<dl>
<dd>\
<em class="sig-param">\
<span class="n"><span class="pre">name</span></span>\
</em>\
</dd>
</dl>
<span class="sig-paren">)</span>\
<a class="headerlink" href="#hello" title="Link to this definition"></a>\
</dt>\
"""
assert expected_parameter_list_hello in content
param_line_fmt = '<dd>{}</dd>\n'
param_name_fmt = (
'<em class="sig-param"><span class="n"><span class="pre">{}</span></span></em>'
)
optional_fmt = '<span class="optional">{}</span>'
expected_a = param_line_fmt.format(
optional_fmt.format('[')
+ param_name_fmt.format('a')
+ ','
+ optional_fmt.format('['),
)
assert expected_a in content
expected_b = param_line_fmt.format(
param_name_fmt.format('b')
+ ','
+ optional_fmt.format(']')
+ optional_fmt.format(']'),
)
assert expected_b in content
expected_c = param_line_fmt.format(param_name_fmt.format('c') + ',')
assert expected_c in content
expected_d = param_line_fmt.format(
param_name_fmt.format('d') + optional_fmt.format('[') + ','
)
assert expected_d in content
expected_e = param_line_fmt.format(param_name_fmt.format('e') + ',')
assert expected_e in content
expected_f = param_line_fmt.format(
param_name_fmt.format('f') + optional_fmt.format(']')
)
assert expected_f in content
expected_parameter_list_foo = """\
<dl>
{}{}{}{}{}{}</dl>
<span class="sig-paren">)</span>\
<a class="headerlink" href="#foo" title="Link to this definition"></a>\
</dt>\
""".format(expected_a, expected_b, expected_c, expected_d, expected_e, expected_f)
assert expected_parameter_list_foo in content
@pytest.mark.sphinx(
'text',
testroot='domain-js-javascript_maximum_signature_line_length',
freshenv=True,
confoverrides={'javascript_trailing_comma_in_multi_line_signatures': False},
)
def test_domain_js_javascript_trailing_comma_in_multi_line_signatures_in_text(app):
app.build()
content = (app.outdir / 'index.txt').read_text(encoding='utf8')
param_line_fmt = STDINDENT * ' ' + '{}\n'
expected_parameter_list_hello = '(\n{})'.format(param_line_fmt.format('name'))
assert expected_parameter_list_hello in content
expected_a = param_line_fmt.format('[a,[')
assert expected_a in content
expected_b = param_line_fmt.format('b,]]')
assert expected_b in content
expected_c = param_line_fmt.format('c,')
assert expected_c in content
expected_d = param_line_fmt.format('d[,')
assert expected_d in content
expected_e = param_line_fmt.format('e,')
assert expected_e in content
expected_f = param_line_fmt.format('f]')
assert expected_f in content
expected_parameter_list_foo = '(\n{}{}{}{}{}{})'.format(
expected_a,
expected_b,
expected_c,
expected_d,
expected_e,
expected_f,
)
assert expected_parameter_list_foo in content

View File

@ -1073,6 +1073,133 @@ def test_domain_py_python_maximum_signature_line_length_in_text(app):
assert expected_parameter_list_foo in content
@pytest.mark.sphinx(
'html',
testroot='domain-py-python_maximum_signature_line_length',
confoverrides={'python_trailing_comma_in_multi_line_signatures': False},
)
def test_domain_py_python_trailing_comma_in_multi_line_signatures_in_html(app):
app.build()
content = (app.outdir / 'index.html').read_text(encoding='utf8')
expected_parameter_list_hello = """\
<dl>
<dd>\
<em class="sig-param">\
<span class="n"><span class="pre">name</span></span>\
<span class="p"><span class="pre">:</span></span>\
<span class="w"> </span>\
<span class="n"><span class="pre">str</span></span>\
</em>\
</dd>
</dl>
<span class="sig-paren">)</span> \
<span class="sig-return">\
<span class="sig-return-icon">&#x2192;</span> \
<span class="sig-return-typehint"><span class="pre">str</span></span>\
</span>\
<a class="headerlink" href="#hello" title="Link to this definition"></a>\
</dt>\
"""
assert expected_parameter_list_hello in content
param_line_fmt = '<dd>{}</dd>\n'
param_name_fmt = (
'<em class="sig-param"><span class="n"><span class="pre">{}</span></span></em>'
)
optional_fmt = '<span class="optional">{}</span>'
expected_a = param_line_fmt.format(
optional_fmt.format('[')
+ param_name_fmt.format('a')
+ ','
+ optional_fmt.format('['),
)
assert expected_a in content
expected_b = param_line_fmt.format(
param_name_fmt.format('b')
+ ','
+ optional_fmt.format(']')
+ optional_fmt.format(']'),
)
assert expected_b in content
expected_c = param_line_fmt.format(param_name_fmt.format('c') + ',')
assert expected_c in content
expected_d = param_line_fmt.format(
param_name_fmt.format('d') + optional_fmt.format('[') + ','
)
assert expected_d in content
expected_e = param_line_fmt.format(param_name_fmt.format('e') + ',')
assert expected_e in content
expected_f = param_line_fmt.format(
param_name_fmt.format('f') + optional_fmt.format(']')
)
assert expected_f in content
expected_parameter_list_foo = """\
<dl>
{}{}{}{}{}{}</dl>
<span class="sig-paren">)</span>\
<a class="headerlink" href="#foo" title="Link to this definition"></a>\
</dt>\
""".format(expected_a, expected_b, expected_c, expected_d, expected_e, expected_f)
assert expected_parameter_list_foo in content
@pytest.mark.sphinx(
'text',
testroot='domain-py-python_maximum_signature_line_length',
freshenv=True,
confoverrides={'python_trailing_comma_in_multi_line_signatures': False},
)
def test_domain_py_python_trailing_comma_in_multi_line_signatures_in_text(app):
app.build()
content = (app.outdir / 'index.txt').read_text(encoding='utf8')
param_line_fmt = STDINDENT * ' ' + '{}\n'
expected_parameter_list_hello = '(\n{}) -> str'.format(
param_line_fmt.format('name: str')
)
assert expected_parameter_list_hello in content
expected_a = param_line_fmt.format('[a,[')
assert expected_a in content
expected_b = param_line_fmt.format('b,]]')
assert expected_b in content
expected_c = param_line_fmt.format('c,')
assert expected_c in content
expected_d = param_line_fmt.format('d[,')
assert expected_d in content
expected_e = param_line_fmt.format('e,')
assert expected_e in content
expected_f = param_line_fmt.format('f]')
assert expected_f in content
expected_parameter_list_foo = '(\n{}{}{}{}{}{})'.format(
expected_a,
expected_b,
expected_c,
expected_d,
expected_e,
expected_f,
)
assert expected_parameter_list_foo in content
@pytest.mark.sphinx('html', testroot='root')
def test_module_content_line_number(app):
text = '.. py:module:: foo\n\n Some link here: :ref:`abc`\n'