SF.net SVN: docutils:[9734 ] trunk/docutils
milde--- via Docutils-checkins <[email protected]>
| Newsgroups | gmane.text.docutils.cvs |
|---|---|
| Message-ID | <[email protected]> |
Revision: 9734
http://sourceforge.net/p/docutils/code/9734
Author: milde
Date: 2024-06-06 14:01:35 +0000 (Thu, 06 Jun 2024)
Log Message:
-----------
xml parser: new method `parse_element()`.
The method `Parser.parse_element()` does not require/populate
a <document> root node.
It parses an XML representation of a "document tree" element
(with possible sub-elements) and returns a `docutils.nodes.Element`
instance (with possible child nodes).
See the unittest script for usage examples.
Modified Paths:
--------------
trunk/docutils/docutils/parsers/docutils_xml.py
trunk/docutils/test/test_nodes.py
Added Paths:
-----------
trunk/docutils/test/test_parsers/test_docutils_xml/test_parse_element.py
Modified: trunk/docutils/docutils/parsers/docutils_xml.py
===================================================================
--- trunk/docutils/docutils/parsers/docutils_xml.py 2024-06-06 14:01:22 UTC (rev 9733)
+++ trunk/docutils/docutils/parsers/docutils_xml.py 2024-06-06 14:01:35 UTC (rev 9734)
@@ -58,6 +58,26 @@
self.finish_parse()
+def parse_element(inputstring):
+ """
+ Parse `inputstring` as "Docutils XML", return `nodes.Element` instance.
+
+ :inputstring: XML source.
+
+ Caution:
+ The function does not detect invalid XML.
+
+ To check the validity of the returned node,
+ you may use its `validate()` method::
+
+ node = parse_element('<tip><hint>text</hint></tip>')
+ node.validate()
+
+ Provisional.
+ """
+ return element2node(ET.fromstring(inputstring))
+
+
def element2node(element):
"""
Convert an `etree` element and its children to Docutils doctree nodes.
Modified: trunk/docutils/test/test_nodes.py
===================================================================
--- trunk/docutils/test/test_nodes.py 2024-06-06 14:01:22 UTC (rev 9733)
+++ trunk/docutils/test/test_nodes.py 2024-06-06 14:01:35 UTC (rev 9734)
@@ -1121,7 +1121,13 @@
class AttributeTypeTests(unittest.TestCase):
+ """Test validator functions for the supported `attribute data types`__
+ See also test_parsers/test_docutils_xml/test_parse_element.py.
+
+ __ https://docutils.sourceforge.io/docs/ref/doctree.html#attribute-types
+ """
+
def test_validate_enumerated_type(self):
# function factory for "choice validators"
food = nodes.validate_enumerated_type('ham', 'spam')
Added: trunk/docutils/test/test_parsers/test_docutils_xml/test_parse_element.py
===================================================================
--- trunk/docutils/test/test_parsers/test_docutils_xml/test_parse_element.py (rev 0)
+++ trunk/docutils/test/test_parsers/test_docutils_xml/test_parse_element.py 2024-06-06 14:01:35 UTC (rev 9734)
@@ -0,0 +1,207 @@
+#!/usr/bin/env python3
+# :Copyright: © 2024 Günter Milde.
+# :License: Released under the terms of the `2-Clause BSD license`_, in short:
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+# This file is offered as-is, without any warranty.
+#
+# .. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause
+
+"""Tests for parsers/docutils_xml.py."""
+
+from pathlib import Path
+import sys
+import unittest
+import xml.etree.ElementTree as ET
+
+if __name__ == '__main__':
+ # prepend the "docutils root" to the Python library path
+ # so we import the local `docutils` package.
+ sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
+
+from docutils.parsers import docutils_xml
+
+
+class ParseElementTestCase(unittest.TestCase):
+
+ def test_element_with_child_with_text(self):
+ xml = '<tip><paragraph>some text</paragraph></tip>'
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(xml, str(node))
+
+ def test_tailing_text(self):
+ xml = '<strong>text</strong>trailing text'
+ with self.assertRaisesRegex(ET.ParseError,
+ 'junk after document element'):
+ docutils_xml.parse_element(xml)
+
+
+class XmlAttributesTestCase(unittest.TestCase):
+ """
+ Test correct parsing of the `supported element attributes`_.
+
+ See also `AttributeTypeTests` in ../../test_nodes.py.
+
+ __ https://docutils.sourceforge.io/
+ docs/ref/doctree.html#attribute-reference
+ """
+ common_attributes = {'classes': [],
+ 'dupnames': [],
+ 'ids': [],
+ 'names': []}
+
+ def test_alt(self): # CDATA (str)
+ xml = ('<image alt="a barking dog" align="left" height="3ex"'
+ ' loading="embed" scale="3" uri="dog.jpg" width="4cm"/>')
+ expected = {'alt': 'a barking dog',
+ 'align': 'left',
+ 'height': '3ex',
+ 'loading': 'embed',
+ 'scale': 3,
+ 'uri': 'dog.jpg',
+ 'width': '4cm'}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ # 'align': CDATA (str) → test_alt
+
+ def test_anonymous(self): # yesorno (int)
+ xml = '<target anonymous="1" ids="target-1" refuri="example.html" />'
+ expected = {'anonymous': 1,
+ 'ids': ['target-1'],
+ 'refuri': 'example.html'}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ def test_auto(self): # CDATA (str) number sequence: '1' or '*'
+ xml = '<footnote auto="*" backrefs="footnote-reference-2" />'
+ expected = {'auto': '*',
+ 'backrefs': ['footnote-reference-2']}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ # 'backrefs': idrefs.type (list[str]) → test_auto
+
+ def test_bullet(self): # CDATA (str)
+ xml = '<bullet_list bullet="*" classes="first x-2nd" />'
+ expected = {'bullet': '*',
+ 'classes': ['first', 'x-2nd']}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ # 'classes': classnames.type (list[str]) → test_bullet
+
+ def test_colwidth(self): # CDATA (int) sic!
+ xml = '<colspec colwidth="33" stub="1" />'
+ expected = {'colwidth': 33, 'stub': 1}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ def test_delimiter(self): # CDATA (str)
+ xml = '<option_argument delimiter="=">FILE</option_argument>'
+ expected = {'delimiter': '='}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ def test_dupnames(self): # refnames.type (list[str]).
+ xml = r'<section dupnames="title\ 1" ids="title-1" />'
+ expected = {'dupnames': ['title 1'],
+ 'ids': ['title-1']}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ def test_enumtype(self): # EnumeratedType (str)
+ xml = ('<enumerated_list enumtype="upperroman"'
+ ' prefix="(" start="2" suffix=")" />')
+ expected = {'enumtype': 'upperroman',
+ 'prefix': '(',
+ 'start': 2,
+ 'suffix': ')'}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ def test_format(self): # NMTOKENS (str) (space-delimited list of keywords)
+ xml = '<raw format="html latex" xml:space="preserve" />'
+ expected = {'format': 'html latex',
+ 'xml:space': 'preserve'}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ # 'height': measure (str) → test_alt
+ # 'ids': ids.type (list[str]) → test_names
+
+ def test_level(self): # level (int)
+ xml = ('<system_message level="3" line="21" source="string"'
+ ' type="ERROR" />')
+ expected = {'backrefs': [],
+ 'level': 3,
+ 'line': 21,
+ 'source': 'string',
+ 'type': 'ERROR'}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ def test_ltrim(self): # yesorno (int)
+ xml = '<substitution_definition ltrim="1" names="nbsp" />'
+ expected = {'ltrim': 1, 'names': ['nbsp']}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ # 'loading': EnumeratedType (str) → test_alt
+
+ def test_morecols(self): # number (int)
+ xml = '<entry morecols="1" />'
+ expected = {'morecols': 1}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ def test_names(self): # refnames.type (list[str])
+ # internal whitespace in XML escaped
+ xml = r'<section ids="title-2 title-1" names="title\ 2\\ title\ 1" />'
+ expected = {'ids': ['title-2', 'title-1'],
+ 'names': ['title 2\\', 'title 1']}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ # 'prefix': CDATA (str) → test_enumtype
+
+ def test_refid(self): # idref.type (str)
+ xml = '<target refid="title-1-1"></target>'
+ expected = {'refid': 'title-1-1'}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ def test_refname(self): # refname.type (str)
+ xml = '<target refname="title 2"></target>'
+ expected = {'refname': 'title 2'}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ # 'refuri: CDATA (str) → test_anonymous
+
+ def test_rtrim(self): # yesorno (int)
+ xml = '<substitution_definition ltrim="1" names="nbsp" />'
+ expected = {'ltrim': 1,
+ 'names': ['nbsp']}
+ node = docutils_xml.parse_element(xml)
+ self.assertEqual(node.attributes, self.common_attributes | expected)
+
+ # 'scale': number (int) → test_alt
+ # 'source': CDATA (str) → test_title
+ # 'start': number (int) → test_enumtype
+ # 'stub': yesorno (int) → test_colwidth
+ # 'suffix': CDATA (str) → test_enumtype
+
+ def test_title(self): # CDATA (str)
+ ...
+ # TODO: <document> does not work with parse_element()
+
+ # 'uri': CDATA (str) → test_alt
+ # 'width' measure (str) → test_alt
+ # 'xml:space' EnumeratedType (str) → test_format
+
+
+if __name__ == '__main__':
+ unittest.main()
Property changes on: trunk/docutils/test/test_parsers/test_docutils_xml/test_parse_element.py
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+Author Date Id Revision
\ No newline at end of property
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