From 1d852009f062e5d87c23c373e0894566e08881af Mon Sep 17 00:00:00 2001 From: Som Date: Tue, 11 Aug 2026 18:09:14 +0000 Subject: [PATCH 1/2] gh-100710: Add nodeValue default to xml.dom.minidom.Node base class Node.nodeValue is documented per the DOM interface but was never declared on the base Node class in Lib/xml/dom/minidom.py, only on its concrete subclasses. At runtime this was harmless (every subclass already sets it), but static type checkers such as Pylance flag `.nodeValue` access on Node-typed references as an unknown member. Add nodeValue = None as a class-level default on Node, matching the existing pattern for namespaceURI/parentNode/etc. --- Lib/test/test_minidom.py | 9 +++++++++ Lib/xml/dom/minidom.py | 1 + .../2026-08-11-18-30-00.gh-issue-100710.6V5Prz.rst | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-08-11-18-30-00.gh-issue-100710.6V5Prz.rst diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 46249e5138aed52..ce7c05335af801a 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -64,6 +64,15 @@ def testDocumentAsyncAttr(self): self.assertFalse(doc.async_) self.assertFalse(Document.async_) + def testNodeValueDefaultOnBaseNode(self): + # gh-100710: nodeValue must be declared on the base Node class + # (not just on its subclasses) so that generic Node-typed code + # and static type checkers see the attribute. + self.assertIsNone(Node.nodeValue) + node = Node() + self.assertTrue(hasattr(node, 'nodeValue')) + self.assertIsNone(node.nodeValue) + def testParseFromBinaryFile(self): with open(tstfile, 'rb') as file: dom = parse(file) diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py index 16b33b90184dc59..330fd42d5c7fb82 100644 --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -37,6 +37,7 @@ class Node(xml.dom.Node): ownerDocument = None nextSibling = None previousSibling = None + nodeValue = None prefix = EMPTY_PREFIX # non-null only for NS elements and attributes diff --git a/Misc/NEWS.d/next/Library/2026-08-11-18-30-00.gh-issue-100710.6V5Prz.rst b/Misc/NEWS.d/next/Library/2026-08-11-18-30-00.gh-issue-100710.6V5Prz.rst new file mode 100644 index 000000000000000..87cab85525f40ad --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-11-18-30-00.gh-issue-100710.6V5Prz.rst @@ -0,0 +1,5 @@ +Add a class-level ``nodeValue`` default (``None``) to the base +:class:`!Node` class in :mod:`xml.dom.minidom`, matching the DOM +interface it implements. Every concrete subclass already sets +``nodeValue`` at runtime, so this only affects code and static type +checkers that reference a bare ``Node``-typed value. From 2ab3b4c4857d22b33bd495875910ccf7ba7b7510 Mon Sep 17 00:00:00 2001 From: Som Date: Mon, 31 Aug 2026 14:49:39 +0000 Subject: [PATCH 2/2] gh-100710: trim minidom nodeValue fix per review serhiy-storchaka reviewed the base-class nodeValue = None addition as correct, but requested three trims: - Remove testNodeValueDefaultOnBaseNode: Node is practically an abstract class not meant to be instantiated, so a test that constructs a bare Node() shouldn't exist. - Remove the NEWS entry: nothing changes at runtime (every instantiable subclass already set nodeValue), and typeshed already declares nodeValue on Node for static type checkers, so there is nothing user-facing to announce. - Remove the now-redundant nodeValue = None from DocumentFragment, Element, DocumentType, Entity, Notation, and Document (six classes; the review mentioned a seventh, EntityReference, which does not exist as a class in this file), plus parentNode from DocumentFragment and Document, and previousSibling/nextSibling from Document, which already duplicated Node's existing defaults. The core fix -- nodeValue = None on the base Node class -- is unchanged. Verified by building this branch from source and running the full test_minidom suite (131 tests, all passing) plus test_xml_etree, test_pyexpat, and test_sax for regression coverage (636 tests total, all passing). Also directly confirmed nodeValue still correctly resolves to None via inheritance on Document, Element, DocumentType, Entity, Notation, and DocumentFragment instances after removing their redundant declarations. --- Lib/test/test_minidom.py | 9 --------- Lib/xml/dom/minidom.py | 9 --------- .../2026-08-11-18-30-00.gh-issue-100710.6V5Prz.rst | 5 ----- 3 files changed, 23 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2026-08-11-18-30-00.gh-issue-100710.6V5Prz.rst diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index ce7c05335af801a..46249e5138aed52 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -64,15 +64,6 @@ def testDocumentAsyncAttr(self): self.assertFalse(doc.async_) self.assertFalse(Document.async_) - def testNodeValueDefaultOnBaseNode(self): - # gh-100710: nodeValue must be declared on the base Node class - # (not just on its subclasses) so that generic Node-typed code - # and static type checkers see the attribute. - self.assertIsNone(Node.nodeValue) - node = Node() - self.assertTrue(hasattr(node, 'nodeValue')) - self.assertIsNone(node.nodeValue) - def testParseFromBinaryFile(self): with open(tstfile, 'rb') as file: dom = parse(file) diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py index 330fd42d5c7fb82..f6b213fc12a8a5e 100644 --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -337,9 +337,7 @@ def _get_elements_by_tagName_ns_helper(parent, nsURI, localName, rc): class DocumentFragment(Node): nodeType = Node.DOCUMENT_FRAGMENT_NODE nodeName = "#document-fragment" - nodeValue = None attributes = None - parentNode = None _child_node_types = (Node.ELEMENT_NODE, Node.TEXT_NODE, Node.CDATA_SECTION_NODE, @@ -677,7 +675,6 @@ class Element(Node): 'namespaceURI', '_localName', 'childNodes', '_attrs', '_attrsNS', 'nextSibling', 'previousSibling') nodeType = Node.ELEMENT_NODE - nodeValue = None schemaType = _no_type _magic_id_nodes = 0 @@ -1319,7 +1316,6 @@ def _get_systemId(self): class DocumentType(Identified, Childless, Node): nodeType = Node.DOCUMENT_TYPE_NODE - nodeValue = None name = None publicId = None systemId = None @@ -1380,7 +1376,6 @@ def writexml(self, writer, indent="", addindent="", newl=""): class Entity(Identified, Node): attributes = None nodeType = Node.ENTITY_NODE - nodeValue = None actualEncoding = None encoding = None @@ -1419,7 +1414,6 @@ def replaceChild(self, newChild, oldChild): class Notation(Identified, Childless, Node): nodeType = Node.NOTATION_NODE - nodeValue = None def __init__(self, name, publicId, systemId): self.nodeName = name @@ -1564,10 +1558,7 @@ class Document(Node, DocumentLS): implementation = DOMImplementation() nodeType = Node.DOCUMENT_NODE nodeName = "#document" - nodeValue = None attributes = None - parentNode = None - previousSibling = nextSibling = None # Document attributes from Level 3 (WD 9 April 2002) diff --git a/Misc/NEWS.d/next/Library/2026-08-11-18-30-00.gh-issue-100710.6V5Prz.rst b/Misc/NEWS.d/next/Library/2026-08-11-18-30-00.gh-issue-100710.6V5Prz.rst deleted file mode 100644 index 87cab85525f40ad..000000000000000 --- a/Misc/NEWS.d/next/Library/2026-08-11-18-30-00.gh-issue-100710.6V5Prz.rst +++ /dev/null @@ -1,5 +0,0 @@ -Add a class-level ``nodeValue`` default (``None``) to the base -:class:`!Node` class in :mod:`xml.dom.minidom`, matching the DOM -interface it implements. Every concrete subclass already sets -``nodeValue`` at runtime, so this only affects code and static type -checkers that reference a bare ``Node``-typed value.