Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
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
10 changes: 10 additions & 0 deletions Doc/library/xml.dom.minidom.rst
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,23 @@ module documentation. This section lists the differences between the API and

The *standalone* argument behaves exactly as in :meth:`writexml`.

No indentation is added inside an element
which is marked with ``xml:space="preserve"``,
which is declared in the DTD as not having element content,
or, in absence of such declaration, which contains text,
because this would change its content.

.. versionchanged:: 3.8
The :meth:`toprettyxml` method now preserves the attribute order specified
by the user.

.. versionchanged:: 3.9
The *standalone* parameter was added.

.. versionchanged:: next
Whitespace is no longer added inside an element with mixed content
or marked with ``xml:space="preserve"``.

.. _dom-example:

DOM Example
Expand Down
8 changes: 8 additions & 0 deletions Doc/library/xml.etree.elementtree.rst
Original file line number Diff line number Diff line change
Expand Up @@ -631,8 +631,16 @@ Functions
characters by default. For indenting partial subtrees inside of an
already indented tree, pass the initial indentation level as *level*.

No whitespace is added inside an element
which is marked with ``xml:space="preserve"``
or which contains text, because this would change its content.

.. versionadded:: 3.9

.. versionchanged:: next
Whitespace is no longer added inside an element with mixed content
or marked with ``xml:space="preserve"``.


.. function:: iselement(element)

Expand Down
17 changes: 17 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,14 @@ xml
and :meth:`!Document.createEntityReference`.
(Contributed by Jason Orendorff and Serhiy Storchaka in :gh:`44871`.)

* :meth:`~xml.dom.minidom.Node.toprettyxml` in :mod:`xml.dom.minidom`
and :func:`~xml.etree.ElementTree.indent` in :mod:`xml.etree.ElementTree`
no longer add whitespace inside an element
which is marked with ``xml:space="preserve"`` or which contains text.
:meth:`!toprettyxml` also takes into account
the content model declared in the DTD.
(Contributed by Serhiy Storchaka in :gh:`81623`.)

* Add :meth:`!GetSpecifiedAttributeCount` method
to the :mod:`XML parser <xml.parsers.expat>` objects.
It tells how many of the reported attributes were given in the start tag
Expand Down Expand Up @@ -940,6 +948,15 @@ that may require changes to your code.
Attributes defaulted in the DTD are no longer omitted when parsing.
(Contributed by Jason Orendorff and Serhiy Storchaka in :gh:`44871`.)

* :meth:`~xml.dom.minidom.Node.toprettyxml` in :mod:`xml.dom.minidom`
and :func:`~xml.etree.ElementTree.indent` in :mod:`xml.etree.ElementTree`
no longer add whitespace inside an element
which is marked with ``xml:space="preserve"`` or which contains text,
because this changed the content of the element.
:meth:`!toprettyxml` also takes into account
the content model declared in the DTD.
(Contributed by Serhiy Storchaka in :gh:`81623`.)

* On Windows, seeking a pipe now fails instead of silently appearing to
succeed: :func:`os.lseek` and :meth:`~io.IOBase.seek` raise :exc:`OSError`,
and :meth:`~io.IOBase.seekable` returns ``False``. As a consequence,
Expand Down
60 changes: 52 additions & 8 deletions Lib/test/test_minidom.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,32 +617,76 @@ def testAltNewline(self):
self.assertEqual(domstr, str.replace("\n", "\r\n"))

def test_toprettyxml_with_text_nodes(self):
# see issue #4147, text nodes are not indented
# see gh-48397 and gh-81623,
# the content of an element with text is not changed
decl = '<?xml version="1.0" ?>\n'
self.assertEqual(parseString('<B>A</B>').toprettyxml(),
decl + '<B>A</B>\n')
self.assertEqual(parseString('<C>A<B>A</B></C>').toprettyxml(),
decl + '<C>\n\tA\n\t<B>A</B>\n</C>\n')
decl + '<C>A<B>A</B></C>\n')
self.assertEqual(parseString('<C><B>A</B>A</C>').toprettyxml(),
decl + '<C>\n\t<B>A</B>\n\tA\n</C>\n')
decl + '<C><B>A</B>A</C>\n')
self.assertEqual(parseString('<C><B>A</B><B>A</B></C>').toprettyxml(),
decl + '<C>\n\t<B>A</B>\n\t<B>A</B>\n</C>\n')
self.assertEqual(parseString('<C><B>A</B>A<B>A</B></C>').toprettyxml(),
decl + '<C>\n\t<B>A</B>\n\tA\n\t<B>A</B>\n</C>\n')
decl + '<C><B>A</B>A<B>A</B></C>\n')
# toprettyxml treats whitespace between elements as insignificant
self.assertEqual(parseString('<C> <B>A</B> </C>').toprettyxml(),
decl + '<C>\n\t \n\t<B>A</B>\n\t \n</C>\n')

