Skip to content

Commit 622dba6

Browse files
[3.15] gh-157406: Report all document type declarations in the Python XMLParser (GH-157408)
The Python implementation of XMLParser only reported a document type declaration with an external identifier. Use the Expat handler, like the C implementation does. (cherry picked from commit f802d2d) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent e325fae commit 622dba6

3 files changed

Lines changed: 29 additions & 33 deletions

File tree

Lib/test/test_xml_etree.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4036,6 +4036,21 @@ def close(self):
40364036
('html', '-//W3C//DTD XHTML 1.0 Transitional//EN',
40374037
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'))
40384038

4039+
for doctype, expected in [
4040+
('<!DOCTYPE html>', ('html', None, None)),
4041+
('<!DOCTYPE html [<!ENTITY e "v">]>', ('html', None, None)),
4042+
('<!DOCTYPE html SYSTEM "a.dtd">', ('html', None, 'a.dtd')),
4043+
('<!DOCTYPE html SYSTEM "a.dtd" [<!ENTITY e "v">]>',
4044+
('html', None, 'a.dtd')),
4045+
('<!DOCTYPE html PUBLIC "-//P" "a.dtd">', ('html', '-//P', 'a.dtd')),
4046+
("<!DOCTYPE\nhtml\nPUBLIC\n'-//P'\n'a.dtd'\n>",
4047+
('html', '-//P', 'a.dtd')),
4048+
]:
4049+
with self.subTest(doctype=doctype):
4050+
parser = ET.XMLParser(target=DoctypeParser())
4051+
parser.feed(doctype + '<html/>')
4052+
self.assertEqual(parser.close(), expected)
4053+
40394054
def test_builder_lookup_errors(self):
40404055
class RaisingBuilder:
40414056
def __init__(self, raise_in=None, what=ValueError):

Lib/xml/etree/ElementTree.py

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,10 +1583,10 @@ def __init__(self, *, target=None, encoding=None):
15831583
parser.CommentHandler = target.comment
15841584
if hasattr(target, 'pi'):
15851585
parser.ProcessingInstructionHandler = target.pi
1586+
parser.StartDoctypeDeclHandler = self._start_doctype
15861587
# Configure pyexpat: buffering, new-style attribute handling.
15871588
parser.buffer_text = 1
15881589
parser.ordered_attributes = 1
1589-
self._doctype = None
15901590
self.entity = {}
15911591
try:
15921592
self.version = "Expat %d.%d.%d" % expat.version_info
@@ -1705,38 +1705,15 @@ def _default(self, text):
17051705
err.lineno = self.parser.ErrorLineNumber
17061706
err.offset = self.parser.ErrorColumnNumber
17071707
raise err
1708-
elif prefix == "<" and text[:9] == "<!DOCTYPE":
1709-
self._doctype = [] # inside a doctype declaration
1710-
elif self._doctype is not None:
1711-
# parse doctype contents
1712-
if prefix == ">":
1713-
self._doctype = None
1714-
return
1715-
text = text.strip()
1716-
if not text:
1717-
return
1718-
self._doctype.append(text)
1719-
n = len(self._doctype)
1720-
if n > 2:
1721-
type = self._doctype[1]
1722-
if type == "PUBLIC" and n == 4:
1723-
name, type, pubid, system = self._doctype
1724-
if pubid:
1725-
pubid = pubid[1:-1]
1726-
elif type == "SYSTEM" and n == 3:
1727-
name, type, system = self._doctype
1728-
pubid = None
1729-
else:
1730-
return
1731-
if hasattr(self.target, "doctype"):
1732-
self.target.doctype(name, pubid, system[1:-1])
1733-
elif hasattr(self, "doctype"):
1734-
warnings.warn(
1735-
"The doctype() method of XMLParser is ignored. "
1736-
"Define doctype() method on the TreeBuilder target.",
1737-
RuntimeWarning)
1738-
1739-
self._doctype = None
1708+
1709+
def _start_doctype(self, name, system, pubid, has_internal_subset):
1710+
if hasattr(self.target, "doctype"):
1711+
self.target.doctype(name, pubid, system)
1712+
elif hasattr(self, "doctype"):
1713+
warnings.warn(
1714+
"The doctype() method of XMLParser is ignored. "
1715+
"Define doctype() method on the TreeBuilder target.",
1716+
RuntimeWarning)
17401717

17411718
def feed(self, data):
17421719
"""Feed encoded data to parser."""
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix the Python implementation of :class:`xml.etree.ElementTree.XMLParser`:
2+
the ``doctype()`` method of the target is now called for a document type
3+
declaration without an external identifier, like ``<!DOCTYPE html>``,
4+
as in the C implementation.

0 commit comments

Comments
 (0)