diff options
author | Bernt Røskar Brenna <bernt.brenna@gmail.com> | 2019-04-14 08:07:02 (GMT) |
---|---|---|
committer | Stefan Behnel <stefan_ml@behnel.de> | 2019-04-14 08:07:02 (GMT) |
commit | ffca16e25a70fd44a87b13b379b5ec0c7a11e926 (patch) | |
tree | 6855e55ff815720daf989ebc6e8eab778b197f73 /Lib | |
parent | 830b43d03cc47a27a22a50d777f23c8e60820867 (diff) | |
download | cpython-ffca16e25a70fd44a87b13b379b5ec0c7a11e926.zip cpython-ffca16e25a70fd44a87b13b379b5ec0c7a11e926.tar.gz cpython-ffca16e25a70fd44a87b13b379b5ec0c7a11e926.tar.bz2 |
bpo-36227: ElementTree.tostring() default_namespace and xml_declaration arguments (GH-12225)
Add new keyword arguments "default_namespace" and "xml_declaration" to functions ET.tostring() and ET.tostringlist(), as known from ElementTree.write().
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/test/test_xml_etree.py | 123 | ||||
-rw-r--r-- | Lib/xml/etree/ElementTree.py | 15 |
2 files changed, 135 insertions, 3 deletions
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index 8a7ec00..bdcd4e0 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -9,6 +9,7 @@ import copy import functools import html import io +import locale import operator import pickle import sys @@ -756,6 +757,128 @@ class ElementTreeTest(unittest.TestCase): elem = ET.fromstring("<html><body>text</body></html>") self.assertEqual(ET.tostring(elem), b'<html><body>text</body></html>') + def test_tostring_default_namespace(self): + elem = ET.XML('<body xmlns="http://effbot.org/ns"><tag/></body>') + self.assertEqual( + ET.tostring(elem, encoding='unicode'), + '<ns0:body xmlns:ns0="http://effbot.org/ns"><ns0:tag /></ns0:body>' + ) + self.assertEqual( + ET.tostring(elem, encoding='unicode', default_namespace='http://effbot.org/ns'), + '<body xmlns="http://effbot.org/ns"><tag /></body>' + ) + + def test_tostring_default_namespace_different_namespace(self): + elem = ET.XML('<body xmlns="http://effbot.org/ns"><tag/></body>') + self.assertEqual( + ET.tostring(elem, encoding='unicode', default_namespace='foobar'), + '<ns1:body xmlns="foobar" xmlns:ns1="http://effbot.org/ns"><ns1:tag /></ns1:body>' + ) + + def test_tostring_default_namespace_original_no_namespace(self): + elem = ET.XML('<body><tag/></body>') + EXPECTED_MSG = '^cannot use non-qualified names with default_namespace option$' + with self.assertRaisesRegex(ValueError, EXPECTED_MSG): + ET.tostring(elem, encoding='unicode', default_namespace='foobar') + + def test_tostring_no_xml_declaration(self): + elem = ET.XML('<body><tag/></body>') + self.assertEqual( + ET.tostring(elem, encoding='unicode'), + '<body><tag /></body>' + ) + + def test_tostring_xml_declaration(self): + elem = ET.XML('<body><tag/></body>') + self.assertEqual( + ET.tostring(elem, encoding='utf8', xml_declaration=True), + b"<?xml version='1.0' encoding='utf8'?>\n<body><tag /></body>" + ) + + def test_tostring_xml_declaration_unicode_encoding(self): + elem = ET.XML('<body><tag/></body>') + preferredencoding = locale.getpreferredencoding() + self.assertEqual( + f"<?xml version='1.0' encoding='{preferredencoding}'?>\n<body><tag /></body>", + ET.tostring(elem, encoding='unicode', xml_declaration=True) + ) + + def test_tostring_xml_declaration_cases(self): + elem = ET.XML('<body><tag>ø</tag></body>') + preferredencoding = locale.getpreferredencoding() + TESTCASES = [ + # (expected_retval, encoding, xml_declaration) + # ... xml_declaration = None + (b'<body><tag>ø</tag></body>', None, None), + (b'<body><tag>\xc3\xb8</tag></body>', 'UTF-8', None), + (b'<body><tag>ø</tag></body>', 'US-ASCII', None), + (b"<?xml version='1.0' encoding='ISO-8859-1'?>\n" + b"<body><tag>\xf8</tag></body>", 'ISO-8859-1', None), + ('<body><tag>ø</tag></body>', 'unicode', None), + + # ... xml_declaration = False + (b"<body><tag>ø</tag></body>", None, False), + (b"<body><tag>\xc3\xb8</tag></body>", 'UTF-8', False), + (b"<body><tag>ø</tag></body>", 'US-ASCII', False), + (b"<body><tag>\xf8</tag></body>", 'ISO-8859-1', False), + ("<body><tag>ø</tag></body>", 'unicode', False), + + # ... xml_declaration = True + (b"<?xml version='1.0' encoding='us-ascii'?>\n" + b"<body><tag>ø</tag></body>", None, True), + (b"<?xml version='1.0' encoding='UTF-8'?>\n" + b"<body><tag>\xc3\xb8</tag></body>", 'UTF-8', True), + (b"<?xml version='1.0' encoding='US-ASCII'?>\n" + b"<body><tag>ø</tag></body>", 'US-ASCII', True), + (b"<?xml version='1.0' encoding='ISO-8859-1'?>\n" + b"<body><tag>\xf8</tag></body>", 'ISO-8859-1', True), + (f"<?xml version='1.0' encoding='{preferredencoding}'?>\n" + "<body><tag>ø</tag></body>", 'unicode', True), + + ] + for expected_retval, encoding, xml_declaration in TESTCASES: + with self.subTest(f'encoding={encoding} ' + f'xml_declaration={xml_declaration}'): + self.assertEqual( + ET.tostring( + elem, + encoding=encoding, + xml_declaration=xml_declaration + ), + expected_retval + ) + + def test_tostringlist_default_namespace(self): + elem = ET.XML('<body xmlns="http://effbot.org/ns"><tag/></body>') + self.assertEqual( + ''.join(ET.tostringlist(elem, encoding='unicode')), + '<ns0:body xmlns:ns0="http://effbot.org/ns"><ns0:tag /></ns0:body>' + ) + self.assertEqual( + ''.join(ET.tostringlist(elem, encoding='unicode', default_namespace='http://effbot.org/ns')), + '<body xmlns="http://effbot.org/ns"><tag /></body>' + ) + + def test_tostringlist_xml_declaration(self): + elem = ET.XML('<body><tag/></body>') + self.assertEqual( + ''.join(ET.tostringlist(elem, encoding='unicode')), + '<body><tag /></body>' + ) + self.assertEqual( + b''.join(ET.tostringlist(elem, xml_declaration=True)), + b"<?xml version='1.0' encoding='us-ascii'?>\n<body><tag /></body>" + ) + + preferredencoding = locale.getpreferredencoding() + stringlist = ET.tostringlist(elem, encoding='unicode', xml_declaration=True) + self.assertEqual( + ''.join(stringlist), + f"<?xml version='1.0' encoding='{preferredencoding}'?>\n<body><tag /></body>" + ) + self.assertRegex(stringlist[0], r"^<\?xml version='1.0' encoding='.+'?>") + self.assertEqual(['<body', '>', '<tag', ' />', '</body>'], stringlist[1:]) + def test_encoding(self): def check(encoding, body=''): xml = ("<?xml version='1.0' encoding='%s'?><xml>%s</xml>" % diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index b5ad8e1..c9e2f36 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -1113,6 +1113,7 @@ def _escape_attrib_html(text): # -------------------------------------------------------------------- def tostring(element, encoding=None, method=None, *, + xml_declaration=None, default_namespace=None, short_empty_elements=True): """Generate string representation of XML element. @@ -1121,13 +1122,17 @@ def tostring(element, encoding=None, method=None, *, *element* is an Element instance, *encoding* is an optional output encoding defaulting to US-ASCII, *method* is an optional output which can - be one of "xml" (default), "html", "text" or "c14n". + be one of "xml" (default), "html", "text" or "c14n", *default_namespace* + sets the default XML namespace (for "xmlns"). Returns an (optionally) encoded string containing the XML data. """ stream = io.StringIO() if encoding == 'unicode' else io.BytesIO() - ElementTree(element).write(stream, encoding, method=method, + ElementTree(element).write(stream, encoding, + xml_declaration=xml_declaration, + default_namespace=default_namespace, + method=method, short_empty_elements=short_empty_elements) return stream.getvalue() @@ -1149,10 +1154,14 @@ class _ListDataStream(io.BufferedIOBase): return len(self.lst) def tostringlist(element, encoding=None, method=None, *, + xml_declaration=None, default_namespace=None, short_empty_elements=True): lst = [] stream = _ListDataStream(lst) - ElementTree(element).write(stream, encoding, method=method, + ElementTree(element).write(stream, encoding, + xml_declaration=xml_declaration, + default_namespace=default_namespace, + method=method, short_empty_elements=short_empty_elements) return lst |