Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions Doc/library/xml.dom.minidom.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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_attrs=True)

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
Expand All @@ -143,11 +143,13 @@ module documentation. This section lists the differences between the API and
For the :class:`Document` node, an additional keyword argument *encoding* can
be used to specify the encoding field of the XML header.

If *sort_attrs* is true (by default), then element attributes will be

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If *sort_attrs* is true (by default), then element attributes will be
If *sort_attrs* is ``True`` (the default value), then element attributes will be

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the current wording is more correct. Element attributes will be sorted not only when sort_attrs is True, but when its boolean value is true.

"True" is a concrete bool instance, "true" is used when the boolean value should be true, and not just bool instances are accepted.

written in lexical order.

.. versionchanged:: 3.8
The :meth:`writexml` method now preserves the attribute order specified
by the user.
Added support for the *sort_attrs* argument.

.. method:: Node.toxml(encoding=None)
.. method:: Node.toxml(encoding=None, *, sort_attrs=True)

Return a string or byte string containing the XML represented by
the DOM node.
Expand All @@ -159,22 +161,23 @@ module documentation. This section lists the differences between the API and
encoding. Encoding this string in an encoding other than UTF-8 is
likely incorrect, since UTF-8 is the default encoding of XML.

The *sort_attrs* argument behaves like the corresponding argument of
:meth:`writexml`.
Comment thread
serhiy-storchaka marked this conversation as resolved.

.. versionchanged:: 3.8
The :meth:`toxml` method now preserves the attribute order specified
by the user.
Added support for the *sort_attrs* argument.

.. method:: Node.toprettyxml(indent="", newl="", encoding="")
.. method:: Node.toprettyxml(indent="", newl="", encoding="", *, sort_attrs=True)

Return a pretty-printed version of the document. *indent* specifies the
indentation string and defaults to a tabulator; *newl* specifies the string
emitted at the end of each line and defaults to ``\n``.

The *encoding* argument behaves like the corresponding argument of
:meth:`toxml`.
The *encoding* and *sort_attrs* arguments behave like the corresponding
arguments of :meth:`toxml`.

.. versionchanged:: 3.8
The :meth:`toprettyxml` method now preserves the attribute order specified
by the user.
Added support for the *sort_attrs* argument.


.. _dom-example:
Expand Down
25 changes: 17 additions & 8 deletions Doc/library/xml.etree.elementtree.rst
Original file line number Diff line number Diff line change
Expand Up @@ -592,29 +592,34 @@ Functions


.. function:: tostring(element, encoding="us-ascii", method="xml", *, \
short_empty_elements=True)
short_empty_elements=True, sort_attrs=True)

Generates a string representation of an XML element, including all
subelements. *element* is an :class:`Element` instance. *encoding* [1]_ is
the output encoding (default is US-ASCII). Use ``encoding="unicode"`` to
generate a Unicode string (otherwise, a bytestring is generated). *method*
is either ``"xml"``, ``"html"`` or ``"text"`` (default is ``"xml"``).
*short_empty_elements* has the same meaning as in :meth:`ElementTree.write`.
*short_empty_elements* and *sort_attrs* have the same meaning as in
:meth:`ElementTree.write`.
Returns an (optionally) encoded string containing the XML data.

.. versionadded:: 3.4
The *short_empty_elements* parameter.

.. versionadded:: 3.8
The *sort_attrs* parameter.


.. function:: tostringlist(element, encoding="us-ascii", method="xml", *, \
short_empty_elements=True)
short_empty_elements=True, sort_attrs=True)

Generates a string representation of an XML element, including all
subelements. *element* is an :class:`Element` instance. *encoding* [1]_ is
the output encoding (default is US-ASCII). Use ``encoding="unicode"`` to
generate a Unicode string (otherwise, a bytestring is generated). *method*
is either ``"xml"``, ``"html"`` or ``"text"`` (default is ``"xml"``).
*short_empty_elements* has the same meaning as in :meth:`ElementTree.write`.
*short_empty_elements* and *sort_attrs* have the same meaning as in
:meth:`ElementTree.write`.
Returns a list of (optionally) encoded strings containing the XML data.
It does not guarantee any specific sequence, except that
``b"".join(tostringlist(element)) == tostring(element)``.
Expand All @@ -624,6 +629,9 @@ Functions
.. versionadded:: 3.4
The *short_empty_elements* parameter.

