From 1292a276ef01dd1378d925779076bc33c48a2569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=87=E5=BD=AC=E5=AE=87?= <3431359639@qq.com> Date: Mon, 31 Aug 2026 16:05:04 +0800 Subject: [PATCH] gh-156667: Add DOM Level 3 names to minidom --- Lib/test/test_minidom.py | 55 ++++++++++++ Lib/xml/dom/minidom.py | 86 +++++++++++++++++-- ...-08-31-16-10-00.gh-issue-156667.kQ4mLs.rst | 2 + 3 files changed, 135 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-08-31-16-10-00.gh-issue-156667.kQ4mLs.rst diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 3735a6046891ea..059e7767a3f623 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -59,6 +59,59 @@ def checkWholeText(self, node, s): t = node.wholeText self.assertEqual(t, s, "looking for %r, found %r" % (s, t)) + def testDOMLevel3GetFeature(self): + impl = getDOMImplementation() + doc = impl.createDocument(None, "doc", None) + node = doc.documentElement + + self.assertIs(impl.getFeature("Core", "2.0"), impl) + self.assertIs(node.getFeature("Core", "2.0"), node) + self.assertIsNone(impl.getFeature("Core", "3.0")) + self.assertIsNone(node.getFeature("Core", "3.0")) + + # The names from the working draft are kept for compatibility. + self.assertIs(impl.getInterface("Core"), impl) + self.assertIs(node.getInterface("Core"), node) + + def testDOMLevel3AttributeNames(self): + doc = Document() + pairs = ( + ("actualEncoding", "inputEncoding", "us-ascii", "utf-8"), + ("encoding", "xmlEncoding", "utf-8", "utf-16"), + ("standalone", "xmlStandalone", False, True), + ("version", "xmlVersion", "1.0", "1.1"), + ) + for old_name, new_name, old_value, new_value in pairs: + with self.subTest(node="Document", name=new_name): + setattr(doc, old_name, old_value) + self.assertEqual(getattr(doc, new_name), old_value) + setattr(doc, new_name, new_value) + self.assertEqual(getattr(doc, old_name), new_value) + + entity = xml.dom.minidom.Entity("entity", None, None, None) + pairs = ( + ("actualEncoding", "inputEncoding", "us-ascii", "utf-8"), + ("encoding", "xmlEncoding", "utf-8", "utf-16"), + ("version", "xmlVersion", "1.0", "1.1"), + ) + for old_name, new_name, old_value, new_value in pairs: + with self.subTest(node="Entity", name=new_name): + setattr(entity, old_name, old_value) + self.assertEqual(getattr(entity, new_name), old_value) + setattr(entity, new_name, new_value) + self.assertEqual(getattr(entity, old_name), new_value) + + doc = parseString( + "" + "" + "]> " + ) + text = doc.documentElement.firstChild + self.assertTrue(text.isElementContentWhitespace) + self.assertEqual(text.isElementContentWhitespace, + text.isWhitespaceInElementContent) + def testDocumentAsyncAttr(self): doc = Document() self.assertFalse(doc.async_) @@ -1555,12 +1608,14 @@ def testSchemaType(self): # DTD-based namespace is right. The names can vary by loader # since each supports a different level of DTD information. t = elem.schemaType + self.assertIs(elem.schemaTypeInfo, t) self.confirm(t.name is None and t.namespace == xml.dom.EMPTY_NAMESPACE) names = "id notid text enum ref refs ent ents nm nms".split() for name in names: a = elem.getAttributeNode(name) t = a.schemaType + self.assertEqual(a.schemaTypeInfo, t) self.confirm(hasattr(t, "name") and t.namespace == xml.dom.EMPTY_NAMESPACE) diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py index 5fd3911bd3c9eb..ac30f50b19bcbc 100644 --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -227,17 +227,20 @@ def _get_localName(self): # Overridden in Element and Attr where localName can be Non-Null return None - # Node interfaces from Level 3 (WD 9 April 2002) + # Node interfaces from DOM Level 3 def isSameNode(self, other): return self is other - def getInterface(self, feature): - if self.isSupported(feature, None): + def getFeature(self, feature, version): + if self.isSupported(feature, version): return self else: return None + def getInterface(self, feature): + return self.getFeature(feature, None) + # The "user data" functions use a dictionary that is only present # if some user data has been set, so be careful not to assume it # exists. @@ -504,9 +507,12 @@ def _get_schemaType(self): else: return info.getAttributeType(self.nodeName) + _get_schemaTypeInfo = _get_schemaType + defproperty(Attr, "isId", doc="True if this attribute is an ID.") defproperty(Attr, "localName", doc="Namespace-local name of this attribute.") defproperty(Attr, "schemaType", doc="Schema type for this attribute.") +defproperty(Attr, "schemaTypeInfo", doc="Schema type for this attribute.") class NamedNodeMap(object): @@ -712,7 +718,7 @@ class Element(Node): 'nextSibling', 'previousSibling') nodeType = Node.ELEMENT_NODE nodeValue = None - schemaType = _no_type + schemaType = schemaTypeInfo = _no_type _magic_id_nodes = 0 @@ -1220,9 +1226,14 @@ def _get_isWhitespaceInElementContent(self): else: return info.isElementContent() + _get_isElementContentWhitespace = _get_isWhitespaceInElementContent + defproperty(Text, "isWhitespaceInElementContent", doc="True iff this text node contains only whitespace" " and is in element content.") +defproperty(Text, "isElementContentWhitespace", + doc="True iff this text node contains only whitespace" + " and is in element content.") defproperty(Text, "wholeText", doc="The text of all logically-adjacent text nodes.") @@ -1429,12 +1440,36 @@ def __init__(self, name, publicId, systemId, notation): def _get_actualEncoding(self): return self.actualEncoding + def _get_inputEncoding(self): + return self.actualEncoding + + def _set_inputEncoding(self, value): + self.actualEncoding = value + + inputEncoding = property(_get_inputEncoding, _set_inputEncoding) + def _get_encoding(self): return self.encoding + def _get_xmlEncoding(self): + return self.encoding + + def _set_xmlEncoding(self, value): + self.encoding = value + + xmlEncoding = property(_get_xmlEncoding, _set_xmlEncoding) + def _get_version(self): return self.version + def _get_xmlVersion(self): + return self.version + + def _set_xmlVersion(self, value): + self.version = value + + xmlVersion = property(_get_xmlVersion, _set_xmlVersion) + def appendChild(self, newChild): raise xml.dom.HierarchyRequestErr( "cannot append children to an entity node") @@ -1567,14 +1602,17 @@ def createDocumentType(self, qualifiedName, publicId, systemId): doctype.systemId = systemId return doctype - # DOM Level 3 (WD 9 April 2002) + # DOM Level 3 - def getInterface(self, feature): - if self.hasFeature(feature, None): + def getFeature(self, feature, version): + if self.hasFeature(feature, version): return self else: return None + def getInterface(self, feature): + return self.getFeature(feature, None) + # internal def _create_document(self): return Document() @@ -1644,7 +1682,7 @@ class Document(Node, DocumentLS): previousSibling = nextSibling = None - # Document attributes from Level 3 (WD 9 April 2002) + # Document attributes from DOM Level 3 actualEncoding = None encoding = None @@ -1675,6 +1713,14 @@ def _get_elem_info(self, element): def _get_actualEncoding(self): return self.actualEncoding + def _get_inputEncoding(self): + return self.actualEncoding + + def _set_inputEncoding(self, value): + self.actualEncoding = value + + inputEncoding = property(_get_inputEncoding, _set_inputEncoding) + def _get_doctype(self): return self.doctype @@ -1684,18 +1730,42 @@ def _get_documentURI(self): def _get_encoding(self): return self.encoding + def _get_xmlEncoding(self): + return self.encoding + + def _set_xmlEncoding(self, value): + self.encoding = value + + xmlEncoding = property(_get_xmlEncoding, _set_xmlEncoding) + def _get_errorHandler(self): return self.errorHandler def _get_standalone(self): return self.standalone + def _get_xmlStandalone(self): + return self.standalone + + def _set_xmlStandalone(self, value): + self.standalone = value + + xmlStandalone = property(_get_xmlStandalone, _set_xmlStandalone) + def _get_strictErrorChecking(self): return self.strictErrorChecking def _get_version(self): return self.version + def _get_xmlVersion(self): + return self.version + + def _set_xmlVersion(self, value): + self.version = value + + xmlVersion = property(_get_xmlVersion, _set_xmlVersion) + def _check_new_child(self, newChild, oldChild=None): Node._check_new_child(self, newChild, oldChild) # A document can have only one element and only one document type. diff --git a/Misc/NEWS.d/next/Library/2026-08-31-16-10-00.gh-issue-156667.kQ4mLs.rst b/Misc/NEWS.d/next/Library/2026-08-31-16-10-00.gh-issue-156667.kQ4mLs.rst new file mode 100644 index 00000000000000..f46a15983792f3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-31-16-10-00.gh-issue-156667.kQ4mLs.rst @@ -0,0 +1,2 @@ +Add the DOM Level 3 Recommendation names for methods and attributes in +:mod:`xml.dom.minidom`. Keep the names from earlier working drafts as aliases.