mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Fix PEP 695 output for classes in the LaTeX builder (#12561)
This commit is contained in:
parent
aeebfabe09
commit
2a30bb661f
@ -160,6 +160,8 @@ Bugs fixed
|
||||
Patch by Jakob Lykke Andersen and Adam Turner.
|
||||
* #11041: linkcheck: Ignore URLs that respond with non-Unicode content.
|
||||
Patch by James Addison.
|
||||
* #12543: Fix :pep:`695` formatting for LaTeX output.
|
||||
Patch by Bénédikt Tran.
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
@ -724,19 +724,21 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
return e.get('multi_line_parameter_list')
|
||||
|
||||
self.has_tp_list = False
|
||||
self.orphan_tp_list = False
|
||||
|
||||
for child in node:
|
||||
if isinstance(child, addnodes.desc_type_parameter_list):
|
||||
self.has_tp_list = True
|
||||
# recall that return annotations must follow an argument list,
|
||||
# so signatures of the form "foo[tp_list] -> retann" will not
|
||||
# be encountered (if they should, the `domains.python.py_sig_re`
|
||||
# pattern must be modified accordingly)
|
||||
arglist = next_sibling(child)
|
||||
assert isinstance(arglist, addnodes.desc_parameterlist)
|
||||
# tp_list + arglist: \macro{name}{tp_list}{arglist}{return}
|
||||
multi_tp_list = has_multi_line(child)
|
||||
arglist = next_sibling(child)
|
||||
if isinstance(arglist, addnodes.desc_parameterlist):
|
||||
# tp_list + arglist: \macro{name}{tp_list}{arglist}{retann}
|
||||
multi_arglist = has_multi_line(arglist)
|
||||
else:
|
||||
# orphan tp_list: \macro{name}{tp_list}{}{retann}
|
||||
# see: https://github.com/sphinx-doc/sphinx/issues/12543
|
||||
self.orphan_tp_list = True
|
||||
multi_arglist = False
|
||||
|
||||
if multi_tp_list:
|
||||
if multi_arglist:
|
||||
@ -751,7 +753,7 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
break
|
||||
|
||||
if isinstance(child, addnodes.desc_parameterlist):
|
||||
# arglist only: \macro{name}{arglist}{return}
|
||||
# arglist only: \macro{name}{arglist}{retann}
|
||||
if has_multi_line(child):
|
||||
self.body.append(CR + r'\pysigwithonelineperarg{')
|
||||
else:
|
||||
@ -857,7 +859,13 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
self.multi_line_parameter_list = node.get('multi_line_parameter_list', False)
|
||||
|
||||
def visit_desc_parameterlist(self, node: Element) -> None:
|
||||
if not self.has_tp_list:
|
||||
if self.has_tp_list:
|
||||
if self.orphan_tp_list:
|
||||
# close type parameters list (#2)
|
||||
self.body.append('}{')
|
||||
# empty parameters list argument (#3)
|
||||
return
|
||||
else:
|
||||
# close name argument (#1), open parameters list argument (#2)
|
||||
self.body.append('}{')
|
||||
self._visit_sig_parameter_list(node, addnodes.desc_parameter)
|
||||
|
@ -4,3 +4,16 @@ domain-py-maximum_signature_line_length
|
||||
.. py:function:: hello(name: str) -> str
|
||||
|
||||
.. py:function:: foo([a, [b, ]]c, d[, e, f])
|
||||
|
||||
.. py:function:: generic_arg[T]
|
||||
|
||||
.. py:function:: generic_foo[T]()
|
||||
|
||||
.. py:function:: generic_bar[T](x: list[T])
|
||||
|
||||
.. py:function:: generic_ret[R]() -> R
|
||||
|
||||
.. py:class:: MyGenericClass[X]
|
||||
|
||||
.. py:class:: MyList[T](list[T])
|
||||
|
||||
|
@ -376,3 +376,34 @@ def test_html_remove_sources_before_write_gh_issue_10786(app, warning):
|
||||
|
||||
file = os.fsdecode(target)
|
||||
assert f'WARNING: cannot copy image file {file!r}: {file!s} does not exist' == ws[-1]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='domain-py-python_maximum_signature_line_length',
|
||||
confoverrides={'python_maximum_signature_line_length': 1})
|
||||
def test_html_pep_695_one_type_per_line(app, cached_etree_parse):
|
||||
app.build()
|
||||
fname = app.outdir / 'index.html'
|
||||
etree = cached_etree_parse(fname)
|
||||
|
||||
class chk:
|
||||
def __init__(self, expect):
|
||||
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])'))
|
||||
|
@ -1760,6 +1760,28 @@ def test_one_parameter_per_line(app, status, warning):
|
||||
|
||||
assert ('\\pysigwithonelineperarg{\\sphinxbfcode{\\sphinxupquote{foo}}}' in result)
|
||||
|
||||
# generic_arg[T]
|
||||
assert ('\\pysiglinewithargsretwithtypelist{\\sphinxbfcode{\\sphinxupquote{generic\\_arg}}}'
|
||||
'{\\sphinxtypeparam{\\DUrole{n}{T}}}{}{}' in result)
|
||||
|
||||
# generic_foo[T]()
|
||||
assert ('\\pysiglinewithargsretwithtypelist{\\sphinxbfcode{\\sphinxupquote{generic\\_foo}}}' in result)
|
||||
|
||||
# generic_bar[T](x: list[T])
|
||||
assert ('\\pysigwithonelineperargwithtypelist{\\sphinxbfcode{\\sphinxupquote{generic\\_bar}}}' in result)
|
||||
|
||||
# generic_ret[R]() -> R
|
||||
assert ('\\pysiglinewithargsretwithtypelist{\\sphinxbfcode{\\sphinxupquote{generic\\_ret}}}'
|
||||
'{\\sphinxtypeparam{\\DUrole{n}{R}}}{}{{ $\\rightarrow$ R}}' in result)
|
||||
|
||||
# MyGenericClass[X]
|
||||
assert ('\\pysiglinewithargsretwithtypelist{\\sphinxbfcode{\\sphinxupquote{class\\DUrole{w}{ '
|
||||
'}}}\\sphinxbfcode{\\sphinxupquote{MyGenericClass}}}' in result)
|
||||
|
||||
# MyList[T](list[T])
|
||||
assert ('\\pysiglinewithargsretwithtypelist{\\sphinxbfcode{\\sphinxupquote{class\\DUrole{w}{ '
|
||||
'}}}\\sphinxbfcode{\\sphinxupquote{MyList}}}' in result)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('latex', testroot='markup-rubric')
|
||||
def test_latex_rubric(app):
|
||||
|
@ -753,7 +753,7 @@ def test_function_pep_695(app):
|
||||
S,\
|
||||
T: int,\
|
||||
U: (int, str),\
|
||||
R: int | int,\
|
||||
R: int | str,\
|
||||
A: int | Annotated[int, ctype("char")],\
|
||||
*V,\
|
||||
**P\
|
||||
@ -795,7 +795,7 @@ def test_function_pep_695(app):
|
||||
desc_sig_space,
|
||||
[desc_sig_punctuation, '|'],
|
||||
desc_sig_space,
|
||||
[pending_xref, 'int'],
|
||||
[pending_xref, 'str'],
|
||||
)],
|
||||
)],
|
||||
[desc_type_parameter, (
|
||||
|
Loading…
Reference in New Issue
Block a user