bpo-37534: Allow adding Standalone Document Declaration when generating XML documents (GH-14912)

This commit is contained in:
Henry Harutyunyan 2020-02-29 12:22:19 +04:00 committed by GitHub
parent 02673352b5
commit dc04a0571e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 51 additions and 13 deletions

View File

@ -132,7 +132,8 @@ module documentation. This section lists the differences between the API and
... # Work with dom. ... # Work with dom.
.. method:: Node.writexml(writer, indent="", addindent="", newl="") .. method:: Node.writexml(writer, indent="", addindent="", newl="",
encoding=None, standalone=None)
Write XML to the writer object. The writer receives texts but not bytes as input, Write XML to the writer object. The writer receives texts but not bytes as input,
it should have a :meth:`write` method which matches that of the file object it should have a :meth:`write` method which matches that of the file object
@ -144,11 +145,18 @@ module documentation. This section lists the differences between the API and
For the :class:`Document` node, an additional keyword argument *encoding* can For the :class:`Document` node, an additional keyword argument *encoding* can
be used to specify the encoding field of the XML header. be used to specify the encoding field of the XML header.
Silimarly, explicitly stating the *standalone* argument causes the
standalone document declarations to be added to the prologue of the XML
document.
If the value is set to `True`, `standalone="yes"` is added,
otherwise it is set to `"no"`.
Not stating the argument will omit the declaration from the document.
.. versionchanged:: 3.8 .. versionchanged:: 3.8
The :meth:`writexml` method now preserves the attribute order specified The :meth:`writexml` method now preserves the attribute order specified
by the user. by the user.
.. method:: Node.toxml(encoding=None) .. method:: Node.toxml(encoding=None, standalone=None)
Return a string or byte string containing the XML represented by Return a string or byte string containing the XML represented by
the DOM node. the DOM node.
@ -160,11 +168,14 @@ module documentation. This section lists the differences between the API and
encoding. Encoding this string in an encoding other than UTF-8 is encoding. Encoding this string in an encoding other than UTF-8 is
likely incorrect, since UTF-8 is the default encoding of XML. likely incorrect, since UTF-8 is the default encoding of XML.
The *standalone* argument behaves exactly as in :meth:`writexml`.
.. versionchanged:: 3.8 .. versionchanged:: 3.8
The :meth:`toxml` method now preserves the attribute order specified The :meth:`toxml` method now preserves the attribute order specified
by the user. by the user.
.. method:: Node.toprettyxml(indent="\\t", newl="\\n", encoding=None) .. method:: Node.toprettyxml(indent="\\t", newl="\\n", encoding=None,
standalone=None)
Return a pretty-printed version of the document. *indent* specifies the Return a pretty-printed version of the document. *indent* specifies the
indentation string and defaults to a tabulator; *newl* specifies the string indentation string and defaults to a tabulator; *newl* specifies the string
@ -173,6 +184,8 @@ module documentation. This section lists the differences between the API and
The *encoding* argument behaves like the corresponding argument of The *encoding* argument behaves like the corresponding argument of
:meth:`toxml`. :meth:`toxml`.
The *standalone* argument behaves exactly as in :meth:`writexml`.
.. versionchanged:: 3.8 .. versionchanged:: 3.8
The :meth:`toprettyxml` method now preserves the attribute order specified The :meth:`toprettyxml` method now preserves the attribute order specified
by the user. by the user.

View File

@ -1152,6 +1152,22 @@ def testEncodings(self):
doc.unlink() doc.unlink()
def testStandalone(self):
doc = parseString('<foo>&#x20ac;</foo>')
self.assertEqual(doc.toxml(),
'<?xml version="1.0" ?><foo>\u20ac</foo>')
self.assertEqual(doc.toxml(standalone=None),
'<?xml version="1.0" ?><foo>\u20ac</foo>')
self.assertEqual(doc.toxml(standalone=True),
'<?xml version="1.0" standalone="yes"?><foo>\u20ac</foo>')
self.assertEqual(doc.toxml(standalone=False),
'<?xml version="1.0" standalone="no"?><foo>\u20ac</foo>')
self.assertEqual(doc.toxml('utf-8', True),
b'<?xml version="1.0" encoding="utf-8" standalone="yes"?>'
b'<foo>\xe2\x82\xac</foo>')
doc.unlink()
class UserDataHandler: class UserDataHandler:
called = 0 called = 0
def handle(self, operation, key, data, src, dst): def handle(self, operation, key, data, src, dst):

View File

@ -43,10 +43,11 @@ class Node(xml.dom.Node):
def __bool__(self): def __bool__(self):
return True return True
def toxml(self, encoding=None): def toxml(self, encoding=None, standalone=None):
return self.toprettyxml("", "", encoding) return self.toprettyxml("", "", encoding, standalone)
def toprettyxml(self, indent="\t", newl="\n", encoding=None): def toprettyxml(self, indent="\t", newl="\n", encoding=None,
standalone=None):
if encoding is None: if encoding is None:
writer = io.StringIO() writer = io.StringIO()
else: else:
@ -56,7 +57,7 @@ def toprettyxml(self, indent="\t", newl="\n", encoding=None):
newline='\n') newline='\n')
if self.nodeType == Node.DOCUMENT_NODE: if self.nodeType == Node.DOCUMENT_NODE:
# Can pass encoding only to document, to put it into XML header # Can pass encoding only to document, to put it into XML header
self.writexml(writer, "", indent, newl, encoding) self.writexml(writer, "", indent, newl, encoding, standalone)
else: else:
self.writexml(writer, "", indent, newl) self.writexml(writer, "", indent, newl)
if encoding is None: if encoding is None:
@ -1787,12 +1788,17 @@ def importNode(self, node, deep):
raise xml.dom.NotSupportedErr("cannot import document type nodes") raise xml.dom.NotSupportedErr("cannot import document type nodes")
return _clone_node(node, deep, self) return _clone_node(node, deep, self)
def writexml(self, writer, indent="", addindent="", newl="", encoding=None): def writexml(self, writer, indent="", addindent="", newl="", encoding=None,
if encoding is None: standalone=None):
writer.write('<?xml version="1.0" ?>'+newl) declarations = []
else:
writer.write('<?xml version="1.0" encoding="%s"?>%s' % ( if encoding:
encoding, newl)) declarations.append(f'encoding="{encoding}"')
if standalone is not None:
declarations.append(f'standalone="{"yes" if standalone else "no"}"')
writer.write(f'<?xml version="1.0" {" ".join(declarations)}?>{newl}')
for node in self.childNodes: for node in self.childNodes:
node.writexml(writer, indent, addindent, newl) node.writexml(writer, indent, addindent, newl)

View File

@ -659,6 +659,7 @@ David Harrigan
Brian Harring Brian Harring
Jonathan Hartley Jonathan Hartley
Travis B. Hartwell Travis B. Hartwell
Henrik Harutyunyan
Shane Harvey Shane Harvey
Larry Hastings Larry Hastings
Tim Hatch Tim Hatch

View File

@ -0,0 +1,2 @@
When using minidom module to generate XML documents the ability to add Standalone Document Declaration is added.
All the changes are made to generate a document in compliance with Extensible Markup Language (XML) 1.0 (Fifth Edition) W3C Recommendation (available here: https://www.w3.org/TR/xml/#sec-prolog-dtd).