From d8ba12ac6de161ecdf2590dc02b797d834996afc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Mon, 5 Aug 2024 16:51:26 +0200 Subject: [PATCH 1/8] LaTeX: Fix #12735 about siglines with typing but no parameter list Related #11444, #12561 --- sphinx/writers/latex.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 4badfa87f85..00d0ff141de 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -743,26 +743,30 @@ def has_multi_line(e: Element) -> bool: if multi_tp_list: if multi_arglist: - self.body.append(CR + r'\pysigwithonelineperargwithonelinepertparg{') + self.body.append(CR + r'\pysigwithonelineperargwithonelinepertparg' + + CR + '{') else: - self.body.append(CR + r'\pysiglinewithargsretwithonelinepertparg{') + self.body.append(CR + r'\pysiglinewithargsretwithonelinepertparg' + + CR + '{') else: if multi_arglist: - self.body.append(CR + r'\pysigwithonelineperargwithtypelist{') + self.body.append(CR + r'\pysigwithonelineperargwithtypelist' + + CR + '{') else: - self.body.append(CR + r'\pysiglinewithargsretwithtypelist{') + self.body.append(CR + r'\pysiglinewithargsretwithtypelist' + + CR + '{') break if isinstance(child, addnodes.desc_parameterlist): # arglist only: \macro{name}{arglist}{retann} if has_multi_line(child): - self.body.append(CR + r'\pysigwithonelineperarg{') + self.body.append(CR + r'\pysigwithonelineperarg' + CR + '{') else: - self.body.append(CR + r'\pysiglinewithargsret{') + self.body.append(CR + r'\pysiglinewithargsret' + CR + '{') break else: # no tp_list, no arglist: \macro{name} - self.body.append(CR + r'\pysigline{') + self.body.append(CR + r'\pysigline' + CR + '{') def _depart_signature_line(self, node: Element) -> None: self.body.append('}') @@ -863,26 +867,30 @@ def visit_desc_parameterlist(self, node: Element) -> None: if self.has_tp_list: if self.orphan_tp_list: # close type parameters list (#2) - self.body.append('}{') + self.body.append('}' + CR + '{') # empty parameters list argument (#3) return else: # close name argument (#1), open parameters list argument (#2) - self.body.append('}{') + self.body.append('}' + CR + '{') self._visit_sig_parameter_list(node, addnodes.desc_parameter) def depart_desc_parameterlist(self, node: Element) -> None: # close parameterlist, open return annotation - self.body.append('}{') + self.body.append('}' + CR + '{') def visit_desc_type_parameter_list(self, node: Element) -> None: # close name argument (#1), open type parameters list argument (#2) - self.body.append('}{') + self.body.append('}' + CR + '{') self._visit_sig_parameter_list(node, addnodes.desc_type_parameter) def depart_desc_type_parameter_list(self, node: Element) -> None: # close type parameters list, open parameters list argument (#3) - self.body.append('}{') + if self.orphan_tp_list: + # no parameters + self.body.append('}' + CR + '{}' + CR + '{') + else: + self.body.append('}' + CR + '{') def _visit_sig_parameter(self, node: Element, parameter_macro: str) -> None: if self.is_first_param: From 0418aa66f9d1c17c356b54810316f79c37969bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Mon, 5 Aug 2024 17:15:39 +0200 Subject: [PATCH 2/8] Update test (valid with DocUtils < (0,22)) --- tests/test_builders/test_build_latex.py | 48 ++++++++++++++++++------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/tests/test_builders/test_build_latex.py b/tests/test_builders/test_build_latex.py index c2bd2d970d1..10680c2b821 100644 --- a/tests/test_builders/test_build_latex.py +++ b/tests/test_builders/test_build_latex.py @@ -1758,31 +1758,55 @@ def test_one_parameter_per_line(app): # 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 ('\\pysiglinewithargsret\n' + '{\\sphinxbfcode{\\sphinxupquote{hello}}}\n' + '{\\sphinxparam{' in result) - assert ('\\pysigwithonelineperarg{\\sphinxbfcode{\\sphinxupquote{foo}}}' in result) + assert ('\\pysigwithonelineperarg\n' + '{\\sphinxbfcode{\\sphinxupquote{foo}}}\n' + '{\\sphinxoptional{\\sphinxparam{' in result) # generic_arg[T] - assert ('\\pysiglinewithargsretwithtypelist{\\sphinxbfcode{\\sphinxupquote{generic\\_arg}}}' - '{\\sphinxtypeparam{\\DUrole{n}{T}}}{}{}' in result) + assert ('\\pysiglinewithargsretwithtypelist\n' + '{\\sphinxbfcode{\\sphinxupquote{generic\\_arg}}}\n' + '{\\sphinxtypeparam{\\DUrole{n}{T}}}\n' + '{}\n' + '{}\n' in result) # generic_foo[T]() - assert ('\\pysiglinewithargsretwithtypelist{\\sphinxbfcode{\\sphinxupquote{generic\\_foo}}}' in result) + assert ('\\pysiglinewithargsretwithtypelist\n' + '{\\sphinxbfcode{\\sphinxupquote{generic\\_foo}}}\n' + '{\\sphinxtypeparam{\\DUrole{n}{T}}}\n' + '{}\n' + '{}\n' in result) # generic_bar[T](x: list[T]) - assert ('\\pysigwithonelineperargwithtypelist{\\sphinxbfcode{\\sphinxupquote{generic\\_bar}}}' in result) + assert ('\\pysigwithonelineperargwithtypelist\n' + '{\\sphinxbfcode{\\sphinxupquote{generic\\_bar}}}\n' + '{\\sphinxtypeparam{' in result) # generic_ret[R]() -> R - assert ('\\pysiglinewithargsretwithtypelist{\\sphinxbfcode{\\sphinxupquote{generic\\_ret}}}' - '{\\sphinxtypeparam{\\DUrole{n}{R}}}{}{{ $\\rightarrow$ R}}' in result) + assert ('\\pysiglinewithargsretwithtypelist\n' + '{\\sphinxbfcode{\\sphinxupquote{generic\\_ret}}}\n' + '{\\sphinxtypeparam{\\DUrole{n}{R}}}\n' + '{}\n' + '{{ $\\rightarrow$ R}}\n' in result) # MyGenericClass[X] - assert ('\\pysiglinewithargsretwithtypelist{\\sphinxbfcode{\\sphinxupquote{class\\DUrole{w}{ ' - '}}}\\sphinxbfcode{\\sphinxupquote{MyGenericClass}}}' in result) + assert ('\\pysiglinewithargsretwithtypelist\n' + '{\\sphinxbfcode{\\sphinxupquote{class\\DUrole{w}{ }}}' + '\\sphinxbfcode{\\sphinxupquote{MyGenericClass}}}\n' + '{\\sphinxtypeparam{\\DUrole{n}{X}}}\n' + '{}\n' + '{}\n' in result) # MyList[T](list[T]) - assert ('\\pysiglinewithargsretwithtypelist{\\sphinxbfcode{\\sphinxupquote{class\\DUrole{w}{ ' - '}}}\\sphinxbfcode{\\sphinxupquote{MyList}}}' in result) + assert ('\\pysiglinewithargsretwithtypelist\n' + '{\\sphinxbfcode{\\sphinxupquote{class\\DUrole{w}{ }}}' + '\\sphinxbfcode{\\sphinxupquote{MyList}}}\n' + '{\\sphinxtypeparam{\\DUrole{n}{T}}}\n' + '{\\sphinxparam{list{[}T{]}}}\n' + '{}\n' in result) @pytest.mark.sphinx('latex', testroot='markup-rubric') From a801f529462f99aa82c4db016f1da87bb61e49b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Sun, 11 Aug 2024 22:42:10 +0200 Subject: [PATCH 3/8] Fix linting error from extra line due to merge error --- tests/test_builders/test_build_latex.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_builders/test_build_latex.py b/tests/test_builders/test_build_latex.py index 1c3fc5d4f38..606755045b4 100644 --- a/tests/test_builders/test_build_latex.py +++ b/tests/test_builders/test_build_latex.py @@ -2278,7 +2278,6 @@ def test_one_parameter_per_line(app): '{}\n' in result) - @pytest.mark.sphinx('latex', testroot='markup-rubric') def test_latex_rubric(app): app.build() From 36d3ec09600b09e4bd5d6ed6584231c8c1ceb49d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Sun, 11 Aug 2024 22:51:37 +0200 Subject: [PATCH 4/8] Apply formatter after merge --- tests/test_builders/test_build_latex.py | 88 +++++++++++++++---------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/tests/test_builders/test_build_latex.py b/tests/test_builders/test_build_latex.py index 606755045b4..68232a35809 100644 --- a/tests/test_builders/test_build_latex.py +++ b/tests/test_builders/test_build_latex.py @@ -2227,55 +2227,71 @@ def test_one_parameter_per_line(app): # 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' - '{\\sphinxbfcode{\\sphinxupquote{hello}}}\n' - '{\\sphinxparam{' in result) + assert ( + '\\pysiglinewithargsret\n' + '{\\sphinxbfcode{\\sphinxupquote{hello}}}\n' + '{\\sphinxparam{' in result + ) - assert ('\\pysigwithonelineperarg\n' - '{\\sphinxbfcode{\\sphinxupquote{foo}}}\n' - '{\\sphinxoptional{\\sphinxparam{' in result) + assert ( + '\\pysigwithonelineperarg\n' + '{\\sphinxbfcode{\\sphinxupquote{foo}}}\n' + '{\\sphinxoptional{\\sphinxparam{' in result + ) # generic_arg[T] - assert ('\\pysiglinewithargsretwithtypelist\n' - '{\\sphinxbfcode{\\sphinxupquote{generic\\_arg}}}\n' - '{\\sphinxtypeparam{\\DUrole{n}{T}}}\n' - '{}\n' - '{}\n' in result) + assert ( + '\\pysiglinewithargsretwithtypelist\n' + '{\\sphinxbfcode{\\sphinxupquote{generic\\_arg}}}\n' + '{\\sphinxtypeparam{\\DUrole{n}{T}}}\n' + '{}\n' + '{}\n' in result + ) # generic_foo[T]() - assert ('\\pysiglinewithargsretwithtypelist\n' - '{\\sphinxbfcode{\\sphinxupquote{generic\\_foo}}}\n' - '{\\sphinxtypeparam{\\DUrole{n}{T}}}\n' - '{}\n' - '{}\n' in result) + assert ( + '\\pysiglinewithargsretwithtypelist\n' + '{\\sphinxbfcode{\\sphinxupquote{generic\\_foo}}}\n' + '{\\sphinxtypeparam{\\DUrole{n}{T}}}\n' + '{}\n' + '{}\n' in result + ) # generic_bar[T](x: list[T]) - assert ('\\pysigwithonelineperargwithtypelist\n' - '{\\sphinxbfcode{\\sphinxupquote{generic\\_bar}}}\n' - '{\\sphinxtypeparam{' in result) + assert ( + '\\pysigwithonelineperargwithtypelist\n' + '{\\sphinxbfcode{\\sphinxupquote{generic\\_bar}}}\n' + '{\\sphinxtypeparam{' in result + ) # generic_ret[R]() -> R - assert ('\\pysiglinewithargsretwithtypelist\n' - '{\\sphinxbfcode{\\sphinxupquote{generic\\_ret}}}\n' - '{\\sphinxtypeparam{\\DUrole{n}{R}}}\n' - '{}\n' - '{{ $\\rightarrow$ R}}\n' in result) + assert ( + '\\pysiglinewithargsretwithtypelist\n' + '{\\sphinxbfcode{\\sphinxupquote{generic\\_ret}}}\n' + '{\\sphinxtypeparam{\\DUrole{n}{R}}}\n' + '{}\n' + '{{ $\\rightarrow$ R}}\n' in result + ) # MyGenericClass[X] - assert ('\\pysiglinewithargsretwithtypelist\n' - '{\\sphinxbfcode{\\sphinxupquote{class\\DUrole{w}{ }}}' - '\\sphinxbfcode{\\sphinxupquote{MyGenericClass}}}\n' - '{\\sphinxtypeparam{\\DUrole{n}{X}}}\n' - '{}\n' - '{}\n' in result) + assert ( + '\\pysiglinewithargsretwithtypelist\n' + '{\\sphinxbfcode{\\sphinxupquote{class\\DUrole{w}{ }}}' + '\\sphinxbfcode{\\sphinxupquote{MyGenericClass}}}\n' + '{\\sphinxtypeparam{\\DUrole{n}{X}}}\n' + '{}\n' + '{}\n' in result + ) # MyList[T](list[T]) - assert ('\\pysiglinewithargsretwithtypelist\n' - '{\\sphinxbfcode{\\sphinxupquote{class\\DUrole{w}{ }}}' - '\\sphinxbfcode{\\sphinxupquote{MyList}}}\n' - '{\\sphinxtypeparam{\\DUrole{n}{T}}}\n' - '{\\sphinxparam{list{[}T{]}}}\n' - '{}\n' in result) + assert ( + '\\pysiglinewithargsretwithtypelist\n' + '{\\sphinxbfcode{\\sphinxupquote{class\\DUrole{w}{ }}}' + '\\sphinxbfcode{\\sphinxupquote{MyList}}}\n' + '{\\sphinxtypeparam{\\DUrole{n}{T}}}\n' + '{\\sphinxparam{list{[}T{]}}}\n' + '{}\n' in result + ) @pytest.mark.sphinx('latex', testroot='markup-rubric') From 9c5d66c97050148bc73693ce4f923814a6f276b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Mon, 12 Aug 2024 18:00:29 +0200 Subject: [PATCH 5/8] Update sphinx/writers/latex.py --- sphinx/writers/latex.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 00d0ff141de..0b7c5f53e78 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -885,11 +885,12 @@ def visit_desc_type_parameter_list(self, node: Element) -> None: self._visit_sig_parameter_list(node, addnodes.desc_type_parameter) def depart_desc_type_parameter_list(self, node: Element) -> None: - # close type parameters list, open parameters list argument (#3) if self.orphan_tp_list: - # no parameters + # this node next sibling isn't a desc_parameterlist, there are no parameters: + # close the type list, output an empty parameter list, and open return annotation. self.body.append('}' + CR + '{}' + CR + '{') else: + # close type parameters list, open parameters list argument (#3) self.body.append('}' + CR + '{') def _visit_sig_parameter(self, node: Element, parameter_macro: str) -> None: From 1007d426dc8747e42f18840bbdd779c385c84709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B=2E?= <2589111+jfbu@users.noreply.github.com> Date: Mon, 12 Aug 2024 18:08:50 +0200 Subject: [PATCH 6/8] Update sphinx/writers/latex.py --- sphinx/writers/latex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 0b7c5f53e78..3becb00e4f6 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -887,7 +887,7 @@ def visit_desc_type_parameter_list(self, node: Element) -> None: def depart_desc_type_parameter_list(self, node: Element) -> None: if self.orphan_tp_list: # this node next sibling isn't a desc_parameterlist, there are no parameters: - # close the type list, output an empty parameter list, and open return annotation. + # close the type list, output an empty parameter list, open return annotation. self.body.append('}' + CR + '{}' + CR + '{') else: # close type parameters list, open parameters list argument (#3) From dbfd59239030740d85d307150fc2bea9209c6364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Mon, 12 Aug 2024 18:20:03 +0200 Subject: [PATCH 7/8] Update CHANGES.rst --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index c2c74365e3e..831b11f9b32 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -59,6 +59,8 @@ Bugs fixed e.g., ``index.html#foo`` becomes ``#foo``. (note: continuation of a partial fix added in Sphinx 7.3.0) Patch by James Addison (with reference to prior work by Eric Norige) +* #12735: Fix :pep:`695` generic classes LaTeX output formatting. + Patch by Jean-François B. and Bénédikt Tran. Testing ------- From 5318691f9c215db67c730cfea3b489bce49ad83e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Fri, 23 Aug 2024 19:10:38 +0200 Subject: [PATCH 8/8] Add ``assert not self.orphan_tp_list`` to depart_desc_parameterlist --- sphinx/writers/latex.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 845c0d8e608..00e8db942a2 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -892,6 +892,7 @@ def visit_desc_parameterlist(self, node: Element) -> None: def depart_desc_parameterlist(self, node: Element) -> None: # close parameterlist, open return annotation + assert not self.orphan_tp_list self.body.append('}' + CR + '{') def visit_desc_type_parameter_list(self, node: Element) -> None: