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

milde--- via Docutils-checkins <[email protected]>
Newsgroups gmane.text.docutils.cvs
Message-ID <[email protected]>
Revision: 9919
          http://sourceforge.net/p/docutils/code/9919
Author:   milde
Date:     2024-09-04 19:33:12 +0000 (Wed, 04 Sep 2024)
Log Message:
-----------
New function `nodes.parse_measure()`. Relax `validate_measure()`.

Allow "arbitrary" units: Check for a run of ASCII-letters or a percent sign.
(The definition of "measure" in docutils.dtd and its description in
doctree.txt do not include a restriction of the valid units.)

Modified Paths:
--------------
    trunk/docutils/HISTORY.rst
    trunk/docutils/docutils/nodes.py
    trunk/docutils/docutils/parsers/rst/directives/__init__.py
    trunk/docutils/test/test_nodes.py
    trunk/docutils/test/test_parsers/test_docutils_xml/test_parse_element.py

Modified: trunk/docutils/HISTORY.rst
===================================================================
--- trunk/docutils/HISTORY.rst	2024-09-04 06:16:29 UTC (rev 9918)
+++ trunk/docutils/HISTORY.rst	2024-09-04 19:33:12 UTC (rev 9919)
@@ -63,6 +63,7 @@
     convert string representations to correct data type,
     normalize values,
     raise ValueError for invalid attribute names or values.
+  - New function `parse_measure()`.
   - Removed `Element.set_class()`.
 
 * docutils/parsers/docutils_xml.py

Modified: trunk/docutils/docutils/nodes.py
===================================================================
--- trunk/docutils/docutils/nodes.py	2024-09-04 06:16:29 UTC (rev 9918)
+++ trunk/docutils/docutils/nodes.py	2024-09-04 19:33:12 UTC (rev 9919)
@@ -3074,6 +3074,22 @@
     return '"%s"' % value
 
 
+def parse_measure(measure: str) -> tuple[float, str]:
+    """Parse a measure__, return value + optional unit.
+
+    __ https://docutils.sourceforge.io/docs/ref/doctree.html#measure
+
+    Provisional.
+    """
+    match = re.fullmatch('(-?[0-9.]+) *([a-zA-Zµ]*|%?)', measure)
+    try:
+        value = float(match.group(1))
+        unit = match.group(2)
+    except (AttributeError, ValueError):
+        raise ValueError(f'"{measure}" is no valid measure.')
+    return value, unit
+
+
 # Methods to validate `Element attribute`__ values.
 
 # Ensure the expected Python `data type`__, normalize, and check for
@@ -3139,24 +3155,25 @@
     return value
 
 
-def validate_measure(value: str) -> str:
+def validate_measure(measure: str) -> str:
     """
-    Validate a length measure__ (number + recognized unit).
+    Validate a measure__ (number + optional unit).  Return normalized `str`.
 
-    __ https://docutils.sourceforge.io/docs/ref/doctree.html#measure
+    See `parse_measure()` for a function returning a "number + unit" tuple.
 
+    The unit may be any run of letters or a percent sign.
+
     Provisional.
+
+    __ https://docutils.sourceforge.io/docs/ref/doctree.html#measure
     """
-    units = 'em|ex|px|in|cm|mm|pt|pc|%'
-    if not re.fullmatch(f'[-0-9.]+ *({units}?)', value):
-        raise ValueError(f'"{value}" is no valid measure. '
-                         f'Valid units: {units.replace("|", " ")}.')
-    return value.replace(' ', '').strip()
+    value, unit = parse_measure(measure)
+    return f'{value:g}{unit}'
 
 
 def validate_NMTOKEN(value: str) -> str:
     """
-    Validate a "name token": a `str` of letters, digits, and [-._].
+    Validate a "name token": a `str` of ASCII letters, digits, and [-._].
 
     Provisional.
     """

Modified: trunk/docutils/docutils/parsers/rst/directives/__init__.py
===================================================================
--- trunk/docutils/docutils/parsers/rst/directives/__init__.py	2024-09-04 06:16:29 UTC (rev 9918)
+++ trunk/docutils/docutils/parsers/rst/directives/__init__.py	2024-09-04 19:33:12 UTC (rev 9919)
@@ -261,8 +261,8 @@
         float(match.group(1))
     except (AttributeError, ValueError):
         raise ValueError(
-            'not a positive measure of one of the following units:\n%s'
-            % ' '.join('"%s"' % i for i in units))
+            'not a positive measure of one of the following units:\n"%s"'
+            % '" "'.join(units))
     return match.group(1) + match.group(2)
 
 

Modified: trunk/docutils/test/test_nodes.py
===================================================================
--- trunk/docutils/test/test_nodes.py	2024-09-04 06:16:29 UTC (rev 9918)
+++ trunk/docutils/test/test_nodes.py	2024-09-04 19:33:12 UTC (rev 9919)
@@ -502,11 +502,10 @@
             node.validate()
 
     def test_validate_wrong_attribute_value(self):
