Skip to content

Commit 77c4393

Browse files
committed
fix(docutils): Use extend() instead of direct node.children assignment
Direct assignment to node.children bypasses docutils' setup_child() mechanism which maintains parent-child relationships, document references, and source tracking. Replace with clear() + extend() pattern to properly invoke the docutils protocol.
1 parent f879f92 commit 77c4393

2 files changed

Lines changed: 58 additions & 2 deletions

File tree

docs/_ext/argparse_exemplar.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,8 @@ def process_node(
823823
else:
824824
new_children.append(child)
825825
if children_changed:
826-
node.children = new_children
826+
node.children.clear()
827+
node.extend(new_children)
827828

828829
return node
829830

@@ -1133,7 +1134,8 @@ def _extract_sections_from_container(
11331134
remaining_children.append(child)
11341135

11351136
# Update container with remaining children only
1136-
container.children = remaining_children
1137+
container.children.clear()
1138+
container.extend(remaining_children)
11371139

11381140
return container, extracted_sections
11391141

tests/docs/_ext/test_argparse_exemplar.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
import pytest
1616
from argparse_exemplar import ( # type: ignore[import-not-found]
1717
ExemplarConfig,
18+
_extract_sections_from_container,
1819
_is_examples_section,
1920
_is_usage_block,
2021
_reorder_nodes,
2122
is_base_examples_term,
2223
is_examples_term,
2324
make_section_id,
2425
make_section_title,
26+
process_node,
2527
transform_definition_list,
2628
)
2729
from docutils import nodes
@@ -1017,3 +1019,55 @@ def test_transform_definition_list_with_custom_config() -> None:
10171019
assert code_blocks[0].astext() == "> cmd1" # Custom prefix
10181020
assert code_blocks[0]["language"] == "bash"
10191021
assert "highlight-bash" in code_blocks[0]["classes"]
1022+
1023+
1024+
# --- Parent reference maintenance tests ---
1025+
1026+
1027+
def test_process_node_maintains_parent_reference() -> None:
1028+
"""Verify process_node maintains parent references after child replacement.
1029+
1030+
When children are replaced in a container node, the docutils protocol
1031+
requires using extend() rather than direct assignment to node.children
1032+
to ensure parent-child relationships are properly maintained.
1033+
"""
1034+
# Create a container with ANSI-encoded text children
1035+
container = nodes.container()
1036+
text_with_ansi = nodes.Text("\033[32mgreen text\033[0m")
1037+
container += text_with_ansi
1038+
1039+
# Process the node (will strip ANSI and replace children)
1040+
process_node(container)
1041+
1042+
# Verify children have correct parent reference
1043+
for child in container.children:
1044+
assert child.parent is container, (
1045+
f"Child {child!r} should have parent reference to container"
1046+
)
1047+
1048+
1049+
def test_extract_sections_maintains_parent_reference() -> None:
1050+
"""Verify _extract_sections_from_container maintains parent references.
1051+
1052+
When remaining children are reassigned to the container, the docutils
1053+
protocol requires using extend() to maintain parent-child relationships.
1054+
"""
1055+
from sphinx_argparse_neo.nodes import argparse_program
1056+
1057+
# Create container with mixed children
1058+
container = argparse_program()
1059+
para = nodes.paragraph(text="Description")
1060+
section = nodes.section()
1061+
section["ids"] = ["examples"]
1062+
1063+
container += para
1064+
container += section
1065+
1066+
# Extract sections
1067+
modified, _extracted = _extract_sections_from_container(container)
1068+
1069+
# Verify remaining children have correct parent reference
1070+
for child in modified.children:
1071+
assert child.parent is modified, (
1072+
f"Child {child!r} should have parent reference to modified container"
1073+
)

0 commit comments

Comments
 (0)