summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorBernt Røskar Brenna <bernt.brenna@gmail.com>2019-04-14 08:07:02 (GMT)
committerStefan Behnel <stefan_ml@behnel.de>2019-04-14 08:07:02 (GMT)
commitffca16e25a70fd44a87b13b379b5ec0c7a11e926 (patch)
tree6855e55ff815720daf989ebc6e8eab778b197f73 /Lib
parent830b43d03cc47a27a22a50d777f23c8e60820867 (diff)
downloadcpython-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.py123
-rw-r--r--Lib/xml/etree/ElementTree.py15
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>&#248;</tag></body>', None, None),
+ (b'<body><tag>\xc3\xb8</tag></body>', 'UTF-8', None),
+ (b'<body><tag>&#248;</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>&#248;</tag></body>", None, False),
+ (b"<body><tag>\xc3\xb8</tag></body>", 'UTF-8', False),
+ (b"<body><tag>&#248;</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>&#248;</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>&#248;</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