Add `allow_section_headings` to SphinxDirective parsing methods (#12503)

This commit is contained in:
Adam Turner 2024-07-02 23:53:58 +01:00 committed by GitHub
parent 9276639fa6
commit 24a0385997
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 56 additions and 16 deletions

View File

@ -278,7 +278,8 @@ class ObjectDescription(SphinxDirective, Generic[ObjDescT]):
# needed for association of version{added,changed} directives
self.env.temp_data['object'] = self.names[0]
self.before_content()
content_node = addnodes.desc_content('', *self.parse_content_to_nodes())
content_children = self.parse_content_to_nodes(allow_section_headings=True)
content_node = addnodes.desc_content('', *content_children)
node.append(content_node)
self.transform_content(content_node)
self.env.app.emit('object-description-transform',

View File

@ -311,7 +311,7 @@ class JSModule(SphinxDirective):
self.env.ref_context['js:module'] = mod_name
no_index = 'no-index' in self.options or 'noindex' in self.options
content_nodes = self.parse_content_to_nodes()
content_nodes = self.parse_content_to_nodes(allow_section_headings=True)
ret: list[Node] = []
if not no_index:

View File

@ -416,7 +416,7 @@ class PyModule(SphinxDirective):
no_index = 'no-index' in self.options or 'noindex' in self.options
self.env.ref_context['py:module'] = modname
content_nodes = self.parse_content_to_nodes()
content_nodes = self.parse_content_to_nodes(allow_section_headings=True)
ret: list[Node] = []
if not no_index:

View File

@ -405,7 +405,9 @@ class Glossary(SphinxDirective):
if definition:
offset = definition.items[0][1]
definition_nodes = nested_parse_to_nodes(self.state, definition, offset=offset)
definition_nodes = nested_parse_to_nodes(
self.state, definition, offset=offset, allow_section_headings=False,
)
else:
definition_nodes = []
termnodes.append(nodes.definition('', *definition_nodes))

View File

@ -409,7 +409,8 @@ class Autosummary(SphinxDirective):
for text in column_texts:
vl = StringList([text], f'{source}:{line}:<autosummary>')
with switch_source_input(self.state, vl):
col_nodes = nested_parse_to_nodes(self.state, vl)
col_nodes = nested_parse_to_nodes(self.state, vl,
allow_section_headings=False)
if col_nodes and isinstance(col_nodes[0], nodes.paragraph):
node = col_nodes[0]
else:

View File

@ -47,7 +47,7 @@ class IfConfig(SphinxDirective):
node.document = self.state.document
self.set_source_info(node)
node['expr'] = self.arguments[0]
node += self.parse_content_to_nodes()
node += self.parse_content_to_nodes(allow_section_headings=True)
return [node]

View File

@ -434,21 +434,49 @@ class SphinxDirective(Directive):
return f'<unknown>:{line}'
return ''
def parse_content_to_nodes(self) -> list[Node]:
"""Parse the directive's content into nodes."""
return nested_parse_to_nodes(self.state, self.content, offset=self.content_offset)
def parse_content_to_nodes(self, allow_section_headings: bool = False) -> list[Node]:
"""Parse the directive's content into nodes.
def parse_text_to_nodes(self, text: str = '', /, *, offset: int = -1) -> list[Node]:
:param allow_section_headings:
Are titles (sections) allowed in the directive's content?
Note that this option bypasses Docutils' usual checks on
doctree structure, and misuse of this option can lead to
an incoherent doctree. In Docutils, section nodes should
only be children of ``Structural`` nodes, which includes
``document``, ``section``, and ``sidebar`` nodes.
"""
return nested_parse_to_nodes(
self.state,
self.content,
offset=self.content_offset,
allow_section_headings=allow_section_headings,
)
def parse_text_to_nodes(
self, text: str = '', /, *, offset: int = -1, allow_section_headings: bool = False,
) -> list[Node]:
"""Parse *text* into nodes.
:param text:
Text, in string form. ``StringList`` is also accepted.
:param allow_section_headings:
Are titles (sections) allowed in *text*?
Note that this option bypasses Docutils' usual checks on
doctree structure, and misuse of this option can lead to
an incoherent doctree. In Docutils, section nodes should
only be children of ``Structural`` nodes, which includes
``document``, ``section``, and ``sidebar`` nodes.
:param offset:
The offset of the content.
"""
if offset == -1:
offset = self.content_offset
return nested_parse_to_nodes(self.state, text, offset=offset)
return nested_parse_to_nodes(
self.state,
text,
offset=offset,
allow_section_headings=allow_section_headings,
)
def parse_inline(
self, text: str, *, lineno: int = -1,

View File

@ -334,7 +334,7 @@ def nested_parse_with_titles(state: RSTState, content: StringList, node: Node,
context, such as docstrings.
This function is retained for compatability and will be deprecated in
Sphinx 8. Prefer ``parse_block_text()``.
Sphinx 8. Prefer ``nested_parse_to_nodes()``.
"""
with _fresh_title_style_context(state):
ret = state.nested_parse(content, content_offset, node, match_titles=True)

View File

@ -20,6 +20,7 @@ def nested_parse_to_nodes(
*,
source: str = '<generated text>',
offset: int = 0,
allow_section_headings: bool = True,
keep_title_context: bool = False,
) -> list[nodes.Node]: # Element | nodes.Text
"""Parse *text* into nodes.
@ -32,6 +33,13 @@ def nested_parse_to_nodes(
The text's source, used when creating a new ``StringList``.
:param offset:
The offset of the content.
:param allow_section_headings:
Are titles (sections) allowed in *text*?
Note that this option bypasses Docutils' usual checks on
doctree structure, and misuse of this option can lead to
an incoherent doctree. In Docutils, section nodes should
only be children of ``Structural`` nodes, which includes
``document``, ``section``, and ``sidebar`` nodes.
:param keep_title_context:
If this is False (the default), then *content* is parsed as if it were
an independent document, meaning that title decorations (e.g. underlines)
@ -49,10 +57,10 @@ def nested_parse_to_nodes(
node.document = document
if keep_title_context:
state.nested_parse(content, offset, node, match_titles=True)
state.nested_parse(content, offset, node, match_titles=allow_section_headings)
else:
with _fresh_title_style_context(state):
state.nested_parse(content, offset, node, match_titles=True)
state.nested_parse(content, offset, node, match_titles=allow_section_headings)
return node.children

View File

@ -100,7 +100,7 @@ def test_sphinx_directive_parse_content_to_nodes():
content = 'spam\n====\n\nEggs! *Lobster thermidor.*'
directive.content = StringList(content.split('\n'), source='<source>')
parsed = directive.parse_content_to_nodes()
parsed = directive.parse_content_to_nodes(allow_section_headings=True)
assert len(parsed) == 1
node = parsed[0]
assert isinstance(node, nodes.section)
@ -115,7 +115,7 @@ def test_sphinx_directive_parse_text_to_nodes():
directive = make_directive(env=SimpleNamespace())
content = 'spam\n====\n\nEggs! *Lobster thermidor.*'
parsed = directive.parse_text_to_nodes(content)
parsed = directive.parse_text_to_nodes(content, allow_section_headings=True)
assert len(parsed) == 1
node = parsed[0]
assert isinstance(node, nodes.section)