def test_toprettyxml_with_adjacent_text_nodes(self):
# see issue #4147, adjacent text nodes are indented normally
# see gh-81623, adjacent text nodes are not separated
dom = Document()
elem = dom.createElement('elem')
elem.appendChild(dom.createTextNode('TEXT'))
elem.appendChild(dom.createTextNode('TEXT'))
dom.appendChild(elem)
decl = '<?xml version="1.0" ?>\n'
self.assertEqual(dom.toprettyxml(),
decl + '<elem>\n\tTEXT\n\tTEXT\n</elem>\n')
self.assertEqual(dom.toprettyxml(), decl + '<elem>TEXTTEXT</elem>\n')

def test_toprettyxml_preserve(self):
decl = '<?xml version="1.0" ?>\n'
# xml:space="preserve" applies to the whole subtree
self.assertEqual(
parseString('<C xml:space="preserve"><B>A</B><B>A</B></C>'
).toprettyxml(),
decl + '<C xml:space="preserve"><B>A</B><B>A</B></C>\n')
self.assertEqual(
parseString('<C xml:space="preserve"><B><D/></B></C>'
).toprettyxml(),
decl + '<C xml:space="preserve"><B><D/></B></C>\n')
# other values do not preserve whitespace
self.assertEqual(
parseString('<C xml:space="default"><B>A</B></C>').toprettyxml(),
decl + '<C xml:space="default">\n\t<B>A</B>\n</C>\n')

def test_toprettyxml_with_non_xml_whitespace(self):
# only " \t\r\n" are whitespace in XML (see XML 1.0, 2.3)
decl = '<?xml version="1.0" ?>\n'
self.assertEqual(parseString('<C>\xa0<B>A</B></C>').toprettyxml(),
decl + '<C>\xa0<B>A</B></C>\n')

def test_toprettyxml_with_dtd(self):
decl = '<?xml version="1.0" ?>\n'
# only whitespace in element content is ignorable
doctype = ('<!DOCTYPE C [<!ELEMENT C (#PCDATA|B)*>'
'<!ELEMENT B (#PCDATA)>]>')
self.assertEqual(
parseString(doctype + '<C><B>A</B><B>A</B></C>').toprettyxml(),
decl + doctype + '\n<C><B>A</B><B>A</B></C>\n')
doctype = '<!DOCTYPE C [<!ELEMENT C (B)*><!ELEMENT B (#PCDATA)>]>'
self.assertEqual(
parseString(doctype + '<C><B>A</B><B>A</B></C>').toprettyxml(),
decl + doctype + '\n<C>\n\t<B>A</B>\n\t<B>A</B>\n</C>\n')

def test_toprettyxml_with_cdata_section(self):
decl = '<?xml version="1.0" ?>\n'
self.assertEqual(
parseString('<C><![CDATA[A]]><B>A</B></C>').toprettyxml(),
decl + '<C><![CDATA[A]]><B>A</B></C>\n')

def test_toprettyxml_preserves_content_of_text_node(self):
# see issue #4147
# see gh-48397
for str in ('<B>A</B>', '<A><B>C</B></A>'):
dom = parseString(str)
dom2 = parseString(dom.toprettyxml())
Expand Down
35 changes: 33 additions & 2 deletions Lib/test/test_xml_etree.py
Original file line number Diff line number Diff line change
Expand Up @@ -773,9 +773,10 @@ def test_indent(self):
ET.indent(elem)
self.assertEqual(ET.tostring(elem), b'<html>\n <body>text</body>\n</html>')

# an element with mixed content is not indented
elem = ET.XML("<html><body>text</body>tail</html>")
ET.indent(elem)
self.assertEqual(ET.tostring(elem), b'<html>\n <body>text</body>tail</html>')
self.assertEqual(ET.tostring(elem), b'<html><body>text</body>tail</html>')

elem = ET.XML("<html><body><p>par</p>\n<p>text</p>\t<p><br/></p></body></html>")
ET.indent(elem)
Expand Down Expand Up @@ -851,9 +852,39 @@ def test_indent_non_xml_whitespace(self):
ET.indent(elem)
self.assertEqual(
ET.tostring(elem),
b'<html>&#160;<body>\n <p>text</p>&#160;</body>\n</html>'
b'<html>&#160;<body><p>text</p>&#160;</body></html>'
)

