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