Skip to content

Commit 53efcb0

Browse files
committed
Ensure reference renaming is parallel-safe
This prefixes each reference with a token indicative of which docstring it belongs to and then relabels the text of the reference once the doctree is compiled
1 parent e2c4c97 commit 53efcb0

File tree

1 file changed

+33
-9
lines changed

1 file changed

+33
-9
lines changed

Diff for: numpydoc/numpydoc.py

+33-9
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import sphinx
2525
import inspect
2626
import collections
27+
import hashlib
2728

2829
if sphinx.__version__ < '1.0.1':
2930
raise RuntimeError("Sphinx 1.0.1 or newer is required")
@@ -36,9 +37,9 @@
3637
sixu = lambda s: unicode(s, 'unicode_escape')
3738

3839

39-
def rename_references(app, what, name, obj, options, lines,
40-
reference_offset=[0]):
41-
# replace reference numbers so that there are no duplicates
40+
def rename_references(app, what, name, obj, options, lines):
41+
# decorate reference numbers so that there are no duplicates
42+
# these are later undecorated in the doctree, in relabel_references
4243
references = set()
4344
for line in lines:
4445
line = line.strip()
@@ -48,19 +49,41 @@ def rename_references(app, what, name, obj, options, lines,
4849
references.add(m.group(1))
4950

5051
if references:
51-
for r in references:
52-
if r.isdigit():
53-
new_r = sixu("R%d") % (reference_offset[0] + int(r))
54-
else:
55-
new_r = sixu("%s%d") % (r, reference_offset[0])
52+
# we use a hash to mangle the reference name to avoid invalid names
53+
sha = hashlib.sha256()
54+
sha.update(name.encode('utf8'))
55+
prefix = 'R' + sha.hexdigest()
5656

57+
for r in references:
58+
new_r = prefix + '-' + r
5759
for i, line in enumerate(lines):
5860
lines[i] = lines[i].replace(sixu('[%s]_') % r,
5961
sixu('[%s]_') % new_r)
6062
lines[i] = lines[i].replace(sixu('.. [%s]') % r,
6163
sixu('.. [%s]') % new_r)
6264

63-
reference_offset[0] += len(references)
65+
66+
def relabel_references(app, doc):
67+
# Change name_ref to ref in label text
68+
from docutils.nodes import citation, Text
69+
from sphinx.addnodes import pending_xref
70+
for citation_node in doc.traverse(citation):
71+
label_node = citation_node[0]
72+
new_text = Text(citation_node['names'][0].split('-')[-1])
73+
label_node.replace(label_node[0], new_text)
74+
75+
for id in citation_node['backrefs']:
76+
ref = doc.ids[id]
77+
ref_text = ref[0]
78+
79+
# Sphinx has created pending_xref nodes with [reftext] text.
80+
def matching_pending_xref(node):
81+
return (isinstance(node, pending_xref) and
82+
node[0].astext() == '[%s]' % ref_text)
83+
84+
for xref_node in ref.parent.traverse():
85+
xref_node.replace(xref_node[0], Text('[%s]' % new_text))
86+
ref.replace(ref_text, new_text.copy())
6487

6588

6689
DEDUPLICATION_TAG = ['..', ' processed by numpydoc']
@@ -137,6 +160,7 @@ def setup(app, get_doc_object_=get_doc_object):
137160

138161
app.connect('autodoc-process-docstring', mangle_docstrings)
139162
app.connect('autodoc-process-signature', mangle_signature)
163+
app.connect('doctree-read', relabel_references)
140164
app.add_config_value('numpydoc_edit_link', None, False)
141165
app.add_config_value('numpydoc_use_plots', None, False)
142166
app.add_config_value('numpydoc_show_class_members', True, True)

0 commit comments

Comments
 (0)