123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- from __future__ import absolute_import, division, unicode_literals
- # pylint:disable=protected-access
- from pip._vendor.six import text_type
- import re
- from copy import copy
- from . import base
- from .. import _ihatexml
- from .. import constants
- from ..constants import namespaces
- from .._utils import moduleFactoryFactory
- tag_regexp = re.compile("{([^}]*)}(.*)")
- def getETreeBuilder(ElementTreeImplementation, fullTree=False):
- ElementTree = ElementTreeImplementation
- ElementTreeCommentType = ElementTree.Comment("asd").tag
- class Element(base.Node):
- def __init__(self, name, namespace=None):
- self._name = name
- self._namespace = namespace
- self._element = ElementTree.Element(self._getETreeTag(name,
- namespace))
- if namespace is None:
- self.nameTuple = namespaces["html"], self._name
- else:
- self.nameTuple = self._namespace, self._name
- self.parent = None
- self._childNodes = []
- self._flags = []
- def _getETreeTag(self, name, namespace):
- if namespace is None:
- etree_tag = name
- else:
- etree_tag = "{%s}%s" % (namespace, name)
- return etree_tag
- def _setName(self, name):
- self._name = name
- self._element.tag = self._getETreeTag(self._name, self._namespace)
- def _getName(self):
- return self._name
- name = property(_getName, _setName)
- def _setNamespace(self, namespace):
- self._namespace = namespace
- self._element.tag = self._getETreeTag(self._name, self._namespace)
- def _getNamespace(self):
- return self._namespace
- namespace = property(_getNamespace, _setNamespace)
- def _getAttributes(self):
- return self._element.attrib
- def _setAttributes(self, attributes):
- el_attrib = self._element.attrib
- el_attrib.clear()
- if attributes:
- # calling .items _always_ allocates, and the above truthy check is cheaper than the
- # allocation on average
- for key, value in attributes.items():
- if isinstance(key, tuple):
- name = "{%s}%s" % (key[2], key[1])
- else:
- name = key
- el_attrib[name] = value
- attributes = property(_getAttributes, _setAttributes)
- def _getChildNodes(self):
- return self._childNodes
- def _setChildNodes(self, value):
- del self._element[:]
- self._childNodes = []
- for element in value:
- self.insertChild(element)
- childNodes = property(_getChildNodes, _setChildNodes)
- def hasContent(self):
- """Return true if the node has children or text"""
- return bool(self._element.text or len(self._element))
- def appendChild(self, node):
- self._childNodes.append(node)
- self._element.append(node._element)
- node.parent = self
- def insertBefore(self, node, refNode):
- index = list(self._element).index(refNode._element)
- self._element.insert(index, node._element)
- node.parent = self
- def removeChild(self, node):
- self._childNodes.remove(node)
- self._element.remove(node._element)
- node.parent = None
- def insertText(self, data, insertBefore=None):
- if not(len(self._element)):
- if not self._element.text:
- self._element.text = ""
- self._element.text += data
- elif insertBefore is None:
- # Insert the text as the tail of the last child element
- if not self._element[-1].tail:
- self._element[-1].tail = ""
- self._element[-1].tail += data
- else:
- # Insert the text before the specified node
- children = list(self._element)
- index = children.index(insertBefore._element)
- if index > 0:
- if not self._element[index - 1].tail:
- self._element[index - 1].tail = ""
- self._element[index - 1].tail += data
- else:
- if not self._element.text:
- self._element.text = ""
- self._element.text += data
- def cloneNode(self):
- element = type(self)(self.name, self.namespace)
- if self._element.attrib:
- element._element.attrib = copy(self._element.attrib)
- return element
- def reparentChildren(self, newParent):
- if newParent.childNodes:
- newParent.childNodes[-1]._element.tail += self._element.text
- else:
- if not newParent._element.text:
- newParent._element.text = ""
- if self._element.text is not None:
- newParent._element.text += self._element.text
- self._element.text = ""
- base.Node.reparentChildren(self, newParent)
- class Comment(Element):
- def __init__(self, data):
- # Use the superclass constructor to set all properties on the
- # wrapper element
- self._element = ElementTree.Comment(data)
- self.parent = None
- self._childNodes = []
- self._flags = []
- def _getData(self):
- return self._element.text
- def _setData(self, value):
- self._element.text = value
- data = property(_getData, _setData)
- class DocumentType(Element):
- def __init__(self, name, publicId, systemId):
- Element.__init__(self, "<!DOCTYPE>")
- self._element.text = name
- self.publicId = publicId
- self.systemId = systemId
- def _getPublicId(self):
- return self._element.get("publicId", "")
- def _setPublicId(self, value):
- if value is not None:
- self._element.set("publicId", value)
- publicId = property(_getPublicId, _setPublicId)
- def _getSystemId(self):
- return self._element.get("systemId", "")
- def _setSystemId(self, value):
- if value is not None:
- self._element.set("systemId", value)
- systemId = property(_getSystemId, _setSystemId)
- class Document(Element):
- def __init__(self):
- Element.__init__(self, "DOCUMENT_ROOT")
- class DocumentFragment(Element):
- def __init__(self):
- Element.__init__(self, "DOCUMENT_FRAGMENT")
- def testSerializer(element):
- rv = []
- def serializeElement(element, indent=0):
- if not(hasattr(element, "tag")):
- element = element.getroot()
- if element.tag == "<!DOCTYPE>":
- if element.get("publicId") or element.get("systemId"):
- publicId = element.get("publicId") or ""
- systemId = element.get("systemId") or ""
- rv.append("""<!DOCTYPE %s "%s" "%s">""" %
- (element.text, publicId, systemId))
- else:
- rv.append("<!DOCTYPE %s>" % (element.text,))
- elif element.tag == "DOCUMENT_ROOT":
- rv.append("#document")
- if element.text is not None:
- rv.append("|%s\"%s\"" % (' ' * (indent + 2), element.text))
- if element.tail is not None:
- raise TypeError("Document node cannot have tail")
- if hasattr(element, "attrib") and len(element.attrib):
- raise TypeError("Document node cannot have attributes")
- elif element.tag == ElementTreeCommentType:
- rv.append("|%s<!-- %s -->" % (' ' * indent, element.text))
- else:
- assert isinstance(element.tag, text_type), \
- "Expected unicode, got %s, %s" % (type(element.tag), element.tag)
- nsmatch = tag_regexp.match(element.tag)
- if nsmatch is None:
- name = element.tag
- else:
- ns, name = nsmatch.groups()
- prefix = constants.prefixes[ns]
- name = "%s %s" % (prefix, name)
- rv.append("|%s<%s>" % (' ' * indent, name))
- if hasattr(element, "attrib"):
- attributes = []
- for name, value in element.attrib.items():
- nsmatch = tag_regexp.match(name)
- if nsmatch is not None:
- ns, name = nsmatch.groups()
- prefix = constants.prefixes[ns]
- attr_string = "%s %s" % (prefix, name)
- else:
- attr_string = name
- attributes.append((attr_string, value))
- for name, value in sorted(attributes):
- rv.append('|%s%s="%s"' % (' ' * (indent + 2), name, value))
- if element.text:
- rv.append("|%s\"%s\"" % (' ' * (indent + 2), element.text))
- indent += 2
- for child in element:
- serializeElement(child, indent)
- if element.tail:
- rv.append("|%s\"%s\"" % (' ' * (indent - 2), element.tail))
- serializeElement(element, 0)
- return "\n".join(rv)
- def tostring(element): # pylint:disable=unused-variable
- """Serialize an element and its child nodes to a string"""
- rv = []
- filter = _ihatexml.InfosetFilter()
- def serializeElement(element):
- if isinstance(element, ElementTree.ElementTree):
- element = element.getroot()
- if element.tag == "<!DOCTYPE>":
- if element.get("publicId") or element.get("systemId"):
- publicId = element.get("publicId") or ""
- systemId = element.get("systemId") or ""
- rv.append("""<!DOCTYPE %s PUBLIC "%s" "%s">""" %
- (element.text, publicId, systemId))
- else:
- rv.append("<!DOCTYPE %s>" % (element.text,))
- elif element.tag == "DOCUMENT_ROOT":
- if element.text is not None:
- rv.append(element.text)
- if element.tail is not None:
- raise TypeError("Document node cannot have tail")
- if hasattr(element, "attrib") and len(element.attrib):
- raise TypeError("Document node cannot have attributes")
- for child in element:
- serializeElement(child)
- elif element.tag == ElementTreeCommentType:
- rv.append("<!--%s-->" % (element.text,))
- else:
- # This is assumed to be an ordinary element
- if not element.attrib:
- rv.append("<%s>" % (filter.fromXmlName(element.tag),))
- else:
- attr = " ".join(["%s=\"%s\"" % (
- filter.fromXmlName(name), value)
- for name, value in element.attrib.items()])
- rv.append("<%s %s>" % (element.tag, attr))
- if element.text:
- rv.append(element.text)
- for child in element:
- serializeElement(child)
- rv.append("</%s>" % (element.tag,))
- if element.tail:
- rv.append(element.tail)
- serializeElement(element)
- return "".join(rv)
- class TreeBuilder(base.TreeBuilder): # pylint:disable=unused-variable
- documentClass = Document
- doctypeClass = DocumentType
- elementClass = Element
- commentClass = Comment
- fragmentClass = DocumentFragment
- implementation = ElementTreeImplementation
- def testSerializer(self, element):
- return testSerializer(element)
- def getDocument(self):
- if fullTree:
- return self.document._element
- else:
- if self.defaultNamespace is not None:
- return self.document._element.find(
- "{%s}html" % self.defaultNamespace)
- else:
- return self.document._element.find("html")
- def getFragment(self):
- return base.TreeBuilder.getFragment(self)._element
- return locals()
- getETreeModule = moduleFactoryFactory(getETreeBuilder)
|