mirror of
https://github.com/python/cpython.git
synced 2025-07-23 11:15:24 +00:00
[3.13] gh-128302: Fix bugs in xml.dom.xmlbuilder (GH-128284) (#128582)
gh-128302: Fix bugs in xml.dom.xmlbuilder (GH-128284)
* Allow DOMParser.parse() to correctly handle DOMInputSource instances
that only have a systemId attribute set.
* Fix DOMEntityResolver.resolveEntity(), which was broken by the
Python 3.0 transition.
* Add Lib/test/test_xml_dom_xmlbuilder.py with few tests.
(cherry picked from commit 6ea04da270
)
Co-authored-by: Stephen Morton <git@tungol.org>
This commit is contained in:
parent
65da5db28a
commit
3714fd07c5
4 changed files with 100 additions and 5 deletions
88
Lib/test/test_xml_dom_xmlbuilder.py
Normal file
88
Lib/test/test_xml_dom_xmlbuilder.py
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import io
|
||||||
|
import unittest
|
||||||
|
from http import client
|
||||||
|
from test.test_httplib import FakeSocket
|
||||||
|
from unittest import mock
|
||||||
|
from xml.dom import getDOMImplementation, minidom, xmlbuilder
|
||||||
|
|
||||||
|
SMALL_SAMPLE = b"""<?xml version="1.0"?>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xdc="http://www.xml.com/books">
|
||||||
|
<!-- A comment -->
|
||||||
|
<title>Introduction to XSL</title>
|
||||||
|
<hr/>
|
||||||
|
<p><xdc:author xdc:attrib="prefixed attribute" attrib="other attrib">A. Namespace</xdc:author></p>
|
||||||
|
</html>"""
|
||||||
|
|
||||||
|
|
||||||
|
class XMLBuilderTest(unittest.TestCase):
|
||||||
|
def test_entity_resolver(self):
|
||||||
|
body = (
|
||||||
|
b"HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset=utf-8\r\n\r\n"
|
||||||
|
+ SMALL_SAMPLE
|
||||||
|
)
|
||||||
|
|
||||||
|
sock = FakeSocket(body)
|
||||||
|
response = client.HTTPResponse(sock)
|
||||||
|
response.begin()
|
||||||
|
attrs = {"open.return_value": response}
|
||||||
|
opener = mock.Mock(**attrs)
|
||||||
|
|
||||||
|
resolver = xmlbuilder.DOMEntityResolver()
|
||||||
|
|
||||||
|
with mock.patch("urllib.request.build_opener") as mock_build:
|
||||||
|
mock_build.return_value = opener
|
||||||
|
source = resolver.resolveEntity(None, "http://example.com/2000/svg")
|
||||||
|
|
||||||
|
self.assertIsInstance(source, xmlbuilder.DOMInputSource)
|
||||||
|
self.assertIsNone(source.publicId)
|
||||||
|
self.assertEqual(source.systemId, "http://example.com/2000/svg")
|
||||||
|
self.assertEqual(source.baseURI, "http://example.com/2000/")
|
||||||
|
self.assertEqual(source.encoding, "utf-8")
|
||||||
|
self.assertIs(source.byteStream, response)
|
||||||
|
|
||||||
|
self.assertIsNone(source.characterStream)
|
||||||
|
self.assertIsNone(source.stringData)
|
||||||
|
|
||||||
|
def test_builder(self):
|
||||||
|
imp = getDOMImplementation()
|
||||||
|
self.assertIsInstance(imp, xmlbuilder.DOMImplementationLS)
|
||||||
|
|
||||||
|
builder = imp.createDOMBuilder(imp.MODE_SYNCHRONOUS, None)
|
||||||
|
self.assertIsInstance(builder, xmlbuilder.DOMBuilder)
|
||||||
|
|
||||||
|
def test_parse_uri(self):
|
||||||
|
body = (
|
||||||
|
b"HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset=utf-8\r\n\r\n"
|
||||||
|
+ SMALL_SAMPLE
|
||||||
|
)
|
||||||
|
|
||||||
|
sock = FakeSocket(body)
|
||||||
|
response = client.HTTPResponse(sock)
|
||||||
|
response.begin()
|
||||||
|
attrs = {"open.return_value": response}
|
||||||
|
opener = mock.Mock(**attrs)
|
||||||
|
|
||||||
|
with mock.patch("urllib.request.build_opener") as mock_build:
|
||||||
|
mock_build.return_value = opener
|
||||||
|
|
||||||
|
imp = getDOMImplementation()
|
||||||
|
builder = imp.createDOMBuilder(imp.MODE_SYNCHRONOUS, None)
|
||||||
|
document = builder.parseURI("http://example.com/2000/svg")
|
||||||
|
|
||||||
|
self.assertIsInstance(document, minidom.Document)
|
||||||
|
self.assertEqual(len(document.childNodes), 1)
|
||||||
|
|
||||||
|
def test_parse_with_systemId(self):
|
||||||
|
response = io.BytesIO(SMALL_SAMPLE)
|
||||||
|
|
||||||
|
with mock.patch("urllib.request.urlopen") as mock_open:
|
||||||
|
mock_open.return_value = response
|
||||||
|
|
||||||
|
imp = getDOMImplementation()
|
||||||
|
source = imp.createDOMInputSource()
|
||||||
|
builder = imp.createDOMBuilder(imp.MODE_SYNCHRONOUS, None)
|
||||||
|
source.systemId = "http://example.com/2000/svg"
|
||||||
|
document = builder.parse(source)
|
||||||
|
|
||||||
|
self.assertIsInstance(document, minidom.Document)
|
||||||
|
self.assertEqual(len(document.childNodes), 1)
|
|
@ -189,7 +189,7 @@ class DOMBuilder:
|
||||||
options.filter = self.filter
|
options.filter = self.filter
|
||||||
options.errorHandler = self.errorHandler
|
options.errorHandler = self.errorHandler
|
||||||
fp = input.byteStream
|
fp = input.byteStream
|
||||||
if fp is None and options.systemId:
|
if fp is None and input.systemId:
|
||||||
import urllib.request
|
import urllib.request
|
||||||
fp = urllib.request.urlopen(input.systemId)
|
fp = urllib.request.urlopen(input.systemId)
|
||||||
return self._parse_bytestream(fp, options)
|
return self._parse_bytestream(fp, options)
|
||||||
|
@ -247,10 +247,12 @@ class DOMEntityResolver(object):
|
||||||
|
|
||||||
def _guess_media_encoding(self, source):
|
def _guess_media_encoding(self, source):
|
||||||
info = source.byteStream.info()
|
info = source.byteStream.info()
|
||||||
if "Content-Type" in info:
|
# import email.message
|
||||||
for param in info.getplist():
|
# assert isinstance(info, email.message.Message)
|
||||||
if param.startswith("charset="):
|
charset = info.get_param('charset')
|
||||||
return param.split("=", 1)[1].lower()
|
if charset is not None:
|
||||||
|
return charset.lower()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class DOMInputSource(object):
|
class DOMInputSource(object):
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Allow :meth:`!xml.dom.xmlbuilder.DOMParser.parse` to correctly handle
|
||||||
|
:class:`!xml.dom.xmlbuilder.DOMInputSource` instances that only have a
|
||||||
|
:attr:`!systemId` attribute set.
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix :meth:`!xml.dom.xmlbuilder.DOMEntityResolver.resolveEntity`, which was
|
||||||
|
broken by the Python 3.0 transition.
|
Loading…
Add table
Add a link
Reference in a new issue