Skip to content

Commit 6157651

Browse files
committed
Fix duplicated labels in TeX output (#11093)
1 parent 5a6b2b1 commit 6157651

File tree

8 files changed

+111
-1
lines changed

8 files changed

+111
-1
lines changed

sphinx/writers/latex.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -1499,7 +1499,26 @@ def add_target(id: str) -> None:
14991499
pass
15001500
else:
15011501
add_target(node['refid'])
1502-
for id in node['ids']:
1502+
# Temporary fix for https://github.com/sphinx-doc/sphinx/issues/11093
1503+
# TODO: investigate if a more elegant solution exists (see comments of #11093)
1504+
if node.get('ismod', False):
1505+
# Detect if the previous nodes are label targets. If so, remove
1506+
# the refid thereof from node['ids'] to avoid duplicated ids.
1507+
def has_dup_label(sib: Element | None) -> bool:
1508+
return isinstance(sib, nodes.target) and sib.get('refid') in node['ids']
1509+
1510+
prev: Element | None = get_prev_node(node)
1511+
if has_dup_label(prev):
1512+
ids = node['ids'][:] # copy to avoid side-effects
1513+
while has_dup_label(prev):
1514+
ids.remove(prev['refid'])
1515+
prev = get_prev_node(prev)
1516+
else:
1517+
ids = iter(node['ids']) # read-only iterator
1518+
else:
1519+
ids = iter(node['ids']) # read-only iterator
1520+
1521+
for id in ids:
15031522
add_target(id)
15041523

15051524
def depart_target(self, node: Element) -> None:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"""docstring"""
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"""docstring"""
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"""docstring"""
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"""docstring"""
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import os
2+
import sys
3+
4+
sys.path.insert(0, os.path.abspath('.'))
5+
6+
extensions = ['sphinx.ext.autodoc']
7+
8+
nitpicky = True
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
latex-labels-before-module
2+
==========================
3+
4+
.. _label_1a:
5+
.. _label_1b:
6+
7+
.. module:: module1
8+
9+
text
10+
11+
.. _label_2:
12+
13+
.. module:: module2a
14+
15+
text
16+
17+
.. module:: module2b
18+
19+
text
20+
21+
.. _label_3:
22+
23+
.. module:: module3
24+
25+
text
26+
27+
.. _label_auto_1a:
28+
.. _label_auto_1b:
29+
30+
.. automodule:: automodule1
31+
32+
text
33+
34+
.. _label_auto_2:
35+
36+
.. automodule:: automodule2a
37+
38+
text
39+
40+
.. automodule:: automodule2b
41+
42+
text
43+
44+
.. _label_auto_3:
45+
46+
.. automodule:: automodule3
47+
48+
text

tests/test_build_latex.py

+27
Original file line numberDiff line numberDiff line change
@@ -1708,3 +1708,30 @@ def test_copy_images(app, status, warning):
17081708
'rimg.png',
17091709
'testimäge.png',
17101710
}
1711+
1712+
1713+
@pytest.mark.sphinx('latex', testroot='latex-labels-before-module')
1714+
def test_duplicated_labels_before_module(app, status, warning):
1715+
app.build()
1716+
content: str = (app.outdir / 'python.tex').read_text()
1717+
1718+
def count_label(name):
1719+
text = r'\phantomsection\label{\detokenize{%s}}' % name
1720+
return content.count(text)
1721+
1722+
pattern = r'\\phantomsection\\label\{\\detokenize\{index:label-(?:auto-)?\d+[a-z]*}}'
1723+
expect_labels = {match.group() for match in re.finditer(pattern, content)}
1724+
result_labels = set()
1725+
1726+
# iterate over the (explicit) labels in the corresponding index.rst
1727+
for rst_label_name in {
1728+
'label_1a', 'label_1b', 'label_2', 'label_3',
1729+
'label_auto_1a', 'label_auto_1b', 'label_auto_2', 'label_auto_3',
1730+
}:
1731+
tex_label_name = 'index:' + rst_label_name.replace('_', '-')
1732+
tex_label_code = r'\phantomsection\label{\detokenize{%s}}' % tex_label_name
1733+
assert content.count(tex_label_code) == 1, f'duplicated label: {tex_label_name!r}'
1734+
result_labels.add(tex_label_code)
1735+
1736+
# sort the labels for a better visual diff, if any
1737+
assert sorted(result_labels) == sorted(expect_labels)

0 commit comments

Comments
 (0)