-        node = nodes.image(uri='test.png', width='20 inch')  # invalid unit
+        node = nodes.image(uri='test.png', width='1in 3pt')
         with self.assertRaisesRegex(nodes.ValidationError,
                                     'Element <image.*> invalid:\n'
-                                    '.*"width" has invalid value "20 inch".\n'
-                                    '.*Valid units: em ex '):
+                                    '.*"width" has invalid value "1in 3pt".'):
             node.validate()
 
     def test_validate_spurious_element(self):
@@ -1114,7 +1113,25 @@
         self.assertEqual(nodes.split_name_list(r'a\ n\ame two\\ n\\ames'),
                          ['a name', 'two\\', r'n\ames'])
 
+    def test_parse_measure(self):
+        # measure is number + optional unit (letter(s) or percentage)
+        self.assertEqual(nodes.parse_measure('8ex'), (8, 'ex'))
+        self.assertEqual(nodes.parse_measure('2.5'), (2.5, ''))
+        self.assertEqual(nodes.parse_measure('-2s'), (-2, 's'))
+        self.assertEqual(nodes.parse_measure('2 µF'), (2, 'µF'))
+        self.assertEqual(nodes.parse_measure('10 EUR'), (10, 'EUR'))
+        self.assertEqual(nodes.parse_measure('.5 %'), (.5, '%'))
+        # scientific notation not supported
+        with self.assertRaisesRegex(ValueError, '"3e-4 mm" is no valid '):
+            nodes.parse_measure('3e-4 mm')
+        # unit must follow the number
+        with self.assertRaisesRegex(ValueError, '"EUR 23" is no valid '):
+            nodes.parse_measure('EUR 23')
+        # only single percent sign allowed
+        with self.assertRaisesRegex(ValueError, '"2%%" is no valid measure'):
+            nodes.parse_measure('2%%')
 
+
 class AttributeTypeTests(unittest.TestCase):
     """Test validator functions for the supported `attribute data types`__
 
@@ -1154,17 +1171,19 @@
             nodes.validate_identifier_list(s2)
 
     def test_validate_measure(self):
-        # number (may be decimal fraction) + optional CSS2 length unit
+        # number (may be decimal fraction) + optional unit
         self.assertEqual(nodes.validate_measure('8ex'), '8ex')
+        self.assertEqual(nodes.validate_measure('2'), '2')
+        # internal whitespace is removed
         self.assertEqual(nodes.validate_measure('3.5 %'), '3.5%')
-        self.assertEqual(nodes.validate_measure('2'), '2')
-        with self.assertRaisesRegex(ValueError, '"2km" is no valid measure. '
-                                    'Valid units: em ex '):
-            nodes.validate_measure('2km')
-        # negative numbers are currently not supported
-        # TODO: allow? the spec doesnot mention negative numbers.
-        # but a negative width or height of an image is odd.
-        # nodes.validate_measure('-2')
+        # padding whitespace is not valid
+        with self.assertRaisesRegex(ValueError, '"8ex " is no valid measure'):
+            nodes.validate_measure('8ex ')
+        # Negative numbers:
+        # * ``doctree.txt`` does not mention negative numbers,
+        # * in rST, negative numbers are not valid.
+        # Provisional: currently valid but may become invalid!
+        # self.assertEqual(nodes.validate_measure('-2'), '-2')
 
     def test_validate_NMTOKEN(self):
         # str with ASCII-letters, digits, hyphen, underscore, and full-stop.

Modified: trunk/docutils/test/test_parsers/test_docutils_xml/test_parse_element.py
===================================================================
--- trunk/docutils/test/test_parsers/test_docutils_xml/test_parse_element.py	2024-09-04 06:16:29 UTC (rev 9918)
+++ trunk/docutils/test/test_parsers/test_docutils_xml/test_parse_element.py	2024-09-04 19:33:12 UTC (rev 9919)
@@ -93,12 +93,10 @@
         """
         xml = ('<image breadth="3 cm" height="3 inch"/>')
         node = docutils_xml.parse_element(xml)
-        self.assertEqual(xml, str(node))
+        self.assertEqual(xml.replace('3 inch', '3inch'), str(node))
         with self.assertRaisesRegex(ValueError,
                                     'Element <image breadth="3 cm".*invalid:\n'
-                                    '.*"breadth" not one of "ids",.*\n'
-                                    '.*"height" has invalid value "3 inch".\n'
-                                    '.*Valid units: em ex px in cm mm pt '
+                                    '.*"breadth" not one of "ids", '
                                     ):
             node.validate()
 

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



_______________________________________________
Docutils-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/docutils-checkins
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.