Allow multi-line object description signatures (#11011)

Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Co-authored-by: Jean-François B <2589111+jfbu@users.noreply.github.com>
Co-authored-by: TLouf <loufthomas@gmail.com>
This commit is contained in:
TLouf 2023-05-11 15:28:57 +02:00 committed by GitHub
parent c73628dfca
commit 86b07d4a97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1635 additions and 43 deletions

View File

@ -26,6 +26,13 @@ Features added
generated HTML, using the CRC32 algorithm.
* :meth:`~sphinx.application.Sphinx.require_sphinx` now allows the version
requirement to be specified as ``(major, minor)``.
* #11011: Allow configuring a line-length limit for object signatures, via
:confval:`maximum_signature_line_length` and the domain-specific variants.
If the length of the signature (in characters) is greater than the configured
limit, each parameter in the signature will be split to its own logical line.
This behaviour may also be controlled by options on object description
directives, for example :rst:dir:`py:function:single-line-parameter-list`.
Patch by Thomas Louf, Adam Turner, and Jean-François Burnol.
Bugs fixed
----------

View File

@ -1479,6 +1479,12 @@ Macros
.. versionadded:: 6.2.0
``\sphinxparam``, ``\sphinxsamedocref``
.. versionadded:: 7.1.0
``\sphinxparamcomma`` which defaults to a comma followed by a space and
``\sphinxparamcommaoneperline`` which is used for one-parameter-per-line
signatures (see :confval:`maximum_signature_line_length`). It defaults
to ``\texttt{,}`` to make these end-of-line separators more distinctive.
- More text styling:
.. csv-table::

View File

@ -675,6 +675,25 @@ General configuration
If the value is a fully-qualified name of a custom Pygments style class,
this is then used as custom style.
.. confval:: maximum_signature_line_length
If a signature's length in characters exceeds the number set, each
parameter within the signature will be displayed on an individual logical
line.
When ``None`` (the default), there is no maximum length and the entire
signature will be displayed on a single logical line.
A 'logical line' is similar to a hard line break---builders or themes may
choose to 'soft wrap' a single logical line, and this setting does not affect
that behaviour.
Domains may provide options to suppress any hard wrapping on an individual
object directive, such as seen in the C, C++, and Python domains (e.g.
:rst:dir:`py:function:single-line-parameter-list`).
.. versionadded:: 7.1
.. confval:: add_function_parentheses
A boolean that decides whether parentheses are appended to function and
@ -2912,6 +2931,14 @@ Options for the C domain
.. versionadded:: 4.0.3
.. confval:: c_maximum_signature_line_length
If a signature's length in characters exceeds the number set, each
parameter will be displayed on an individual logical line. This is a
domain-specific setting, overriding :confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. _cpp-config:
Options for the C++ domain
@ -2942,6 +2969,14 @@ Options for the C++ domain
.. versionadded:: 1.5
.. confval:: cpp_maximum_signature_line_length
If a signature's length in characters exceeds the number set, each
parameter will be displayed on an individual logical line. This is a
domain-specific setting, overriding :confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
Options for the Python domain
-----------------------------
@ -2984,6 +3019,25 @@ Options for the Python domain
.. note:: This configuration is still in experimental
.. confval:: python_maximum_signature_line_length
If a signature's length in characters exceeds the number set, each
argument will be displayed on an individual logical line. This is a
domain-specific setting, overriding :confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
Options for the Javascript domain
---------------------------------
.. confval:: javascript_maximum_signature_line_length
If a signature's length in characters exceeds the number set, each
parameter will be displayed on an individual logical line. This is a
domain-specific setting, overriding :confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
Example of configuration file
-----------------------------

View File

@ -231,6 +231,16 @@ The following directives are provided for module and class contents:
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the function's arguments will be emitted on a single logical
line, overriding :confval:`python_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. py:data:: name
Describes global data in a module, including both variables and values used
@ -329,6 +339,15 @@ The following directives are provided for module and class contents:
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the class constructor's arguments will be emitted on a single
logical line, overriding :confval:`python_maximum_signature_line_length`
and :confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. py:attribute:: name
Describes an object data attribute. The description should include
@ -441,6 +460,15 @@ The following directives are provided for module and class contents:
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the method's arguments will be emitted on a single logical
line, overriding :confval:`python_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:option:: staticmethod
:type: no value
@ -494,6 +522,15 @@ The following directives are provided for module and class contents:
There is no ``py:deco`` role to link to a decorator that is marked up with
this directive; rather, use the :rst:role:`py:func` role.
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the decorator's arguments will be emitted on a single logical
line, overriding :confval:`python_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. py:decoratormethod:: name
.. py:decoratormethod:: name(signature)
@ -763,6 +800,15 @@ The C domain (name **c**) is suited for documentation of C API.
:retval NULL: under some conditions.
:retval NULL: under some other conditions as well.
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the function's parameters will be emitted on a single logical
line, overriding :confval:`c_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. c:macro:: name
.. c:macro:: name(arg list)
@ -776,6 +822,15 @@ The C domain (name **c**) is suited for documentation of C API.
.. versionadded:: 3.0
The function style variant.
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the macro's parameters will be emitted on a single logical
line, overriding :confval:`c_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. c:struct:: name
Describes a C struct.
@ -1126,6 +1181,15 @@ visibility statement (``public``, ``private`` or ``protected``).
.. cpp:function:: template<> \
void print(int i)
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the function's parameters will be emitted on a single logical
line, overriding :confval:`cpp_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. cpp:member:: (member) variable declaration
.. cpp:var:: (member) variable declaration
@ -1908,6 +1972,15 @@ The JavaScript domain (name **js**) provides the following directives:
:throws SomeError: For whatever reason in that case.
:returns: Something.
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the function's parameters will be emitted on a single logical
line, overriding :confval:`javascript_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. js:method:: name(signature)
This directive is an alias for :rst:dir:`js:function`, however it describes
@ -1915,6 +1988,15 @@ The JavaScript domain (name **js**) provides the following directives:
.. versionadded:: 1.6
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the function's parameters will be emitted on a single logical
line, overriding :confval:`javascript_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. js:class:: name
Describes a constructor that creates an object. This is basically like a
@ -1933,6 +2015,15 @@ The JavaScript domain (name **js**) provides the following directives:
:param string name: The name of the animal
:param number age: an optional age for the animal
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the function's parameters will be emitted on a single logical
line, overriding :confval:`javascript_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. js:data:: name
Describes a global variable or constant.

View File

@ -246,7 +246,12 @@ class desc_returns(desc_type):
class desc_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for a general parameter list."""
"""Node for a general parameter list.
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.
"""
child_text_separator = ', '
def astext(self):

View File

@ -137,7 +137,7 @@ class Config:
'numfig': (False, 'env', []),
'numfig_secnum_depth': (1, 'env', []),
'numfig_format': ({}, 'env', []), # will be initialized in init_numfig_format()
'maximum_signature_line_length': (None, 'env', {int, None}),
'math_number_all': (False, 'env', []),
'math_eqref_format': (None, 'env', [str]),
'math_numfig': (True, 'env', []),

View File

@ -727,9 +727,19 @@ class ASTParameters(ASTBase):
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
verify_description_mode(mode)
multi_line_parameter_list = False
test_node: Element = signode
while test_node.parent:
if not isinstance(test_node, addnodes.desc_signature):
test_node = test_node.parent
continue
multi_line_parameter_list = test_node.get('multi_line_parameter_list', False)
break
# only use the desc_parameterlist for the outer list, not for inner lists
if mode == 'lastIsName':
paramlist = addnodes.desc_parameterlist()
paramlist['multi_line_parameter_list'] = multi_line_parameter_list
for arg in self.args:
param = addnodes.desc_parameter('', '', noemph=True)
arg.describe_signature(param, 'param', env, symbol=symbol)
@ -3153,6 +3163,7 @@ class CObject(ObjectDescription[ASTDeclaration]):
option_spec: OptionSpec = {
'noindexentry': directives.flag,
'nocontentsentry': directives.flag,
'single-line-parameter-list': directives.flag,
}
def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None:
@ -3258,6 +3269,14 @@ class CObject(ObjectDescription[ASTDeclaration]):
def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration:
parentSymbol: Symbol = self.env.temp_data['c:parent_symbol']
max_len = (self.env.config.c_maximum_signature_line_length
or self.env.config.maximum_signature_line_length
or 0)
signode['multi_line_parameter_list'] = (
'single-line-parameter-list' not in self.options
and (len(sig) > max_len > 0)
)
parser = DefinitionParser(sig, location=signode, config=self.env.config)
try:
ast = self.parse_definition(parser)
@ -3866,11 +3885,12 @@ def setup(app: Sphinx) -> dict[str, Any]:
app.add_config_value("c_id_attributes", [], 'env')
app.add_config_value("c_paren_attributes", [], 'env')
app.add_config_value("c_extra_keywords", _macroKeywords, 'env')
app.add_config_value("c_maximum_signature_line_length", None, 'env', types={int, None})
app.add_post_transform(AliasTransform)
return {
'version': 'builtin',
'env_version': 2,
'env_version': 3,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -2142,9 +2142,19 @@ class ASTParametersQualifiers(ASTBase):
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
verify_description_mode(mode)
multi_line_parameter_list = False
test_node: Element = signode
while test_node.parent:
if not isinstance(test_node, addnodes.desc_signature):
test_node = test_node.parent
continue
multi_line_parameter_list = test_node.get('multi_line_parameter_list', False)
break
# only use the desc_parameterlist for the outer list, not for inner lists
if mode == 'lastIsName':
paramlist = addnodes.desc_parameterlist()
paramlist['multi_line_parameter_list'] = multi_line_parameter_list
for arg in self.args:
param = addnodes.desc_parameter('', '', noemph=True)
arg.describe_signature(param, 'param', env, symbol=symbol)
@ -7192,6 +7202,7 @@ class CPPObject(ObjectDescription[ASTDeclaration]):
'noindexentry': directives.flag,
'nocontentsentry': directives.flag,
'tparam-line-spec': directives.flag,
'single-line-parameter-list': directives.flag,
}
def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None:
@ -7348,6 +7359,14 @@ class CPPObject(ObjectDescription[ASTDeclaration]):
def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration:
parentSymbol: Symbol = self.env.temp_data['cpp:parent_symbol']
max_len = (self.env.config.cpp_maximum_signature_line_length
or self.env.config.maximum_signature_line_length
or 0)
signode['multi_line_parameter_list'] = (
'single-line-parameter-list' not in self.options
and (len(sig) > max_len > 0)
)
parser = DefinitionParser(sig, location=signode, config=self.env.config)
try:
ast = self.parse_definition(parser)
@ -8140,6 +8159,7 @@ def setup(app: Sphinx) -> dict[str, Any]:
app.add_config_value("cpp_index_common_prefix", [], 'env')
app.add_config_value("cpp_id_attributes", [], 'env')
app.add_config_value("cpp_paren_attributes", [], 'env')
app.add_config_value("cpp_maximum_signature_line_length", None, 'env', types={int, None})
app.add_post_transform(AliasTransform)
# debug stuff
@ -8154,7 +8174,7 @@ def setup(app: Sphinx) -> dict[str, Any]:
return {
'version': 'builtin',
'env_version': 8,
'env_version': 9,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -43,6 +43,7 @@ class JSObject(ObjectDescription[Tuple[str, str]]):
'noindex': directives.flag,
'noindexentry': directives.flag,
'nocontentsentry': directives.flag,
'single-line-parameter-list': directives.flag,
}
def get_display_prefix(self) -> list[Node]:
@ -88,6 +89,14 @@ class JSObject(ObjectDescription[Tuple[str, str]]):
signode['object'] = prefix
signode['fullname'] = fullname
max_len = (self.env.config.javascript_maximum_signature_line_length
or self.env.config.maximum_signature_line_length
or 0)
multi_line_parameter_list = (
'single-line-parameter-list' not in self.options
and (len(sig) > max_len > 0)
)
display_prefix = self.get_display_prefix()
if display_prefix:
signode += addnodes.desc_annotation('', '', *display_prefix)
@ -108,7 +117,7 @@ class JSObject(ObjectDescription[Tuple[str, str]]):
if not arglist:
signode += addnodes.desc_parameterlist()
else:
_pseudo_parse_arglist(signode, arglist)
_pseudo_parse_arglist(signode, arglist, multi_line_parameter_list)
return fullname, prefix
def _object_hierarchy_parts(self, sig_node: desc_signature) -> tuple[str, ...]:
@ -473,10 +482,12 @@ class JavaScriptDomain(Domain):
def setup(app: Sphinx) -> dict[str, Any]:
app.add_domain(JavaScriptDomain)
app.add_config_value(
'javascript_maximum_signature_line_length', None, 'env', types={int, None},
)
return {
'version': 'builtin',
'env_version': 2,
'env_version': 3,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -258,10 +258,11 @@ def _parse_annotation(annotation: str, env: BuildEnvironment | None) -> list[Nod
def _parse_arglist(
arglist: str, env: BuildEnvironment | None = None,
arglist: str, env: BuildEnvironment | None = None, multi_line_parameter_list: bool = False,
) -> 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
sig = signature_from_str('(%s)' % arglist)
last_kind = None
for param in sig.parameters.values():
@ -309,7 +310,9 @@ def _parse_arglist(
return params
def _pseudo_parse_arglist(signode: desc_signature, arglist: str) -> None:
def _pseudo_parse_arglist(
signode: desc_signature, arglist: str, multi_line_parameter_list: bool = False,
) -> None:
""""Parse" a list of arguments separated by commas.
Arguments can have "optional" annotations given by enclosing them in
@ -317,6 +320,7 @@ def _pseudo_parse_arglist(signode: desc_signature, arglist: str) -> None:
string literal (e.g. default argument value).
"""
paramlist = addnodes.desc_parameterlist()
paramlist['multi_line_parameter_list'] = multi_line_parameter_list
stack: list[Element] = [paramlist]
try:
for argument in arglist.split(','):
@ -459,6 +463,7 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
'noindex': directives.flag,
'noindexentry': directives.flag,
'nocontentsentry': directives.flag,
'single-line-parameter-list': directives.flag,
'module': directives.unchanged,
'canonical': directives.unchanged,
'annotation': directives.unchanged,
@ -541,6 +546,14 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
signode['class'] = classname
signode['fullname'] = fullname
max_len = (self.env.config.python_maximum_signature_line_length
or self.env.config.maximum_signature_line_length
or 0)
multi_line_parameter_list = (
'single-line-parameter-list' not in self.options
and (len(sig) > max_len > 0)
)
sig_prefix = self.get_signature_prefix(sig)
if sig_prefix:
if type(sig_prefix) is str:
@ -559,15 +572,15 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
signode += addnodes.desc_name(name, name)
if arglist:
try:
signode += _parse_arglist(arglist, self.env)
signode += _parse_arglist(arglist, self.env, multi_line_parameter_list)
except SyntaxError:
# fallback to parse arglist original parser.
# it supports to represent optional arguments (ex. "func(foo [, bar])")
_pseudo_parse_arglist(signode, arglist)
_pseudo_parse_arglist(signode, arglist, multi_line_parameter_list)
except NotImplementedError as exc:
logger.warning("could not parse arglist (%r): %s", arglist, exc,
location=signode)
_pseudo_parse_arglist(signode, arglist)
_pseudo_parse_arglist(signode, arglist, multi_line_parameter_list)
else:
if self.needs_arglist():
# for callables, add an empty parameter list
@ -1505,13 +1518,15 @@ def setup(app: Sphinx) -> dict[str, Any]:
app.add_domain(PythonDomain)
app.add_config_value('python_use_unqualified_type_names', False, 'env')
app.add_config_value('python_maximum_signature_line_length', None, 'env',
types={int, None})
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)
return {
'version': 'builtin',
'env_version': 3,
'env_version': 4,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -146,6 +146,27 @@
\item[{#1\sphinxcode{(}\py@sigparams{#2}{#3}\strut}]
\pysigadjustitemsep
}
\def\sphinxoptionalextraspace{0.5mm}
\newcommand{\pysigwithonelineperarg}[3]{%
% render each argument on its own line
\item[#1\sphinxcode{(}\strut]
\leavevmode\par\nopagebreak
% this relies on \pysigstartsignatures having set \parskip to zero
\begingroup
\let\sphinxparamcomma\sphinxparamcommaoneperline
\def\sphinxoptionalhook{\ifvmode\else\kern\sphinxoptionalextraspace\relax\fi}%
% The very first \sphinxparam should not emit a \par hence a complication
% with a group and global definition here as it may occur in a \sphinxoptional
\global\let\spx@sphinxparam\sphinxparam
\gdef\sphinxparam{\gdef\sphinxparam{\par\spx@sphinxparam}\spx@sphinxparam}%
#2\par
\endgroup
\global\let\sphinxparam\spx@sphinxparam
% fulllineitems sets \labelwidth to be like \leftmargin
\nopagebreak\noindent\kern-\labelwidth\sphinxcode{)}{#3}
\pysigadjustitemsep
}
\newcommand{\pysigadjustitemsep}{%
% adjust \itemsep to control the separation with the next signature
% sharing common description

View File

@ -58,7 +58,8 @@
\protected\def\sphinxparam#1{\emph{#1}}
% \optional is used for ``[, arg]``, i.e. desc_optional nodes.
\long\protected\def\sphinxoptional#1{%
{\textnormal{\Large[}}{#1}\hspace{0.5mm}{\textnormal{\Large]}}}
{\sphinxoptionalhook\textnormal{\Large[}}{#1}\hspace{0.5mm}{\textnormal{\Large]}}}
\let\sphinxoptionalhook\empty
% additional customizable styling
\def\sphinxstyleindexentry #1{\texttt{#1}}
@ -112,6 +113,11 @@
% Special characters
%
\def\sphinxparamcomma{, }% by default separate parameters with comma + space
% If the signature is rendered with one line per param, this wil be used
% instead (this \texttt makes the comma slightly more distinctive).
\def\sphinxparamcommaoneperline{\texttt{,}}
%
% The \kern\z@ is to prevent en-dash and em-dash TeX ligatures.
% A linebreak can occur after the dash in regular text (this is
% normal behaviour of "-" in TeX, it is not related to \kern\z@).

View File

@ -670,6 +670,16 @@ dd {
margin-left: 30px;
}
.sig dd {
margin-top: 0px;
margin-bottom: 0px;
}
.sig dl {
margin-top: 0px;
margin-bottom: 0px;
}
dl > dd:last-child,
dl > dd:last-child > :last-child {
margin-bottom: 0;

View File

@ -458,6 +458,11 @@ dl {
margin-bottom: 15px;
}
.sig dl {
margin-top: 0px;
margin-bottom: 0px;
}
dd p {
margin-top: 0px;
}
@ -472,6 +477,11 @@ dd {
margin-left: 30px;
}
.sig dd {
margin-top: 0px;
margin-bottom: 0px;
}
dt:target, .highlighted {
background-color: #ddd;
}

View File

@ -150,14 +150,26 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
def visit_desc_parameterlist(self, node: Element) -> None:
self.body.append('<span class="sig-paren">(</span>')
self.first_param = 1
self.is_first_param = True
self.optional_param_level = 0
self.params_left_at_level = 0
self.param_group_index = 0
# Counts as what we call a parameter group either a required parameter, or a
# set of contiguous optional ones.
self.list_is_required_param = [isinstance(c, addnodes.desc_parameter)
for c in node.children]
# How many required parameters are left.
self.required_params_left = sum([isinstance(c, addnodes.desc_parameter)
for c in node.children])
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)
if self.multi_line_parameter_list:
self.body.append('\n\n')
self.body.append(self.starttag(node, 'dl'))
self.param_separator = self.param_separator.rstrip()
def depart_desc_parameterlist(self, node: Element) -> None:
if node.get('multi_line_parameter_list'):
self.body.append('</dl>\n\n')
self.body.append('<span class="sig-paren">)</span>')
# If required parameters are still to come, then put the comma after
@ -167,28 +179,82 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
# foo([a, ]b, c[, d])
#
def visit_desc_parameter(self, node: Element) -> None:
if self.first_param:
self.first_param = 0
elif not self.required_params_left:
on_separate_line = self.multi_line_parameter_list
if on_separate_line and not (self.is_first_param and self.optional_param_level > 0):
self.body.append(self.starttag(node, 'dd', ''))
if self.is_first_param:
self.is_first_param = False
elif not on_separate_line and not self.required_params_left:
self.body.append(self.param_separator)
if self.optional_param_level == 0:
self.required_params_left -= 1
else:
self.params_left_at_level -= 1
if not node.hasattr('noemph'):
self.body.append('<em class="sig-param">')
def depart_desc_parameter(self, node: Element) -> None:
if not node.hasattr('noemph'):
self.body.append('</em>')
if self.required_params_left:
is_required = self.list_is_required_param[self.param_group_index]
if self.multi_line_parameter_list:
is_last_group = self.param_group_index + 1 == len(self.list_is_required_param)
next_is_required = (
not is_last_group
and self.list_is_required_param[self.param_group_index + 1]
)
opt_param_left_at_level = self.params_left_at_level > 0
if opt_param_left_at_level or is_required and (is_last_group or next_is_required):
self.body.append(self.param_separator)
self.body.append('</dd>\n')
elif self.required_params_left:
self.body.append(self.param_separator)
if is_required:
self.param_group_index += 1
def visit_desc_optional(self, node: Element) -> None:
self.params_left_at_level = sum([isinstance(c, addnodes.desc_parameter)
for c in node.children])
self.optional_param_level += 1
self.body.append('<span class="optional">[</span>')
self.max_optional_param_level = self.optional_param_level
if self.multi_line_parameter_list:
# If the first parameter is optional, start a new line and open the bracket.
if self.is_first_param:
self.body.append(self.starttag(node, 'dd', ''))
self.body.append('<span class="optional">[</span>')
# Else, if there remains at least one required parameter, append the
# parameter separator, open a new bracket, and end the line.
elif self.required_params_left:
self.body.append(self.param_separator)
self.body.append('<span class="optional">[</span>')
self.body.append('</dd>\n')
# Else, open a new bracket, append the parameter separator,
# and end the line.
else:
self.body.append('<span class="optional">[</span>')
self.body.append(self.param_separator)
self.body.append('</dd>\n')
else:
self.body.append('<span class="optional">[</span>')
def depart_desc_optional(self, node: Element) -> None:
self.optional_param_level -= 1
self.body.append('<span class="optional">]</span>')
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:
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:
self.body.append('</dd>\n')
else:
self.body.append('<span class="optional">]</span>')
if self.optional_param_level == 0:
self.param_group_index += 1
def visit_desc_annotation(self, node: Element) -> None:
self.body.append(self.starttag(node, 'em', '', CLASS='property'))

View File

@ -702,7 +702,10 @@ class LaTeXTranslator(SphinxTranslator):
def _visit_signature_line(self, node: Element) -> None:
for child in node:
if isinstance(child, addnodes.desc_parameterlist):
self.body.append(CR + r'\pysiglinewithargsret{')
if child.get('multi_line_parameter_list'):
self.body.append(CR + r'\pysigwithonelineperarg{')
else:
self.body.append(CR + r'\pysiglinewithargsret{')
break
else:
self.body.append(CR + r'\pysigline{')
@ -784,29 +787,82 @@ class LaTeXTranslator(SphinxTranslator):
def visit_desc_parameterlist(self, node: Element) -> None:
# close name, open parameterlist
self.body.append('}{')
self.first_param = 1
self.is_first_param = True
self.optional_param_level = 0
self.params_left_at_level = 0
self.param_group_index = 0
# Counts as what we call a parameter group either a required parameter, or a
# set of contiguous optional ones.
self.list_is_required_param = [isinstance(c, addnodes.desc_parameter)
for c in node.children]
# How many required parameters are left.
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)
def depart_desc_parameterlist(self, node: Element) -> None:
# close parameterlist, open return annotation
self.body.append('}{')
def visit_desc_parameter(self, node: Element) -> None:
if not self.first_param:
self.body.append(', ')
if self.is_first_param:
self.is_first_param = False
elif not self.multi_line_parameter_list and not self.required_params_left:
self.body.append(self.param_separator)
if self.optional_param_level == 0:
self.required_params_left -= 1
else:
self.first_param = 0
self.params_left_at_level -= 1
if not node.hasattr('noemph'):
self.body.append(r'\sphinxparam{')
def depart_desc_parameter(self, node: Element) -> None:
if not node.hasattr('noemph'):
self.body.append('}')
is_required = self.list_is_required_param[self.param_group_index]
if self.multi_line_parameter_list:
is_last_group = self.param_group_index + 1 == len(self.list_is_required_param)
next_is_required = (
not is_last_group
and self.list_is_required_param[self.param_group_index + 1]
)
opt_param_left_at_level = self.params_left_at_level > 0
if opt_param_left_at_level or is_required and (is_last_group or next_is_required):
self.body.append(self.param_separator)
elif self.required_params_left:
self.body.append(self.param_separator)
if is_required:
self.param_group_index += 1
def visit_desc_optional(self, node: Element) -> None:
self.body.append(r'\sphinxoptional{')
self.params_left_at_level = sum([isinstance(c, addnodes.desc_parameter)
for c in node.children])
self.optional_param_level += 1
self.max_optional_param_level = self.optional_param_level
if self.multi_line_parameter_list:
if self.is_first_param:
self.body.append(r'\sphinxoptional{')
elif self.required_params_left:
self.body.append(self.param_separator)
self.body.append(r'\sphinxoptional{')
else:
self.body.append(r'\sphinxoptional{')
self.body.append(self.param_separator)
else:
self.body.append(r'\sphinxoptional{')
def depart_desc_optional(self, node: Element) -> None:
self.optional_param_level -= 1
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:
self.body.append(self.param_separator)
self.body.append('}')
if self.optional_param_level == 0:
self.param_group_index += 1
def visit_desc_annotation(self, node: Element) -> None:
self.body.append(r'\sphinxbfcode{\sphinxupquote{')

View File

@ -594,24 +594,99 @@ class TextTranslator(SphinxTranslator):
def visit_desc_parameterlist(self, node: Element) -> None:
self.add_text('(')
self.first_param = 1
self.is_first_param = True
self.optional_param_level = 0
self.params_left_at_level = 0
self.param_group_index = 0
# Counts as what we call a parameter group are either a required parameter, or a
# set of contiguous optional ones.
self.list_is_required_param = [isinstance(c, addnodes.desc_parameter)
for c in node.children]
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)
if self.multi_line_parameter_list:
self.param_separator = self.param_separator.rstrip()
def depart_desc_parameterlist(self, node: Element) -> None:
self.add_text(')')
def visit_desc_parameter(self, node: Element) -> None:
if not self.first_param:
self.add_text(', ')
on_separate_line = self.multi_line_parameter_list
if on_separate_line and not (self.is_first_param and self.optional_param_level > 0):
self.new_state()
if self.is_first_param:
self.is_first_param = False
elif not on_separate_line and not self.required_params_left:
self.add_text(self.param_separator)
if self.optional_param_level == 0:
self.required_params_left -= 1
else:
self.first_param = 0
self.params_left_at_level -= 1
self.add_text(node.astext())
is_required = self.list_is_required_param[self.param_group_index]
if on_separate_line:
is_last_group = self.param_group_index + 1 == len(self.list_is_required_param)
next_is_required = (
not is_last_group
and self.list_is_required_param[self.param_group_index + 1]
)
opt_param_left_at_level = self.params_left_at_level > 0
if opt_param_left_at_level or is_required and (is_last_group or next_is_required):
self.add_text(self.param_separator)
self.end_state(wrap=False, end=None)
elif self.required_params_left:
self.add_text(self.param_separator)
if is_required:
self.param_group_index += 1
raise nodes.SkipNode
def visit_desc_optional(self, node: Element) -> None:
self.add_text('[')
self.params_left_at_level = sum([isinstance(c, addnodes.desc_parameter)
for c in node.children])
self.optional_param_level += 1
self.max_optional_param_level = self.optional_param_level
if self.multi_line_parameter_list:
# If the first parameter is optional, start a new line and open the bracket.
if self.is_first_param:
self.new_state()
self.add_text('[')
# Else, if there remains at least one required parameter, append the
# parameter separator, open a new bracket, and end the line.
elif self.required_params_left:
self.add_text(self.param_separator)
self.add_text('[')
self.end_state(wrap=False, end=None)
# Else, open a new bracket, append the parameter separator, and end the
# line.
else:
self.add_text('[')
self.add_text(self.param_separator)
self.end_state(wrap=False, end=None)
else:
self.add_text('[')
def depart_desc_optional(self, node: Element) -> None:
self.add_text(']')
self.optional_param_level -= 1
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:
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:
self.end_state(wrap=False, end=None)
else:
self.add_text(']')
if self.optional_param_level == 0:
self.param_group_index += 1
def visit_desc_annotation(self, node: Element) -> None:
pass

View File

@ -0,0 +1 @@
c_maximum_signature_line_length = len("str hello(str name)") - 1

View File

@ -0,0 +1,4 @@
domain-c-c_maximum_signature_line_length
========================================
.. c:function:: str hello(str name)

View File

@ -0,0 +1 @@
cpp_maximum_signature_line_length = len("str hello(str name)") - 1

View File

@ -0,0 +1,4 @@
domain-cpp-cpp_maximum_signature_line_length
============================================
.. cpp:function:: str hello(str name)

View File

@ -0,0 +1 @@
javascript_maximum_signature_line_length = 1

View File

@ -0,0 +1,6 @@
domain-js-maximum_signature_line_length
=======================================
.. js:function:: hello(name)
.. js:function:: foo([a, [b, ]]c, d[, e, f])

View File

@ -0,0 +1 @@
python_maximum_signature_line_length = 1

View File

@ -0,0 +1,6 @@
domain-py-maximum_signature_line_length
=======================================
.. py:function:: hello(name: str) -> str
.. py:function:: foo([a, [b, ]]c, d[, e, f])

View File

@ -3,7 +3,7 @@
import os
import re
import subprocess
from itertools import product
from itertools import chain, product
from pathlib import Path
from shutil import copyfile
from subprocess import CalledProcessError
@ -95,11 +95,18 @@ def skip_if_stylefiles_notfound(testfunc):
@skip_if_requested
@skip_if_stylefiles_notfound
@pytest.mark.parametrize(
"engine,docclass",
product(LATEX_ENGINES, DOCCLASSES),
"engine,docclass,python_maximum_signature_line_length",
# Only running test with `python_maximum_signature_line_length` not None with last
# LaTeX engine to reduce testing time, as if this configuration does not fail with
# one engine, it's almost impossible it would fail with another.
chain(
product(LATEX_ENGINES[:-1], DOCCLASSES, [None]),
product([LATEX_ENGINES[-1]], DOCCLASSES, [1]),
),
)
@pytest.mark.sphinx('latex')
def test_build_latex_doc(app, status, warning, engine, docclass):
@pytest.mark.sphinx('latex', freshenv=True)
def test_build_latex_doc(app, status, warning, engine, docclass, python_maximum_signature_line_length):
app.config.python_maximum_signature_line_length = python_maximum_signature_line_length
app.config.intersphinx_mapping = {
'sphinx': ('https://www.sphinx-doc.org/en/master/', None),
}
@ -113,7 +120,6 @@ def test_build_latex_doc(app, status, warning, engine, docclass):
normalize_intersphinx_mapping(app, app.config)
load_mappings(app)
app.builder.init()
LaTeXTranslator.ignore_missing_images = True
app.builder.build_all()
@ -1734,3 +1740,16 @@ def test_duplicated_labels_before_module(app, status, warning):
# ensure that we did not forget any label to check
# and if so, report them nicely in case of failure
assert sorted(tested_labels) == sorted(output_labels)
@pytest.mark.sphinx('latex', testroot='domain-py-python_maximum_signature_line_length',
confoverrides={'python_maximum_signature_line_length': 23})
def test_one_parameter_per_line(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'python.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{\\sphinxbfcode{\\sphinxupquote{hello}}}' in result)
assert ('\\pysigwithonelineperarg{\\sphinxbfcode{\\sphinxupquote{foo}}}' in result)

View File

@ -7,7 +7,18 @@ from xml.etree import ElementTree
import pytest
from sphinx import addnodes
from sphinx.addnodes import desc
from sphinx.addnodes import (
desc,
desc_content,
desc_name,
desc_parameter,
desc_parameterlist,
desc_sig_name,
desc_sig_space,
desc_signature,
desc_signature_line,
pending_xref,
)
from sphinx.domains.c import (
DefinitionError,
DefinitionParser,
@ -19,6 +30,7 @@ from sphinx.domains.c import (
from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
from sphinx.writers.text import STDINDENT
class Config:
@ -814,3 +826,251 @@ def test_domain_c_parse_noindexentry(app):
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C function)', 'c.f', '', None)])
assert_node(doctree[2], addnodes.index, entries=[])
@pytest.mark.sphinx('html', confoverrides={
'c_maximum_signature_line_length': len("str hello(str name)"),
})
def test_cfunction_signature_with_c_maximum_signature_line_length_equal(app):
text = ".. c:function:: str hello(str name)"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_signature_line, (
pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, "hello"]],
desc_parameterlist,
)],
)],
desc_content,
)],
))
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="c", objtype="function", noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, "str"]],
desc_sig_space,
[desc_sig_name, "name"],
)])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'c_maximum_signature_line_length': len("str hello(str name)"),
})
def test_cfunction_signature_with_c_maximum_signature_line_length_force_single(app):
text = (".. c:function:: str hello(str names)\n"
" :single-line-parameter-list:")
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_signature_line, (
pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, "hello"]],
desc_parameterlist,
)],
)],
desc_content,
)],
))
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="c", objtype="function", noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, "str"]],
desc_sig_space,
[desc_sig_name, "names"],
)])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'c_maximum_signature_line_length': len("str hello(str name)"),
})
def test_cfunction_signature_with_c_maximum_signature_line_length_break(app):
text = ".. c:function:: str hello(str names)"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_signature_line, (
pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, "hello"]],
desc_parameterlist,
)],
)],
desc_content,
)],
))
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="c", objtype="function", noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, "str"]],
desc_sig_space,
[desc_sig_name, "names"],
)])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=True)
@pytest.mark.sphinx('html', confoverrides={
'maximum_signature_line_length': len("str hello(str name)"),
})
def test_cfunction_signature_with_maximum_signature_line_length_equal(app):
text = ".. c:function:: str hello(str name)"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_signature_line, (
pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, "hello"]],
desc_parameterlist,
)],
)],
desc_content,
)],
))
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="c", objtype="function", noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, "str"]],
desc_sig_space,
[desc_sig_name, "name"],
)])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'maximum_signature_line_length': len("str hello(str name)"),
})
def test_cfunction_signature_with_maximum_signature_line_length_force_single(app):
text = (".. c:function:: str hello(str names)\n"
" :single-line-parameter-list:")
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_signature_line, (
pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, "hello"]],
desc_parameterlist,
)],
)],
desc_content,
)],
))
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="c", objtype="function", noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, "str"]],
desc_sig_space,
[desc_sig_name, "names"],
)])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'maximum_signature_line_length': len("str hello(str name)"),
})
def test_cfunction_signature_with_maximum_signature_line_length_break(app):
text = ".. c:function:: str hello(str names)"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_signature_line, (
pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, "hello"]],
desc_parameterlist,
)],
)],
desc_content,
)],
))
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="c", objtype="function", noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, "str"]],
desc_sig_space,
[desc_sig_name, "names"],
)])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=True)
@pytest.mark.sphinx('html', confoverrides={
'c_maximum_signature_line_length': len('str hello(str name)'),
'maximum_signature_line_length': 1,
})
def test_c_maximum_signature_line_length_overrides_global(app):
text = '.. c:function:: str hello(str name)'
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_signature_line, (
pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, 'hello']],
desc_parameterlist,
)]
)],
desc_content,
)],
))
assert_node(doctree[1], addnodes.desc, desctype='function',
domain='c', objtype='function', noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, "str"]],
desc_sig_space,
[desc_sig_name, 'name'],
)])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', testroot='domain-c-c_maximum_signature_line_length')
def test_domain_c_c_maximum_signature_line_length_in_html(app, status, warning):
app.build()
content = (app.outdir / 'index.html').read_text(encoding='utf-8')
expected = """\
<dl>
<dd>\
<span class="n"><span class="pre">str</span></span>\
<span class="w"> </span>\
<span class="n"><span class="pre">name</span></span>,\
</dd>
</dl>
<span class="sig-paren">)</span>\
<a class="headerlink" href="#c.hello" title="Permalink to this definition"></a>\
<br />\
</dt>
"""
assert expected in content
@pytest.mark.sphinx(
'text', testroot='domain-c-c_maximum_signature_line_length',
)
def test_domain_c_c_maximum_signature_line_length_in_text(app, status, warning):
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("str name,"))
assert expected_parameter_list_hello in content

View File

@ -8,7 +8,18 @@ import pytest
import sphinx.domains.cpp as cppDomain
from sphinx import addnodes
from sphinx.addnodes import desc
from sphinx.addnodes import (
desc,
desc_content,
desc_name,
desc_parameter,
desc_parameterlist,
desc_sig_name,
desc_sig_space,
desc_signature,
desc_signature_line,
pending_xref,
)
from sphinx.domains.cpp import (
DefinitionError,
DefinitionParser,
@ -20,6 +31,7 @@ from sphinx.domains.cpp import (
from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
from sphinx.writers.text import STDINDENT
def parse(name, string):
@ -1486,3 +1498,243 @@ def test_domain_cpp_normalize_unspecialized_template_args(make_app, app_params):
)
warning = app2._warning.getvalue()
assert 'Internal C++ domain error during symbol merging' not in warning
@pytest.mark.sphinx('html', confoverrides={
'cpp_maximum_signature_line_length': len('str hello(str name)'),
})
def test_cpp_function_signature_with_cpp_maximum_signature_line_length_equal(app):
text = '.. cpp:function:: str hello(str name)'
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_signature_line, (
pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, 'hello']],
desc_parameterlist,
)],
)],
desc_content,
)],
))
assert_node(doctree[1], addnodes.desc, desctype='function',
domain='cpp', objtype='function', noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, 'str']],
desc_sig_space,
[desc_sig_name, 'name'],
)])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'cpp_maximum_signature_line_length': len('str hello(str name)'),
})
def test_cpp_function_signature_with_cpp_maximum_signature_line_length_force_single(app):
text = ('.. cpp:function:: str hello(str names)\n'
' :single-line-parameter-list:')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_signature_line, (
pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, 'hello']],
desc_parameterlist,
)],
)],
desc_content,
)],
))
assert_node(doctree[1], addnodes.desc, desctype='function',
domain='cpp', objtype='function', noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, 'str']],
desc_sig_space,
[desc_sig_name, 'names']),
])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'cpp_maximum_signature_line_length': len("str hello(str name)"),
})
def test_cpp_function_signature_with_cpp_maximum_signature_line_length_break(app):
text = '.. cpp:function:: str hello(str names)'
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_signature_line, (
pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, 'hello']],
desc_parameterlist,
)],
)],
desc_content,
)],
))
assert_node(doctree[1], addnodes.desc, desctype='function',
domain='cpp', objtype='function', noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, 'str']],
desc_sig_space,
[desc_sig_name, 'names']),
])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=True)
@pytest.mark.sphinx('html', confoverrides={
'maximum_signature_line_length': len('str hello(str name)'),
})
def test_cpp_function_signature_with_maximum_signature_line_length_equal(app):
text = '.. cpp:function:: str hello(str name)'
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_signature_line, (
pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, 'hello']],
desc_parameterlist,
)],
)],
desc_content,
)],
))
assert_node(doctree[1], addnodes.desc, desctype='function',
domain='cpp', objtype='function', noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, 'str']],
desc_sig_space,
[desc_sig_name, 'name'],
)])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'maximum_signature_line_length': len('str hello(str name)'),
})
def test_cpp_function_signature_with_maximum_signature_line_length_force_single(app):
text = ('.. cpp:function:: str hello(str names)\n'
' :single-line-parameter-list:')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_signature_line, (
pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, 'hello']],
desc_parameterlist,
)],
)],
desc_content,
)],
))
assert_node(doctree[1], addnodes.desc, desctype='function',
domain='cpp', objtype='function', noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, 'str']],
desc_sig_space,
[desc_sig_name, 'names']),
])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'maximum_signature_line_length': len("str hello(str name)"),
})
def test_cpp_function_signature_with_maximum_signature_line_length_break(app):
text = '.. cpp:function:: str hello(str names)'
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_signature_line, (
pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, 'hello']],
desc_parameterlist,
)],
)],
desc_content,
)],
))
assert_node(doctree[1], addnodes.desc, desctype='function',
domain='cpp', objtype='function', noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, 'str']],
desc_sig_space,
[desc_sig_name, 'names']),
])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=True)
@pytest.mark.sphinx('html', confoverrides={
'cpp_maximum_signature_line_length': len('str hello(str name)'),
'maximum_signature_line_length': 1,
})
def test_cpp_maximum_signature_line_length_overrides_global(app):
text = '.. cpp:function:: str hello(str name)'
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, ([desc_signature, ([desc_signature_line, (pending_xref,
desc_sig_space,
[desc_name, [desc_sig_name, "hello"]],
desc_parameterlist)])],
desc_content)],
))
assert_node(doctree[1], addnodes.desc, desctype='function',
domain='cpp', objtype='function', noindex=False)
assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, (
[pending_xref, [desc_sig_name, 'str']],
desc_sig_space,
[desc_sig_name, 'name'],
)])
assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', testroot='domain-cpp-cpp_maximum_signature_line_length')
def test_domain_cpp_cpp_maximum_signature_line_length_in_html(app, status, warning):
app.build()
content = (app.outdir / 'index.html').read_text(encoding='utf-8')
expected = """\
<dl>
<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>,\
</dd>
</dl>
<span class="sig-paren">)</span>\
<a class="headerlink" href=\
"""
assert expected in content
@pytest.mark.sphinx(
'text', testroot='domain-cpp-cpp_maximum_signature_line_length',
)
def test_domain_cpp_cpp_maximum_signature_line_length_in_text(app, status, warning):
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("str name,"))
assert expected_parameter_list_hello in content

View File

@ -22,6 +22,7 @@ from sphinx.addnodes import (
from sphinx.domains.javascript import JavaScriptDomain
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
from sphinx.writers.text import STDINDENT
@pytest.mark.sphinx('dummy', testroot='domain-js')
@ -242,3 +243,263 @@ def test_module_content_line_number(app):
source, line = docutils.utils.get_source_line(xrefs[0])
assert 'index.rst' in source
assert line == 3
@pytest.mark.sphinx('html', confoverrides={
'javascript_maximum_signature_line_length': len("hello(name)"),
})
def test_jsfunction_signature_with_javascript_maximum_signature_line_length_equal(app):
text = ".. js:function:: hello(name)"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_name, ([desc_sig_name, "hello"])],
desc_parameterlist,
)],
desc_content,
)],
))
assert_node(doctree[1], desc, desctype="function",
domain="js", objtype="function", noindex=False)
assert_node(doctree[1][0][1],
[desc_parameterlist, desc_parameter, ([desc_sig_name, "name"])])
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'javascript_maximum_signature_line_length': len("hello(name)"),
})
def test_jsfunction_signature_with_javascript_maximum_signature_line_length_force_single(app):
text = (".. js:function:: hello(names)\n"
" :single-line-parameter-list:")
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_name, ([desc_sig_name, "hello"])],
desc_parameterlist,
)],
desc_content,
)],
))
assert_node(doctree[1], desc, desctype="function",
domain="js", objtype="function", noindex=False)
assert_node(doctree[1][0][1],
[desc_parameterlist, desc_parameter, ([desc_sig_name, "names"])])
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'javascript_maximum_signature_line_length': len("hello(name)"),
})
def test_jsfunction_signature_with_javascript_maximum_signature_line_length_break(app):
text = ".. js:function:: hello(names)"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_name, ([desc_sig_name, "hello"])],
desc_parameterlist,
)],
desc_content,
)],
))
assert_node(doctree[1], desc, desctype="function",
domain="js", objtype="function", noindex=False)
assert_node(doctree[1][0][1],
[desc_parameterlist, desc_parameter, ([desc_sig_name, "names"])])
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=True)
@pytest.mark.sphinx('html', confoverrides={
'maximum_signature_line_length': len("hello(name)"),
})
def test_jsfunction_signature_with_maximum_signature_line_length_equal(app):
text = ".. js:function:: hello(name)"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_name, ([desc_sig_name, "hello"])],
desc_parameterlist,
)],
desc_content,
)],
))
assert_node(doctree[1], desc, desctype="function",
domain="js", objtype="function", noindex=False)
assert_node(doctree[1][0][1],
[desc_parameterlist, desc_parameter, ([desc_sig_name, "name"])])
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'maximum_signature_line_length': len("hello(name)"),
})
def test_jsfunction_signature_with_maximum_signature_line_length_force_single(app):
text = (".. js:function:: hello(names)\n"
" :single-line-parameter-list:")
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_name, ([desc_sig_name, "hello"])],
desc_parameterlist,
)],
desc_content,
)],
))
assert_node(doctree[1], desc, desctype="function",
domain="js", objtype="function", noindex=False)
assert_node(doctree[1][0][1],
[desc_parameterlist, desc_parameter, ([desc_sig_name, "names"])])
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'maximum_signature_line_length': len("hello(name)"),
})
def test_jsfunction_signature_with_maximum_signature_line_length_break(app):
text = ".. js:function:: hello(names)"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_name, ([desc_sig_name, "hello"])],
desc_parameterlist,
)],
desc_content,
)],
))
assert_node(doctree[1], desc, desctype="function",
domain="js", objtype="function", noindex=False)
assert_node(doctree[1][0][1],
[desc_parameterlist, desc_parameter, ([desc_sig_name, "names"])])
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=True)
@pytest.mark.sphinx(
'html',
confoverrides={
'javascript_maximum_signature_line_length': len("hello(name)"),
'maximum_signature_line_length': 1,
},
)
def test_javascript_maximum_signature_line_length_overrides_global(app):
text = ".. js:function:: hello(name)"
doctree = restructuredtext.parse(app, text)
expected_doctree = (addnodes.index,
[desc, ([desc_signature, ([desc_name, ([desc_sig_name, "hello"])],
desc_parameterlist)],
desc_content)])
assert_node(doctree, expected_doctree)
assert_node(doctree[1], desc, desctype="function",
domain="js", objtype="function", noindex=False)
expected_sig = [desc_parameterlist, desc_parameter, [desc_sig_name, "name"]]
assert_node(doctree[1][0][1], expected_sig)
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx(
'html', testroot='domain-js-javascript_maximum_signature_line_length',
)
def test_domain_js_javascript_maximum_signature_line_length_in_html(app, status, warning):
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="Permalink 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="Permalink 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',
)
def test_domain_js_javascript_maximum_signature_line_length_in_text(app, status, warning):
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

@ -38,6 +38,7 @@ from sphinx.domains.python import (
)
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
from sphinx.writers.text import STDINDENT
def parse(sig):
@ -1460,6 +1461,308 @@ def test_signature_line_number(app, include_options):
assert line == 1
@pytest.mark.sphinx('html', confoverrides={
'python_maximum_signature_line_length': len("hello(name: str) -> str"),
})
def test_pyfunction_signature_with_python_maximum_signature_line_length_equal(app):
text = ".. py:function:: hello(name: str) -> str"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_name, "hello"],
desc_parameterlist,
[desc_returns, pending_xref, "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, (
[desc_sig_name, "name"],
[desc_sig_punctuation, ":"],
desc_sig_space,
[nodes.inline, pending_xref, "str"],
)])
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'python_maximum_signature_line_length': len("hello(name: str) -> str"),
})
def test_pyfunction_signature_with_python_maximum_signature_line_length_force_single(app):
text = (".. py:function:: hello(names: str) -> str\n"
" :single-line-parameter-list:")
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_name, "hello"],
desc_parameterlist,
[desc_returns, pending_xref, "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, (
[desc_sig_name, "names"],
[desc_sig_punctuation, ":"],
desc_sig_space,
[nodes.inline, pending_xref, "str"],
)])
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'python_maximum_signature_line_length': len("hello(name: str) -> str"),
})
def test_pyfunction_signature_with_python_maximum_signature_line_length_break(app):
text = ".. py:function:: hello(names: str) -> str"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_name, "hello"],
desc_parameterlist,
[desc_returns, pending_xref, "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, (
[desc_sig_name, "names"],
[desc_sig_punctuation, ":"],
desc_sig_space,
[nodes.inline, pending_xref, "str"],
)])
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=True)
@pytest.mark.sphinx('html', confoverrides={
'maximum_signature_line_length': len("hello(name: str) -> str"),
})
def test_pyfunction_signature_with_maximum_signature_line_length_equal(app):
text = ".. py:function:: hello(name: str) -> str"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_name, "hello"],
desc_parameterlist,
[desc_returns, pending_xref, "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, (
[desc_sig_name, "name"],
[desc_sig_punctuation, ":"],
desc_sig_space,
[nodes.inline, pending_xref, "str"],
)])
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'maximum_signature_line_length': len("hello(name: str) -> str"),
})
def test_pyfunction_signature_with_maximum_signature_line_length_force_single(app):
text = (".. py:function:: hello(names: str) -> str\n"
" :single-line-parameter-list:")
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_name, "hello"],
desc_parameterlist,
[desc_returns, pending_xref, "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, (
[desc_sig_name, "names"],
[desc_sig_punctuation, ":"],
desc_sig_space,
[nodes.inline, pending_xref, "str"],
)])
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx('html', confoverrides={
'maximum_signature_line_length': len("hello(name: str) -> str"),
})
def test_pyfunction_signature_with_maximum_signature_line_length_break(app):
text = ".. py:function:: hello(names: str) -> str"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (
addnodes.index,
[desc, (
[desc_signature, (
[desc_name, "hello"],
desc_parameterlist,
[desc_returns, pending_xref, "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, (
[desc_sig_name, "names"],
[desc_sig_punctuation, ":"],
desc_sig_space,
[nodes.inline, pending_xref, "str"],
)])
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=True)
@pytest.mark.sphinx(
'html',
confoverrides={
'python_maximum_signature_line_length': len("hello(name: str) -> str"),
'maximum_signature_line_length': 1,
},
)
def test_python_maximum_signature_line_length_overrides_global(app):
text = ".. py:function:: hello(name: str) -> str"
doctree = restructuredtext.parse(app, text)
expected_doctree = (addnodes.index,
[desc, ([desc_signature, ([desc_name, "hello"],
desc_parameterlist,
[desc_returns, pending_xref, "str"])],
desc_content)])
assert_node(doctree, expected_doctree)
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="py", objtype="function", noindex=False)
signame_node = [desc_sig_name, "name"]
expected_sig = [desc_parameterlist, desc_parameter, (signame_node,
[desc_sig_punctuation, ":"],
desc_sig_space,
[nodes.inline, pending_xref, "str"])]
assert_node(doctree[1][0][1], expected_sig)
assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False)
@pytest.mark.sphinx(
'html', testroot='domain-py-python_maximum_signature_line_length',
)
def test_domain_py_python_maximum_signature_line_length_in_html(app, status, warning):
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="Permalink 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="Permalink 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',
)
def test_domain_py_python_maximum_signature_line_length_in_text(app, status, warning):
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
def test_module_content_line_number(app):
text = (".. py:module:: foo\n" +
"\n" +