.. versionadded:: 3.8
The *sort_attrs* parameter.


.. function:: XML(text, parser=None)

Expand Down Expand Up @@ -925,7 +933,7 @@ ElementTree Objects

.. method:: write(file, encoding="us-ascii", xml_declaration=None, \
default_namespace=None, method="xml", *, \
short_empty_elements=True)
short_empty_elements=True, sort_attrs=True)

Writes the element tree to a file, as XML. *file* is a file name, or a
:term:`file object` opened for writing. *encoding* [1]_ is the output
Expand All @@ -940,6 +948,8 @@ ElementTree Objects
of elements that contain no content. If ``True`` (the default), they are
emitted as a single self-closed tag, otherwise they are emitted as a pair
of start/end tags.
If *sort_attrs* is true (by default), then element attributes will be

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If *sort_attrs* is true (by default), then element attributes will be
If *sort_attrs* is ``True`` (the default value), then element attributes will be

written in lexical order.

The output is either a string (:class:`str`) or binary (:class:`bytes`).
This is controlled by the *encoding* argument. If *encoding* is
Expand All @@ -951,9 +961,8 @@ 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.
.. versionadded:: 3.8
The *sort_attrs* parameter.


This is the XML file that is going to be manipulated::
Expand Down
11 changes: 10 additions & 1 deletion Lib/test/test_minidom.py
Original file line number Diff line number Diff line change
Expand Up @@ -1567,17 +1567,26 @@ def test_minidom_attribute_order(self):
doc = parseString(xml_str)
output = io.StringIO()
doc.writexml(output)
self.assertEqual(output.getvalue(),
'<?xml version="1.0" ?><curriculum company="example" status="public"/>')
output = io.StringIO()
doc.writexml(output, sort_attrs=False)
self.assertEqual(output.getvalue(), xml_str)

def test_toxml_with_attributes_ordered(self):
xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>'
doc = parseString(xml_str)
self.assertEqual(doc.toxml(), xml_str)
self.assertEqual(doc.toxml(),
'<?xml version="1.0" ?><curriculum company="example" status="public"/>')
self.assertEqual(doc.toxml(sort_attrs=False), xml_str)

def test_toprettyxml_with_attributes_ordered(self):
xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>'
doc = parseString(xml_str)
self.assertEqual(doc.toprettyxml(),
'<?xml version="1.0" ?>\n'
'<curriculum company="example" status="public"/>\n')
self.assertEqual(doc.toprettyxml(sort_attrs=False),
'<?xml version="1.0" ?>\n'
'<curriculum status="public" company="example"/>\n')

Expand Down
7 changes: 6 additions & 1 deletion Lib/test/test_xml_etree.py
Original file line number Diff line number Diff line change
Expand Up @@ -1054,10 +1054,15 @@ def test_dump_attribute_order(self):

def test_tree_write_attribute_order(self):
# See BPO 34160
root = ET.Element('cirriculum', status='public', company='example')
root = ET.XML('<cirriculum status="public" company="example"></cirriculum>')
#root = ET.Element('cirriculum', status='public', company='example')
self.assertEqual(serialize(root),
'<cirriculum company="example" status="public" />')
self.assertEqual(serialize(root, sort_attrs=False),
'<cirriculum status="public" company="example" />')
self.assertEqual(serialize(root, method='html'),
'<cirriculum company="example" status="public"></cirriculum>')
self.assertEqual(serialize(root, method='html', sort_attrs=False),
'<cirriculum status="public" company="example"></cirriculum>')


Expand Down
46 changes: 30 additions & 16 deletions Lib/xml/dom/minidom.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ class Node(xml.dom.Node):
def __bool__(self):
return True

def toxml(self, encoding=None):
return self.toprettyxml("", "", encoding)
def toxml(self, encoding=None, *, sort_attrs=True):
return self.toprettyxml("", "", encoding, sort_attrs=sort_attrs)

