Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/lxml/sax.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author | shellac |
---|---|
date | Sat, 02 May 2020 07:14:21 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:26e78fe6e8c4 |
---|---|
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 http://codespeak.net/lxml/sax.html | |
13 """ | |
14 | |
15 from __future__ import absolute_import | |
16 | |
17 from xml.sax.handler import ContentHandler | |
18 from lxml import etree | |
19 from lxml.etree import ElementTree, SubElement | |
20 from lxml.etree import Comment, ProcessingInstruction | |
21 | |
22 | |
23 class SaxError(etree.LxmlError): | |
24 """General SAX error. | |
25 """ | |
26 | |
27 | |
28 def _getNsTag(tag): | |
29 if tag[0] == '{': | |
30 return tuple(tag[1:].split('}', 1)) | |
31 else: | |
32 return None, tag | |
33 | |
34 | |
35 class ElementTreeContentHandler(ContentHandler): | |
36 """Build an lxml ElementTree from SAX events. | |
37 """ | |
38 def __init__(self, makeelement=None): | |
39 ContentHandler.__init__(self) | |
40 self._root = None | |
41 self._root_siblings = [] | |
42 self._element_stack = [] | |
43 self._default_ns = None | |
44 self._ns_mapping = { None : [None] } | |
45 self._new_mappings = {} | |
46 if makeelement is None: | |
47 makeelement = etree.Element | |
48 self._makeelement = makeelement | |
49 | |
50 def _get_etree(self): | |
51 "Contains the generated ElementTree after parsing is finished." | |
52 return ElementTree(self._root) | |
53 | |
54 etree = property(_get_etree, doc=_get_etree.__doc__) | |
55 | |
56 def setDocumentLocator(self, locator): | |
57 pass | |
58 | |
59 def startDocument(self): | |
60 pass | |
61 | |
62 def endDocument(self): | |
63 pass | |
64 | |
65 def startPrefixMapping(self, prefix, uri): | |
66 self._new_mappings[prefix] = uri | |
67 try: | |
68 self._ns_mapping[prefix].append(uri) | |
69 except KeyError: | |
70 self._ns_mapping[prefix] = [uri] | |
71 if prefix is None: | |
72 self._default_ns = uri | |
73 | |
74 def endPrefixMapping(self, prefix): | |
75 ns_uri_list = self._ns_mapping[prefix] | |
76 ns_uri_list.pop() | |
77 if prefix is None: | |
78 self._default_ns = ns_uri_list[-1] | |
79 | |
80 def _buildTag(self, ns_name_tuple): | |
81 ns_uri, local_name = ns_name_tuple | |
82 if ns_uri: | |
83 el_tag = "{%s}%s" % ns_name_tuple | |
84 elif self._default_ns: | |
85 el_tag = "{%s}%s" % (self._default_ns, local_name) | |
86 else: | |
87 el_tag = local_name | |
88 return el_tag | |
89 | |
90 def startElementNS(self, ns_name, qname, attributes=None): | |
91 el_name = self._buildTag(ns_name) | |
92 if attributes: | |
93 attrs = {} | |
94 try: | |
95 iter_attributes = attributes.iteritems() | |
96 except AttributeError: | |
97 iter_attributes = attributes.items() | |
98 | |
99 for name_tuple, value in iter_attributes: | |
100 if name_tuple[0]: | |
101 attr_name = "{%s}%s" % name_tuple | |
102 else: | |
103 attr_name = name_tuple[1] | |
104 attrs[attr_name] = value | |
105 else: | |
106 attrs = None | |
107 | |
108 element_stack = self._element_stack | |
109 if self._root is None: | |
110 element = self._root = \ | |
111 self._makeelement(el_name, attrs, self._new_mappings) | |
112 if self._root_siblings and hasattr(element, 'addprevious'): | |
113 for sibling in self._root_siblings: | |
114 element.addprevious(sibling) | |
115 del self._root_siblings[:] | |
116 else: | |
117 element = SubElement(element_stack[-1], el_name, | |
118 attrs, self._new_mappings) | |
119 element_stack.append(element) | |
120 | |
121 self._new_mappings.clear() | |
122 | |
123 def processingInstruction(self, target, data): | |
124 pi = ProcessingInstruction(target, data) | |
125 if self._root is None: | |
126 self._root_siblings.append(pi) | |
127 else: | |
128 self._element_stack[-1].append(pi) | |
129 | |
130 def endElementNS(self, ns_name, qname): | |
131 element = self._element_stack.pop() | |
132 el_tag = self._buildTag(ns_name) | |
133 if el_tag != element.tag: | |
134 raise SaxError("Unexpected element closed: " + el_tag) | |
135 | |
136 def startElement(self, name, attributes=None): | |
137 if attributes: | |
138 attributes = dict( | |
139 [((None, k), v) for k, v in attributes.items()] | |
140 ) | |
141 self.startElementNS((None, name), name, attributes) | |
142 | |
143 def endElement(self, name): | |
144 self.endElementNS((None, name), name) | |
145 | |
146 def characters(self, data): | |
147 last_element = self._element_stack[-1] | |
148 try: | |
149 # if there already is a child element, we must append to its tail | |
150 last_element = last_element[-1] | |
151 last_element.tail = (last_element.tail or '') + data | |
152 except IndexError: | |
153 # otherwise: append to the text | |
154 last_element.text = (last_element.text or '') + data | |
155 | |
156 ignorableWhitespace = characters | |
157 | |
158 | |
159 class ElementTreeProducer(object): | |
160 """Produces SAX events for an element and children. | |
161 """ | |
162 def __init__(self, element_or_tree, content_handler): | |
163 try: | |
164 element = element_or_tree.getroot() | |
165 except AttributeError: | |
166 element = element_or_tree | |
167 self._element = element | |
168 self._content_handler = content_handler | |
169 from xml.sax.xmlreader import AttributesNSImpl as attr_class | |
170 self._attr_class = attr_class | |
171 self._empty_attributes = attr_class({}, {}) | |
172 | |
173 def saxify(self): | |
174 self._content_handler.startDocument() | |
175 | |
176 element = self._element | |
177 if hasattr(element, 'getprevious'): | |
178 siblings = [] | |
179 sibling = element.getprevious() | |
180 while getattr(sibling, 'tag', None) is ProcessingInstruction: | |
181 siblings.append(sibling) | |
182 sibling = sibling.getprevious() | |
183 for sibling in siblings[::-1]: | |
184 self._recursive_saxify(sibling, {}) | |
185 | |
186 self._recursive_saxify(element, {}) | |
187 | |
188 if hasattr(element, 'getnext'): | |
189 sibling = element.getnext() | |
190 while getattr(sibling, 'tag', None) is ProcessingInstruction: | |
191 self._recursive_saxify(sibling, {}) | |
192 sibling = sibling.getnext() | |
193 | |
194 self._content_handler.endDocument() | |
195 | |
196 def _recursive_saxify(self, element, parent_nsmap): | |
197 content_handler = self._content_handler | |
198 tag = element.tag | |
199 if tag is Comment or tag is ProcessingInstruction: | |
200 if tag is ProcessingInstruction: | |
201 content_handler.processingInstruction( | |
202 element.target, element.text) | |
203 tail = element.tail | |
204 if tail: | |
205 content_handler.characters(tail) | |
206 return | |
207 | |
208 element_nsmap = element.nsmap | |
209 new_prefixes = [] | |
210 if element_nsmap != parent_nsmap: | |
211 # There have been updates to the namespace | |
212 for prefix, ns_uri in element_nsmap.items(): | |
213 if parent_nsmap.get(prefix) != ns_uri: | |
214 new_prefixes.append( (prefix, ns_uri) ) | |
215 | |
216 attribs = element.items() | |
217 if attribs: | |
218 attr_values = {} | |
219 attr_qnames = {} | |
220 for attr_ns_name, value in attribs: | |
221 attr_ns_tuple = _getNsTag(attr_ns_name) | |
222 attr_values[attr_ns_tuple] = value | |
223 attr_qnames[attr_ns_tuple] = self._build_qname( | |
224 attr_ns_tuple[0], attr_ns_tuple[1], element_nsmap, | |
225 preferred_prefix=None, is_attribute=True) | |
226 sax_attributes = self._attr_class(attr_values, attr_qnames) | |
227 else: | |
228 sax_attributes = self._empty_attributes | |
229 | |
230 ns_uri, local_name = _getNsTag(tag) | |
231 qname = self._build_qname( | |
232 ns_uri, local_name, element_nsmap, element.prefix, is_attribute=False) | |
233 | |
234 for prefix, uri in new_prefixes: | |
235 content_handler.startPrefixMapping(prefix, uri) | |
236 content_handler.startElementNS( | |
237 (ns_uri, local_name), qname, sax_attributes) | |
238 text = element.text | |
239 if text: | |
240 content_handler.characters(text) | |
241 for child in element: | |
242 self._recursive_saxify(child, element_nsmap) | |
243 content_handler.endElementNS((ns_uri, local_name), qname) | |
244 for prefix, uri in new_prefixes: | |
245 content_handler.endPrefixMapping(prefix) | |
246 tail = element.tail | |
247 if tail: | |
248 content_handler.characters(tail) | |
249 | |
250 def _build_qname(self, ns_uri, local_name, nsmap, preferred_prefix, is_attribute): | |
251 if ns_uri is None: | |
252 return local_name | |
253 | |
254 if not is_attribute and nsmap.get(preferred_prefix) == ns_uri: | |
255 prefix = preferred_prefix | |
256 else: | |
257 # Pick the first matching prefix, in alphabetical order. | |
258 candidates = [ | |
259 pfx for (pfx, uri) in nsmap.items() | |
260 if pfx is not None and uri == ns_uri | |
261 ] | |
262 prefix = ( | |
263 candidates[0] if len(candidates) == 1 | |
264 else min(candidates) if candidates | |
265 else None | |
266 ) | |
267 | |
268 if prefix is None: | |
269 # Default namespace | |
270 return local_name | |
271 return prefix + ':' + local_name | |
272 | |
273 | |
274 def saxify(element_or_tree, content_handler): | |
275 """One-shot helper to generate SAX events from an XML tree and fire | |
276 them against a SAX ContentHandler. | |
277 """ | |
278 return ElementTreeProducer(element_or_tree, content_handler).saxify() |