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.
|
Patch by Jakob Lykke Andersen and Adam Turner.
|
||||||
* #11041: linkcheck: Ignore URLs that respond with non-Unicode content.
|
* #11041: linkcheck: Ignore URLs that respond with non-Unicode content.
|
||||||
Patch by James Addison.
|
Patch by James Addison.
|
||||||
|
* #12543: Fix :pep:`695` formatting for LaTeX output.
|
||||||
|
Patch by Bénédikt Tran.
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
-------
|
-------
|
||||||
|
@ -724,19 +724,21 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
return e.get('multi_line_parameter_list')
|
return e.get('multi_line_parameter_list')
|
||||||
|
|
||||||
self.has_tp_list = False
|
self.has_tp_list = False
|
||||||
|
self.orphan_tp_list = False
|
||||||
|
|
||||||
for child in node:
|
for child in node:
|
||||||
if isinstance(child, addnodes.desc_type_parameter_list):
|
if isinstance(child, addnodes.desc_type_parameter_list):
|
||||||
self.has_tp_list = True
|
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)
|
multi_tp_list = has_multi_line(child)
|
||||||
multi_arglist = has_multi_line(arglist)
|
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_tp_list:
|
||||||
if multi_arglist:
|
if multi_arglist:
|
||||||
@ -751,7 +753,7 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if isinstance(child, addnodes.desc_parameterlist):
|
if isinstance(child, addnodes.desc_parameterlist):
|
||||||
# arglist only: \macro{name}{arglist}{return}
|
# arglist only: \macro{name}{arglist}{retann}
|
||||||
if has_multi_line(child):
|
if has_multi_line(child):
|
||||||
self.body.append(CR + r'\pysigwithonelineperarg{')
|
self.body.append(CR + r'\pysigwithonelineperarg{')
|
||||||
else:
|
else:
|
||||||
@ -857,7 +859,13 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
self.multi_line_parameter_list = node.get('multi_line_parameter_list', False)
|
self.multi_line_parameter_list = node.get('multi_line_parameter_list', False)
|
||||||
|
|
||||||
def visit_desc_parameterlist(self, node: Element) -> None:
|
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)
|
# close name argument (#1), open parameters list argument (#2)
|
||||||
self.body.append('}{')
|
self.body.append('}{')
|
||||||
self._visit_sig_parameter_list(node, addnodes.desc_parameter)
|
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:: hello(name: str) -> str
|
||||||
|
|
||||||
.. py:function:: foo([a, [b, ]]c, d[, e, f])
|
.. 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)
|
file = os.fsdecode(target)
|
||||||
assert f'WARNING: cannot copy image file {file!r}: {file!s} does not exist' == ws[-1]
|
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)
|
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')
|
@pytest.mark.sphinx('latex', testroot='markup-rubric')
|
||||||
def test_latex_rubric(app):
|
def test_latex_rubric(app):
|
||||||
|
@ -753,7 +753,7 @@ def test_function_pep_695(app):
|
|||||||
S,\
|
S,\
|
||||||
T: int,\
|
T: int,\
|
||||||
U: (int, str),\
|
U: (int, str),\
|
||||||
R: int | int,\
|
R: int | str,\
|
||||||
A: int | Annotated[int, ctype("char")],\
|
A: int | Annotated[int, ctype("char")],\
|
||||||
*V,\
|
*V,\
|
||||||
**P\
|
**P\
|
||||||
@ -795,7 +795,7 @@ def test_function_pep_695(app):
|
|||||||
desc_sig_space,
|
desc_sig_space,
|
||||||
[desc_sig_punctuation, '|'],
|
[desc_sig_punctuation, '|'],
|
||||||
desc_sig_space,
|
desc_sig_space,
|
||||||
[pending_xref, 'int'],
|
[pending_xref, 'str'],
|
||||||
)],
|
)],
|
||||||
)],
|
)],
|
||||||
[desc_type_parameter, (
|
[desc_type_parameter, (
|
||||||
|
Loading…
Reference in New Issue
Block a user