SF.net SVN: docutils:[10299 ] trunk/docutils
milde--- via Docutils-checkins <[email protected]> Fri, 06 Feb 2026 09:09:19 +0000
| Newsgroups | gmane.text.docutils.cvs |
|---|---|
| Message-ID | <[email protected]> |
Revision: 10299
http://sourceforge.net/p/docutils/code/10299
Author: milde
Date: 2026-02-06 09:09:19 +0000 (Fri, 06 Feb 2026)
Log Message:
-----------
Fix nodes.transition.validate_position()
Ignore title elements as well as moving or invisible elements (except comments)
when validating the restriction:
"Transitions may not begin or end a section or document."
This brings the validation in line with the documentation in
"The Docutils Document Tree" updated in commit [r10268].
Modified Paths:
--------------
trunk/docutils/HISTORY.rst
trunk/docutils/docutils/nodes.py
trunk/docutils/test/test_nodes.py
Modified: trunk/docutils/HISTORY.rst
===================================================================
--- trunk/docutils/HISTORY.rst 2026-02-06 09:09:11 UTC (rev 10298)
+++ trunk/docutils/HISTORY.rst 2026-02-06 09:09:19 UTC (rev 10299)
@@ -21,6 +21,13 @@
- Apply patch #216 by Dmitry Shachnev: fix type annotations.
+* docutils/nodes.py
+
+ - `transition.validate_position()` now checks all siblings (not just
+ neighbours and also ignores <pending>, <substitution_definition>, and
+ <target> elements when testing for a <transition> at the begin or end
+ of a <section> or the <document>.
+
* docutils/parsers/rst/directives/body.py
- Add source and line info to <rubric> elements.
Modified: trunk/docutils/docutils/nodes.py
===================================================================
--- trunk/docutils/docutils/nodes.py 2026-02-06 09:09:11 UTC (rev 10298)
+++ trunk/docutils/docutils/nodes.py 2026-02-06 09:09:19 UTC (rev 10299)
@@ -1746,31 +1746,35 @@
class transition(SubStructural, Element):
- """Transitions__ are breaks between untitled text parts.
+ """Transitions__ represent "semantic breaks".
__ https://docutils.sourceforge.io/docs/ref/doctree.html#transition
"""
+ # Sibling nodes that are ignored when validating a transition's position
+ # (titles plus moving and invisible elements except comments):
+ ignored_siblings = (decoration, meta, pending, substitution_definition,
+ subtitle, target, title)
def validate_position(self) -> None:
"""Check additional constraints on `transition` placement.
- A transition may not begin or end a section or document,
+ A transition may not begin or end section or document text,
nor may two transitions be immediately adjacent.
"""
messages = [f'Element {self.parent.starttag()} invalid:']
- predecessor = self.previous_sibling()
- if (predecessor is None # index == 0
- or isinstance(predecessor, (title, subtitle, meta, decoration))
- # A transition following these elements still counts as
- # "at the beginning of a document or section".
- ):
+ if isinstance(self.previous_sibling(), transition):
messages.append(
+ '<transition> may not directly follow another transition.')
+ i = self.parent.index(self)
+ prev_siblings = self.parent[:i]
+ if not [sibling for sibling in prev_siblings
+ if not isinstance(sibling, self.ignored_siblings)]:
+ messages.append(
'<transition> may not begin a section or document.')
- if self.parent.index(self) == len(self.parent) - 1:
+ next_siblings = self.parent[i+1:]
+ if not [sibling for sibling in next_siblings
+ if not isinstance(sibling, self.ignored_siblings)]:
messages.append('<transition> may not end a section or document.')
- if isinstance(predecessor, transition):
- messages.append(
- '<transition> may not directly follow another transition.')
if len(messages) > 1:
raise ValidationError('\n '.join(messages),
problematic_element=self)
Modified: trunk/docutils/test/test_nodes.py
===================================================================
--- trunk/docutils/test/test_nodes.py 2026-02-06 09:09:11 UTC (rev 10298)
+++ trunk/docutils/test/test_nodes.py 2026-02-06 09:09:19 UTC (rev 10299)
@@ -818,28 +818,38 @@
def test_validate_content_transition(self):
"""Test additional constraints on <transition> placement:
- Not at begin or end of a section or document,
+ Not at begin or end of section or document text,
not after another transition.
"""
transition = nodes.transition()
+ comment = nodes.comment()
paragraph = nodes.paragraph()
- section = nodes.section('', nodes.title(), transition, paragraph)
+ subdef = nodes.substitution_definition()
+ target = nodes.target()
+ title = nodes.title()
+
+ section = nodes.section('', title, comment, transition, paragraph)
+ self.assertEqual(section.validate_content(), [])
+ section = nodes.section('', title, subdef, transition, target, subdef)
with self.assertRaisesRegex(nodes.ValidationError,
- '<transition> may not begin a section '):
- section.validate_content()
- section = nodes.section('', nodes.title(), paragraph, transition)
+ '<transition> may not begin a section .*\n'
+ ' <transition> may not end a section '):
+ self.assertEqual(section.validate_content(), [])
+ section = nodes.section('', title, paragraph, transition,
+ nodes.transition(), paragraph)
with self.assertRaisesRegex(nodes.ValidationError,
- '<transition> may not end a section '):
- section.validate_content()
- section = nodes.section('', nodes.title(), paragraph,
- nodes.transition(), transition)
- with self.assertRaisesRegex(nodes.ValidationError,
'Element <section> invalid:\n'
- ' <transition> may not end .*\n'
- ' <transition> may not directly '):
- section.validate_content()
+ ' <transition> may not directly follow'):
+ self.assertEqual(section.validate_content(), [])
+ document = utils.new_document('test')
+ document.extend([nodes.decoration(), transition, nodes.meta()])
+ with self.assertRaisesRegex(nodes.ValidationError,
+ '<transition> may not begin a section .*\n'
+ ' <transition> may not end a section '):
+ self.assertEqual(section.validate_content(), [])
+
class MiscTests(unittest.TestCase):
def test_node_class_names(self):
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.