From e3685fd5fdd8808acda81bfc12fb9702d4b59a60 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 28 Oct 2018 11:18:22 -0700 Subject: bpo-34160: Preserve user specified order of Element attributes (GH-10163) --- Doc/library/xml.etree.elementtree.rst | 8 ++++++++ Lib/test/test_xml_etree.py | 20 ++++++++++++++++++++ Lib/xml/etree/ElementTree.py | 2 +- .../Library/2018-10-27-21-11-42.bpo-34160.UzyPZf.rst | 1 + 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2018-10-27-21-11-42.bpo-34160.UzyPZf.rst 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(), + '\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"\n" + b'') + 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. -- cgit v0.12