Skip to content

Commit

Permalink
Fix duplicated labels in TeX output (#11093)
Browse files Browse the repository at this point in the history
  • Loading branch information
picnixz committed Apr 17, 2023
1 parent 5a6b2b1 commit 6157651
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 1 deletion.
21 changes: 20 additions & 1 deletion sphinx/writers/latex.py
Expand Up @@ -1499,7 +1499,26 @@ def add_target(id: str) -> None:
pass
else:
add_target(node['refid'])
for id in node['ids']:
# Temporary fix for https://github.com/sphinx-doc/sphinx/issues/11093
# TODO: investigate if a more elegant solution exists (see comments of #11093)
if node.get('ismod', False):
# Detect if the previous nodes are label targets. If so, remove
# the refid thereof from node['ids'] to avoid duplicated ids.
def has_dup_label(sib: Element | None) -> bool:
return isinstance(sib, nodes.target) and sib.get('refid') in node['ids']

prev: Element | None = get_prev_node(node)
if has_dup_label(prev):
ids = node['ids'][:] # copy to avoid side-effects
while has_dup_label(prev):
ids.remove(prev['refid'])
prev = get_prev_node(prev)
else:
ids = iter(node['ids']) # read-only iterator
else:
ids = iter(node['ids']) # read-only iterator

for id in ids:
add_target(id)

def depart_target(self, node: Element) -> None:
Expand Down
2 changes: 2 additions & 0 deletions tests/roots/test-latex-labels-before-module/automodule1.py
@@ -0,0 +1,2 @@
"""docstring"""

2 changes: 2 additions & 0 deletions tests/roots/test-latex-labels-before-module/automodule2a.py
@@ -0,0 +1,2 @@
"""docstring"""

2 changes: 2 additions & 0 deletions tests/roots/test-latex-labels-before-module/automodule2b.py
@@ -0,0 +1,2 @@
"""docstring"""

2 changes: 2 additions & 0 deletions tests/roots/test-latex-labels-before-module/automodule3.py
@@ -0,0 +1,2 @@
"""docstring"""

8 changes: 8 additions & 0 deletions tests/roots/test-latex-labels-before-module/conf.py
@@ -0,0 +1,8 @@
import os
import sys

sys.path.insert(0, os.path.abspath('.'))

extensions = ['sphinx.ext.autodoc']

nitpicky = True
48 changes: 48 additions & 0 deletions tests/roots/test-latex-labels-before-module/index.rst
@@ -0,0 +1,48 @@
latex-labels-before-module
==========================

.. _label_1a:
.. _label_1b:

.. module:: module1

text

.. _label_2:

.. module:: module2a

text

.. module:: module2b

text

.. _label_3:

.. module:: module3

text

.. _label_auto_1a:
.. _label_auto_1b:

.. automodule:: automodule1

text

.. _label_auto_2:

.. automodule:: automodule2a

text

.. automodule:: automodule2b

text

.. _label_auto_3:

.. automodule:: automodule3

text
27 changes: 27 additions & 0 deletions tests/test_build_latex.py
Expand Up @@ -1708,3 +1708,30 @@ def test_copy_images(app, status, warning):
'rimg.png',
'testimäge.png',
}


@pytest.mark.sphinx('latex', testroot='latex-labels-before-module')
def test_duplicated_labels_before_module(app, status, warning):
app.build()
content: str = (app.outdir / 'python.tex').read_text()

def count_label(name):
text = r'\phantomsection\label{\detokenize{%s}}' % name
return content.count(text)

pattern = r'\\phantomsection\\label\{\\detokenize\{index:label-(?:auto-)?\d+[a-z]*}}'
expect_labels = {match.group() for match in re.finditer(pattern, content)}
result_labels = set()

# iterate over the (explicit) labels in the corresponding index.rst
for rst_label_name in {
'label_1a', 'label_1b', 'label_2', 'label_3',
'label_auto_1a', 'label_auto_1b', 'label_auto_2', 'label_auto_3',
}:
tex_label_name = 'index:' + rst_label_name.replace('_', '-')
tex_label_code = r'\phantomsection\label{\detokenize{%s}}' % tex_label_name
assert content.count(tex_label_code) == 1, f'duplicated label: {tex_label_name!r}'
result_labels.add(tex_label_code)

# sort the labels for a better visual diff, if any
assert sorted(result_labels) == sorted(expect_labels)

0 comments on commit 6157651

Please sign in to comment.