diff --git a/Doc/library/xml.dom.minidom.rst b/Doc/library/xml.dom.minidom.rst
index af09ea98c818211..5acfdc03a55536a 100644
--- a/Doc/library/xml.dom.minidom.rst
+++ b/Doc/library/xml.dom.minidom.rst
@@ -132,7 +132,7 @@ module documentation. This section lists the differences between the API and
... # Work with dom.
-.. method:: Node.writexml(writer, indent="", addindent="", newl="")
+.. method:: Node.writexml(writer, indent="", addindent="", newl="", sort=False)
Write XML to the writer object. The writer should have a :meth:`write` method
which matches that of the file object interface. The *indent* parameter is the
@@ -147,6 +147,11 @@ module documentation. This section lists the differences between the API and
The :meth:`writexml` method now preserves the attribute order specified
by the user.
+ .. versionadded:: 3.8
+ The *sort* parameter sorts the XML attributes of the tags. For the
+ :class:`Document` node, an additional keyword argument *sort* can be used
+ to specify the sort of the attributes.
+
.. method:: Node.toxml(encoding=None)
Return a string or byte string containing the XML represented by
diff --git a/Doc/library/xml.dom.rst b/Doc/library/xml.dom.rst
index 18519a75a546115..38011fe1ea72a7d 100644
--- a/Doc/library/xml.dom.rst
+++ b/Doc/library/xml.dom.rst
@@ -576,6 +576,23 @@ inherits properties from :class:`Node`.
particular namespace URI and localname. The localname is the part of the
namespace after the prefix.
+.. method:: Document.writexml(self, writer, indent="", addindent="", newl="", encoding=None, sort=False)
+
+ Write XML to the writer object. The writer should have a :meth:`write` method
+ which matches that of the file object interface. The *indent* parameter is the
+ indentation of the current node. The *addindent* parameter is the incremental
+ indentation to use for subnodes of the current one. The *newl* parameter
+ specifies the string to use to terminate newlines.
+
+ The keyword argument *encoding* can be used to specify the encoding field of
+ the XML header.
+
+ .. versionchanged:: 3.8
+ The :meth:`writexml` method now preserves the attribute order specified
+ by the user.
+
+ .. versionadded:: 3.8
+ The *sort* parameter sorts the XML attributes of the tags.
.. _dom-element-objects:
diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py
index f3ef958b535373f..41970855455509f 100644
--- a/Lib/test/test_minidom.py
+++ b/Lib/test/test_minidom.py
@@ -1612,6 +1612,14 @@ def testProcessingInstructionNameError(self):
pi = doc.createProcessingInstruction("y", "z")
pi.nodeValue = "crash"
+ def test_minidom_attribute_sorted(self):
+ xml_str = ''
+ sorted_xml_str = ''
+ doc = parseString(xml_str)
+ output = io.StringIO()
+ doc.writexml(output, sort=True)
+ self.assertEqual(output.getvalue(), sorted_xml_str)
+
def test_minidom_attribute_order(self):
xml_str = ''
doc = parseString(xml_str)
diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py
index 43569ddcbeacd9d..c97d548e3715fc4 100644
--- a/Lib/xml/dom/minidom.py
+++ b/Lib/xml/dom/minidom.py
@@ -847,7 +847,7 @@ def getElementsByTagNameNS(self, namespaceURI, localName):
def __repr__(self):
return "" % (self.tagName, id(self))
- def writexml(self, writer, indent="", addindent="", newl=""):
+ def writexml(self, writer, indent="", addindent="", newl="", sort=False):
# indent = current indentation
# addindent = indentation to add to higher levels
# newl = newline string
@@ -855,7 +855,11 @@ def writexml(self, writer, indent="", addindent="", newl=""):
attrs = self._get_attributes()
- for a_name in attrs.keys():
+ attribute_names = attrs.keys()
+ if sort:
+ attribute_names = sorted(attribute_names)
+
+ for a_name in attribute_names:
writer.write(" %s=\"" % a_name)
_write_data(writer, attrs[a_name].value)
writer.write("\"")
@@ -863,11 +867,11 @@ def writexml(self, writer, indent="", addindent="", newl=""):
writer.write(">")
if (len(self.childNodes) == 1 and
self.childNodes[0].nodeType == Node.TEXT_NODE):
- self.childNodes[0].writexml(writer, '', '', '')
+ self.childNodes[0].writexml(writer, '', '', '', sort=sort)
else:
writer.write(newl)
for node in self.childNodes:
- node.writexml(writer, indent+addindent, addindent, newl)
+ node.writexml(writer, indent+addindent, addindent, newl, sort=sort)
writer.write(indent)
writer.write("%s>%s" % (self.tagName, newl))
else:
@@ -984,8 +988,8 @@ def _set_nodeName(self, value):
self.target = value
nodeName = property(_get_nodeName, _set_nodeName)
- def writexml(self, writer, indent="", addindent="", newl=""):
- writer.write("%s%s %s?>%s" % (indent,self.target, self.data, newl))
+ def writexml(self, writer, indent="", addindent="", newl="", sort=False):
+ writer.write("%s%s %s?>%s" % (indent,self.target, self.data, newl), sort=sort)
class CharacterData(Childless, Node):
@@ -1084,7 +1088,7 @@ def splitText(self, offset):
self.data = self.data[:offset]
return newText
- def writexml(self, writer, indent="", addindent="", newl=""):
+ def writexml(self, writer, indent="", addindent="", newl="", sort=False):
_write_data(writer, "%s%s%s" % (indent, self.data, newl))
# DOM Level 3 (WD 9 April 2002)
@@ -1179,10 +1183,10 @@ def __init__(self, data):
CharacterData.__init__(self)
self._data = data
- def writexml(self, writer, indent="", addindent="", newl=""):
+ def writexml(self, writer, indent="", addindent="", newl="", sort=False):
if "--" in self.data:
raise ValueError("'--' is not allowed in a comment node")
- writer.write("%s%s" % (indent, self.data, newl))
+ writer.write("%s%s" % (indent, self.data, newl), sort=sort)
class CDATASection(Text):
@@ -1191,7 +1195,7 @@ class CDATASection(Text):
nodeType = Node.CDATA_SECTION_NODE
nodeName = "#cdata-section"
- def writexml(self, writer, indent="", addindent="", newl=""):
+ def writexml(self, writer, indent="", addindent="", newl="", sort=False):
if self.data.find("]]>") >= 0:
raise ValueError("']]>' not allowed in a CDATA section")
writer.write("" % self.data)
@@ -1324,7 +1328,7 @@ def cloneNode(self, deep):
else:
return None
- def writexml(self, writer, indent="", addindent="", newl=""):
+ def writexml(self, writer, indent="", addindent="", newl="", sort=False):
writer.write("'+newl)
else:
writer.write('%s' % (
encoding, newl))
for node in self.childNodes:
- node.writexml(writer, indent, addindent, newl)
+ node.writexml(writer, indent, addindent, newl, sort=sort)
# DOM Level 3 (WD 9 April 2002)
diff --git a/Misc/NEWS.d/next/Library/2019-03-15-19-08-36.bpo-34160.aDomJL.rst b/Misc/NEWS.d/next/Library/2019-03-15-19-08-36.bpo-34160.aDomJL.rst
new file mode 100644
index 000000000000000..1b12543b1d1398d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-03-15-19-08-36.bpo-34160.aDomJL.rst
@@ -0,0 +1,5 @@
+Add the *sort* attribute to the :meth:`xml.dom.minidom.Node.writexml` method
+allowing to sort the attributes by name (``True``) or by the attribute order
+specified by the user (by default ``False``). Document
+:meth:`xml.dom.minidom.Document.writexml` method. Contributed by Stéphane
+Wirtel.