Skip to content

Commit e3685fd

Browse files
authored
bpo-34160: Preserve user specified order of Element attributes (GH-10163)
1 parent 18d57b4 commit e3685fd

File tree

4 files changed

+30
-1
lines changed

4 files changed

+30
-1
lines changed

Doc/library/xml.etree.elementtree.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,10 @@ Functions
489489

490490
*elem* is an element tree or an individual element.
491491

492+
.. versionchanged:: 3.8
493+
The :func:`dump` function now preserves the attribute order specified
494+
by the user.
495+
492496

493497
.. function:: fromstring(text)
494498

@@ -947,6 +951,10 @@ ElementTree Objects
947951
.. versionadded:: 3.4
948952
The *short_empty_elements* parameter.
949953

954+
.. versionchanged:: 3.8
955+
The :meth:`write` method now preserves the attribute order specified
956+
by the user.
957+
950958

951959
This is the XML file that is going to be manipulated::
952960

Lib/test/test_xml_etree.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# For this purpose, the module-level "ET" symbol is temporarily
66
# monkey-patched when running the "test_xml_etree_c" test suite.
77

8+
import contextlib
89
import copy
910
import functools
1011
import html
@@ -1044,6 +1045,25 @@ def test_html_empty_elems_serialization(self):
10441045
method='html')
10451046
self.assertEqual(serialized, expected)
10461047

1048+
def test_dump_attribute_order(self):
1049+
# See BPO 34160
1050+
e = ET.Element('cirriculum', status='public', company='example')
1051+
with support.captured_stdout() as stdout:
1052+
ET.dump(e)
1053+
self.assertEqual(stdout.getvalue(),
1054+
'<cirriculum status="public" company="example" />\n')
1055+
1056+
def test_tree_write_attribute_order(self):
1057+
# See BPO 34160
1058+
root = ET.Element('cirriculum', status='public', company='example')
1059+
tree = ET.ElementTree(root)
1060+
f = io.BytesIO()
1061+
with contextlib.redirect_stdout(f):
1062+
tree.write(f, encoding='utf-8', xml_declaration=True)
1063+
self.assertEqual(f.getvalue(),
1064+
b"<?xml version='1.0' encoding='utf-8'?>\n"
1065+
b'<cirriculum status="public" company="example" />')
1066+
10471067

10481068
class XMLPullParserTest(unittest.TestCase):
10491069

Lib/xml/etree/ElementTree.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,7 @@ def _serialize_xml(write, elem, qnames, namespaces,
923923
k,
924924
_escape_attrib(v)
925925
))
926-
for k, v in sorted(items): # lexical order
926+
for k, v in items:
927927
if isinstance(k, QName):
928928
k = k.text
929929
if isinstance(v, QName):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ElementTree now preserves the attribute order specified by the user.

0 commit comments

Comments
 (0)