Skip to content

Commit 601642b

Browse files
drammockjarrodmillmanlarsoner
authored
Use node.findall if available (docutils 18.x) (#403)
* use node.findall if available (docutils 18.x) * fix test_reference * keep test_reference backwards-compatible * Add comments about when triage becomes unnecessary/obsolete Co-authored-by: Jarrod Millman <[email protected]> * whitespace * fix docutils version as noted in docstring Co-authored-by: Jarrod Millman <[email protected]> Co-authored-by: Eric Larson <[email protected]>
1 parent 85a4327 commit 601642b

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

numpydoc/numpydoc.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@
4343
HASH_LEN = 12
4444

4545

46+
def _traverse_or_findall(node, condition, **kwargs):
47+
"""Triage node.traverse (docutils <0.18.1) vs node.findall.
48+
49+
TODO: This check can be removed when the minimum supported docutils version
50+
for numpydoc is docutils>=0.18.1
51+
"""
52+
return (
53+
node.findall(condition, **kwargs)
54+
if hasattr(node, "findall")
55+
else node.traverse(condition, **kwargs)
56+
)
57+
58+
4659
def rename_references(app, what, name, obj, options, lines):
4760
# decorate reference numbers so that there are no duplicates
4861
# these are later undecorated in the doctree, in relabel_references
@@ -81,8 +94,12 @@ def is_docstring_section(node):
8194
return False
8295

8396
sibling_sections = itertools.chain(
84-
section_node.traverse(
85-
is_docstring_section, include_self=True, descend=False, siblings=True
97+
_traverse_or_findall(
98+
section_node,
99+
is_docstring_section,
100+
include_self=True,
101+
descend=False,
102+
siblings=True,
86103
)
87104
)
88105
for sibling_section in sibling_sections:
@@ -101,7 +118,7 @@ def is_docstring_section(node):
101118

102119
def relabel_references(app, doc):
103120
# Change 'hash-ref' to 'ref' in label text
104-
for citation_node in doc.traverse(citation):
121+
for citation_node in _traverse_or_findall(doc, citation):
105122
if not _is_cite_in_numpydoc_docstring(citation_node):
106123
continue
107124
label_node = citation_node[0]
@@ -121,18 +138,18 @@ def matching_pending_xref(node):
121138
and node[0].astext() == f"[{ref_text}]"
122139
)
123140

124-
for xref_node in ref.parent.traverse(matching_pending_xref):
141+
for xref_node in _traverse_or_findall(ref.parent, matching_pending_xref):
125142
xref_node.replace(xref_node[0], Text(f"[{new_text}]"))
126143
ref.replace(ref_text, new_text.copy())
127144

128145

129146
def clean_backrefs(app, doc, docname):
130147
# only::latex directive has resulted in citation backrefs without reference
131148
known_ref_ids = set()
132-
for ref in doc.traverse(reference, descend=True):
149+
for ref in _traverse_or_findall(doc, reference, descend=True):
133150
for id_ in ref["ids"]:
134151
known_ref_ids.add(id_)
135-
for citation_node in doc.traverse(citation, descend=True):
152+
for citation_node in _traverse_or_findall(doc, citation, descend=True):
136153
# remove backrefs to non-existent refs
137154
citation_node["backrefs"] = [
138155
id_ for id_ in citation_node["backrefs"] if id_ in known_ref_ids

numpydoc/tests/test_full.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import os.path as op
22
import re
33
import shutil
4+
from packaging import version
45

56
import pytest
67
import sphinx
78
from sphinx.application import Sphinx
89
from sphinx.util.docutils import docutils_namespace
10+
from docutils import __version__ as docutils_version
911

1012

1113
# Test framework adapted from sphinx-gallery (BSD 3-clause)
@@ -89,7 +91,14 @@ def test_reference(sphinx_app, html_file, expected_length):
8991
with open(op.join(out_dir, *html_file)) as fid:
9092
html = fid.read()
9193

92-
reference_list = re.findall(r'<a class="fn-backref" href="\#id\d+">(.*)<\/a>', html)
94+
# TODO: This check can be removed when the minimum supported docutils version
95+
# for numpydoc is docutils>=0.18
96+
pattern = (
97+
'role="doc-backlink"'
98+
if version.parse(docutils_version) >= version.parse("0.18")
99+
else 'class="fn-backref"'
100+
)
101+
reference_list = re.findall(rf'<a {pattern} href="\#id\d+">(.*)<\/a>', html)
93102

94103
assert len(reference_list) == expected_length
95104
for ref in reference_list:

0 commit comments

Comments
 (0)