summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaymond Hettinger <rhettinger@users.noreply.github.com>2018-10-28 18:18:22 (GMT)
committerGitHub <noreply@github.com>2018-10-28 18:18:22 (GMT)
commite3685fd5fdd8808acda81bfc12fb9702d4b59a60 (patch)
tree930b05ba14510edd10fbec2840ee3e953cae7e39
parent18d57b4d6262bf96b5ac307bd84837c29ea04083 (diff)
downloadcpython-e3685fd5fdd8808acda81bfc12fb9702d4b59a60.zip
cpython-e3685fd5fdd8808acda81bfc12fb9702d4b59a60.tar.gz
cpython-e3685fd5fdd8808acda81bfc12fb9702d4b59a60.tar.bz2
bpo-34160: Preserve user specified order of Element attributes (GH-10163)
-rw-r--r--Doc/library/xml.etree.elementtree.rst8
-rw-r--r--Lib/test/test_xml_etree.py20
-rw-r--r--Lib/xml/etree/ElementTree.py2
-rw-r--r--Misc/NEWS.d/next/Library/2018-10-27-21-11-42.bpo-34160.UzyPZf.rst1
4 files changed, 30 insertions, 1 deletions
diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst
index 0cb949a..aae8014 100644
--- a/Doc/library/xml.etree.elementtree.rst
+++ b/Doc/library/xml.etree.elementtree.rst
@@ -489,6 +489,10 @@ Functions
*elem* is an element tree or an individual element.
+ .. versionchanged:: 3.8
+ The :func:`dump` function now preserves the attribute order specified
+ by the user.
+
.. function:: fromstring(text)
@@ -947,6 +951,10 @@ ElementTree Objects
.. versionadded:: 3.4
The *short_empty_elements* parameter.
+ .. versionchanged:: 3.8
+ The :meth:`write` method now preserves the attribute order specified
+ by the user.
+
This is the XML file that is going to be manipulated::
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index 8b16905..9988dad 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -5,6 +5,7 @@
# For this purpose, the module-level "ET" symbol is temporarily
# monkey-patched when running the "test_xml_etree_c" test suite.
+import contextlib
import copy
import functools
import html
@@ -1044,6 +1045,25 @@ class ElementTreeTest(unittest.TestCase):
method='html')
self.assertEqual(serialized, expected)
+ def test_dump_attribute_order(self):
+ # See BPO 34160
+ e = ET.Element('cirriculum', status='public', company='example')
+ with support.captured_stdout() as stdout:
+ ET.dump(e)
+ self.assertEqual(stdout.getvalue(),
+ '<cirriculum status="public" company="example" />\n')
+
+ def test_tree_write_attribute_order(self):
+ # See BPO 34160
+ root = ET.Element('cirriculum', status='public', company='example')
+ tree = ET.ElementTree(root)
+ f = io.BytesIO()
+ with contextlib.redirect_stdout(f):
+ tree.write(f, encoding='utf-8', xml_declaration=True)
+ self.assertEqual(f.getvalue(),
+ b"<?xml version='1.0' encoding='utf-8'?>\n"
+ b'<cirriculum status="public" company="example" />')
+
class XMLPullParserTest(unittest.TestCase):
diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py
index 85586d0..d4df83f 100644
--- a/Lib/xml/etree/ElementTree.py
+++ b/Lib/xml/etree/ElementTree.py
@@ -923,7 +923,7 @@ def _serialize_xml(write, elem, qnames, namespaces,
k,
_escape_attrib(v)
))
- for k, v in sorted(items): # lexical order
+ for k, v in items:
if isinstance(k, QName):
k = k.text
if isinstance(v, QName):
diff --git a/Misc/NEWS.d/next/Library/2018-10-27-21-11-42.bpo-34160.UzyPZf.rst b/Misc/NEWS.d/next/Library/2018-10-27-21-11-42.bpo-34160.UzyPZf.rst
new file mode 100644
index 0000000..6f3c076
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-10-27-21-11-42.bpo-34160.UzyPZf.rst
@@ -0,0 +1 @@
+ElementTree now preserves the attribute order specified by the user.