def toprettyxml(self, indent="\t", newl="\n", encoding=None):
def toprettyxml(self, indent="\t", newl="\n", encoding=None, *, sort_attrs=True):
if encoding is None:
writer = io.StringIO()
else:
Expand All @@ -56,9 +56,11 @@ def toprettyxml(self, indent="\t", newl="\n", encoding=None):
newline='\n')
if self.nodeType == Node.DOCUMENT_NODE:
# Can pass encoding only to document, to put it into XML header
self.writexml(writer, "", indent, newl, encoding)
self.writexml(writer, "", indent, newl, encoding,
sort_attrs=sort_attrs)
else:
self.writexml(writer, "", indent, newl)
self.writexml(writer, "", indent, newl,
sort_attrs=sort_attrs)
if encoding is None:
return writer.getvalue()
else:
Expand Down Expand Up @@ -847,27 +849,33 @@ def getElementsByTagNameNS(self, namespaceURI, localName):
def __repr__(self):
return "<DOM Element: %s at %#x>" % (self.tagName, id(self))

def writexml(self, writer, indent="", addindent="", newl=""):
def writexml(self, writer, indent="", addindent="", newl="", *,
sort_attrs=True):
# indent = current indentation
# addindent = indentation to add to higher levels
# newl = newline string
writer.write(indent+"<" + self.tagName)

attrs = self._get_attributes()
a_names = attrs.keys()
if sort_attrs:
a_names = sorted(a_names)

for a_name in attrs.keys():
for a_name in a_names:
writer.write(" %s=\"" % a_name)
_write_data(writer, attrs[a_name].value)
writer.write("\"")
if self.childNodes:
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_attrs=sort_attrs)
else:
writer.write(newl)
for node in self.childNodes:
node.writexml(writer, indent+addindent, addindent, newl)
node.writexml(writer, indent+addindent, addindent, newl,
sort_attrs=sort_attrs)
writer.write(indent)
writer.write("</%s>%s" % (self.tagName, newl))
else:
Expand Down Expand Up @@ -984,7 +992,8 @@ def _set_nodeName(self, value):
self.target = value
nodeName = property(_get_nodeName, _set_nodeName)

def writexml(self, writer, indent="", addindent="", newl=""):
def writexml(self, writer, indent="", addindent="", newl="", *,
sort_attrs=True):
writer.write("%s<?%s %s?>%s" % (indent,self.target, self.data, newl))


Expand Down Expand Up @@ -1084,7 +1093,8 @@ 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_attrs=True):
_write_data(writer, "%s%s%s" % (indent, self.data, newl))

# DOM Level 3 (WD 9 April 2002)
Expand Down Expand Up @@ -1179,7 +1189,8 @@ 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_attrs=True):
if "--" in self.data:
raise ValueError("'--' is not allowed in a comment node")
writer.write("%s<!--%s-->%s" % (indent, self.data, newl))
Expand All @@ -1191,7 +1202,8 @@ 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_attrs=True):
if self.data.find("]]>") >= 0:
raise ValueError("']]>' not allowed in a CDATA section")
writer.write("<![CDATA[%s]]>" % self.data)
Expand Down Expand Up @@ -1324,7 +1336,8 @@ def cloneNode(self, deep):
else:
return None

def writexml(self, writer, indent="", addindent="", newl=""):
def writexml(self, writer, indent="", addindent="", newl="", *,
sort_attrs=True):
writer.write("<!DOCTYPE ")
writer.write(self.name)
if self.publicId:
Expand Down Expand Up @@ -1786,14 +1799,15 @@ def importNode(self, node, deep):
raise xml.dom.NotSupportedErr("cannot import document type nodes")
return _clone_node(node, deep, self)

def writexml(self, writer, indent="", addindent="", newl="", encoding=None):
def writexml(self, writer, indent="", addindent="", newl="", encoding=None,
*, sort_attrs=True):
if encoding is None:
writer.write('<?xml version="1.0" ?>'+newl)
else:
writer.write('<?xml version="1.0" encoding="%s"?>%s' % (
encoding, newl))
for node in self.childNodes:
node.writexml(writer, indent, addindent, newl)
node.writexml(writer, indent, addindent, newl, sort_attrs=sort_attrs)

# DOM Level 3 (WD 9 April 2002)

Expand Down
Loading