mirror of https://github.com/python/cpython
Patch #407965: Improve Level 2 conformance of minidom
- addition of a DocumentFragment implementation and createDocumentFragment method - proper setting of ownerDocument for all nodes - setting of namespaceURI to None in Element as a class attribute - addition of setAttributeNodeNS and removeAttributeNodeNS as aliases for setAttributeNode and removeAttributeNode - support for inheriting from DOMImplementation to extend it with additional features (to override the Document class) in pulldom: - support for nodes (comment and PI) that occur before he document element; that became necessary as pulldom now delays creation of the document until it has the document element.
This commit is contained in:
parent
a8f7e59761
commit
126f2f62db
|
@ -38,10 +38,11 @@ class Node(_Node):
|
||||||
_makeParentNodes = 1
|
_makeParentNodes = 1
|
||||||
debug = None
|
debug = None
|
||||||
childNodeTypes = ()
|
childNodeTypes = ()
|
||||||
|
namespaceURI = None # this is non-null only for elements and attributes
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.childNodes = []
|
self.childNodes = []
|
||||||
self.parentNode = None
|
self.parentNode = self.ownerDocument = None
|
||||||
if Node._debug:
|
if Node._debug:
|
||||||
index = repr(id(self)) + repr(self.__class__)
|
index = repr(id(self)) + repr(self.__class__)
|
||||||
Node.allnodes[index] = repr(self.__dict__)
|
Node.allnodes[index] = repr(self.__dict__)
|
||||||
|
@ -107,6 +108,11 @@ class Node(_Node):
|
||||||
return self.childNodes[-1]
|
return self.childNodes[-1]
|
||||||
|
|
||||||
def insertBefore(self, newChild, refChild):
|
def insertBefore(self, newChild, refChild):
|
||||||
|
if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
|
||||||
|
for c in newChild.childNodes:
|
||||||
|
self.insertBefore(c, refChild)
|
||||||
|
### The DOM does not clearly specify what to return in this case
|
||||||
|
return newChild
|
||||||
if newChild.nodeType not in self.childNodeTypes:
|
if newChild.nodeType not in self.childNodeTypes:
|
||||||
raise HierarchyRequestErr, \
|
raise HierarchyRequestErr, \
|
||||||
"%s cannot be child of %s" % (repr(newChild), repr(self))
|
"%s cannot be child of %s" % (repr(newChild), repr(self))
|
||||||
|
@ -130,6 +136,11 @@ class Node(_Node):
|
||||||
return newChild
|
return newChild
|
||||||
|
|
||||||
def appendChild(self, node):
|
def appendChild(self, node):
|
||||||
|
if node.nodeType == self.DOCUMENT_FRAGMENT_NODE:
|
||||||
|
for c in node.childNodes:
|
||||||
|
self.appendChild(c)
|
||||||
|
### The DOM does not clearly specify what to return in this case
|
||||||
|
return node
|
||||||
if node.nodeType not in self.childNodeTypes:
|
if node.nodeType not in self.childNodeTypes:
|
||||||
raise HierarchyRequestErr, \
|
raise HierarchyRequestErr, \
|
||||||
"%s cannot be child of %s" % (repr(node), repr(self))
|
"%s cannot be child of %s" % (repr(node), repr(self))
|
||||||
|
@ -148,6 +159,10 @@ class Node(_Node):
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def replaceChild(self, newChild, oldChild):
|
def replaceChild(self, newChild, oldChild):
|
||||||
|
if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
|
||||||
|
refChild = oldChild.nextSibling
|
||||||
|
self.removeChild(oldChild)
|
||||||
|
return self.insertBefore(newChild, refChild)
|
||||||
if newChild.nodeType not in self.childNodeTypes:
|
if newChild.nodeType not in self.childNodeTypes:
|
||||||
raise HierarchyRequestErr, \
|
raise HierarchyRequestErr, \
|
||||||
"%s cannot be child of %s" % (repr(newChild), repr(self))
|
"%s cannot be child of %s" % (repr(newChild), repr(self))
|
||||||
|
@ -233,7 +248,7 @@ class Node(_Node):
|
||||||
# minidom-specific API:
|
# minidom-specific API:
|
||||||
|
|
||||||
def unlink(self):
|
def unlink(self):
|
||||||
self.parentNode = None
|
self.parentNode = self.ownerDocument = None
|
||||||
for child in self.childNodes:
|
for child in self.childNodes:
|
||||||
child.unlink()
|
child.unlink()
|
||||||
self.childNodes = None
|
self.childNodes = None
|
||||||
|
@ -270,6 +285,21 @@ def _getElementsByTagNameNSHelper(parent, nsURI, localName, rc):
|
||||||
_getElementsByTagNameNSHelper(node, nsURI, localName, rc)
|
_getElementsByTagNameNSHelper(node, nsURI, localName, rc)
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
|
class DocumentFragment(Node):
|
||||||
|
nodeType = Node.DOCUMENT_FRAGMENT_NODE
|
||||||
|
nodeName = "#document-fragment"
|
||||||
|
nodeValue = None
|
||||||
|
attributes = None
|
||||||
|
parentNode = None
|
||||||
|
childNodeTypes = (Node.ELEMENT_NODE,
|
||||||
|
Node.TEXT_NODE,
|
||||||
|
Node.CDATA_SECTION_NODE,
|
||||||
|
Node.ENTITY_REFERENCE_NODE,
|
||||||
|
Node.PROCESSING_INSTRUCTION_NODE,
|
||||||
|
Node.COMMENT_NODE,
|
||||||
|
Node.NOTATION_NODE)
|
||||||
|
|
||||||
|
|
||||||
class Attr(Node):
|
class Attr(Node):
|
||||||
nodeType = Node.ATTRIBUTE_NODE
|
nodeType = Node.ATTRIBUTE_NODE
|
||||||
attributes = None
|
attributes = None
|
||||||
|
@ -409,7 +439,7 @@ class Element(Node):
|
||||||
Node.COMMENT_NODE, Node.TEXT_NODE,
|
Node.COMMENT_NODE, Node.TEXT_NODE,
|
||||||
Node.CDATA_SECTION_NODE, Node.ENTITY_REFERENCE_NODE)
|
Node.CDATA_SECTION_NODE, Node.ENTITY_REFERENCE_NODE)
|
||||||
|
|
||||||
def __init__(self, tagName, namespaceURI="", prefix="",
|
def __init__(self, tagName, namespaceURI=None, prefix="",
|
||||||
localName=None):
|
localName=None):
|
||||||
Node.__init__(self)
|
Node.__init__(self)
|
||||||
self.tagName = self.nodeName = tagName
|
self.tagName = self.nodeName = tagName
|
||||||
|
@ -494,6 +524,8 @@ class Element(Node):
|
||||||
# it doesn't represent a change, and should not be returned.
|
# it doesn't represent a change, and should not be returned.
|
||||||
return old
|
return old
|
||||||
|
|
||||||
|
setAttributeNodeNS = setAttributeNode
|
||||||
|
|
||||||
def removeAttribute(self, name):
|
def removeAttribute(self, name):
|
||||||
attr = self._attrs[name]
|
attr = self._attrs[name]
|
||||||
self.removeAttributeNode(attr)
|
self.removeAttributeNode(attr)
|
||||||
|
@ -507,6 +539,8 @@ class Element(Node):
|
||||||
del self._attrs[node.name]
|
del self._attrs[node.name]
|
||||||
del self._attrsNS[(node.namespaceURI, node.localName)]
|
del self._attrsNS[(node.namespaceURI, node.localName)]
|
||||||
|
|
||||||
|
removeAttributeNodeNS = removeAttributeNode
|
||||||
|
|
||||||
def hasAttribute(self, name):
|
def hasAttribute(self, name):
|
||||||
return self._attrs.has_key(name)
|
return self._attrs.has_key(name)
|
||||||
|
|
||||||
|
@ -651,7 +685,7 @@ class DOMImplementation:
|
||||||
if doctype and doctype.parentNode is not None:
|
if doctype and doctype.parentNode is not None:
|
||||||
raise xml.dom.WrongDocumentErr(
|
raise xml.dom.WrongDocumentErr(
|
||||||
"doctype object owned by another DOM tree")
|
"doctype object owned by another DOM tree")
|
||||||
doc = Document()
|
doc = self._createDocument()
|
||||||
if doctype is None:
|
if doctype is None:
|
||||||
doctype = self.createDocumentType(qualifiedName, None, None)
|
doctype = self.createDocumentType(qualifiedName, None, None)
|
||||||
if not qualifiedName:
|
if not qualifiedName:
|
||||||
|
@ -671,7 +705,7 @@ class DOMImplementation:
|
||||||
"illegal use of prefix without namespaces")
|
"illegal use of prefix without namespaces")
|
||||||
element = doc.createElementNS(namespaceURI, qualifiedName)
|
element = doc.createElementNS(namespaceURI, qualifiedName)
|
||||||
doc.appendChild(element)
|
doc.appendChild(element)
|
||||||
doctype.parentNode = doc
|
doctype.parentNode = doctype.ownerDocument = doc
|
||||||
doc.doctype = doctype
|
doc.doctype = doctype
|
||||||
doc.implementation = self
|
doc.implementation = self
|
||||||
return doc
|
return doc
|
||||||
|
@ -682,6 +716,9 @@ class DOMImplementation:
|
||||||
doctype.systemId = systemId
|
doctype.systemId = systemId
|
||||||
return doctype
|
return doctype
|
||||||
|
|
||||||
|
# internal
|
||||||
|
def _createDocument(self):
|
||||||
|
return Document()
|
||||||
|
|
||||||
class Document(Node):
|
class Document(Node):
|
||||||
nodeType = Node.DOCUMENT_NODE
|
nodeType = Node.DOCUMENT_NODE
|
||||||
|
@ -690,6 +727,7 @@ class Document(Node):
|
||||||
attributes = None
|
attributes = None
|
||||||
doctype = None
|
doctype = None
|
||||||
parentNode = None
|
parentNode = None
|
||||||
|
previousSibling = nextSibling = None
|
||||||
|
|
||||||
implementation = DOMImplementation()
|
implementation = DOMImplementation()
|
||||||
childNodeTypes = (Node.ELEMENT_NODE, Node.PROCESSING_INSTRUCTION_NODE,
|
childNodeTypes = (Node.ELEMENT_NODE, Node.PROCESSING_INSTRUCTION_NODE,
|
||||||
|
@ -728,25 +766,47 @@ class Document(Node):
|
||||||
self.doctype = None
|
self.doctype = None
|
||||||
Node.unlink(self)
|
Node.unlink(self)
|
||||||
|
|
||||||
createElement = Element
|
def createDocumentFragment(self):
|
||||||
|
d = DocumentFragment()
|
||||||
|
d.ownerDoc = self
|
||||||
|
return d
|
||||||
|
|
||||||
createTextNode = Text
|
def createElement(self, tagName):
|
||||||
|
e = Element(tagName)
|
||||||
|
e.ownerDocument = self
|
||||||
|
return e
|
||||||
|
|
||||||
createComment = Comment
|
def createTextNode(self, data):
|
||||||
|
t = Text(data)
|
||||||
|
t.ownerDocument = self
|
||||||
|
return t
|
||||||
|
|
||||||
createProcessingInstruction = ProcessingInstruction
|
def createComment(self, data):
|
||||||
|
c = Comment(data)
|
||||||
|
c.ownerDocument = self
|
||||||
|
return c
|
||||||
|
|
||||||
createAttribute = Attr
|
def createProcessingInstruction(self, target, data):
|
||||||
|
p = ProcessingInstruction(target, data)
|
||||||
|
p.ownerDocument = self
|
||||||
|
return p
|
||||||
|
|
||||||
|
def createAttribute(self, qName):
|
||||||
|
a = Attr(qName)
|
||||||
|
a.ownerDocument = self
|
||||||
|
return a
|
||||||
|
|
||||||
def createElementNS(self, namespaceURI, qualifiedName):
|
def createElementNS(self, namespaceURI, qualifiedName):
|
||||||
prefix, localName = _nssplit(qualifiedName)
|
prefix, localName = _nssplit(qualifiedName)
|
||||||
return self.createElement(qualifiedName, namespaceURI,
|
e = Element(qualifiedName, namespaceURI, prefix, localName)
|
||||||
prefix, localName)
|
e.ownerDocument = self
|
||||||
|
return e
|
||||||
|
|
||||||
def createAttributeNS(self, namespaceURI, qualifiedName):
|
def createAttributeNS(self, namespaceURI, qualifiedName):
|
||||||
prefix, localName = _nssplit(qualifiedName)
|
prefix, localName = _nssplit(qualifiedName)
|
||||||
return self.createAttribute(qualifiedName, namespaceURI,
|
a = Attr(qualifiedName, namespaceURI, localName, prefix)
|
||||||
localName, prefix)
|
a.ownerDocument = self
|
||||||
|
return a
|
||||||
|
|
||||||
def getElementsByTagNameNS(self, namespaceURI, localName):
|
def getElementsByTagNameNS(self, namespaceURI, localName):
|
||||||
_getElementsByTagNameNSHelper(self, namespaceURI, localName)
|
_getElementsByTagNameNSHelper(self, namespaceURI, localName)
|
||||||
|
|
|
@ -33,6 +33,7 @@ class PullDOM(xml.sax.ContentHandler):
|
||||||
pass
|
pass
|
||||||
self._ns_contexts = [{}] # contains uri -> prefix dicts
|
self._ns_contexts = [{}] # contains uri -> prefix dicts
|
||||||
self._current_context = self._ns_contexts[-1]
|
self._current_context = self._ns_contexts[-1]
|
||||||
|
self.pending_events = []
|
||||||
|
|
||||||
def pop(self):
|
def pop(self):
|
||||||
result = self.elementStack[-1]
|
result = self.elementStack[-1]
|
||||||
|
@ -115,15 +116,22 @@ class PullDOM(xml.sax.ContentHandler):
|
||||||
self.lastEvent = self.lastEvent[1]
|
self.lastEvent = self.lastEvent[1]
|
||||||
|
|
||||||
def comment(self, s):
|
def comment(self, s):
|
||||||
|
if self.document:
|
||||||
node = self.document.createComment(s)
|
node = self.document.createComment(s)
|
||||||
self.lastEvent[1] = [(COMMENT, node), None]
|
self.lastEvent[1] = [(COMMENT, node), None]
|
||||||
self.lastEvent = self.lastEvent[1]
|
self.lastEvent = self.lastEvent[1]
|
||||||
|
else:
|
||||||
|
event = [(COMMENT, s), None]
|
||||||
|
self.pending_events.append(event)
|
||||||
|
|
||||||
def processingInstruction(self, target, data):
|
def processingInstruction(self, target, data):
|
||||||
|
if self.document:
|
||||||
node = self.document.createProcessingInstruction(target, data)
|
node = self.document.createProcessingInstruction(target, data)
|
||||||
|
|
||||||
self.lastEvent[1] = [(PROCESSING_INSTRUCTION, node), None]
|
self.lastEvent[1] = [(PROCESSING_INSTRUCTION, node), None]
|
||||||
self.lastEvent = self.lastEvent[1]
|
self.lastEvent = self.lastEvent[1]
|
||||||
|
else:
|
||||||
|
event = [(PROCESSING_INSTRUCTION, target, data), None]
|
||||||
|
self.pending_events.append(event)
|
||||||
|
|
||||||
def ignorableWhitespace(self, chars):
|
def ignorableWhitespace(self, chars):
|
||||||
node = self.document.createTextNode(chars)
|
node = self.document.createTextNode(chars)
|
||||||
|
@ -148,6 +156,20 @@ class PullDOM(xml.sax.ContentHandler):
|
||||||
self.lastEvent[1] = [(START_DOCUMENT, node), None]
|
self.lastEvent[1] = [(START_DOCUMENT, node), None]
|
||||||
self.lastEvent = self.lastEvent[1]
|
self.lastEvent = self.lastEvent[1]
|
||||||
self.push(node)
|
self.push(node)
|
||||||
|
# Put everything we have seen so far into the document
|
||||||
|
for e in self.pending_events:
|
||||||
|
if e[0][0] == PROCESSING_INSTRUCTION:
|
||||||
|
_,target,data = e[0]
|
||||||
|
n = self.document.createProcessingInstruction(target, data)
|
||||||
|
e[0] = (PROCESSING_INSTRUCTION, n)
|
||||||
|
elif e[0][0] == COMMENT:
|
||||||
|
n = self.document.createComment(e[0][1])
|
||||||
|
e[0] = (COMMENT, n)
|
||||||
|
else:
|
||||||
|
raise AssertionError("Unknown pending event ",e[0][0])
|
||||||
|
self.lastEvent[1] = e
|
||||||
|
self.lastEvent = e
|
||||||
|
self.pending_events = None
|
||||||
return node.firstChild
|
return node.firstChild
|
||||||
|
|
||||||
def endDocument(self):
|
def endDocument(self):
|
||||||
|
|
Loading…
Reference in New Issue