def test_indent_preserve(self):
# xml:space="preserve" applies to the whole subtree
elem = ET.XML('<html xml:space="preserve"> <body><p>text</p></body> </html>')
ET.indent(elem)
self.assertEqual(
ET.tostring(elem),
b'<html xml:space="preserve"> <body><p>text</p></body> </html>'
)
# other values do not preserve whitespace
elem = ET.XML('<html xml:space="default"><body><p>text</p></body></html>')
ET.indent(elem)
self.assertEqual(
ET.tostring(elem),
b'<html xml:space="default">\n'
b' <body>\n'
b' <p>text</p>\n'
b' </body>\n'
b'</html>'
)

def test_indent_mixed_content(self):
# whitespace in an element which contains text is significant
elem = ET.XML('<p>hello <b>x</b> <i>y</i></p>')
ET.indent(elem)
self.assertEqual(ET.tostring(elem), b'<p>hello <b>x</b> <i>y</i></p>')
# the subtree of such element is not indented either
elem = ET.XML('<p>hello <b><i>y</i></b></p>')
ET.indent(elem)
self.assertEqual(ET.tostring(elem), b'<p>hello <b><i>y</i></b></p>')

def test_indent_level(self):
elem = ET.XML("<html><body><p>pre<br/>post</p><p>text</p></body></html>")
with self.assertRaises(ValueError):
Expand Down
23 changes: 23 additions & 0 deletions Lib/xml/dom/minidom.py
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,10 @@ def writexml(self, writer, indent="", addindent="", newl=""):
self.childNodes[0].nodeType in (
Node.TEXT_NODE, Node.CDATA_SECTION_NODE)):
self.childNodes[0].writexml(writer, '', '', '')
elif self._preserves_whitespace():
# Adding whitespace here would change the content.
for node in self.childNodes:
node.writexml(writer, '', '', '')
else:
writer.write(newl)
for node in self.childNodes:
Expand All @@ -949,6 +953,25 @@ def writexml(self, writer, indent="", addindent="", newl=""):
else:
writer.write("/>%s"%(newl))

def _preserves_whitespace(self):
"""Returns true iff whitespace in the content is significant.

This is the case if the element is marked with xml:space="preserve",
if the DTD declares that its content model is not element content,
or, in absence of such declaration, if it contains text.
"""
if self.getAttribute("xml:space") == "preserve":
return True
doc = self.ownerDocument
info = doc and doc._get_elem_info(self)
if info is not None:
# Only whitespace in element content is ignorable
# (see XML 1.0, 3.2.1).
return not info.isElementContent()
return any(node.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE)
and node.data.strip(_XML_WHITESPACE)
for node in self.childNodes)

def _get_attributes(self):
self._ensure_attributes()
return NamedNodeMap(self._attrs, self._attrsNS, self)
Expand Down
25 changes: 19 additions & 6 deletions Lib/xml/etree/ElementTree.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@
# The white space characters of the XML specification (see XML 1.0, 2.3).
_XML_WHITESPACE = " \t\r\n"

# The xml:space attribute (see XML 1.0, 2.10).
_XML_SPACE = "{http://www.w3.org/XML/1998/namespace}space"

class ParseError(SyntaxError):
"""An error when parsing an XML document.

Expand Down Expand Up @@ -1196,7 +1199,20 @@ def indent(tree, space=" ", level=0):
# Reduce the memory consumption by reusing indentation strings.
indentations = ["\n" + level * space]

def _preserves_whitespace(elem):
# True iff whitespace in the content of the element is significant.
if elem.get(_XML_SPACE) == "preserve":
return True
if elem.text and elem.text.strip(_XML_WHITESPACE):
return True
return any(child.tail and child.tail.strip(_XML_WHITESPACE)
for child in elem)

def _indent_children(elem, level):
if _preserves_whitespace(elem):
# Adding whitespace here would change the content.
return

# Start a new indentation level for the first child.
child_level = level + 1
try:
Expand All @@ -1205,18 +1221,15 @@ def _indent_children(elem, level):
child_indentation = indentations[level] + space
indentations.append(child_indentation)

if not elem.text or not elem.text.strip(_XML_WHITESPACE):
elem.text = child_indentation
elem.text = child_indentation

for child in elem:
if len(child):
_indent_children(child, child_level)
if not child.tail or not child.tail.strip(_XML_WHITESPACE):
child.tail = child_indentation
child.tail = child_indentation

# Dedent after the last child by overwriting the previous indentation.
if not child.tail.strip(_XML_WHITESPACE):
child.tail = indentations[level]
child.tail = indentations[level]

_indent_children(tree, 0)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
:meth:`~xml.dom.minidom.Node.toprettyxml` in :mod:`xml.dom.minidom` and
:func:`~xml.etree.ElementTree.indent` in :mod:`xml.etree.ElementTree` no longer
add whitespace inside an element which is marked with ``xml:space="preserve"``
or which contains text (:meth:`!toprettyxml` also takes into account the
content model declared in the DTD). Previously such indentation changed the
content of the element.
Loading