SF.net SVN: docutils:[10128 ] trunk/docutils

milde--- via Docutils-checkins <[email protected]> Tue, 13 May 2025 20:49:26 +0000
Newsgroups gmane.text.docutils.cvs
Message-ID <[email protected]>
Revision: 10128
          http://sourceforge.net/p/docutils/code/10128
Author:   milde
Date:     2025-05-13 20:49:24 +0000 (Tue, 13 May 2025)
Log Message:
-----------
Relax the transform handling transitions.

Report a WARNING instead of raising an AssertionError, if a
`<transition>` element's parent is neither `<document>` nor `<section>`.
This prevents errors with 3rd-party parsers or some (invalid)
Docutils-XML input.

Downgrade the system message reporting a `<transition>` at an
invalid position from ERROR to WARNING.

Insert the `<system_message>` *after* the transition (instead of before)
to prevent an infinite loop with `document.findall()`

Insert `<system_message>` element only if valid at this position.1
Dangling `<system_messages>` are collected and moved to a
system messages" section by the `universal.Messages` transform).

1This check can lead to overcautious behaviour if there are
 other validity violations.
 This is an implementation detail, we want to keep it simple.

Adapt "transition" transform tests. Add a test for transitions at
"strange" places (with Docutils-XML sample).

Modified Paths:
--------------
    trunk/docutils/docutils/transforms/misc.py
    trunk/docutils/test/test_transforms/test_transitions.py

Modified: trunk/docutils/docutils/transforms/misc.py
===================================================================
--- trunk/docutils/docutils/transforms/misc.py	2025-05-13 20:49:10 UTC (rev 10127)
+++ trunk/docutils/docutils/transforms/misc.py	2025-05-13 20:49:24 UTC (rev 10128)
@@ -101,20 +101,30 @@
         index = node.parent.index(node)
         previous_sibling = node.previous_sibling()
         msg = ''
-        assert isinstance(node.parent, (nodes.document, nodes.section))
-        if index == 0 or isinstance(previous_sibling, (nodes.title,
-                                                       nodes.subtitle,
-                                                       nodes.meta,
-                                                       nodes.decoration)):
+        if not isinstance(node.parent, (nodes.document, nodes.section)):
+            msg = 'Transition must be child of <document> or <section>.'
+        elif index == 0 or isinstance(previous_sibling, (nodes.title,
+                                                         nodes.subtitle,
+                                                         nodes.meta,
+                                                         nodes.decoration)):
             msg = 'Document or section may not begin with a transition.'
         elif isinstance(previous_sibling, nodes.transition):
             msg = ('At least one body element must separate transitions; '
                    'adjacent transitions are not allowed.')
         if msg:
-            # Insert before node and update index.
-            error = self.document.reporter.error(msg, base_node=node)
-            node.parent.insert(index, error)
-            index += 1
+            warning = self.document.reporter.warning(msg, base_node=node)
+            # Check, if it is valid to insert a body element
+            node.parent[index] = nodes.paragraph()
+            try:
+                node.parent.validate(recursive=False)
+            except nodes.ValidationError:
+                node.parent[index] = node
+            else:
+                node.parent[index] = node
+                node.parent.insert(index+1, warning)
+                index += 1
+        if not isinstance(node.parent, (nodes.document, nodes.section)):
+            return
         assert index < len(node.parent)
         if index != len(node.parent) - 1:
             # No need to move the node.
@@ -124,14 +134,13 @@
         # While sibling is the last node of its parent.
         while index == len(sibling.parent) - 1:
             sibling = sibling.parent
-            # If sibling is the whole document (i.e. it has no parent).
-            if sibling.parent is None:
+            if sibling.parent is None:  # sibling is the top node (document)
                 # Transition at the end of document.  Do not move the
-                # transition up, and place an error behind.
-                error = self.document.reporter.error(
-                    'Document may not end with a transition.',
-                    line=node.line)
-                node.parent.insert(node.parent.index(node) + 1, error)
+                # transition up, and place a warning behind.
+                warning = self.document.reporter.warning(
+                              'Document may not end with a transition.',
+                              base_node=node)
+                node.parent.append(warning)
                 return
             index = sibling.parent.index(sibling)
         # Remove the original transition node.

