Skip to content

Commit bcd4b3c

Browse files
🐛 functions: Fix broken parent-child node relationships (#1564)
The find_and_replace_node_content function breaks parent-child node relationships since it does not go through the docutils tools for assigning node children. Utilize one of the built-in docutils methods [1] for this since it correctly assigns the parent-child relationship of each node. Notably, the sphinxcontrib-spelling [2] extension is broken by this broken parent-child relationship since if something is misspelled it tries to identify which source line was impacted. When a node is added as a child, setup_child(...) [3] code fixes up the source/line location with the parent's information if needed. A test has been added at the top-level which covers every single test run by adding a build hook into the "doctree-resolved" hook. This test is fairly simple - check each node (outside of the top node) and validate that it has a parent assigned. [1] https://github.com/docutils/docutils/blob/4e912fe000b1b6dc1466c0ad1c3d1787d40fd96d/docutils/docutils/nodes.py#L788-L794 [2] https://pypi.org/project/sphinxcontrib-spelling/ [3] https://github.com/docutils/docutils/blob/4e912fe000b1b6dc1466c0ad1c3d1787d40fd96d/docutils/docutils/nodes.py#L150-L157
1 parent 954021e commit bcd4b3c

3 files changed

Lines changed: 24 additions & 1 deletion

File tree

docs/changelog.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ Bug fixes
5656
of need load order (e.g. when using :ref:`needs_external_needs`)
5757
(:issue:`1371`)
5858

59+
- 🐛 Fix parent-child relationship of newly created nodes for needs. This
60+
fixes interoperability with Sphinx extensions that look up source lines,
61+
like sphinxcontrib-spelling (:issue:`1564`)
62+
5963
.. _`release:8.1.1`:
6064

6165
8.1.1

sphinx_needs/functions/functions.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,10 @@ def find_and_replace_node_content(
263263
for child in node.children:
264264
new_child = find_and_replace_node_content(child, env, need)
265265
new_children.append(new_child)
266-
node.children = new_children
266+
267+
node.children = new_children
268+
for subchild in node.children:
269+
node.setup_child(subchild)
267270
else:
268271
node = nodes.Text(new_text)
269272
return node
@@ -275,7 +278,10 @@ def find_and_replace_node_content(
275278
continue
276279
new_child = find_and_replace_node_content(child, env, need)
277280
new_children.append(new_child)
281+
278282
node.children = new_children
283+
for subchild in node.children:
284+
node.setup_child(subchild)
279285
return node
280286

281287

tests/conftest.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,13 @@ def sphinx_test_tempdir(request) -> Path:
255255
return sphinx_test_tempdir
256256

257257

258+
def _check_parent_child(app: Sphinx, doctree: document, docname: str):
259+
for idx, node in enumerate(doctree.findall()):
260+
if idx == 0:
261+
continue
262+
assert node.parent is not None
263+
264+
258265
@pytest.fixture(scope="function")
259266
def test_app(make_app, sphinx_test_tempdir, request):
260267
"""
@@ -333,6 +340,12 @@ def test_app(make_app, sphinx_test_tempdir, request):
333340
# ``test_js`` behaves like a method.
334341
app.test_js = test_js.__get__(app, Sphinx)
335342

343+
# Check created all parent-child node relationships after any other
344+
# code within the doctree-resolved event by setting the priority to 999.
345+
# Placing this test here provides coverage for quite a few tests in
346+
# the suite, and hopefully will test any future features.
347+
app.connect("doctree-resolved", _check_parent_child, priority=999)
348+
336349
yield app
337350

338351
app.cleanup()

0 commit comments

Comments
 (0)