lxml 5.2.0__cp310-cp310-win32.whl → 5.2.2__cp310-cp310-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lxml/ElementInclude.py +244 -244
- lxml/__init__.py +22 -22
- lxml/_elementpath.cp310-win32.pyd +0 -0
- lxml/_elementpath.py +341 -341
- lxml/apihelpers.pxi +1793 -1793
- lxml/builder.cp310-win32.pyd +0 -0
- lxml/builder.py +232 -232
- lxml/classlookup.pxi +580 -580
- lxml/cleanup.pxi +215 -215
- lxml/cssselect.py +101 -101
- lxml/debug.pxi +90 -90
- lxml/docloader.pxi +178 -178
- lxml/doctestcompare.py +488 -488
- lxml/dtd.pxi +478 -478
- lxml/etree.cp310-win32.pyd +0 -0
- lxml/etree.h +6 -6
- lxml/etree.pyx +3732 -3711
- lxml/extensions.pxi +833 -833
- lxml/html/ElementSoup.py +10 -10
- lxml/html/__init__.py +1923 -1923
- lxml/html/_diffcommand.py +86 -86
- lxml/html/_html5builder.py +100 -100
- lxml/html/_setmixin.py +56 -56
- lxml/html/builder.py +133 -133
- lxml/html/clean.py +21 -21
- lxml/html/defs.py +135 -135
- lxml/html/diff.cp310-win32.pyd +0 -0
- lxml/html/diff.py +878 -878
- lxml/html/formfill.py +299 -299
- lxml/html/html5parser.py +260 -260
- lxml/html/soupparser.py +314 -314
- lxml/html/usedoctest.py +13 -13
- lxml/includes/c14n.pxd +25 -25
- lxml/includes/config.pxd +3 -3
- lxml/includes/dtdvalid.pxd +18 -18
- lxml/includes/etree_defs.h +379 -379
- lxml/includes/etreepublic.pxd +237 -237
- lxml/includes/htmlparser.pxd +56 -56
- lxml/includes/lxml-version.h +1 -1
- lxml/includes/relaxng.pxd +64 -64
- lxml/includes/schematron.pxd +34 -34
- lxml/includes/tree.pxd +494 -494
- lxml/includes/uri.pxd +5 -5
- lxml/includes/xinclude.pxd +22 -22
- lxml/includes/xmlerror.pxd +852 -852
- lxml/includes/xmlparser.pxd +265 -265
- lxml/includes/xmlschema.pxd +35 -35
- lxml/includes/xpath.pxd +136 -136
- lxml/includes/xslt.pxd +190 -190
- lxml/isoschematron/__init__.py +348 -348
- lxml/isoschematron/resources/rng/iso-schematron.rng +709 -709
- lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl +75 -75
- lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl +312 -312
- lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl +1159 -1159
- lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl +54 -54
- lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl +1796 -1796
- lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl +588 -588
- lxml/iterparse.pxi +438 -438
- lxml/lxml.etree.h +6 -6
- lxml/nsclasses.pxi +281 -281
- lxml/objectify.cp310-win32.pyd +0 -0
- lxml/objectify.pyx +2145 -2145
- lxml/objectpath.pxi +332 -332
- lxml/parser.pxi +1994 -1994
- lxml/parsertarget.pxi +180 -180
- lxml/proxy.pxi +619 -619
- lxml/public-api.pxi +178 -178
- lxml/pyclasslookup.py +3 -3
- lxml/readonlytree.pxi +565 -565
- lxml/relaxng.pxi +165 -165
- lxml/sax.cp310-win32.pyd +0 -0
- lxml/sax.py +275 -275
- lxml/saxparser.pxi +875 -875
- lxml/schematron.pxi +168 -168
- lxml/serializer.pxi +1871 -1871
- lxml/usedoctest.py +13 -13
- lxml/xinclude.pxi +67 -67
- lxml/xmlerror.pxi +1654 -1654
- lxml/xmlid.pxi +179 -179
- lxml/xmlschema.pxi +215 -215
- lxml/xpath.pxi +487 -487
- lxml/xslt.pxi +950 -950
- lxml/xsltext.pxi +242 -242
- {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/LICENSE.txt +29 -29
- {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/LICENSES.txt +29 -29
- {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/METADATA +9 -17
- {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/RECORD +89 -89
- {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/WHEEL +0 -0
- {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/top_level.txt +0 -0
lxml/sax.py
CHANGED
@@ -1,275 +1,275 @@
|
|
1
|
-
# cython: language_level=2
|
2
|
-
|
3
|
-
"""
|
4
|
-
SAX-based adapter to copy trees from/to the Python standard library.
|
5
|
-
|
6
|
-
Use the `ElementTreeContentHandler` class to build an ElementTree from
|
7
|
-
SAX events.
|
8
|
-
|
9
|
-
Use the `ElementTreeProducer` class or the `saxify()` function to fire
|
10
|
-
the SAX events of an ElementTree against a SAX ContentHandler.
|
11
|
-
|
12
|
-
See https://lxml.de/sax.html
|
13
|
-
"""
|
14
|
-
|
15
|
-
|
16
|
-
from xml.sax.handler import ContentHandler
|
17
|
-
from lxml import etree
|
18
|
-
from lxml.etree import ElementTree, SubElement
|
19
|
-
from lxml.etree import Comment, ProcessingInstruction
|
20
|
-
|
21
|
-
|
22
|
-
class SaxError(etree.LxmlError):
|
23
|
-
"""General SAX error.
|
24
|
-
"""
|
25
|
-
|
26
|
-
|
27
|
-
def _getNsTag(tag):
|
28
|
-
if tag[0] == '{':
|
29
|
-
return tuple(tag[1:].split('}', 1))
|
30
|
-
else:
|
31
|
-
return None, tag
|
32
|
-
|
33
|
-
|
34
|
-
class ElementTreeContentHandler(ContentHandler):
|
35
|
-
"""Build an lxml ElementTree from SAX events.
|
36
|
-
"""
|
37
|
-
def __init__(self, makeelement=None):
|
38
|
-
ContentHandler.__init__(self)
|
39
|
-
self._root = None
|
40
|
-
self._root_siblings = []
|
41
|
-
self._element_stack = []
|
42
|
-
self._default_ns = None
|
43
|
-
self._ns_mapping = { None : [None] }
|
44
|
-
self._new_mappings = {}
|
45
|
-
if makeelement is None:
|
46
|
-
makeelement = etree.Element
|
47
|
-
self._makeelement = makeelement
|
48
|
-
|
49
|
-
def _get_etree(self):
|
50
|
-
"Contains the generated ElementTree after parsing is finished."
|
51
|
-
return ElementTree(self._root)
|
52
|
-
|
53
|
-
etree = property(_get_etree, doc=_get_etree.__doc__)
|
54
|
-
|
55
|
-
def setDocumentLocator(self, locator):
|
56
|
-
pass
|
57
|
-
|
58
|
-
def startDocument(self):
|
59
|
-
pass
|
60
|
-
|
61
|
-
def endDocument(self):
|
62
|
-
pass
|
63
|
-
|
64
|
-
def startPrefixMapping(self, prefix, uri):
|
65
|
-
self._new_mappings[prefix] = uri
|
66
|
-
try:
|
67
|
-
self._ns_mapping[prefix].append(uri)
|
68
|
-
except KeyError:
|
69
|
-
self._ns_mapping[prefix] = [uri]
|
70
|
-
if prefix is None:
|
71
|
-
self._default_ns = uri
|
72
|
-
|
73
|
-
def endPrefixMapping(self, prefix):
|
74
|
-
ns_uri_list = self._ns_mapping[prefix]
|
75
|
-
ns_uri_list.pop()
|
76
|
-
if prefix is None:
|
77
|
-
self._default_ns = ns_uri_list[-1]
|
78
|
-
|
79
|
-
def _buildTag(self, ns_name_tuple):
|
80
|
-
ns_uri, local_name = ns_name_tuple
|
81
|
-
if ns_uri:
|
82
|
-
el_tag = "{%s}%s" % ns_name_tuple
|
83
|
-
elif self._default_ns:
|
84
|
-
el_tag = "{%s}%s" % (self._default_ns, local_name)
|
85
|
-
else:
|
86
|
-
el_tag = local_name
|
87
|
-
return el_tag
|
88
|
-
|
89
|
-
def startElementNS(self, ns_name, qname, attributes=None):
|
90
|
-
el_name = self._buildTag(ns_name)
|
91
|
-
if attributes:
|
92
|
-
attrs = {}
|
93
|
-
try:
|
94
|
-
iter_attributes = attributes.iteritems()
|
95
|
-
except AttributeError:
|
96
|
-
iter_attributes = attributes.items()
|
97
|
-
|
98
|
-
for name_tuple, value in iter_attributes:
|
99
|
-
if name_tuple[0]:
|
100
|
-
attr_name = "{%s}%s" % name_tuple
|
101
|
-
else:
|
102
|
-
attr_name = name_tuple[1]
|
103
|
-
attrs[attr_name] = value
|
104
|
-
else:
|
105
|
-
attrs = None
|
106
|
-
|
107
|
-
element_stack = self._element_stack
|
108
|
-
if self._root is None:
|
109
|
-
element = self._root = \
|
110
|
-
self._makeelement(el_name, attrs, self._new_mappings)
|
111
|
-
if self._root_siblings and hasattr(element, 'addprevious'):
|
112
|
-
for sibling in self._root_siblings:
|
113
|
-
element.addprevious(sibling)
|
114
|
-
del self._root_siblings[:]
|
115
|
-
else:
|
116
|
-
element = SubElement(element_stack[-1], el_name,
|
117
|
-
attrs, self._new_mappings)
|
118
|
-
element_stack.append(element)
|
119
|
-
|
120
|
-
self._new_mappings.clear()
|
121
|
-
|
122
|
-
def processingInstruction(self, target, data):
|
123
|
-
pi = ProcessingInstruction(target, data)
|
124
|
-
if self._root is None:
|
125
|
-
self._root_siblings.append(pi)
|
126
|
-
else:
|
127
|
-
self._element_stack[-1].append(pi)
|
128
|
-
|
129
|
-
def endElementNS(self, ns_name, qname):
|
130
|
-
element = self._element_stack.pop()
|
131
|
-
el_tag = self._buildTag(ns_name)
|
132
|
-
if el_tag != element.tag:
|
133
|
-
raise SaxError("Unexpected element closed: " + el_tag)
|
134
|
-
|
135
|
-
def startElement(self, name, attributes=None):
|
136
|
-
if attributes:
|
137
|
-
attributes = {(None, k): v for k, v in attributes.items()}
|
138
|
-
self.startElementNS((None, name), name, attributes)
|
139
|
-
|
140
|
-
def endElement(self, name):
|
141
|
-
self.endElementNS((None, name), name)
|
142
|
-
|
143
|
-
def characters(self, data):
|
144
|
-
last_element = self._element_stack[-1]
|
145
|
-
try:
|
146
|
-
# if there already is a child element, we must append to its tail
|
147
|
-
last_element = last_element[-1]
|
148
|
-
last_element.tail = (last_element.tail or '') + data
|
149
|
-
except IndexError:
|
150
|
-
# otherwise: append to the text
|
151
|
-
last_element.text = (last_element.text or '') + data
|
152
|
-
|
153
|
-
ignorableWhitespace = characters
|
154
|
-
|
155
|
-
|
156
|
-
class ElementTreeProducer:
|
157
|
-
"""Produces SAX events for an element and children.
|
158
|
-
"""
|
159
|
-
def __init__(self, element_or_tree, content_handler):
|
160
|
-
try:
|
161
|
-
element = element_or_tree.getroot()
|
162
|
-
except AttributeError:
|
163
|
-
element = element_or_tree
|
164
|
-
self._element = element
|
165
|
-
self._content_handler = content_handler
|
166
|
-
from xml.sax.xmlreader import AttributesNSImpl as attr_class
|
167
|
-
self._attr_class = attr_class
|
168
|
-
self._empty_attributes = attr_class({}, {})
|
169
|
-
|
170
|
-
def saxify(self):
|
171
|
-
self._content_handler.startDocument()
|
172
|
-
|
173
|
-
element = self._element
|
174
|
-
if hasattr(element, 'getprevious'):
|
175
|
-
siblings = []
|
176
|
-
sibling = element.getprevious()
|
177
|
-
while getattr(sibling, 'tag', None) is ProcessingInstruction:
|
178
|
-
siblings.append(sibling)
|
179
|
-
sibling = sibling.getprevious()
|
180
|
-
for sibling in siblings[::-1]:
|
181
|
-
self._recursive_saxify(sibling, {})
|
182
|
-
|
183
|
-
self._recursive_saxify(element, {})
|
184
|
-
|
185
|
-
if hasattr(element, 'getnext'):
|
186
|
-
sibling = element.getnext()
|
187
|
-
while getattr(sibling, 'tag', None) is ProcessingInstruction:
|
188
|
-
self._recursive_saxify(sibling, {})
|
189
|
-
sibling = sibling.getnext()
|
190
|
-
|
191
|
-
self._content_handler.endDocument()
|
192
|
-
|
193
|
-
def _recursive_saxify(self, element, parent_nsmap):
|
194
|
-
content_handler = self._content_handler
|
195
|
-
tag = element.tag
|
196
|
-
if tag is Comment or tag is ProcessingInstruction:
|
197
|
-
if tag is ProcessingInstruction:
|
198
|
-
content_handler.processingInstruction(
|
199
|
-
element.target, element.text)
|
200
|
-
tail = element.tail
|
201
|
-
if tail:
|
202
|
-
content_handler.characters(tail)
|
203
|
-
return
|
204
|
-
|
205
|
-
element_nsmap = element.nsmap
|
206
|
-
new_prefixes = []
|
207
|
-
if element_nsmap != parent_nsmap:
|
208
|
-
# There have been updates to the namespace
|
209
|
-
for prefix, ns_uri in element_nsmap.items():
|
210
|
-
if parent_nsmap.get(prefix) != ns_uri:
|
211
|
-
new_prefixes.append( (prefix, ns_uri) )
|
212
|
-
|
213
|
-
attribs = element.items()
|
214
|
-
if attribs:
|
215
|
-
attr_values = {}
|
216
|
-
attr_qnames = {}
|
217
|
-
for attr_ns_name, value in attribs:
|
218
|
-
attr_ns_tuple = _getNsTag(attr_ns_name)
|
219
|
-
attr_values[attr_ns_tuple] = value
|
220
|
-
attr_qnames[attr_ns_tuple] = self._build_qname(
|
221
|
-
attr_ns_tuple[0], attr_ns_tuple[1], element_nsmap,
|
222
|
-
preferred_prefix=None, is_attribute=True)
|
223
|
-
sax_attributes = self._attr_class(attr_values, attr_qnames)
|
224
|
-
else:
|
225
|
-
sax_attributes = self._empty_attributes
|
226
|
-
|
227
|
-
ns_uri, local_name = _getNsTag(tag)
|
228
|
-
qname = self._build_qname(
|
229
|
-
ns_uri, local_name, element_nsmap, element.prefix, is_attribute=False)
|
230
|
-
|
231
|
-
for prefix, uri in new_prefixes:
|
232
|
-
content_handler.startPrefixMapping(prefix, uri)
|
233
|
-
content_handler.startElementNS(
|
234
|
-
(ns_uri, local_name), qname, sax_attributes)
|
235
|
-
text = element.text
|
236
|
-
if text:
|
237
|
-
content_handler.characters(text)
|
238
|
-
for child in element:
|
239
|
-
self._recursive_saxify(child, element_nsmap)
|
240
|
-
content_handler.endElementNS((ns_uri, local_name), qname)
|
241
|
-
for prefix, uri in new_prefixes:
|
242
|
-
content_handler.endPrefixMapping(prefix)
|
243
|
-
tail = element.tail
|
244
|
-
if tail:
|
245
|
-
content_handler.characters(tail)
|
246
|
-
|
247
|
-
def _build_qname(self, ns_uri, local_name, nsmap, preferred_prefix, is_attribute):
|
248
|
-
if ns_uri is None:
|
249
|
-
return local_name
|
250
|
-
|
251
|
-
if not is_attribute and nsmap.get(preferred_prefix) == ns_uri:
|
252
|
-
prefix = preferred_prefix
|
253
|
-
else:
|
254
|
-
# Pick the first matching prefix, in alphabetical order.
|
255
|
-
candidates = [
|
256
|
-
pfx for (pfx, uri) in nsmap.items()
|
257
|
-
if pfx is not None and uri == ns_uri
|
258
|
-
]
|
259
|
-
prefix = (
|
260
|
-
candidates[0] if len(candidates) == 1
|
261
|
-
else min(candidates) if candidates
|
262
|
-
else None
|
263
|
-
)
|
264
|
-
|
265
|
-
if prefix is None:
|
266
|
-
# Default namespace
|
267
|
-
return local_name
|
268
|
-
return prefix + ':' + local_name
|
269
|
-
|
270
|
-
|
271
|
-
def saxify(element_or_tree, content_handler):
|
272
|
-
"""One-shot helper to generate SAX events from an XML tree and fire
|
273
|
-
them against a SAX ContentHandler.
|
274
|
-
"""
|
275
|
-
return ElementTreeProducer(element_or_tree, content_handler).saxify()
|
1
|
+
# cython: language_level=2
|
2
|
+
|
3
|
+
"""
|
4
|
+
SAX-based adapter to copy trees from/to the Python standard library.
|
5
|
+
|
6
|
+
Use the `ElementTreeContentHandler` class to build an ElementTree from
|
7
|
+
SAX events.
|
8
|
+
|
9
|
+
Use the `ElementTreeProducer` class or the `saxify()` function to fire
|
10
|
+
the SAX events of an ElementTree against a SAX ContentHandler.
|
11
|
+
|
12
|
+
See https://lxml.de/sax.html
|
13
|
+
"""
|
14
|
+
|
15
|
+
|
16
|
+
from xml.sax.handler import ContentHandler
|
17
|
+
from lxml import etree
|
18
|
+
from lxml.etree import ElementTree, SubElement
|
19
|
+
from lxml.etree import Comment, ProcessingInstruction
|
20
|
+
|
21
|
+
|
22
|
+
class SaxError(etree.LxmlError):
|
23
|
+
"""General SAX error.
|
24
|
+
"""
|
25
|
+
|
26
|
+
|
27
|
+
def _getNsTag(tag):
|
28
|
+
if tag[0] == '{':
|
29
|
+
return tuple(tag[1:].split('}', 1))
|
30
|
+
else:
|
31
|
+
return None, tag
|
32
|
+
|
33
|
+
|
34
|
+
class ElementTreeContentHandler(ContentHandler):
|
35
|
+
"""Build an lxml ElementTree from SAX events.
|
36
|
+
"""
|
37
|
+
def __init__(self, makeelement=None):
|
38
|
+
ContentHandler.__init__(self)
|
39
|
+
self._root = None
|
40
|
+
self._root_siblings = []
|
41
|
+
self._element_stack = []
|
42
|
+
self._default_ns = None
|
43
|
+
self._ns_mapping = { None : [None] }
|
44
|
+
self._new_mappings = {}
|
45
|
+
if makeelement is None:
|
46
|
+
makeelement = etree.Element
|
47
|
+
self._makeelement = makeelement
|
48
|
+
|
49
|
+
def _get_etree(self):
|
50
|
+
"Contains the generated ElementTree after parsing is finished."
|
51
|
+
return ElementTree(self._root)
|
52
|
+
|
53
|
+
etree = property(_get_etree, doc=_get_etree.__doc__)
|
54
|
+
|
55
|
+
def setDocumentLocator(self, locator):
|
56
|
+
pass
|
57
|
+
|
58
|
+
def startDocument(self):
|
59
|
+
pass
|
60
|
+
|
61
|
+
def endDocument(self):
|
62
|
+
pass
|
63
|
+
|
64
|
+
def startPrefixMapping(self, prefix, uri):
|
65
|
+
self._new_mappings[prefix] = uri
|
66
|
+
try:
|
67
|
+
self._ns_mapping[prefix].append(uri)
|
68
|
+
except KeyError:
|
69
|
+
self._ns_mapping[prefix] = [uri]
|
70
|
+
if prefix is None:
|
71
|
+
self._default_ns = uri
|
72
|
+
|
73
|
+
def endPrefixMapping(self, prefix):
|
74
|
+
ns_uri_list = self._ns_mapping[prefix]
|
75
|
+
ns_uri_list.pop()
|
76
|
+
if prefix is None:
|
77
|
+
self._default_ns = ns_uri_list[-1]
|
78
|
+
|
79
|
+
def _buildTag(self, ns_name_tuple):
|
80
|
+
ns_uri, local_name = ns_name_tuple
|
81
|
+
if ns_uri:
|
82
|
+
el_tag = "{%s}%s" % ns_name_tuple
|
83
|
+
elif self._default_ns:
|
84
|
+
el_tag = "{%s}%s" % (self._default_ns, local_name)
|
85
|
+
else:
|
86
|
+
el_tag = local_name
|
87
|
+
return el_tag
|
88
|
+
|
89
|
+
def startElementNS(self, ns_name, qname, attributes=None):
|
90
|
+
el_name = self._buildTag(ns_name)
|
91
|
+
if attributes:
|
92
|
+
attrs = {}
|
93
|
+
try:
|
94
|
+
iter_attributes = attributes.iteritems()
|
95
|
+
except AttributeError:
|
96
|
+
iter_attributes = attributes.items()
|
97
|
+
|
98
|
+
for name_tuple, value in iter_attributes:
|
99
|
+
if name_tuple[0]:
|
100
|
+
attr_name = "{%s}%s" % name_tuple
|
101
|
+
else:
|
102
|
+
attr_name = name_tuple[1]
|
103
|
+
attrs[attr_name] = value
|
104
|
+
else:
|
105
|
+
attrs = None
|
106
|
+
|
107
|
+
element_stack = self._element_stack
|
108
|
+
if self._root is None:
|
109
|
+
element = self._root = \
|
110
|
+
self._makeelement(el_name, attrs, self._new_mappings)
|
111
|
+
if self._root_siblings and hasattr(element, 'addprevious'):
|
112
|
+
for sibling in self._root_siblings:
|
113
|
+
element.addprevious(sibling)
|
114
|
+
del self._root_siblings[:]
|
115
|
+
else:
|
116
|
+
element = SubElement(element_stack[-1], el_name,
|
117
|
+
attrs, self._new_mappings)
|
118
|
+
element_stack.append(element)
|
119
|
+
|
120
|
+
self._new_mappings.clear()
|
121
|
+
|
122
|
+
def processingInstruction(self, target, data):
|
123
|
+
pi = ProcessingInstruction(target, data)
|
124
|
+
if self._root is None:
|
125
|
+
self._root_siblings.append(pi)
|
126
|
+
else:
|
127
|
+
self._element_stack[-1].append(pi)
|
128
|
+
|
129
|
+
def endElementNS(self, ns_name, qname):
|
130
|
+
element = self._element_stack.pop()
|
131
|
+
el_tag = self._buildTag(ns_name)
|
132
|
+
if el_tag != element.tag:
|
133
|
+
raise SaxError("Unexpected element closed: " + el_tag)
|
134
|
+
|
135
|
+
def startElement(self, name, attributes=None):
|
136
|
+
if attributes:
|
137
|
+
attributes = {(None, k): v for k, v in attributes.items()}
|
138
|
+
self.startElementNS((None, name), name, attributes)
|
139
|
+
|
140
|
+
def endElement(self, name):
|
141
|
+
self.endElementNS((None, name), name)
|
142
|
+
|
143
|
+
def characters(self, data):
|
144
|
+
last_element = self._element_stack[-1]
|
145
|
+
try:
|
146
|
+
# if there already is a child element, we must append to its tail
|
147
|
+
last_element = last_element[-1]
|
148
|
+
last_element.tail = (last_element.tail or '') + data
|
149
|
+
except IndexError:
|
150
|
+
# otherwise: append to the text
|
151
|
+
last_element.text = (last_element.text or '') + data
|
152
|
+
|
153
|
+
ignorableWhitespace = characters
|
154
|
+
|
155
|
+
|
156
|
+
class ElementTreeProducer:
|
157
|
+
"""Produces SAX events for an element and children.
|
158
|
+
"""
|
159
|
+
def __init__(self, element_or_tree, content_handler):
|
160
|
+
try:
|
161
|
+
element = element_or_tree.getroot()
|
162
|
+
except AttributeError:
|
163
|
+
element = element_or_tree
|
164
|
+
self._element = element
|
165
|
+
self._content_handler = content_handler
|
166
|
+
from xml.sax.xmlreader import AttributesNSImpl as attr_class
|
167
|
+
self._attr_class = attr_class
|
168
|
+
self._empty_attributes = attr_class({}, {})
|
169
|
+
|
170
|
+
def saxify(self):
|
171
|
+
self._content_handler.startDocument()
|
172
|
+
|
173
|
+
element = self._element
|
174
|
+
if hasattr(element, 'getprevious'):
|
175
|
+
siblings = []
|
176
|
+
sibling = element.getprevious()
|
177
|
+
while getattr(sibling, 'tag', None) is ProcessingInstruction:
|
178
|
+
siblings.append(sibling)
|
179
|
+
sibling = sibling.getprevious()
|
180
|
+
for sibling in siblings[::-1]:
|
181
|
+
self._recursive_saxify(sibling, {})
|
182
|
+
|
183
|
+
self._recursive_saxify(element, {})
|
184
|
+
|
185
|
+
if hasattr(element, 'getnext'):
|
186
|
+
sibling = element.getnext()
|
187
|
+
while getattr(sibling, 'tag', None) is ProcessingInstruction:
|
188
|
+
self._recursive_saxify(sibling, {})
|
189
|
+
sibling = sibling.getnext()
|
190
|
+
|
191
|
+
self._content_handler.endDocument()
|
192
|
+
|
193
|
+
def _recursive_saxify(self, element, parent_nsmap):
|
194
|
+
content_handler = self._content_handler
|
195
|
+
tag = element.tag
|
196
|
+
if tag is Comment or tag is ProcessingInstruction:
|
197
|
+
if tag is ProcessingInstruction:
|
198
|
+
content_handler.processingInstruction(
|
199
|
+
element.target, element.text)
|
200
|
+
tail = element.tail
|
201
|
+
if tail:
|
202
|
+
content_handler.characters(tail)
|
203
|
+
return
|
204
|
+
|
205
|
+
element_nsmap = element.nsmap
|
206
|
+
new_prefixes = []
|
207
|
+
if element_nsmap != parent_nsmap:
|
208
|
+
# There have been updates to the namespace
|
209
|
+
for prefix, ns_uri in element_nsmap.items():
|
210
|
+
if parent_nsmap.get(prefix) != ns_uri:
|
211
|
+
new_prefixes.append( (prefix, ns_uri) )
|
212
|
+
|
213
|
+
attribs = element.items()
|
214
|
+
if attribs:
|
215
|
+
attr_values = {}
|
216
|
+
attr_qnames = {}
|
217
|
+
for attr_ns_name, value in attribs:
|
218
|
+
attr_ns_tuple = _getNsTag(attr_ns_name)
|
219
|
+
attr_values[attr_ns_tuple] = value
|
220
|
+
attr_qnames[attr_ns_tuple] = self._build_qname(
|
221
|
+
attr_ns_tuple[0], attr_ns_tuple[1], element_nsmap,
|
222
|
+
preferred_prefix=None, is_attribute=True)
|
223
|
+
sax_attributes = self._attr_class(attr_values, attr_qnames)
|
224
|
+
else:
|
225
|
+
sax_attributes = self._empty_attributes
|
226
|
+
|
227
|
+
ns_uri, local_name = _getNsTag(tag)
|
228
|
+
qname = self._build_qname(
|
229
|
+
ns_uri, local_name, element_nsmap, element.prefix, is_attribute=False)
|
230
|
+
|
231
|
+
for prefix, uri in new_prefixes:
|
232
|
+
content_handler.startPrefixMapping(prefix, uri)
|
233
|
+
content_handler.startElementNS(
|
234
|
+
(ns_uri, local_name), qname, sax_attributes)
|
235
|
+
text = element.text
|
236
|
+
if text:
|
237
|
+
content_handler.characters(text)
|
238
|
+
for child in element:
|
239
|
+
self._recursive_saxify(child, element_nsmap)
|
240
|
+
content_handler.endElementNS((ns_uri, local_name), qname)
|
241
|
+
for prefix, uri in new_prefixes:
|
242
|
+
content_handler.endPrefixMapping(prefix)
|
243
|
+
tail = element.tail
|
244
|
+
if tail:
|
245
|
+
content_handler.characters(tail)
|
246
|
+
|
247
|
+
def _build_qname(self, ns_uri, local_name, nsmap, preferred_prefix, is_attribute):
|
248
|
+
if ns_uri is None:
|
249
|
+
return local_name
|
250
|
+
|
251
|
+
if not is_attribute and nsmap.get(preferred_prefix) == ns_uri:
|
252
|
+
prefix = preferred_prefix
|
253
|
+
else:
|
254
|
+
# Pick the first matching prefix, in alphabetical order.
|
255
|
+
candidates = [
|
256
|
+
pfx for (pfx, uri) in nsmap.items()
|
257
|
+
if pfx is not None and uri == ns_uri
|
258
|
+
]
|
259
|
+
prefix = (
|
260
|
+
candidates[0] if len(candidates) == 1
|
261
|
+
else min(candidates) if candidates
|
262
|
+
else None
|
263
|
+
)
|
264
|
+
|
265
|
+
if prefix is None:
|
266
|
+
# Default namespace
|
267
|
+
return local_name
|
268
|
+
return prefix + ':' + local_name
|
269
|
+
|
270
|
+
|
271
|
+
def saxify(element_or_tree, content_handler):
|
272
|
+
"""One-shot helper to generate SAX events from an XML tree and fire
|
273
|
+
them against a SAX ContentHandler.
|
274
|
+
"""
|
275
|
+
return ElementTreeProducer(element_or_tree, content_handler).saxify()
|