Modified: trunk/docutils/test/test_transforms/test_transitions.py
===================================================================
--- trunk/docutils/test/test_transforms/test_transitions.py	2025-05-13 20:49:10 UTC (rev 10127)
+++ trunk/docutils/test/test_transforms/test_transitions.py	2025-05-13 20:49:24 UTC (rev 10128)
@@ -18,7 +18,7 @@
     sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
 
 from docutils.frontend import get_default_settings
-from docutils.parsers.rst import Parser
+from docutils.parsers import rst, docutils_xml
 from docutils.transforms.misc import Transitions
 from docutils.transforms.universal import TestMessages
 from docutils.utils import new_document
@@ -25,9 +25,12 @@
 
 
 class TransformTestCase(unittest.TestCase):
+
+    maxDiff = None
+
     def test_transforms(self):
-        parser = Parser()
-        settings = get_default_settings(Parser)
+        parser = rst.Parser()
+        settings = get_default_settings(rst.Parser)
         settings.warning_stream = ''
         for name, (transforms, cases) in totest.items():
             for casenum, (case_input, case_expected) in enumerate(cases):
@@ -42,9 +45,29 @@
                     output = document.pformat()
                     self.assertEqual(case_expected, output)
 
+    def test_transfroms_x(self):
+        # test samples given as Docutils XML in `totest_x`
+        # (for document trees that cannot be generated from rST)
+        parser = docutils_xml.Parser()
+        settings = get_default_settings(docutils_xml.Parser)
+        settings.warning_stream = ''
+        for name, (transforms, cases) in totest_x.items():
+            for casenum, (case_input, case_expected) in enumerate(cases):
+                with self.subTest(id=f'totest[{name!r}][{casenum}]'):
+                    document = new_document('test data', settings.copy())
+                    parser.parse(case_input, document)
+                    # Don't do a ``populate_from_components()`` because that
+                    # would enable the Transformer's default transforms.
+                    document.transformer.add_transforms(transforms)
+                    document.transformer.add_transform(TestMessages)
+                    document.transformer.apply_transforms()
+                    output = document.pformat()
+                    self.assertEqual(case_expected, output)
 
-totest = {}
 
