mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-22 21:32:31 +00:00
python-3.6.zip added from Github
README.cosmo contains the necessary links.
This commit is contained in:
parent
75fc601ff5
commit
0c4c56ff39
4219 changed files with 1968626 additions and 0 deletions
27
third_party/python/Lib/xml/dom/NodeFilter.py
vendored
Normal file
27
third_party/python/Lib/xml/dom/NodeFilter.py
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
# This is the Python mapping for interface NodeFilter from
|
||||
# DOM2-Traversal-Range. It contains only constants.
|
||||
|
||||
class NodeFilter:
|
||||
"""
|
||||
This is the DOM2 NodeFilter interface. It contains only constants.
|
||||
"""
|
||||
FILTER_ACCEPT = 1
|
||||
FILTER_REJECT = 2
|
||||
FILTER_SKIP = 3
|
||||
|
||||
SHOW_ALL = 0xFFFFFFFF
|
||||
SHOW_ELEMENT = 0x00000001
|
||||
SHOW_ATTRIBUTE = 0x00000002
|
||||
SHOW_TEXT = 0x00000004
|
||||
SHOW_CDATA_SECTION = 0x00000008
|
||||
SHOW_ENTITY_REFERENCE = 0x00000010
|
||||
SHOW_ENTITY = 0x00000020
|
||||
SHOW_PROCESSING_INSTRUCTION = 0x00000040
|
||||
SHOW_COMMENT = 0x00000080
|
||||
SHOW_DOCUMENT = 0x00000100
|
||||
SHOW_DOCUMENT_TYPE = 0x00000200
|
||||
SHOW_DOCUMENT_FRAGMENT = 0x00000400
|
||||
SHOW_NOTATION = 0x00000800
|
||||
|
||||
def acceptNode(self, node):
|
||||
raise NotImplementedError
|
140
third_party/python/Lib/xml/dom/__init__.py
vendored
Normal file
140
third_party/python/Lib/xml/dom/__init__.py
vendored
Normal file
|
@ -0,0 +1,140 @@
|
|||
"""W3C Document Object Model implementation for Python.
|
||||
|
||||
The Python mapping of the Document Object Model is documented in the
|
||||
Python Library Reference in the section on the xml.dom package.
|
||||
|
||||
This package contains the following modules:
|
||||
|
||||
minidom -- A simple implementation of the Level 1 DOM with namespace
|
||||
support added (based on the Level 2 specification) and other
|
||||
minor Level 2 functionality.
|
||||
|
||||
pulldom -- DOM builder supporting on-demand tree-building for selected
|
||||
subtrees of the document.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class Node:
|
||||
"""Class giving the NodeType constants."""
|
||||
__slots__ = ()
|
||||
|
||||
# DOM implementations may use this as a base class for their own
|
||||
# Node implementations. If they don't, the constants defined here
|
||||
# should still be used as the canonical definitions as they match
|
||||
# the values given in the W3C recommendation. Client code can
|
||||
# safely refer to these values in all tests of Node.nodeType
|
||||
# values.
|
||||
|
||||
ELEMENT_NODE = 1
|
||||
ATTRIBUTE_NODE = 2
|
||||
TEXT_NODE = 3
|
||||
CDATA_SECTION_NODE = 4
|
||||
ENTITY_REFERENCE_NODE = 5
|
||||
ENTITY_NODE = 6
|
||||
PROCESSING_INSTRUCTION_NODE = 7
|
||||
COMMENT_NODE = 8
|
||||
DOCUMENT_NODE = 9
|
||||
DOCUMENT_TYPE_NODE = 10
|
||||
DOCUMENT_FRAGMENT_NODE = 11
|
||||
NOTATION_NODE = 12
|
||||
|
||||
|
||||
#ExceptionCode
|
||||
INDEX_SIZE_ERR = 1
|
||||
DOMSTRING_SIZE_ERR = 2
|
||||
HIERARCHY_REQUEST_ERR = 3
|
||||
WRONG_DOCUMENT_ERR = 4
|
||||
INVALID_CHARACTER_ERR = 5
|
||||
NO_DATA_ALLOWED_ERR = 6
|
||||
NO_MODIFICATION_ALLOWED_ERR = 7
|
||||
NOT_FOUND_ERR = 8
|
||||
NOT_SUPPORTED_ERR = 9
|
||||
INUSE_ATTRIBUTE_ERR = 10
|
||||
INVALID_STATE_ERR = 11
|
||||
SYNTAX_ERR = 12
|
||||
INVALID_MODIFICATION_ERR = 13
|
||||
NAMESPACE_ERR = 14
|
||||
INVALID_ACCESS_ERR = 15
|
||||
VALIDATION_ERR = 16
|
||||
|
||||
|
||||
class DOMException(Exception):
|
||||
"""Abstract base class for DOM exceptions.
|
||||
Exceptions with specific codes are specializations of this class."""
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
if self.__class__ is DOMException:
|
||||
raise RuntimeError(
|
||||
"DOMException should not be instantiated directly")
|
||||
Exception.__init__(self, *args, **kw)
|
||||
|
||||
def _get_code(self):
|
||||
return self.code
|
||||
|
||||
|
||||
class IndexSizeErr(DOMException):
|
||||
code = INDEX_SIZE_ERR
|
||||
|
||||
class DomstringSizeErr(DOMException):
|
||||
code = DOMSTRING_SIZE_ERR
|
||||
|
||||
class HierarchyRequestErr(DOMException):
|
||||
code = HIERARCHY_REQUEST_ERR
|
||||
|
||||
class WrongDocumentErr(DOMException):
|
||||
code = WRONG_DOCUMENT_ERR
|
||||
|
||||
class InvalidCharacterErr(DOMException):
|
||||
code = INVALID_CHARACTER_ERR
|
||||
|
||||
class NoDataAllowedErr(DOMException):
|
||||
code = NO_DATA_ALLOWED_ERR
|
||||
|
||||
class NoModificationAllowedErr(DOMException):
|
||||
code = NO_MODIFICATION_ALLOWED_ERR
|
||||
|
||||
class NotFoundErr(DOMException):
|
||||
code = NOT_FOUND_ERR
|
||||
|
||||
class NotSupportedErr(DOMException):
|
||||
code = NOT_SUPPORTED_ERR
|
||||
|
||||
class InuseAttributeErr(DOMException):
|
||||
code = INUSE_ATTRIBUTE_ERR
|
||||
|
||||
class InvalidStateErr(DOMException):
|
||||
code = INVALID_STATE_ERR
|
||||
|
||||
class SyntaxErr(DOMException):
|
||||
code = SYNTAX_ERR
|
||||
|
||||
class InvalidModificationErr(DOMException):
|
||||
code = INVALID_MODIFICATION_ERR
|
||||
|
||||
class NamespaceErr(DOMException):
|
||||
code = NAMESPACE_ERR
|
||||
|
||||
class InvalidAccessErr(DOMException):
|
||||
code = INVALID_ACCESS_ERR
|
||||
|
||||
class ValidationErr(DOMException):
|
||||
code = VALIDATION_ERR
|
||||
|
||||
class UserDataHandler:
|
||||
"""Class giving the operation constants for UserDataHandler.handle()."""
|
||||
|
||||
# Based on DOM Level 3 (WD 9 April 2002)
|
||||
|
||||
NODE_CLONED = 1
|
||||
NODE_IMPORTED = 2
|
||||
NODE_DELETED = 3
|
||||
NODE_RENAMED = 4
|
||||
|
||||
XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace"
|
||||
XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/"
|
||||
XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml"
|
||||
EMPTY_NAMESPACE = None
|
||||
EMPTY_PREFIX = None
|
||||
|
||||
from .domreg import getDOMImplementation, registerDOMImplementation
|
99
third_party/python/Lib/xml/dom/domreg.py
vendored
Normal file
99
third_party/python/Lib/xml/dom/domreg.py
vendored
Normal file
|
@ -0,0 +1,99 @@
|
|||
"""Registration facilities for DOM. This module should not be used
|
||||
directly. Instead, the functions getDOMImplementation and
|
||||
registerDOMImplementation should be imported from xml.dom."""
|
||||
|
||||
# This is a list of well-known implementations. Well-known names
|
||||
# should be published by posting to xml-sig@python.org, and are
|
||||
# subsequently recorded in this file.
|
||||
|
||||
import sys
|
||||
|
||||
well_known_implementations = {
|
||||
'minidom':'xml.dom.minidom',
|
||||
'4DOM': 'xml.dom.DOMImplementation',
|
||||
}
|
||||
|
||||
# DOM implementations not officially registered should register
|
||||
# themselves with their
|
||||
|
||||
registered = {}
|
||||
|
||||
def registerDOMImplementation(name, factory):
|
||||
"""registerDOMImplementation(name, factory)
|
||||
|
||||
Register the factory function with the name. The factory function
|
||||
should return an object which implements the DOMImplementation
|
||||
interface. The factory function can either return the same object,
|
||||
or a new one (e.g. if that implementation supports some
|
||||
customization)."""
|
||||
|
||||
registered[name] = factory
|
||||
|
||||
def _good_enough(dom, features):
|
||||
"_good_enough(dom, features) -> Return 1 if the dom offers the features"
|
||||
for f,v in features:
|
||||
if not dom.hasFeature(f,v):
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def getDOMImplementation(name=None, features=()):
|
||||
"""getDOMImplementation(name = None, features = ()) -> DOM implementation.
|
||||
|
||||
Return a suitable DOM implementation. The name is either
|
||||
well-known, the module name of a DOM implementation, or None. If
|
||||
it is not None, imports the corresponding module and returns
|
||||
DOMImplementation object if the import succeeds.
|
||||
|
||||
If name is not given, consider the available implementations to
|
||||
find one with the required feature set. If no implementation can
|
||||
be found, raise an ImportError. The features list must be a sequence
|
||||
of (feature, version) pairs which are passed to hasFeature."""
|
||||
|
||||
import os
|
||||
creator = None
|
||||
mod = well_known_implementations.get(name)
|
||||
if mod:
|
||||
mod = __import__(mod, {}, {}, ['getDOMImplementation'])
|
||||
return mod.getDOMImplementation()
|
||||
elif name:
|
||||
return registered[name]()
|
||||
elif not sys.flags.ignore_environment and "PYTHON_DOM" in os.environ:
|
||||
return getDOMImplementation(name = os.environ["PYTHON_DOM"])
|
||||
|
||||
# User did not specify a name, try implementations in arbitrary
|
||||
# order, returning the one that has the required features
|
||||
if isinstance(features, str):
|
||||
features = _parse_feature_string(features)
|
||||
for creator in registered.values():
|
||||
dom = creator()
|
||||
if _good_enough(dom, features):
|
||||
return dom
|
||||
|
||||
for creator in well_known_implementations.keys():
|
||||
try:
|
||||
dom = getDOMImplementation(name = creator)
|
||||
except Exception: # typically ImportError, or AttributeError
|
||||
continue
|
||||
if _good_enough(dom, features):
|
||||
return dom
|
||||
|
||||
raise ImportError("no suitable DOM implementation found")
|
||||
|
||||
def _parse_feature_string(s):
|
||||
features = []
|
||||
parts = s.split()
|
||||
i = 0
|
||||
length = len(parts)
|
||||
while i < length:
|
||||
feature = parts[i]
|
||||
if feature[0] in "0123456789":
|
||||
raise ValueError("bad feature name: %r" % (feature,))
|
||||
i = i + 1
|
||||
version = None
|
||||
if i < length:
|
||||
v = parts[i]
|
||||
if v[0] in "0123456789":
|
||||
i = i + 1
|
||||
version = v
|
||||
features.append((feature, version))
|
||||
return tuple(features)
|
965
third_party/python/Lib/xml/dom/expatbuilder.py
vendored
Normal file
965
third_party/python/Lib/xml/dom/expatbuilder.py
vendored
Normal file
|
@ -0,0 +1,965 @@
|
|||
"""Facility to use the Expat parser to load a minidom instance
|
||||
from a string or file.
|
||||
|
||||
This avoids all the overhead of SAX and pulldom to gain performance.
|
||||
"""
|
||||
|
||||
# Warning!
|
||||
#
|
||||
# This module is tightly bound to the implementation details of the
|
||||
# minidom DOM and can't be used with other DOM implementations. This
|
||||
# is due, in part, to a lack of appropriate methods in the DOM (there is
|
||||
# no way to create Entity and Notation nodes via the DOM Level 2
|
||||
# interface), and for performance. The latter is the cause of some fairly
|
||||
# cryptic code.
|
||||
#
|
||||
# Performance hacks:
|
||||
#
|
||||
# - .character_data_handler() has an extra case in which continuing
|
||||
# data is appended to an existing Text node; this can be a
|
||||
# speedup since pyexpat can break up character data into multiple
|
||||
# callbacks even though we set the buffer_text attribute on the
|
||||
# parser. This also gives us the advantage that we don't need a
|
||||
# separate normalization pass.
|
||||
#
|
||||
# - Determining that a node exists is done using an identity comparison
|
||||
# with None rather than a truth test; this avoids searching for and
|
||||
# calling any methods on the node object if it exists. (A rather
|
||||
# nice speedup is achieved this way as well!)
|
||||
|
||||
from xml.dom import xmlbuilder, minidom, Node
|
||||
from xml.dom import EMPTY_NAMESPACE, EMPTY_PREFIX, XMLNS_NAMESPACE
|
||||
from xml.parsers import expat
|
||||
from xml.dom.minidom import _append_child, _set_attribute_node
|
||||
from xml.dom.NodeFilter import NodeFilter
|
||||
|
||||
TEXT_NODE = Node.TEXT_NODE
|
||||
CDATA_SECTION_NODE = Node.CDATA_SECTION_NODE
|
||||
DOCUMENT_NODE = Node.DOCUMENT_NODE
|
||||
|
||||
FILTER_ACCEPT = xmlbuilder.DOMBuilderFilter.FILTER_ACCEPT
|
||||
FILTER_REJECT = xmlbuilder.DOMBuilderFilter.FILTER_REJECT
|
||||
FILTER_SKIP = xmlbuilder.DOMBuilderFilter.FILTER_SKIP
|
||||
FILTER_INTERRUPT = xmlbuilder.DOMBuilderFilter.FILTER_INTERRUPT
|
||||
|
||||
theDOMImplementation = minidom.getDOMImplementation()
|
||||
|
||||
# Expat typename -> TypeInfo
|
||||
_typeinfo_map = {
|
||||
"CDATA": minidom.TypeInfo(None, "cdata"),
|
||||
"ENUM": minidom.TypeInfo(None, "enumeration"),
|
||||
"ENTITY": minidom.TypeInfo(None, "entity"),
|
||||
"ENTITIES": minidom.TypeInfo(None, "entities"),
|
||||
"ID": minidom.TypeInfo(None, "id"),
|
||||
"IDREF": minidom.TypeInfo(None, "idref"),
|
||||
"IDREFS": minidom.TypeInfo(None, "idrefs"),
|
||||
"NMTOKEN": minidom.TypeInfo(None, "nmtoken"),
|
||||
"NMTOKENS": minidom.TypeInfo(None, "nmtokens"),
|
||||
}
|
||||
|
||||
class ElementInfo(object):
|
||||
__slots__ = '_attr_info', '_model', 'tagName'
|
||||
|
||||
def __init__(self, tagName, model=None):
|
||||
self.tagName = tagName
|
||||
self._attr_info = []
|
||||
self._model = model
|
||||
|
||||
def __getstate__(self):
|
||||
return self._attr_info, self._model, self.tagName
|
||||
|
||||
def __setstate__(self, state):
|
||||
self._attr_info, self._model, self.tagName = state
|
||||
|
||||
def getAttributeType(self, aname):
|
||||
for info in self._attr_info:
|
||||
if info[1] == aname:
|
||||
t = info[-2]
|
||||
if t[0] == "(":
|
||||
return _typeinfo_map["ENUM"]
|
||||
else:
|
||||
return _typeinfo_map[info[-2]]
|
||||
return minidom._no_type
|
||||
|
||||
def getAttributeTypeNS(self, namespaceURI, localName):
|
||||
return minidom._no_type
|
||||
|
||||
def isElementContent(self):
|
||||
if self._model:
|
||||
type = self._model[0]
|
||||
return type not in (expat.model.XML_CTYPE_ANY,
|
||||
expat.model.XML_CTYPE_MIXED)
|
||||
else:
|
||||
return False
|
||||
|
||||
def isEmpty(self):
|
||||
if self._model:
|
||||
return self._model[0] == expat.model.XML_CTYPE_EMPTY
|
||||
else:
|
||||
return False
|
||||
|
||||
def isId(self, aname):
|
||||
for info in self._attr_info:
|
||||
if info[1] == aname:
|
||||
return info[-2] == "ID"
|
||||
return False
|
||||
|
||||
def isIdNS(self, euri, ename, auri, aname):
|
||||
# not sure this is meaningful
|
||||
return self.isId((auri, aname))
|
||||
|
||||
def _intern(builder, s):
|
||||
return builder._intern_setdefault(s, s)
|
||||
|
||||
def _parse_ns_name(builder, name):
|
||||
assert ' ' in name
|
||||
parts = name.split(' ')
|
||||
intern = builder._intern_setdefault
|
||||
if len(parts) == 3:
|
||||
uri, localname, prefix = parts
|
||||
prefix = intern(prefix, prefix)
|
||||
qname = "%s:%s" % (prefix, localname)
|
||||
qname = intern(qname, qname)
|
||||
localname = intern(localname, localname)
|
||||
elif len(parts) == 2:
|
||||
uri, localname = parts
|
||||
prefix = EMPTY_PREFIX
|
||||
qname = localname = intern(localname, localname)
|
||||
else:
|
||||
raise ValueError("Unsupported syntax: spaces in URIs not supported: %r" % name)
|
||||
return intern(uri, uri), localname, prefix, qname
|
||||
|
||||
|
||||
class ExpatBuilder:
|
||||
"""Document builder that uses Expat to build a ParsedXML.DOM document
|
||||
instance."""
|
||||
|
||||
def __init__(self, options=None):
|
||||
if options is None:
|
||||
options = xmlbuilder.Options()
|
||||
self._options = options
|
||||
if self._options.filter is not None:
|
||||
self._filter = FilterVisibilityController(self._options.filter)
|
||||
else:
|
||||
self._filter = None
|
||||
# This *really* doesn't do anything in this case, so
|
||||
# override it with something fast & minimal.
|
||||
self._finish_start_element = id
|
||||
self._parser = None
|
||||
self.reset()
|
||||
|
||||
def createParser(self):
|
||||
"""Create a new parser object."""
|
||||
return expat.ParserCreate()
|
||||
|
||||
def getParser(self):
|
||||
"""Return the parser object, creating a new one if needed."""
|
||||
if not self._parser:
|
||||
self._parser = self.createParser()
|
||||
self._intern_setdefault = self._parser.intern.setdefault
|
||||
self._parser.buffer_text = True
|
||||
self._parser.ordered_attributes = True
|
||||
self._parser.specified_attributes = True
|
||||
self.install(self._parser)
|
||||
return self._parser
|
||||
|
||||
def reset(self):
|
||||
"""Free all data structures used during DOM construction."""
|
||||
self.document = theDOMImplementation.createDocument(
|
||||
EMPTY_NAMESPACE, None, None)
|
||||
self.curNode = self.document
|
||||
self._elem_info = self.document._elem_info
|
||||
self._cdata = False
|
||||
|
||||
def install(self, parser):
|
||||
"""Install the callbacks needed to build the DOM into the parser."""
|
||||
# This creates circular references!
|
||||
parser.StartDoctypeDeclHandler = self.start_doctype_decl_handler
|
||||
parser.StartElementHandler = self.first_element_handler
|
||||
parser.EndElementHandler = self.end_element_handler
|
||||
parser.ProcessingInstructionHandler = self.pi_handler
|
||||
if self._options.entities:
|
||||
parser.EntityDeclHandler = self.entity_decl_handler
|
||||
parser.NotationDeclHandler = self.notation_decl_handler
|
||||
if self._options.comments:
|
||||
parser.CommentHandler = self.comment_handler
|
||||
if self._options.cdata_sections:
|
||||
parser.StartCdataSectionHandler = self.start_cdata_section_handler
|
||||
parser.EndCdataSectionHandler = self.end_cdata_section_handler
|
||||
parser.CharacterDataHandler = self.character_data_handler_cdata
|
||||
else:
|
||||
parser.CharacterDataHandler = self.character_data_handler
|
||||
parser.ExternalEntityRefHandler = self.external_entity_ref_handler
|
||||
parser.XmlDeclHandler = self.xml_decl_handler
|
||||
parser.ElementDeclHandler = self.element_decl_handler
|
||||
parser.AttlistDeclHandler = self.attlist_decl_handler
|
||||
|
||||
def parseFile(self, file):
|
||||
"""Parse a document from a file object, returning the document
|
||||
node."""
|
||||
parser = self.getParser()
|
||||
first_buffer = True
|
||||
try:
|
||||
while 1:
|
||||
buffer = file.read(16*1024)
|
||||
if not buffer:
|
||||
break
|
||||
parser.Parse(buffer, 0)
|
||||
if first_buffer and self.document.documentElement:
|
||||
self._setup_subset(buffer)
|
||||
first_buffer = False
|
||||
parser.Parse("", True)
|
||||
except ParseEscape:
|
||||
pass
|
||||
doc = self.document
|
||||
self.reset()
|
||||
self._parser = None
|
||||
return doc
|
||||
|
||||
def parseString(self, string):
|
||||
"""Parse a document from a string, returning the document node."""
|
||||
parser = self.getParser()
|
||||
try:
|
||||
parser.Parse(string, True)
|
||||
self._setup_subset(string)
|
||||
except ParseEscape:
|
||||
pass
|
||||
doc = self.document
|
||||
self.reset()
|
||||
self._parser = None
|
||||
return doc
|
||||
|
||||
def _setup_subset(self, buffer):
|
||||
"""Load the internal subset if there might be one."""
|
||||
if self.document.doctype:
|
||||
extractor = InternalSubsetExtractor()
|
||||
extractor.parseString(buffer)
|
||||
subset = extractor.getSubset()
|
||||
self.document.doctype.internalSubset = subset
|
||||
|
||||
def start_doctype_decl_handler(self, doctypeName, systemId, publicId,
|
||||
has_internal_subset):
|
||||
doctype = self.document.implementation.createDocumentType(
|
||||
doctypeName, publicId, systemId)
|
||||
doctype.ownerDocument = self.document
|
||||
_append_child(self.document, doctype)
|
||||
self.document.doctype = doctype
|
||||
if self._filter and self._filter.acceptNode(doctype) == FILTER_REJECT:
|
||||
self.document.doctype = None
|
||||
del self.document.childNodes[-1]
|
||||
doctype = None
|
||||
self._parser.EntityDeclHandler = None
|
||||
self._parser.NotationDeclHandler = None
|
||||
if has_internal_subset:
|
||||
if doctype is not None:
|
||||
doctype.entities._seq = []
|
||||
doctype.notations._seq = []
|
||||
self._parser.CommentHandler = None
|
||||
self._parser.ProcessingInstructionHandler = None
|
||||
self._parser.EndDoctypeDeclHandler = self.end_doctype_decl_handler
|
||||
|
||||
def end_doctype_decl_handler(self):
|
||||
if self._options.comments:
|
||||
self._parser.CommentHandler = self.comment_handler
|
||||
self._parser.ProcessingInstructionHandler = self.pi_handler
|
||||
if not (self._elem_info or self._filter):
|
||||
self._finish_end_element = id
|
||||
|
||||
def pi_handler(self, target, data):
|
||||
node = self.document.createProcessingInstruction(target, data)
|
||||
_append_child(self.curNode, node)
|
||||
if self._filter and self._filter.acceptNode(node) == FILTER_REJECT:
|
||||
self.curNode.removeChild(node)
|
||||
|
||||
def character_data_handler_cdata(self, data):
|
||||
childNodes = self.curNode.childNodes
|
||||
if self._cdata:
|
||||
if ( self._cdata_continue
|
||||
and childNodes[-1].nodeType == CDATA_SECTION_NODE):
|
||||
childNodes[-1].appendData(data)
|
||||
return
|
||||
node = self.document.createCDATASection(data)
|
||||
self._cdata_continue = True
|
||||
elif childNodes and childNodes[-1].nodeType == TEXT_NODE:
|
||||
node = childNodes[-1]
|
||||
value = node.data + data
|
||||
node.data = value
|
||||
return
|
||||
else:
|
||||
node = minidom.Text()
|
||||
node.data = data
|
||||
node.ownerDocument = self.document
|
||||
_append_child(self.curNode, node)
|
||||
|
||||
def character_data_handler(self, data):
|
||||
childNodes = self.curNode.childNodes
|
||||
if childNodes and childNodes[-1].nodeType == TEXT_NODE:
|
||||
node = childNodes[-1]
|
||||
node.data = node.data + data
|
||||
return
|
||||
node = minidom.Text()
|
||||
node.data = node.data + data
|
||||
node.ownerDocument = self.document
|
||||
_append_child(self.curNode, node)
|
||||
|
||||
def entity_decl_handler(self, entityName, is_parameter_entity, value,
|
||||
base, systemId, publicId, notationName):
|
||||
if is_parameter_entity:
|
||||
# we don't care about parameter entities for the DOM
|
||||
return
|
||||
if not self._options.entities:
|
||||
return
|
||||
node = self.document._create_entity(entityName, publicId,
|
||||
systemId, notationName)
|
||||
if value is not None:
|
||||
# internal entity
|
||||
# node *should* be readonly, but we'll cheat
|
||||
child = self.document.createTextNode(value)
|
||||
node.childNodes.append(child)
|
||||
self.document.doctype.entities._seq.append(node)
|
||||
if self._filter and self._filter.acceptNode(node) == FILTER_REJECT:
|
||||
del self.document.doctype.entities._seq[-1]
|
||||
|
||||
def notation_decl_handler(self, notationName, base, systemId, publicId):
|
||||
node = self.document._create_notation(notationName, publicId, systemId)
|
||||
self.document.doctype.notations._seq.append(node)
|
||||
if self._filter and self._filter.acceptNode(node) == FILTER_ACCEPT:
|
||||
del self.document.doctype.notations._seq[-1]
|
||||
|
||||
def comment_handler(self, data):
|
||||
node = self.document.createComment(data)
|
||||
_append_child(self.curNode, node)
|
||||
if self._filter and self._filter.acceptNode(node) == FILTER_REJECT:
|
||||
self.curNode.removeChild(node)
|
||||
|
||||
def start_cdata_section_handler(self):
|
||||
self._cdata = True
|
||||
self._cdata_continue = False
|
||||
|
||||
def end_cdata_section_handler(self):
|
||||
self._cdata = False
|
||||
self._cdata_continue = False
|
||||
|
||||
def external_entity_ref_handler(self, context, base, systemId, publicId):
|
||||
return 1
|
||||
|
||||
def first_element_handler(self, name, attributes):
|
||||
if self._filter is None and not self._elem_info:
|
||||
self._finish_end_element = id
|
||||
self.getParser().StartElementHandler = self.start_element_handler
|
||||
self.start_element_handler(name, attributes)
|
||||
|
||||
def start_element_handler(self, name, attributes):
|
||||
node = self.document.createElement(name)
|
||||
_append_child(self.curNode, node)
|
||||
self.curNode = node
|
||||
|
||||
if attributes:
|
||||
for i in range(0, len(attributes), 2):
|
||||
a = minidom.Attr(attributes[i], EMPTY_NAMESPACE,
|
||||
None, EMPTY_PREFIX)
|
||||
value = attributes[i+1]
|
||||
a.value = value
|
||||
a.ownerDocument = self.document
|
||||
_set_attribute_node(node, a)
|
||||
|
||||
if node is not self.document.documentElement:
|
||||
self._finish_start_element(node)
|
||||
|
||||
def _finish_start_element(self, node):
|
||||
if self._filter:
|
||||
# To be general, we'd have to call isSameNode(), but this
|
||||
# is sufficient for minidom:
|
||||
if node is self.document.documentElement:
|
||||
return
|
||||
filt = self._filter.startContainer(node)
|
||||
if filt == FILTER_REJECT:
|
||||
# ignore this node & all descendents
|
||||
Rejecter(self)
|
||||
elif filt == FILTER_SKIP:
|
||||
# ignore this node, but make it's children become
|
||||
# children of the parent node
|
||||
Skipper(self)
|
||||
else:
|
||||
return
|
||||
self.curNode = node.parentNode
|
||||
node.parentNode.removeChild(node)
|
||||
node.unlink()
|
||||
|
||||
# If this ever changes, Namespaces.end_element_handler() needs to
|
||||
# be changed to match.
|
||||
#
|
||||
def end_element_handler(self, name):
|
||||
curNode = self.curNode
|
||||
self.curNode = curNode.parentNode
|
||||
self._finish_end_element(curNode)
|
||||
|
||||
def _finish_end_element(self, curNode):
|
||||
info = self._elem_info.get(curNode.tagName)
|
||||
if info:
|
||||
self._handle_white_text_nodes(curNode, info)
|
||||
if self._filter:
|
||||
if curNode is self.document.documentElement:
|
||||
return
|
||||
if self._filter.acceptNode(curNode) == FILTER_REJECT:
|
||||
self.curNode.removeChild(curNode)
|
||||
curNode.unlink()
|
||||
|
||||
def _handle_white_text_nodes(self, node, info):
|
||||
if (self._options.whitespace_in_element_content
|
||||
or not info.isElementContent()):
|
||||
return
|
||||
|
||||
# We have element type information and should remove ignorable
|
||||
# whitespace; identify for text nodes which contain only
|
||||
# whitespace.
|
||||
L = []
|
||||
for child in node.childNodes:
|
||||
if child.nodeType == TEXT_NODE and not child.data.strip():
|
||||
L.append(child)
|
||||
|
||||
# Remove ignorable whitespace from the tree.
|
||||
for child in L:
|
||||
node.removeChild(child)
|
||||
|
||||
def element_decl_handler(self, name, model):
|
||||
info = self._elem_info.get(name)
|
||||
if info is None:
|
||||
self._elem_info[name] = ElementInfo(name, model)
|
||||
else:
|
||||
assert info._model is None
|
||||
info._model = model
|
||||
|
||||
def attlist_decl_handler(self, elem, name, type, default, required):
|
||||
info = self._elem_info.get(elem)
|
||||
if info is None:
|
||||
info = ElementInfo(elem)
|
||||
self._elem_info[elem] = info
|
||||
info._attr_info.append(
|
||||
[None, name, None, None, default, 0, type, required])
|
||||
|
||||
def xml_decl_handler(self, version, encoding, standalone):
|
||||
self.document.version = version
|
||||
self.document.encoding = encoding
|
||||
# This is still a little ugly, thanks to the pyexpat API. ;-(
|
||||
if standalone >= 0:
|
||||
if standalone:
|
||||
self.document.standalone = True
|
||||
else:
|
||||
self.document.standalone = False
|
||||
|
||||
|
||||
# Don't include FILTER_INTERRUPT, since that's checked separately
|
||||
# where allowed.
|
||||
_ALLOWED_FILTER_RETURNS = (FILTER_ACCEPT, FILTER_REJECT, FILTER_SKIP)
|
||||
|
||||
class FilterVisibilityController(object):
|
||||
"""Wrapper around a DOMBuilderFilter which implements the checks
|
||||
to make the whatToShow filter attribute work."""
|
||||
|
||||
__slots__ = 'filter',
|
||||
|
||||
def __init__(self, filter):
|
||||
self.filter = filter
|
||||
|
||||
def startContainer(self, node):
|
||||
mask = self._nodetype_mask[node.nodeType]
|
||||
if self.filter.whatToShow & mask:
|
||||
val = self.filter.startContainer(node)
|
||||
if val == FILTER_INTERRUPT:
|
||||
raise ParseEscape
|
||||
if val not in _ALLOWED_FILTER_RETURNS:
|
||||
raise ValueError(
|
||||
"startContainer() returned illegal value: " + repr(val))
|
||||
return val
|
||||
else:
|
||||
return FILTER_ACCEPT
|
||||
|
||||
def acceptNode(self, node):
|
||||
mask = self._nodetype_mask[node.nodeType]
|
||||
if self.filter.whatToShow & mask:
|
||||
val = self.filter.acceptNode(node)
|
||||
if val == FILTER_INTERRUPT:
|
||||
raise ParseEscape
|
||||
if val == FILTER_SKIP:
|
||||
# move all child nodes to the parent, and remove this node
|
||||
parent = node.parentNode
|
||||
for child in node.childNodes[:]:
|
||||
parent.appendChild(child)
|
||||
# node is handled by the caller
|
||||
return FILTER_REJECT
|
||||
if val not in _ALLOWED_FILTER_RETURNS:
|
||||
raise ValueError(
|
||||
"acceptNode() returned illegal value: " + repr(val))
|
||||
return val
|
||||
else:
|
||||
return FILTER_ACCEPT
|
||||
|
||||
_nodetype_mask = {
|
||||
Node.ELEMENT_NODE: NodeFilter.SHOW_ELEMENT,
|
||||
Node.ATTRIBUTE_NODE: NodeFilter.SHOW_ATTRIBUTE,
|
||||
Node.TEXT_NODE: NodeFilter.SHOW_TEXT,
|
||||
Node.CDATA_SECTION_NODE: NodeFilter.SHOW_CDATA_SECTION,
|
||||
Node.ENTITY_REFERENCE_NODE: NodeFilter.SHOW_ENTITY_REFERENCE,
|
||||
Node.ENTITY_NODE: NodeFilter.SHOW_ENTITY,
|
||||
Node.PROCESSING_INSTRUCTION_NODE: NodeFilter.SHOW_PROCESSING_INSTRUCTION,
|
||||
Node.COMMENT_NODE: NodeFilter.SHOW_COMMENT,
|
||||
Node.DOCUMENT_NODE: NodeFilter.SHOW_DOCUMENT,
|
||||
Node.DOCUMENT_TYPE_NODE: NodeFilter.SHOW_DOCUMENT_TYPE,
|
||||
Node.DOCUMENT_FRAGMENT_NODE: NodeFilter.SHOW_DOCUMENT_FRAGMENT,
|
||||
Node.NOTATION_NODE: NodeFilter.SHOW_NOTATION,
|
||||
}
|
||||
|
||||
|
||||
class FilterCrutch(object):
|
||||
__slots__ = '_builder', '_level', '_old_start', '_old_end'
|
||||
|
||||
def __init__(self, builder):
|
||||
self._level = 0
|
||||
self._builder = builder
|
||||
parser = builder._parser
|
||||
self._old_start = parser.StartElementHandler
|
||||
self._old_end = parser.EndElementHandler
|
||||
parser.StartElementHandler = self.start_element_handler
|
||||
parser.EndElementHandler = self.end_element_handler
|
||||
|
||||
class Rejecter(FilterCrutch):
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, builder):
|
||||
FilterCrutch.__init__(self, builder)
|
||||
parser = builder._parser
|
||||
for name in ("ProcessingInstructionHandler",
|
||||
"CommentHandler",
|
||||
"CharacterDataHandler",
|
||||
"StartCdataSectionHandler",
|
||||
"EndCdataSectionHandler",
|
||||
"ExternalEntityRefHandler",
|
||||
):
|
||||
setattr(parser, name, None)
|
||||
|
||||
def start_element_handler(self, *args):
|
||||
self._level = self._level + 1
|
||||
|
||||
def end_element_handler(self, *args):
|
||||
if self._level == 0:
|
||||
# restore the old handlers
|
||||
parser = self._builder._parser
|
||||
self._builder.install(parser)
|
||||
parser.StartElementHandler = self._old_start
|
||||
parser.EndElementHandler = self._old_end
|
||||
else:
|
||||
self._level = self._level - 1
|
||||
|
||||
class Skipper(FilterCrutch):
|
||||
__slots__ = ()
|
||||
|
||||
def start_element_handler(self, *args):
|
||||
node = self._builder.curNode
|
||||
self._old_start(*args)
|
||||
if self._builder.curNode is not node:
|
||||
self._level = self._level + 1
|
||||
|
||||
def end_element_handler(self, *args):
|
||||
if self._level == 0:
|
||||
# We're popping back out of the node we're skipping, so we
|
||||
# shouldn't need to do anything but reset the handlers.
|
||||
self._builder._parser.StartElementHandler = self._old_start
|
||||
self._builder._parser.EndElementHandler = self._old_end
|
||||
self._builder = None
|
||||
else:
|
||||
self._level = self._level - 1
|
||||
self._old_end(*args)
|
||||
|
||||
|
||||
# framework document used by the fragment builder.
|
||||
# Takes a string for the doctype, subset string, and namespace attrs string.
|
||||
|
||||
_FRAGMENT_BUILDER_INTERNAL_SYSTEM_ID = \
|
||||
"http://xml.python.org/entities/fragment-builder/internal"
|
||||
|
||||
_FRAGMENT_BUILDER_TEMPLATE = (
|
||||
'''\
|
||||
<!DOCTYPE wrapper
|
||||
%%s [
|
||||
<!ENTITY fragment-builder-internal
|
||||
SYSTEM "%s">
|
||||
%%s
|
||||
]>
|
||||
<wrapper %%s
|
||||
>&fragment-builder-internal;</wrapper>'''
|
||||
% _FRAGMENT_BUILDER_INTERNAL_SYSTEM_ID)
|
||||
|
||||
|
||||
class FragmentBuilder(ExpatBuilder):
|
||||
"""Builder which constructs document fragments given XML source
|
||||
text and a context node.
|
||||
|
||||
The context node is expected to provide information about the
|
||||
namespace declarations which are in scope at the start of the
|
||||
fragment.
|
||||
"""
|
||||
|
||||
def __init__(self, context, options=None):
|
||||
if context.nodeType == DOCUMENT_NODE:
|
||||
self.originalDocument = context
|
||||
self.context = context
|
||||
else:
|
||||
self.originalDocument = context.ownerDocument
|
||||
self.context = context
|
||||
ExpatBuilder.__init__(self, options)
|
||||
|
||||
def reset(self):
|
||||
ExpatBuilder.reset(self)
|
||||
self.fragment = None
|
||||
|
||||
def parseFile(self, file):
|
||||
"""Parse a document fragment from a file object, returning the
|
||||
fragment node."""
|
||||
return self.parseString(file.read())
|
||||
|
||||
def parseString(self, string):
|
||||
"""Parse a document fragment from a string, returning the
|
||||
fragment node."""
|
||||
self._source = string
|
||||
parser = self.getParser()
|
||||
doctype = self.originalDocument.doctype
|
||||
ident = ""
|
||||
if doctype:
|
||||
subset = doctype.internalSubset or self._getDeclarations()
|
||||
if doctype.publicId:
|
||||
ident = ('PUBLIC "%s" "%s"'
|
||||
% (doctype.publicId, doctype.systemId))
|
||||
elif doctype.systemId:
|
||||
ident = 'SYSTEM "%s"' % doctype.systemId
|
||||
else:
|
||||
subset = ""
|
||||
nsattrs = self._getNSattrs() # get ns decls from node's ancestors
|
||||
document = _FRAGMENT_BUILDER_TEMPLATE % (ident, subset, nsattrs)
|
||||
try:
|
||||
parser.Parse(document, 1)
|
||||
except:
|
||||
self.reset()
|
||||
raise
|
||||
fragment = self.fragment
|
||||
self.reset()
|
||||
## self._parser = None
|
||||
return fragment
|
||||
|
||||
def _getDeclarations(self):
|
||||
"""Re-create the internal subset from the DocumentType node.
|
||||
|
||||
This is only needed if we don't already have the
|
||||
internalSubset as a string.
|
||||
"""
|
||||
doctype = self.context.ownerDocument.doctype
|
||||
s = ""
|
||||
if doctype:
|
||||
for i in range(doctype.notations.length):
|
||||
notation = doctype.notations.item(i)
|
||||
if s:
|
||||
s = s + "\n "
|
||||
s = "%s<!NOTATION %s" % (s, notation.nodeName)
|
||||
if notation.publicId:
|
||||
s = '%s PUBLIC "%s"\n "%s">' \
|
||||
% (s, notation.publicId, notation.systemId)
|
||||
else:
|
||||
s = '%s SYSTEM "%s">' % (s, notation.systemId)
|
||||
for i in range(doctype.entities.length):
|
||||
entity = doctype.entities.item(i)
|
||||
if s:
|
||||
s = s + "\n "
|
||||
s = "%s<!ENTITY %s" % (s, entity.nodeName)
|
||||
if entity.publicId:
|
||||
s = '%s PUBLIC "%s"\n "%s"' \
|
||||
% (s, entity.publicId, entity.systemId)
|
||||
elif entity.systemId:
|
||||
s = '%s SYSTEM "%s"' % (s, entity.systemId)
|
||||
else:
|
||||
s = '%s "%s"' % (s, entity.firstChild.data)
|
||||
if entity.notationName:
|
||||
s = "%s NOTATION %s" % (s, entity.notationName)
|
||||
s = s + ">"
|
||||
return s
|
||||
|
||||
def _getNSattrs(self):
|
||||
return ""
|
||||
|
||||
def external_entity_ref_handler(self, context, base, systemId, publicId):
|
||||
if systemId == _FRAGMENT_BUILDER_INTERNAL_SYSTEM_ID:
|
||||
# this entref is the one that we made to put the subtree
|
||||
# in; all of our given input is parsed in here.
|
||||
old_document = self.document
|
||||
old_cur_node = self.curNode
|
||||
parser = self._parser.ExternalEntityParserCreate(context)
|
||||
# put the real document back, parse into the fragment to return
|
||||
self.document = self.originalDocument
|
||||
self.fragment = self.document.createDocumentFragment()
|
||||
self.curNode = self.fragment
|
||||
try:
|
||||
parser.Parse(self._source, 1)
|
||||
finally:
|
||||
self.curNode = old_cur_node
|
||||
self.document = old_document
|
||||
self._source = None
|
||||
return -1
|
||||
else:
|
||||
return ExpatBuilder.external_entity_ref_handler(
|
||||
self, context, base, systemId, publicId)
|
||||
|
||||
|
||||
class Namespaces:
|
||||
"""Mix-in class for builders; adds support for namespaces."""
|
||||
|
||||
def _initNamespaces(self):
|
||||
# list of (prefix, uri) ns declarations. Namespace attrs are
|
||||
# constructed from this and added to the element's attrs.
|
||||
self._ns_ordered_prefixes = []
|
||||
|
||||
def createParser(self):
|
||||
"""Create a new namespace-handling parser."""
|
||||
parser = expat.ParserCreate(namespace_separator=" ")
|
||||
parser.namespace_prefixes = True
|
||||
return parser
|
||||
|
||||
def install(self, parser):
|
||||
"""Insert the namespace-handlers onto the parser."""
|
||||
ExpatBuilder.install(self, parser)
|
||||
if self._options.namespace_declarations:
|
||||
parser.StartNamespaceDeclHandler = (
|
||||
self.start_namespace_decl_handler)
|
||||
|
||||
def start_namespace_decl_handler(self, prefix, uri):
|
||||
"""Push this namespace declaration on our storage."""
|
||||
self._ns_ordered_prefixes.append((prefix, uri))
|
||||
|
||||
def start_element_handler(self, name, attributes):
|
||||
if ' ' in name:
|
||||
uri, localname, prefix, qname = _parse_ns_name(self, name)
|
||||
else:
|
||||
uri = EMPTY_NAMESPACE
|
||||
qname = name
|
||||
localname = None
|
||||
prefix = EMPTY_PREFIX
|
||||
node = minidom.Element(qname, uri, prefix, localname)
|
||||
node.ownerDocument = self.document
|
||||
_append_child(self.curNode, node)
|
||||
self.curNode = node
|
||||
|
||||
if self._ns_ordered_prefixes:
|
||||
for prefix, uri in self._ns_ordered_prefixes:
|
||||
if prefix:
|
||||
a = minidom.Attr(_intern(self, 'xmlns:' + prefix),
|
||||
XMLNS_NAMESPACE, prefix, "xmlns")
|
||||
else:
|
||||
a = minidom.Attr("xmlns", XMLNS_NAMESPACE,
|
||||
"xmlns", EMPTY_PREFIX)
|
||||
a.value = uri
|
||||
a.ownerDocument = self.document
|
||||
_set_attribute_node(node, a)
|
||||
del self._ns_ordered_prefixes[:]
|
||||
|
||||
if attributes:
|
||||
node._ensure_attributes()
|
||||
_attrs = node._attrs
|
||||
_attrsNS = node._attrsNS
|
||||
for i in range(0, len(attributes), 2):
|
||||
aname = attributes[i]
|
||||
value = attributes[i+1]
|
||||
if ' ' in aname:
|
||||
uri, localname, prefix, qname = _parse_ns_name(self, aname)
|
||||
a = minidom.Attr(qname, uri, localname, prefix)
|
||||
_attrs[qname] = a
|
||||
_attrsNS[(uri, localname)] = a
|
||||
else:
|
||||
a = minidom.Attr(aname, EMPTY_NAMESPACE,
|
||||
aname, EMPTY_PREFIX)
|
||||
_attrs[aname] = a
|
||||
_attrsNS[(EMPTY_NAMESPACE, aname)] = a
|
||||
a.ownerDocument = self.document
|
||||
a.value = value
|
||||
a.ownerElement = node
|
||||
|
||||
if __debug__:
|
||||
# This only adds some asserts to the original
|
||||
# end_element_handler(), so we only define this when -O is not
|
||||
# used. If changing one, be sure to check the other to see if
|
||||
# it needs to be changed as well.
|
||||
#
|
||||
def end_element_handler(self, name):
|
||||
curNode = self.curNode
|
||||
if ' ' in name:
|
||||
uri, localname, prefix, qname = _parse_ns_name(self, name)
|
||||
assert (curNode.namespaceURI == uri
|
||||
and curNode.localName == localname
|
||||
and curNode.prefix == prefix), \
|
||||
"element stack messed up! (namespace)"
|
||||
else:
|
||||
assert curNode.nodeName == name, \
|
||||
"element stack messed up - bad nodeName"
|
||||
assert curNode.namespaceURI == EMPTY_NAMESPACE, \
|
||||
"element stack messed up - bad namespaceURI"
|
||||
self.curNode = curNode.parentNode
|
||||
self._finish_end_element(curNode)
|
||||
|
||||
|
||||
class ExpatBuilderNS(Namespaces, ExpatBuilder):
|
||||
"""Document builder that supports namespaces."""
|
||||
|
||||
def reset(self):
|
||||
ExpatBuilder.reset(self)
|
||||
self._initNamespaces()
|
||||
|
||||
|
||||
class FragmentBuilderNS(Namespaces, FragmentBuilder):
|
||||
"""Fragment builder that supports namespaces."""
|
||||
|
||||
def reset(self):
|
||||
FragmentBuilder.reset(self)
|
||||
self._initNamespaces()
|
||||
|
||||
def _getNSattrs(self):
|
||||
"""Return string of namespace attributes from this element and
|
||||
ancestors."""
|
||||
# XXX This needs to be re-written to walk the ancestors of the
|
||||
# context to build up the namespace information from
|
||||
# declarations, elements, and attributes found in context.
|
||||
# Otherwise we have to store a bunch more data on the DOM
|
||||
# (though that *might* be more reliable -- not clear).
|
||||
attrs = ""
|
||||
context = self.context
|
||||
L = []
|
||||
while context:
|
||||
if hasattr(context, '_ns_prefix_uri'):
|
||||
for prefix, uri in context._ns_prefix_uri.items():
|
||||
# add every new NS decl from context to L and attrs string
|
||||
if prefix in L:
|
||||
continue
|
||||
L.append(prefix)
|
||||
if prefix:
|
||||
declname = "xmlns:" + prefix
|
||||
else:
|
||||
declname = "xmlns"
|
||||
if attrs:
|
||||
attrs = "%s\n %s='%s'" % (attrs, declname, uri)
|
||||
else:
|
||||
attrs = " %s='%s'" % (declname, uri)
|
||||
context = context.parentNode
|
||||
return attrs
|
||||
|
||||
|
||||
class ParseEscape(Exception):
|
||||
"""Exception raised to short-circuit parsing in InternalSubsetExtractor."""
|
||||
pass
|
||||
|
||||
class InternalSubsetExtractor(ExpatBuilder):
|
||||
"""XML processor which can rip out the internal document type subset."""
|
||||
|
||||
subset = None
|
||||
|
||||
def getSubset(self):
|
||||
"""Return the internal subset as a string."""
|
||||
return self.subset
|
||||
|
||||
def parseFile(self, file):
|
||||
try:
|
||||
ExpatBuilder.parseFile(self, file)
|
||||
except ParseEscape:
|
||||
pass
|
||||
|
||||
def parseString(self, string):
|
||||
try:
|
||||
ExpatBuilder.parseString(self, string)
|
||||
except ParseEscape:
|
||||
pass
|
||||
|
||||
def install(self, parser):
|
||||
parser.StartDoctypeDeclHandler = self.start_doctype_decl_handler
|
||||
parser.StartElementHandler = self.start_element_handler
|
||||
|
||||
def start_doctype_decl_handler(self, name, publicId, systemId,
|
||||
has_internal_subset):
|
||||
if has_internal_subset:
|
||||
parser = self.getParser()
|
||||
self.subset = []
|
||||
parser.DefaultHandler = self.subset.append
|
||||
parser.EndDoctypeDeclHandler = self.end_doctype_decl_handler
|
||||
else:
|
||||
raise ParseEscape()
|
||||
|
||||
def end_doctype_decl_handler(self):
|
||||
s = ''.join(self.subset).replace('\r\n', '\n').replace('\r', '\n')
|
||||
self.subset = s
|
||||
raise ParseEscape()
|
||||
|
||||
def start_element_handler(self, name, attrs):
|
||||
raise ParseEscape()
|
||||
|
||||
|
||||
def parse(file, namespaces=True):
|
||||
"""Parse a document, returning the resulting Document node.
|
||||
|
||||
'file' may be either a file name or an open file object.
|
||||
"""
|
||||
if namespaces:
|
||||
builder = ExpatBuilderNS()
|
||||
else:
|
||||
builder = ExpatBuilder()
|
||||
|
||||
if isinstance(file, str):
|
||||
with open(file, 'rb') as fp:
|
||||
result = builder.parseFile(fp)
|
||||
else:
|
||||
result = builder.parseFile(file)
|
||||
return result
|
||||
|
||||
|
||||
def parseString(string, namespaces=True):
|
||||
"""Parse a document from a string, returning the resulting
|
||||
Document node.
|
||||
"""
|
||||
if namespaces:
|
||||
builder = ExpatBuilderNS()
|
||||
else:
|
||||
builder = ExpatBuilder()
|
||||
return builder.parseString(string)
|
||||
|
||||
|
||||
def parseFragment(file, context, namespaces=True):
|
||||
"""Parse a fragment of a document, given the context from which it
|
||||
was originally extracted. context should be the parent of the
|
||||
node(s) which are in the fragment.
|
||||
|
||||
'file' may be either a file name or an open file object.
|
||||
"""
|
||||
if namespaces:
|
||||
builder = FragmentBuilderNS(context)
|
||||
else:
|
||||
builder = FragmentBuilder(context)
|
||||
|
||||
if isinstance(file, str):
|
||||
with open(file, 'rb') as fp:
|
||||
result = builder.parseFile(fp)
|
||||
else:
|
||||
result = builder.parseFile(file)
|
||||
return result
|
||||
|
||||
|
||||
def parseFragmentString(string, context, namespaces=True):
|
||||
"""Parse a fragment of a document from a string, given the context
|
||||
from which it was originally extracted. context should be the
|
||||
parent of the node(s) which are in the fragment.
|
||||
"""
|
||||
if namespaces:
|
||||
builder = FragmentBuilderNS(context)
|
||||
else:
|
||||
builder = FragmentBuilder(context)
|
||||
return builder.parseString(string)
|
||||
|
||||
|
||||
def makeBuilder(options):
|
||||
"""Create a builder based on an Options object."""
|
||||
if options.namespaces:
|
||||
return ExpatBuilderNS(options)
|
||||
else:
|
||||
return ExpatBuilder(options)
|
109
third_party/python/Lib/xml/dom/minicompat.py
vendored
Normal file
109
third_party/python/Lib/xml/dom/minicompat.py
vendored
Normal file
|
@ -0,0 +1,109 @@
|
|||
"""Python version compatibility support for minidom.
|
||||
|
||||
This module contains internal implementation details and
|
||||
should not be imported; use xml.dom.minidom instead.
|
||||
"""
|
||||
|
||||
# This module should only be imported using "import *".
|
||||
#
|
||||
# The following names are defined:
|
||||
#
|
||||
# NodeList -- lightest possible NodeList implementation
|
||||
#
|
||||
# EmptyNodeList -- lightest possible NodeList that is guaranteed to
|
||||
# remain empty (immutable)
|
||||
#
|
||||
# StringTypes -- tuple of defined string types
|
||||
#
|
||||
# defproperty -- function used in conjunction with GetattrMagic;
|
||||
# using these together is needed to make them work
|
||||
# as efficiently as possible in both Python 2.2+
|
||||
# and older versions. For example:
|
||||
#
|
||||
# class MyClass(GetattrMagic):
|
||||
# def _get_myattr(self):
|
||||
# return something
|
||||
#
|
||||
# defproperty(MyClass, "myattr",
|
||||
# "return some value")
|
||||
#
|
||||
# For Python 2.2 and newer, this will construct a
|
||||
# property object on the class, which avoids
|
||||
# needing to override __getattr__(). It will only
|
||||
# work for read-only attributes.
|
||||
#
|
||||
# For older versions of Python, inheriting from
|
||||
# GetattrMagic will use the traditional
|
||||
# __getattr__() hackery to achieve the same effect,
|
||||
# but less efficiently.
|
||||
#
|
||||
# defproperty() should be used for each version of
|
||||
# the relevant _get_<property>() function.
|
||||
|
||||
__all__ = ["NodeList", "EmptyNodeList", "StringTypes", "defproperty"]
|
||||
|
||||
import xml.dom
|
||||
|
||||
StringTypes = (str,)
|
||||
|
||||
|
||||
class NodeList(list):
|
||||
__slots__ = ()
|
||||
|
||||
def item(self, index):
|
||||
if 0 <= index < len(self):
|
||||
return self[index]
|
||||
|
||||
def _get_length(self):
|
||||
return len(self)
|
||||
|
||||
def _set_length(self, value):
|
||||
raise xml.dom.NoModificationAllowedErr(
|
||||
"attempt to modify read-only attribute 'length'")
|
||||
|
||||
length = property(_get_length, _set_length,
|
||||
doc="The number of nodes in the NodeList.")
|
||||
|
||||
# For backward compatibility
|
||||
def __setstate__(self, state):
|
||||
if state is None:
|
||||
state = []
|
||||
self[:] = state
|
||||
|
||||
|
||||
class EmptyNodeList(tuple):
|
||||
__slots__ = ()
|
||||
|
||||
def __add__(self, other):
|
||||
NL = NodeList()
|
||||
NL.extend(other)
|
||||
return NL
|
||||
|
||||
def __radd__(self, other):
|
||||
NL = NodeList()
|
||||
NL.extend(other)
|
||||
return NL
|
||||
|
||||
def item(self, index):
|
||||
return None
|
||||
|
||||
def _get_length(self):
|
||||
return 0
|
||||
|
||||
def _set_length(self, value):
|
||||
raise xml.dom.NoModificationAllowedErr(
|
||||
"attempt to modify read-only attribute 'length'")
|
||||
|
||||
length = property(_get_length, _set_length,
|
||||
doc="The number of nodes in the NodeList.")
|
||||
|
||||
|
||||
def defproperty(klass, name, doc):
|
||||
get = getattr(klass, ("_get_" + name))
|
||||
def set(self, value, name=name):
|
||||
raise xml.dom.NoModificationAllowedErr(
|
||||
"attempt to modify read-only attribute " + repr(name))
|
||||
assert not hasattr(klass, "_set_" + name), \
|
||||
"expected not to find _set_" + name
|
||||
prop = property(get, set, doc=doc)
|
||||
setattr(klass, name, prop)
|
1981
third_party/python/Lib/xml/dom/minidom.py
vendored
Normal file
1981
third_party/python/Lib/xml/dom/minidom.py
vendored
Normal file
File diff suppressed because it is too large
Load diff
342
third_party/python/Lib/xml/dom/pulldom.py
vendored
Normal file
342
third_party/python/Lib/xml/dom/pulldom.py
vendored
Normal file
|
@ -0,0 +1,342 @@
|
|||
import xml.sax
|
||||
import xml.sax.handler
|
||||
|
||||
START_ELEMENT = "START_ELEMENT"
|
||||
END_ELEMENT = "END_ELEMENT"
|
||||
COMMENT = "COMMENT"
|
||||
START_DOCUMENT = "START_DOCUMENT"
|
||||
END_DOCUMENT = "END_DOCUMENT"
|
||||
PROCESSING_INSTRUCTION = "PROCESSING_INSTRUCTION"
|
||||
IGNORABLE_WHITESPACE = "IGNORABLE_WHITESPACE"
|
||||
CHARACTERS = "CHARACTERS"
|
||||
|
||||
class PullDOM(xml.sax.ContentHandler):
|
||||
_locator = None
|
||||
document = None
|
||||
|
||||
def __init__(self, documentFactory=None):
|
||||
from xml.dom import XML_NAMESPACE
|
||||
self.documentFactory = documentFactory
|
||||
self.firstEvent = [None, None]
|
||||
self.lastEvent = self.firstEvent
|
||||
self.elementStack = []
|
||||
self.push = self.elementStack.append
|
||||
try:
|
||||
self.pop = self.elementStack.pop
|
||||
except AttributeError:
|
||||
# use class' pop instead
|
||||
pass
|
||||
self._ns_contexts = [{XML_NAMESPACE:'xml'}] # contains uri -> prefix dicts
|
||||
self._current_context = self._ns_contexts[-1]
|
||||
self.pending_events = []
|
||||
|
||||
def pop(self):
|
||||
result = self.elementStack[-1]
|
||||
del self.elementStack[-1]
|
||||
return result
|
||||
|
||||
def setDocumentLocator(self, locator):
|
||||
self._locator = locator
|
||||
|
||||
def startPrefixMapping(self, prefix, uri):
|
||||
if not hasattr(self, '_xmlns_attrs'):
|
||||
self._xmlns_attrs = []
|
||||
self._xmlns_attrs.append((prefix or 'xmlns', uri))
|
||||
self._ns_contexts.append(self._current_context.copy())
|
||||
self._current_context[uri] = prefix or None
|
||||
|
||||
def endPrefixMapping(self, prefix):
|
||||
self._current_context = self._ns_contexts.pop()
|
||||
|
||||
def startElementNS(self, name, tagName , attrs):
|
||||
# Retrieve xml namespace declaration attributes.
|
||||
xmlns_uri = 'http://www.w3.org/2000/xmlns/'
|
||||
xmlns_attrs = getattr(self, '_xmlns_attrs', None)
|
||||
if xmlns_attrs is not None:
|
||||
for aname, value in xmlns_attrs:
|
||||
attrs._attrs[(xmlns_uri, aname)] = value
|
||||
self._xmlns_attrs = []
|
||||
uri, localname = name
|
||||
if uri:
|
||||
# When using namespaces, the reader may or may not
|
||||
# provide us with the original name. If not, create
|
||||
# *a* valid tagName from the current context.
|
||||
if tagName is None:
|
||||
prefix = self._current_context[uri]
|
||||
if prefix:
|
||||
tagName = prefix + ":" + localname
|
||||
else:
|
||||
tagName = localname
|
||||
if self.document:
|
||||
node = self.document.createElementNS(uri, tagName)
|
||||
else:
|
||||
node = self.buildDocument(uri, tagName)
|
||||
else:
|
||||
# When the tagname is not prefixed, it just appears as
|
||||
# localname
|
||||
if self.document:
|
||||
node = self.document.createElement(localname)
|
||||
else:
|
||||
node = self.buildDocument(None, localname)
|
||||
|
||||
for aname,value in attrs.items():
|
||||
a_uri, a_localname = aname
|
||||
if a_uri == xmlns_uri:
|
||||
if a_localname == 'xmlns':
|
||||
qname = a_localname
|
||||
else:
|
||||
qname = 'xmlns:' + a_localname
|
||||
attr = self.document.createAttributeNS(a_uri, qname)
|
||||
node.setAttributeNodeNS(attr)
|
||||
elif a_uri:
|
||||
prefix = self._current_context[a_uri]
|
||||
if prefix:
|
||||
qname = prefix + ":" + a_localname
|
||||
else:
|
||||
qname = a_localname
|
||||
attr = self.document.createAttributeNS(a_uri, qname)
|
||||
node.setAttributeNodeNS(attr)
|
||||
else:
|
||||
attr = self.document.createAttribute(a_localname)
|
||||
node.setAttributeNode(attr)
|
||||
attr.value = value
|
||||
|
||||
self.lastEvent[1] = [(START_ELEMENT, node), None]
|
||||
self.lastEvent = self.lastEvent[1]
|
||||
self.push(node)
|
||||
|
||||
def endElementNS(self, name, tagName):
|
||||
self.lastEvent[1] = [(END_ELEMENT, self.pop()), None]
|
||||
self.lastEvent = self.lastEvent[1]
|
||||
|
||||
def startElement(self, name, attrs):
|
||||
if self.document:
|
||||
node = self.document.createElement(name)
|
||||
else:
|
||||
node = self.buildDocument(None, name)
|
||||
|
||||
for aname,value in attrs.items():
|
||||
attr = self.document.createAttribute(aname)
|
||||
attr.value = value
|
||||
node.setAttributeNode(attr)
|
||||
|
||||
self.lastEvent[1] = [(START_ELEMENT, node), None]
|
||||
self.lastEvent = self.lastEvent[1]
|
||||
self.push(node)
|
||||
|
||||
def endElement(self, name):
|
||||
self.lastEvent[1] = [(END_ELEMENT, self.pop()), None]
|
||||
self.lastEvent = self.lastEvent[1]
|
||||
|
||||
def comment(self, s):
|
||||
if self.document:
|
||||
node = self.document.createComment(s)
|
||||
self.lastEvent[1] = [(COMMENT, node), None]
|
||||
self.lastEvent = self.lastEvent[1]
|
||||
else:
|
||||
event = [(COMMENT, s), None]
|
||||
self.pending_events.append(event)
|
||||
|
||||
def processingInstruction(self, target, data):
|
||||
if self.document:
|
||||
node = self.document.createProcessingInstruction(target, data)
|
||||
self.lastEvent[1] = [(PROCESSING_INSTRUCTION, node), None]
|
||||
self.lastEvent = self.lastEvent[1]
|
||||
else:
|
||||
event = [(PROCESSING_INSTRUCTION, target, data), None]
|
||||
self.pending_events.append(event)
|
||||
|
||||
def ignorableWhitespace(self, chars):
|
||||
node = self.document.createTextNode(chars)
|
||||
self.lastEvent[1] = [(IGNORABLE_WHITESPACE, node), None]
|
||||
self.lastEvent = self.lastEvent[1]
|
||||
|
||||
def characters(self, chars):
|
||||
node = self.document.createTextNode(chars)
|
||||
self.lastEvent[1] = [(CHARACTERS, node), None]
|
||||
self.lastEvent = self.lastEvent[1]
|
||||
|
||||
def startDocument(self):
|
||||
if self.documentFactory is None:
|
||||
import xml.dom.minidom
|
||||
self.documentFactory = xml.dom.minidom.Document.implementation
|
||||
|
||||
def buildDocument(self, uri, tagname):
|
||||
# Can't do that in startDocument, since we need the tagname
|
||||
# XXX: obtain DocumentType
|
||||
node = self.documentFactory.createDocument(uri, tagname, None)
|
||||
self.document = node
|
||||
self.lastEvent[1] = [(START_DOCUMENT, node), None]
|
||||
self.lastEvent = self.lastEvent[1]
|
||||
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
|
||||
|
||||
def endDocument(self):
|
||||
self.lastEvent[1] = [(END_DOCUMENT, self.document), None]
|
||||
self.pop()
|
||||
|
||||
def clear(self):
|
||||
"clear(): Explicitly release parsing structures"
|
||||
self.document = None
|
||||
|
||||
class ErrorHandler:
|
||||
def warning(self, exception):
|
||||
print(exception)
|
||||
def error(self, exception):
|
||||
raise exception
|
||||
def fatalError(self, exception):
|
||||
raise exception
|
||||
|
||||
class DOMEventStream:
|
||||
def __init__(self, stream, parser, bufsize):
|
||||
self.stream = stream
|
||||
self.parser = parser
|
||||
self.bufsize = bufsize
|
||||
if not hasattr(self.parser, 'feed'):
|
||||
self.getEvent = self._slurp
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self.pulldom = PullDOM()
|
||||
# This content handler relies on namespace support
|
||||
self.parser.setFeature(xml.sax.handler.feature_namespaces, 1)
|
||||
self.parser.setContentHandler(self.pulldom)
|
||||
|
||||
def __getitem__(self, pos):
|
||||
rc = self.getEvent()
|
||||
if rc:
|
||||
return rc
|
||||
raise IndexError
|
||||
|
||||
def __next__(self):
|
||||
rc = self.getEvent()
|
||||
if rc:
|
||||
return rc
|
||||
raise StopIteration
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def expandNode(self, node):
|
||||
event = self.getEvent()
|
||||
parents = [node]
|
||||
while event:
|
||||
token, cur_node = event
|
||||
if cur_node is node:
|
||||
return
|
||||
if token != END_ELEMENT:
|
||||
parents[-1].appendChild(cur_node)
|
||||
if token == START_ELEMENT:
|
||||
parents.append(cur_node)
|
||||
elif token == END_ELEMENT:
|
||||
del parents[-1]
|
||||
event = self.getEvent()
|
||||
|
||||
def getEvent(self):
|
||||
# use IncrementalParser interface, so we get the desired
|
||||
# pull effect
|
||||
if not self.pulldom.firstEvent[1]:
|
||||
self.pulldom.lastEvent = self.pulldom.firstEvent
|
||||
while not self.pulldom.firstEvent[1]:
|
||||
buf = self.stream.read(self.bufsize)
|
||||
if not buf:
|
||||
self.parser.close()
|
||||
return None
|
||||
self.parser.feed(buf)
|
||||
rc = self.pulldom.firstEvent[1][0]
|
||||
self.pulldom.firstEvent[1] = self.pulldom.firstEvent[1][1]
|
||||
return rc
|
||||
|
||||
def _slurp(self):
|
||||
""" Fallback replacement for getEvent() using the
|
||||
standard SAX2 interface, which means we slurp the
|
||||
SAX events into memory (no performance gain, but
|
||||
we are compatible to all SAX parsers).
|
||||
"""
|
||||
self.parser.parse(self.stream)
|
||||
self.getEvent = self._emit
|
||||
return self._emit()
|
||||
|
||||
def _emit(self):
|
||||
""" Fallback replacement for getEvent() that emits
|
||||
the events that _slurp() read previously.
|
||||
"""
|
||||
rc = self.pulldom.firstEvent[1][0]
|
||||
self.pulldom.firstEvent[1] = self.pulldom.firstEvent[1][1]
|
||||
return rc
|
||||
|
||||
def clear(self):
|
||||
"""clear(): Explicitly release parsing objects"""
|
||||
self.pulldom.clear()
|
||||
del self.pulldom
|
||||
self.parser = None
|
||||
self.stream = None
|
||||
|
||||
class SAX2DOM(PullDOM):
|
||||
|
||||
def startElementNS(self, name, tagName , attrs):
|
||||
PullDOM.startElementNS(self, name, tagName, attrs)
|
||||
curNode = self.elementStack[-1]
|
||||
parentNode = self.elementStack[-2]
|
||||
parentNode.appendChild(curNode)
|
||||
|
||||
def startElement(self, name, attrs):
|
||||
PullDOM.startElement(self, name, attrs)
|
||||
curNode = self.elementStack[-1]
|
||||
parentNode = self.elementStack[-2]
|
||||
parentNode.appendChild(curNode)
|
||||
|
||||
def processingInstruction(self, target, data):
|
||||
PullDOM.processingInstruction(self, target, data)
|
||||
node = self.lastEvent[0][1]
|
||||
parentNode = self.elementStack[-1]
|
||||
parentNode.appendChild(node)
|
||||
|
||||
def ignorableWhitespace(self, chars):
|
||||
PullDOM.ignorableWhitespace(self, chars)
|
||||
node = self.lastEvent[0][1]
|
||||
parentNode = self.elementStack[-1]
|
||||
parentNode.appendChild(node)
|
||||
|
||||
def characters(self, chars):
|
||||
PullDOM.characters(self, chars)
|
||||
node = self.lastEvent[0][1]
|
||||
parentNode = self.elementStack[-1]
|
||||
parentNode.appendChild(node)
|
||||
|
||||
|
||||
default_bufsize = (2 ** 14) - 20
|
||||
|
||||
def parse(stream_or_string, parser=None, bufsize=None):
|
||||
if bufsize is None:
|
||||
bufsize = default_bufsize
|
||||
if isinstance(stream_or_string, str):
|
||||
stream = open(stream_or_string, 'rb')
|
||||
else:
|
||||
stream = stream_or_string
|
||||
if not parser:
|
||||
parser = xml.sax.make_parser()
|
||||
return DOMEventStream(stream, parser, bufsize)
|
||||
|
||||
def parseString(string, parser=None):
|
||||
from io import StringIO
|
||||
|
||||
bufsize = len(string)
|
||||
buf = StringIO(string)
|
||||
if not parser:
|
||||
parser = xml.sax.make_parser()
|
||||
return DOMEventStream(buf, parser, bufsize)
|
410
third_party/python/Lib/xml/dom/xmlbuilder.py
vendored
Normal file
410
third_party/python/Lib/xml/dom/xmlbuilder.py
vendored
Normal file
|
@ -0,0 +1,410 @@
|
|||
"""Implementation of the DOM Level 3 'LS-Load' feature."""
|
||||
|
||||
import copy
|
||||
import warnings
|
||||
import xml.dom
|
||||
|
||||
from xml.dom.NodeFilter import NodeFilter
|
||||
|
||||
|
||||
__all__ = ["DOMBuilder", "DOMEntityResolver", "DOMInputSource"]
|
||||
|
||||
|
||||
class Options:
|
||||
"""Features object that has variables set for each DOMBuilder feature.
|
||||
|
||||
The DOMBuilder class uses an instance of this class to pass settings to
|
||||
the ExpatBuilder class.
|
||||
"""
|
||||
|
||||
# Note that the DOMBuilder class in LoadSave constrains which of these
|
||||
# values can be set using the DOM Level 3 LoadSave feature.
|
||||
|
||||
namespaces = 1
|
||||
namespace_declarations = True
|
||||
validation = False
|
||||
external_parameter_entities = True
|
||||
external_general_entities = True
|
||||
external_dtd_subset = True
|
||||
validate_if_schema = False
|
||||
validate = False
|
||||
datatype_normalization = False
|
||||
create_entity_ref_nodes = True
|
||||
entities = True
|
||||
whitespace_in_element_content = True
|
||||
cdata_sections = True
|
||||
comments = True
|
||||
charset_overrides_xml_encoding = True
|
||||
infoset = False
|
||||
supported_mediatypes_only = False
|
||||
|
||||
errorHandler = None
|
||||
filter = None
|
||||
|
||||
|
||||
class DOMBuilder:
|
||||
entityResolver = None
|
||||
errorHandler = None
|
||||
filter = None
|
||||
|
||||
ACTION_REPLACE = 1
|
||||
ACTION_APPEND_AS_CHILDREN = 2
|
||||
ACTION_INSERT_AFTER = 3
|
||||
ACTION_INSERT_BEFORE = 4
|
||||
|
||||
_legal_actions = (ACTION_REPLACE, ACTION_APPEND_AS_CHILDREN,
|
||||
ACTION_INSERT_AFTER, ACTION_INSERT_BEFORE)
|
||||
|
||||
def __init__(self):
|
||||
self._options = Options()
|
||||
|
||||
def _get_entityResolver(self):
|
||||
return self.entityResolver
|
||||
def _set_entityResolver(self, entityResolver):
|
||||
self.entityResolver = entityResolver
|
||||
|
||||
def _get_errorHandler(self):
|
||||
return self.errorHandler
|
||||
def _set_errorHandler(self, errorHandler):
|
||||
self.errorHandler = errorHandler
|
||||
|
||||
def _get_filter(self):
|
||||
return self.filter
|
||||
def _set_filter(self, filter):
|
||||
self.filter = filter
|
||||
|
||||
def setFeature(self, name, state):
|
||||
if self.supportsFeature(name):
|
||||
state = state and 1 or 0
|
||||
try:
|
||||
settings = self._settings[(_name_xform(name), state)]
|
||||
except KeyError:
|
||||
raise xml.dom.NotSupportedErr(
|
||||
"unsupported feature: %r" % (name,))
|
||||
else:
|
||||
for name, value in settings:
|
||||
setattr(self._options, name, value)
|
||||
else:
|
||||
raise xml.dom.NotFoundErr("unknown feature: " + repr(name))
|
||||
|
||||
def supportsFeature(self, name):
|
||||
return hasattr(self._options, _name_xform(name))
|
||||
|
||||
def canSetFeature(self, name, state):
|
||||
key = (_name_xform(name), state and 1 or 0)
|
||||
return key in self._settings
|
||||
|
||||
# This dictionary maps from (feature,value) to a list of
|
||||
# (option,value) pairs that should be set on the Options object.
|
||||
# If a (feature,value) setting is not in this dictionary, it is
|
||||
# not supported by the DOMBuilder.
|
||||
#
|
||||
_settings = {
|
||||
("namespace_declarations", 0): [
|
||||
("namespace_declarations", 0)],
|
||||
("namespace_declarations", 1): [
|
||||
("namespace_declarations", 1)],
|
||||
("validation", 0): [
|
||||
("validation", 0)],
|
||||
("external_general_entities", 0): [
|
||||
("external_general_entities", 0)],
|
||||
("external_general_entities", 1): [
|
||||
("external_general_entities", 1)],
|
||||
("external_parameter_entities", 0): [
|
||||
("external_parameter_entities", 0)],
|
||||
("external_parameter_entities", 1): [
|
||||
("external_parameter_entities", 1)],
|
||||
("validate_if_schema", 0): [
|
||||
("validate_if_schema", 0)],
|
||||
("create_entity_ref_nodes", 0): [
|
||||
("create_entity_ref_nodes", 0)],
|
||||
("create_entity_ref_nodes", 1): [
|
||||
("create_entity_ref_nodes", 1)],
|
||||
("entities", 0): [
|
||||
("create_entity_ref_nodes", 0),
|
||||
("entities", 0)],
|
||||
("entities", 1): [
|
||||
("entities", 1)],
|
||||
("whitespace_in_element_content", 0): [
|
||||
("whitespace_in_element_content", 0)],
|
||||
("whitespace_in_element_content", 1): [
|
||||
("whitespace_in_element_content", 1)],
|
||||
("cdata_sections", 0): [
|
||||
("cdata_sections", 0)],
|
||||
("cdata_sections", 1): [
|
||||
("cdata_sections", 1)],
|
||||
("comments", 0): [
|
||||
("comments", 0)],
|
||||
("comments", 1): [
|
||||
("comments", 1)],
|
||||
("charset_overrides_xml_encoding", 0): [
|
||||
("charset_overrides_xml_encoding", 0)],
|
||||
("charset_overrides_xml_encoding", 1): [
|
||||
("charset_overrides_xml_encoding", 1)],
|
||||
("infoset", 0): [],
|
||||
("infoset", 1): [
|
||||
("namespace_declarations", 0),
|
||||
("validate_if_schema", 0),
|
||||
("create_entity_ref_nodes", 0),
|
||||
("entities", 0),
|
||||
("cdata_sections", 0),
|
||||
("datatype_normalization", 1),
|
||||
("whitespace_in_element_content", 1),
|
||||
("comments", 1),
|
||||
("charset_overrides_xml_encoding", 1)],
|
||||
("supported_mediatypes_only", 0): [
|
||||
("supported_mediatypes_only", 0)],
|
||||
("namespaces", 0): [
|
||||
("namespaces", 0)],
|
||||
("namespaces", 1): [
|
||||
("namespaces", 1)],
|
||||
}
|
||||
|
||||
def getFeature(self, name):
|
||||
xname = _name_xform(name)
|
||||
try:
|
||||
return getattr(self._options, xname)
|
||||
except AttributeError:
|
||||
if name == "infoset":
|
||||
options = self._options
|
||||
return (options.datatype_normalization
|
||||
and options.whitespace_in_element_content
|
||||
and options.comments
|
||||
and options.charset_overrides_xml_encoding
|
||||
and not (options.namespace_declarations
|
||||
or options.validate_if_schema
|
||||
or options.create_entity_ref_nodes
|
||||
or options.entities
|
||||
or options.cdata_sections))
|
||||
raise xml.dom.NotFoundErr("feature %s not known" % repr(name))
|
||||
|
||||
def parseURI(self, uri):
|
||||
if self.entityResolver:
|
||||
input = self.entityResolver.resolveEntity(None, uri)
|
||||
else:
|
||||
input = DOMEntityResolver().resolveEntity(None, uri)
|
||||
return self.parse(input)
|
||||
|
||||
def parse(self, input):
|
||||
options = copy.copy(self._options)
|
||||
options.filter = self.filter
|
||||
options.errorHandler = self.errorHandler
|
||||
fp = input.byteStream
|
||||
if fp is None and options.systemId:
|
||||
import urllib.request
|
||||
fp = urllib.request.urlopen(input.systemId)
|
||||
return self._parse_bytestream(fp, options)
|
||||
|
||||
def parseWithContext(self, input, cnode, action):
|
||||
if action not in self._legal_actions:
|
||||
raise ValueError("not a legal action")
|
||||
raise NotImplementedError("Haven't written this yet...")
|
||||
|
||||
def _parse_bytestream(self, stream, options):
|
||||
import xml.dom.expatbuilder
|
||||
builder = xml.dom.expatbuilder.makeBuilder(options)
|
||||
return builder.parseFile(stream)
|
||||
|
||||
|
||||
def _name_xform(name):
|
||||
return name.lower().replace('-', '_')
|
||||
|
||||
|
||||
class DOMEntityResolver(object):
|
||||
__slots__ = '_opener',
|
||||
|
||||
def resolveEntity(self, publicId, systemId):
|
||||
assert systemId is not None
|
||||
source = DOMInputSource()
|
||||
source.publicId = publicId
|
||||
source.systemId = systemId
|
||||
source.byteStream = self._get_opener().open(systemId)
|
||||
|
||||
# determine the encoding if the transport provided it
|
||||
source.encoding = self._guess_media_encoding(source)
|
||||
|
||||
# determine the base URI is we can
|
||||
import posixpath, urllib.parse
|
||||
parts = urllib.parse.urlparse(systemId)
|
||||
scheme, netloc, path, params, query, fragment = parts
|
||||
# XXX should we check the scheme here as well?
|
||||
if path and not path.endswith("/"):
|
||||
path = posixpath.dirname(path) + "/"
|
||||
parts = scheme, netloc, path, params, query, fragment
|
||||
source.baseURI = urllib.parse.urlunparse(parts)
|
||||
|
||||
return source
|
||||
|
||||
def _get_opener(self):
|
||||
try:
|
||||
return self._opener
|
||||
except AttributeError:
|
||||
self._opener = self._create_opener()
|
||||
return self._opener
|
||||
|
||||
def _create_opener(self):
|
||||
import urllib.request
|
||||
return urllib.request.build_opener()
|
||||
|
||||
def _guess_media_encoding(self, source):
|
||||
info = source.byteStream.info()
|
||||
if "Content-Type" in info:
|
||||
for param in info.getplist():
|
||||
if param.startswith("charset="):
|
||||
return param.split("=", 1)[1].lower()
|
||||
|
||||
|
||||
class DOMInputSource(object):
|
||||
__slots__ = ('byteStream', 'characterStream', 'stringData',
|
||||
'encoding', 'publicId', 'systemId', 'baseURI')
|
||||
|
||||
def __init__(self):
|
||||
self.byteStream = None
|
||||
self.characterStream = None
|
||||
self.stringData = None
|
||||
self.encoding = None
|
||||
self.publicId = None
|
||||
self.systemId = None
|
||||
self.baseURI = None
|
||||
|
||||
def _get_byteStream(self):
|
||||
return self.byteStream
|
||||
def _set_byteStream(self, byteStream):
|
||||
self.byteStream = byteStream
|
||||
|
||||
def _get_characterStream(self):
|
||||
return self.characterStream
|
||||
def _set_characterStream(self, characterStream):
|
||||
self.characterStream = characterStream
|
||||
|
||||
def _get_stringData(self):
|
||||
return self.stringData
|
||||
def _set_stringData(self, data):
|
||||
self.stringData = data
|
||||
|
||||
def _get_encoding(self):
|
||||
return self.encoding
|
||||
def _set_encoding(self, encoding):
|
||||
self.encoding = encoding
|
||||
|
||||
def _get_publicId(self):
|
||||
return self.publicId
|
||||
def _set_publicId(self, publicId):
|
||||
self.publicId = publicId
|
||||
|
||||
def _get_systemId(self):
|
||||
return self.systemId
|
||||
def _set_systemId(self, systemId):
|
||||
self.systemId = systemId
|
||||
|
||||
def _get_baseURI(self):
|
||||
return self.baseURI
|
||||
def _set_baseURI(self, uri):
|
||||
self.baseURI = uri
|
||||
|
||||
|
||||
class DOMBuilderFilter:
|
||||
"""Element filter which can be used to tailor construction of
|
||||
a DOM instance.
|
||||
"""
|
||||
|
||||
# There's really no need for this class; concrete implementations
|
||||
# should just implement the endElement() and startElement()
|
||||
# methods as appropriate. Using this makes it easy to only
|
||||
# implement one of them.
|
||||
|
||||
FILTER_ACCEPT = 1
|
||||
FILTER_REJECT = 2
|
||||
FILTER_SKIP = 3
|
||||
FILTER_INTERRUPT = 4
|
||||
|
||||
whatToShow = NodeFilter.SHOW_ALL
|
||||
|
||||
def _get_whatToShow(self):
|
||||
return self.whatToShow
|
||||
|
||||
def acceptNode(self, element):
|
||||
return self.FILTER_ACCEPT
|
||||
|
||||
def startContainer(self, element):
|
||||
return self.FILTER_ACCEPT
|
||||
|
||||
del NodeFilter
|
||||
|
||||
|
||||
class _AsyncDeprecatedProperty:
|
||||
def warn(self, cls):
|
||||
clsname = cls.__name__
|
||||
warnings.warn(
|
||||
"{cls}.async is deprecated; use {cls}.async_".format(cls=clsname),
|
||||
DeprecationWarning)
|
||||
|
||||
def __get__(self, instance, cls):
|
||||
self.warn(cls)
|
||||
if instance is not None:
|
||||
return instance.async_
|
||||
return False
|
||||
|
||||
def __set__(self, instance, value):
|
||||
self.warn(type(instance))
|
||||
setattr(instance, 'async_', value)
|
||||
|
||||
|
||||
class DocumentLS:
|
||||
"""Mixin to create documents that conform to the load/save spec."""
|
||||
|
||||
async_ = False
|
||||
locals()['async'] = _AsyncDeprecatedProperty() # Avoid DeprecationWarning
|
||||
|
||||
def _get_async(self):
|
||||
return False
|
||||
|
||||
def _set_async(self, flag):
|
||||
if flag:
|
||||
raise xml.dom.NotSupportedErr(
|
||||
"asynchronous document loading is not supported")
|
||||
|
||||
def abort(self):
|
||||
# What does it mean to "clear" a document? Does the
|
||||
# documentElement disappear?
|
||||
raise NotImplementedError(
|
||||
"haven't figured out what this means yet")
|
||||
|
||||
def load(self, uri):
|
||||
raise NotImplementedError("haven't written this yet")
|
||||
|
||||
def loadXML(self, source):
|
||||
raise NotImplementedError("haven't written this yet")
|
||||
|
||||
def saveXML(self, snode):
|
||||
if snode is None:
|
||||
snode = self
|
||||
elif snode.ownerDocument is not self:
|
||||
raise xml.dom.WrongDocumentErr()
|
||||
return snode.toxml()
|
||||
|
||||
|
||||
del _AsyncDeprecatedProperty
|
||||
|
||||
|
||||
class DOMImplementationLS:
|
||||
MODE_SYNCHRONOUS = 1
|
||||
MODE_ASYNCHRONOUS = 2
|
||||
|
||||
def createDOMBuilder(self, mode, schemaType):
|
||||
if schemaType is not None:
|
||||
raise xml.dom.NotSupportedErr(
|
||||
"schemaType not yet supported")
|
||||
if mode == self.MODE_SYNCHRONOUS:
|
||||
return DOMBuilder()
|
||||
if mode == self.MODE_ASYNCHRONOUS:
|
||||
raise xml.dom.NotSupportedErr(
|
||||
"asynchronous builders are not supported")
|
||||
raise ValueError("unknown value for mode")
|
||||
|
||||
def createDOMWriter(self):
|
||||
raise NotImplementedError(
|
||||
"the writer interface hasn't been written yet!")
|
||||
|
||||
def createDOMInputSource(self):
|
||||
return DOMInputSource()
|
Loading…
Add table
Add a link
Reference in a new issue