Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions sphinx/builders/latex/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,10 +440,20 @@ def depart_thead(self, node: nodes.thead) -> None:
self.unrestrict(node)

def depart_table(self, node: nodes.table) -> None:
tbody = next(node.findall(nodes.tbody))
for footnote in reversed(self.table_footnotes):
fntext = footnotetext('', *footnote.children, ids=footnote['ids'])
tbody.insert(0, fntext)
tbody = next(node.findall(nodes.tbody), None)
if tbody is not None:
for footnote in reversed(self.table_footnotes):
fntext = footnotetext('', *footnote.children, ids=footnote['ids'])
tbody.insert(0, fntext)
else:
# If there is no tbody (e.g. a table with only header rows),
# place any collected footnotes after the table node instead.
table_parent = node.parent
if table_parent is not None:
idx = table_parent.index(node)
for i, footnote in enumerate(self.table_footnotes):
fntext = footnotetext('', *footnote.children, ids=footnote['ids'])
table_parent.insert(idx + i + 1, fntext)

self.table_footnotes = []

Expand Down
42 changes: 42 additions & 0 deletions tests/test_builders/test_build_latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import docutils
import pygments
import pytest
from docutils import nodes

from sphinx.builders.latex import default_latex_documents
from sphinx.config import Config
Expand Down Expand Up @@ -2097,6 +2098,47 @@ def test_latex_nested_tables(app: SphinxTestApp) -> None:
assert app.warning.getvalue() == ''


def test_latex_table_empty_body() -> None:
"""Regression test for issue #14271.

Tables with header rows but no body rows (as produced by e.g.
myst_parser from Markdown) should not crash the LaTeX builder
with a StopIteration in LaTeXFootnoteVisitor.depart_table.
"""
from docutils.utils import new_document

from sphinx.builders.latex.transforms import LaTeXFootnoteVisitor

document = new_document('<test>')

# Build a table node with thead but no tbody, as myst_parser would
# generate from a Markdown table with only a header row.
table = nodes.table()
tgroup = nodes.tgroup(cols=2)
table += tgroup
tgroup += nodes.colspec(colwidth=50)
tgroup += nodes.colspec(colwidth=50)
thead = nodes.thead()
tgroup += thead
row = nodes.row()
thead += row
entry1 = nodes.entry()
entry1 += nodes.paragraph(text='Header 1')
row += entry1
entry2 = nodes.entry()
entry2 += nodes.paragraph(text='Header 2')
row += entry2

section = nodes.section()
section += table
document += section

# This should not raise StopIteration
visitor = LaTeXFootnoteVisitor(document, [])
visitor.table_footnotes = []
table.walkabout(visitor)


@pytest.mark.sphinx('latex', testroot='latex-container')
def test_latex_container(app: SphinxTestApp) -> None:
app.build(force_all=True)
Expand Down
Loading