|
From: <mi...@us...> - 2024-06-05 10:22:30
|
Revision: 9723
http://sourceforge.net/p/docutils/code/9723
Author: milde
Date: 2024-06-05 10:22:23 +0000 (Wed, 05 Jun 2024)
Log Message:
-----------
Doctree validation: custom exception `nodes.ValidationError`.
The custom `ValidationError` has the additional attribute "problematic
element" that holds the element close to the violation ("self", for
invalid XML attributes and spurious text or a child node that does not
fit in the content model).
Use for warnings with source-code line
in `transforms.universal.Validate`
Revise validation error messages.
Modified Paths:
--------------
trunk/docutils/HISTORY.txt
trunk/docutils/docutils/nodes.py
trunk/docutils/docutils/transforms/universal.py
trunk/docutils/test/test_nodes.py
Modified: trunk/docutils/HISTORY.txt
===================================================================
--- trunk/docutils/HISTORY.txt 2024-05-31 14:42:06 UTC (rev 9722)
+++ trunk/docutils/HISTORY.txt 2024-06-05 10:22:23 UTC (rev 9723)
@@ -24,12 +24,14 @@
* docutils/nodes.py
- - Raise ValueError if the "rawsource" argument in `Element.__init__()`
+ - Raise TypeError if the "rawsource" argument in `Element.__init__()`
is an `Element` instance.
Catches errors like ``nodes.hint(nodes.paragraph())``.
- New element category classes `SubStructural` and `PureTextElement`.
- Fix element categories.
- - New method `Element.validate()` (work in progress).
+ - New method `Element.validate()`: raise `nodes.ValidationError` if
+ the element does not comply with the "Docutils Document Model"
+ (work in progress).
- New "attribute validating functions"
convert string representations to correct data type,
normalize values,
Modified: trunk/docutils/docutils/nodes.py
===================================================================
--- trunk/docutils/docutils/nodes.py 2024-05-31 14:42:06 UTC (rev 9722)
+++ trunk/docutils/docutils/nodes.py 2024-06-05 10:22:23 UTC (rev 9723)
@@ -515,7 +515,7 @@
NOTE: some elements do not set this value (default '').
"""
if isinstance(rawsource, Element):
- raise ValueError('First argument "rawsource" must be a string.')
+ raise TypeError('First argument "rawsource" must be a string.')
self.children = []
"""List of child nodes (elements and/or `Text`)."""
@@ -1120,7 +1120,7 @@
Convert string values to expected datatype.
Normalize values.
- Raise `ValueError` for invalid attributes or attribute values.
+ Raise `ValidationError` for invalid attributes or attribute values.
Provisional.
"""
@@ -1129,7 +1129,7 @@
if key.startswith('internal:'):
continue # see docs/user/config.html#expose-internals
if key not in self.valid_attributes:
- va = ' '.join(self.valid_attributes)
+ va = '", "'.join(self.valid_attributes)
messages.append(f'Attribute "{key}" not one of "{va}".')
continue
try:
@@ -1136,23 +1136,28 @@
self.attributes[key] = ATTRIBUTE_VALIDATORS[key](value)
except (ValueError, TypeError, KeyError) as e:
messages.append(
- f'Attribute "{key}" has invalid value "{value}".\n{e}')
+ f'Attribute "{key}" has invalid value "{value}".\n {e}')
if messages:
- raise ValueError('\n'.join(messages))
+ raise ValidationError(f'Element {self.starttag()} invalid:\n '
+ + '\n '.join(messages),
+ problematic_element=self)
def validate(self):
- """Validate element against the Docutils Document Model ("doctree").
+ """Validate Docutils Document Tree element ("doctree").
- Raise ValueError if there are violations.
+ Raise ValidationError if there are violations.
+ See `The Docutils Document Tree`__ for details of the
+ Docutils Document Model.
+
+ __ https://docutils.sourceforge.io/docs/ref/doctree.html
+
Provisional (work in progress).
"""
+ self.validate_attributes()
+
+ # test number of children
messages = []
- try:
- self.validate_attributes()
- except ValueError as e:
- messages.append(str(e))
- # test number of children
n_min, n_max = self.valid_len
if len(self.children) < n_min:
messages.append(f'Expects at least {n_min} children, '
@@ -1212,7 +1217,7 @@
class SubStructural(SubRoot):
"""`Structural subelements`__ are children of structural elements.
- Most Structural elements accept only some of the SubStructural elements.
+ Most Structural elements accept only specific `SubStructural` elements.
__ https://docutils.sourceforge.io/docs/ref/doctree.html
#structural-subelements
@@ -2518,6 +2523,16 @@
self.parent = self.parent_stack.pop()
+# Custom Exceptions
+# =================
+
+class ValidationError(ValueError):
+ """Invalid Docutils Document Tree Element."""
+ def __init__(self, msg, problematic_element=None):
+ super().__init__(msg)
+ self.problematic_element = problematic_element
+
+
class TreePruningException(Exception):
"""
Base class for `NodeVisitor`-related tree pruning exceptions.
Modified: trunk/docutils/docutils/transforms/universal.py
===================================================================
--- trunk/docutils/docutils/transforms/universal.py 2024-05-31 14:42:06 UTC (rev 9722)
+++ trunk/docutils/docutils/transforms/universal.py 2024-06-05 10:22:23 UTC (rev 9723)
@@ -350,5 +350,11 @@
for node in self.document.findall():
try:
node.validate()
- except ValueError as e:
- self.document.reporter.warning(e.args[0], base_node=node)
+ except nodes.ValidationError as e:
+ self.document.reporter.warning(
+ str(e), base_node=e.problematic_element or node)
+ # TODO: append a link to the Document Tree documentation?
+ # nodes.paragraph('', 'See ',
+ # nodes.reference('', 'doctree.html#document',
+ # refuri='https://docutils.sourceforge.io/'
+ # 'docs/ref/doctree.html#document'),
Modified: trunk/docutils/test/test_nodes.py
===================================================================
--- trunk/docutils/test/test_nodes.py 2024-05-31 14:42:06 UTC (rev 9722)
+++ trunk/docutils/test/test_nodes.py 2024-06-05 10:22:23 UTC (rev 9723)
@@ -469,7 +469,11 @@
with self.assertWarns(DeprecationWarning):
node.set_class('parrot')
+
+class ElementValidationTests(unittest.TestCase):
+
def test_validate(self):
+ """Valid node: validation should simply pass."""
node = nodes.paragraph('', 'plain text', classes='my test classes')
node.append(nodes.emphasis('', 'emphasised text', ids='emphtext'))
node.validate()
@@ -487,15 +491,15 @@
def test_validate_wrong_attribute(self):
node = nodes.paragraph('', 'text', id='test-paragraph')
- with self.assertRaisesRegex(ValueError,
- 'Element <paragraph> invalid:\n'
- ' Attribute "id" not one of "ids '):
+ with self.assertRaisesRegex(nodes.ValidationError,
+ 'Element <paragraph id=.*> invalid:\n'
+ ' Attribute "id" not one of "ids", '):
node.validate()
def test_validate_wrong_attribute_value(self):
node = nodes.image(uri='test.png', width='20 inch') # invalid unit
- with self.assertRaisesRegex(ValueError,
- 'Element <image> invalid:\n'
+ with self.assertRaisesRegex(nodes.ValidationError,
+ 'Element <image.*> invalid:\n'
'.*"width" has invalid value "20 inch".\n'
'.*Valid units: em ex '):
node.validate()
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|