+totest = {}  # rST samples and expected pseudoXML
+totest_x = {}  # XML samples and expected pseudoXML
+
 totest['transitions'] = ((Transitions,), [
 ["""\
 Section 1
@@ -53,7 +76,7 @@
 Subsection 1
 ------------
 
-Some text.
+A transition at the end of a section is moved behind the section.
 
 ----------
 
@@ -71,7 +94,7 @@
             <title>
                 Subsection 1
             <paragraph>
-                Some text.
+                A transition at the end of a section is moved behind the section.
     <transition>
     <section ids="section-2" names="section\\ 2">
         <title>
@@ -103,47 +126,51 @@
 ["""\
 --------
 
-A section or document may not begin with a transition.
+A system message warns about invalid placement of transitions.
+""",
+"""\
+<document source="test data">
+    <transition>
+    <system_message level="2" line="1" source="test data" type="WARNING">
+        <paragraph>
+            Document or section may not begin with a transition.
+    <paragraph>
+        A system message warns about invalid placement of transitions.
+"""],
+["""\
+The DTD specifies ...
 
-The DTD specifies that two transitions may not
-be adjacent:
-
 --------
 
 --------
 
---------
-
-The DTD also specifies that a section or document
-may not end with a transition.
-
---------
+... that two transitions may not be adjacent:
 """,
 """\
 <document source="test data">
-    <system_message level="3" line="1" source="test data" type="ERROR">
-        <paragraph>
-            Document or section may not begin with a transition.
-    <transition>
     <paragraph>
-        A section or document may not begin with a transition.
-    <paragraph>
-        The DTD specifies that two transitions may not
-        be adjacent:
+        The DTD specifies ...
     <transition>
-    <system_message level="3" line="10" source="test data" type="ERROR">
-        <paragraph>
-            At least one body element must separate transitions; adjacent transitions are not allowed.
     <transition>
-    <system_message level="3" line="12" source="test data" type="ERROR">
+    <system_message level="2" line="5" source="test data" type="WARNING">
         <paragraph>
             At least one body element must separate transitions; adjacent transitions are not allowed.
-    <transition>
     <paragraph>
+        ... that two transitions may not be adjacent:
+"""],
+["""\
+The DTD also specifies that a section or document
+may not end with a transition.
+
+--------
+""",
+"""\
+<document source="test data">
+    <paragraph>
         The DTD also specifies that a section or document
         may not end with a transition.
     <transition>
-    <system_message level="3" line="17" source="test data" type="ERROR">
+    <system_message level="2" line="4" source="test data" type="WARNING">
         <paragraph>
             Document may not end with a transition.
 """],
@@ -155,13 +182,13 @@
 
 ----------
 
-The next transition is legal:
+Some text after transition.
 
-----------
-
 Section 2
 =========
 
+Some text before the transition.
+
 ----------
 """,
 """\
@@ -171,21 +198,19 @@
     <section ids="section-1" names="section\\ 1">
         <title>
             Section 1
-        <system_message level="3" line="6" source="test data" type="ERROR">
+        <transition>
+        <system_message level="2" line="6" source="test data" type="WARNING">
             <paragraph>
                 Document or section may not begin with a transition.
-        <transition>
         <paragraph>
-            The next transition is legal:
-    <transition>
+            Some text after transition.
     <section ids="section-2" names="section\\ 2">
         <title>
             Section 2
-        <system_message level="3" line="15" source="test data" type="ERROR">
-            <paragraph>
-                Document or section may not begin with a transition.
+        <paragraph>
+            Some text before the transition.
         <transition>
-        <system_message level="3" line="15" source="test data" type="ERROR">
+        <system_message level="2" line="15" source="test data" type="WARNING">
             <paragraph>
                 Document may not end with a transition.
 """],
@@ -201,11 +226,11 @@
     <paragraph>
         A paragraph and two transitions.
     <transition>
-    <system_message level="3" line="5" source="test data" type="ERROR">
+    <transition>
+    <system_message level="2" line="5" source="test data" type="WARNING">
         <paragraph>
             At least one body element must separate transitions; adjacent transitions are not allowed.
-    <transition>
-    <system_message level="3" line="5" source="test data" type="ERROR">
+    <system_message level="2" line="5" source="test data" type="WARNING">
         <paragraph>
             Document may not end with a transition.
 """],
@@ -222,11 +247,11 @@
     <paragraph>
         A paragraph, two transitions, and a blank line.
     <transition>
-    <system_message level="3" line="5" source="test data" type="ERROR">
+    <transition>
+    <system_message level="2" line="5" source="test data" type="WARNING">
         <paragraph>
             At least one body element must separate transitions; adjacent transitions are not allowed.
-    <transition>
-    <system_message level="3" line="5" source="test data" type="ERROR">
+    <system_message level="2" line="5" source="test data" type="WARNING">
         <paragraph>
             Document may not end with a transition.
 """],
@@ -237,10 +262,10 @@
 """,
 """\
 <document source="test data">
-    <system_message level="3" line="1" source="test data" type="ERROR">
+    <transition>
+    <system_message level="2" line="1" source="test data" type="WARNING">
         <paragraph>
             Document or section may not begin with a transition.
-    <transition>
     <paragraph>
         Document beginning with a transition.
 """],
@@ -254,10 +279,10 @@
 """\
 <document source="test data">
     <meta content="transition test" name="keywords">
-    <system_message level="3" line="3" source="test data" type="ERROR">
+    <transition>
+    <system_message level="2" line="3" source="test data" type="WARNING">
         <paragraph>
             Document or section may not begin with a transition.
-    <transition>
     <paragraph>
         Document beginning with a transition (meta elements don't count).
 """],
@@ -274,10 +299,10 @@
         <header>
             <paragraph>
                 a header
-    <system_message level="3" line="3" source="test data" type="ERROR">
+    <transition>
+    <system_message level="2" line="3" source="test data" type="WARNING">
         <paragraph>
             Document or section may not begin with a transition.
-    <transition>
     <paragraph>
         Document beginning with a transition (decoration elements don't count).
 """],
@@ -291,10 +316,14 @@
 
 ----------
 
-Section 2
-=========
+Implementation Detail
+=====================
 
-Some text.
+If the element containing the transition is invalid after replacing the
+transition with a body element, the system_message is appended at the end
+of the document (by the "universal.Messages" transform).
+This check can lead to overcautious behaviour if there are other
+validity violations (here: several misplaced transitions).
 """,
 """\
 <document source="test data">
@@ -301,23 +330,27 @@
     <section ids="section-1" names="section\\ 1">
         <title>
             Section 1
-        <system_message level="3" line="4" source="test data" type="ERROR">
-            <paragraph>
-                Document or section may not begin with a transition.
         <transition>
-        <system_message level="3" line="6" source="test data" type="ERROR">
-            <paragraph>
-                At least one body element must separate transitions; adjacent transitions are not allowed.
         <transition>
-        <system_message level="3" line="8" source="test data" type="ERROR">
-            <paragraph>
-                At least one body element must separate transitions; adjacent transitions are not allowed.
     <transition>
-    <section ids="section-2" names="section\\ 2">
+    <section ids="implementation-detail" names="implementation\\ detail">
         <title>
-            Section 2
+            Implementation Detail
         <paragraph>
-            Some text.
+            If the element containing the transition is invalid after replacing the
+            transition with a body element, the system_message is appended at the end
+            of the document (by the "universal.Messages" transform).
+            This check can lead to overcautious behaviour if there are other
+            validity violations (here: several misplaced transitions).
+    <system_message level="2" line="4" source="test data" type="WARNING">
+        <paragraph>
+            Document or section may not begin with a transition.
+    <system_message level="2" line="6" source="test data" type="WARNING">
+        <paragraph>
+            At least one body element must separate transitions; adjacent transitions are not allowed.
+    <system_message level="2" line="8" source="test data" type="WARNING">
+        <paragraph>
+            At least one body element must separate transitions; adjacent transitions are not allowed.
 """],
 ["""\
 ----------
@@ -326,23 +359,25 @@
 
 ----------
 """,
+# The placement of <system_message>s in this sample is an implementation
+# detail, see the remarks in the preceding test nr. 11.
 """\
 <document source="test data">
-    <system_message level="3" line="1" source="test data" type="ERROR">
+    <transition>
+    <transition>
+    <transition>
+    <system_message level="2" line="5" source="test data" type="WARNING">
         <paragraph>
+            Document may not end with a transition.
+    <system_message level="2" line="1" source="test data" type="WARNING">
+        <paragraph>
             Document or section may not begin with a transition.
-    <transition>
-    <system_message level="3" line="3" source="test data" type="ERROR">
+    <system_message level="2" line="3" source="test data" type="WARNING">
         <paragraph>
             At least one body element must separate transitions; adjacent transitions are not allowed.
-    <transition>
-    <system_message level="3" line="5" source="test data" type="ERROR">
+    <system_message level="2" line="5" source="test data" type="WARNING">
         <paragraph>
             At least one body element must separate transitions; adjacent transitions are not allowed.
-    <transition>
-    <system_message level="3" line="5" source="test data" type="ERROR">
-        <paragraph>
-            Document may not end with a transition.
 """],
 ["""\
 A paragraph.
@@ -355,7 +390,7 @@
     <paragraph>
         A paragraph.
     <transition>
-    <system_message level="3" line="3" source="test data" type="ERROR">
+    <system_message level="2" line="3" source="test data" type="WARNING">
         <paragraph>
             Document may not end with a transition.
 """],
@@ -362,5 +397,47 @@
 ])
 
 
+totest_x['transitions extra'] = ((Transitions,), [
+# currently, a <transition> is only valid inside <document> or <section>
+["""\
+<document>
+  <paragraph>Some text.</paragraph>
+  <transition />
+  <paragraph>Some text.</paragraph>
+  <block_quote>
+    <paragraph>Some text.</paragraph>
+    <transition />
+    <paragraph>Some text.</paragraph>
+  </block_quote>
+  <paragraph>A <transition /> in a paragraph.</paragraph>
+</document>
+""",
+"""\
+<document source="test data">
+    <paragraph>
+        Some text.
+    <transition>
+    <paragraph>
+        Some text.
+    <block_quote>
+        <paragraph>
+            Some text.
+        <transition>
+        <system_message level="2" line="7" source="test data" type="WARNING">
+            <paragraph>
+                Transition must be child of <document> or <section>.
+        <paragraph>
+            Some text.
+    <paragraph>
+        A \n\
+        <transition>
+         in a paragraph.
+    <system_message level="2" line="10" source="test data" type="WARNING">
+        <paragraph>
+            Transition must be child of <document> or <section>.
+"""],
+])
+
+
 if __name__ == '__main__':
     unittest.main()

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.