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.
Files changed (89) hide show
  1. lxml/ElementInclude.py +244 -244
  2. lxml/__init__.py +22 -22
  3. lxml/_elementpath.cp310-win32.pyd +0 -0
  4. lxml/_elementpath.py +341 -341
  5. lxml/apihelpers.pxi +1793 -1793
  6. lxml/builder.cp310-win32.pyd +0 -0
  7. lxml/builder.py +232 -232
  8. lxml/classlookup.pxi +580 -580
  9. lxml/cleanup.pxi +215 -215
  10. lxml/cssselect.py +101 -101
  11. lxml/debug.pxi +90 -90
  12. lxml/docloader.pxi +178 -178
  13. lxml/doctestcompare.py +488 -488
  14. lxml/dtd.pxi +478 -478
  15. lxml/etree.cp310-win32.pyd +0 -0
  16. lxml/etree.h +6 -6
  17. lxml/etree.pyx +3732 -3711
  18. lxml/extensions.pxi +833 -833
  19. lxml/html/ElementSoup.py +10 -10
  20. lxml/html/__init__.py +1923 -1923
  21. lxml/html/_diffcommand.py +86 -86
  22. lxml/html/_html5builder.py +100 -100
  23. lxml/html/_setmixin.py +56 -56
  24. lxml/html/builder.py +133 -133
  25. lxml/html/clean.py +21 -21
  26. lxml/html/defs.py +135 -135
  27. lxml/html/diff.cp310-win32.pyd +0 -0
  28. lxml/html/diff.py +878 -878
  29. lxml/html/formfill.py +299 -299
  30. lxml/html/html5parser.py +260 -260
  31. lxml/html/soupparser.py +314 -314
  32. lxml/html/usedoctest.py +13 -13
  33. lxml/includes/c14n.pxd +25 -25
  34. lxml/includes/config.pxd +3 -3
  35. lxml/includes/dtdvalid.pxd +18 -18
  36. lxml/includes/etree_defs.h +379 -379
  37. lxml/includes/etreepublic.pxd +237 -237
  38. lxml/includes/htmlparser.pxd +56 -56
  39. lxml/includes/lxml-version.h +1 -1
  40. lxml/includes/relaxng.pxd +64 -64
  41. lxml/includes/schematron.pxd +34 -34
  42. lxml/includes/tree.pxd +494 -494
  43. lxml/includes/uri.pxd +5 -5
  44. lxml/includes/xinclude.pxd +22 -22
  45. lxml/includes/xmlerror.pxd +852 -852
  46. lxml/includes/xmlparser.pxd +265 -265
  47. lxml/includes/xmlschema.pxd +35 -35
  48. lxml/includes/xpath.pxd +136 -136
  49. lxml/includes/xslt.pxd +190 -190
  50. lxml/isoschematron/__init__.py +348 -348
  51. lxml/isoschematron/resources/rng/iso-schematron.rng +709 -709
  52. lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl +75 -75
  53. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl +312 -312
  54. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl +1159 -1159
  55. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl +54 -54
  56. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl +1796 -1796
  57. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl +588 -588
  58. lxml/iterparse.pxi +438 -438
  59. lxml/lxml.etree.h +6 -6
  60. lxml/nsclasses.pxi +281 -281
  61. lxml/objectify.cp310-win32.pyd +0 -0
  62. lxml/objectify.pyx +2145 -2145
  63. lxml/objectpath.pxi +332 -332
  64. lxml/parser.pxi +1994 -1994
  65. lxml/parsertarget.pxi +180 -180
  66. lxml/proxy.pxi +619 -619
  67. lxml/public-api.pxi +178 -178
  68. lxml/pyclasslookup.py +3 -3
  69. lxml/readonlytree.pxi +565 -565
  70. lxml/relaxng.pxi +165 -165
  71. lxml/sax.cp310-win32.pyd +0 -0
  72. lxml/sax.py +275 -275
  73. lxml/saxparser.pxi +875 -875
  74. lxml/schematron.pxi +168 -168
  75. lxml/serializer.pxi +1871 -1871
  76. lxml/usedoctest.py +13 -13
  77. lxml/xinclude.pxi +67 -67
  78. lxml/xmlerror.pxi +1654 -1654
  79. lxml/xmlid.pxi +179 -179
  80. lxml/xmlschema.pxi +215 -215
  81. lxml/xpath.pxi +487 -487
  82. lxml/xslt.pxi +950 -950
  83. lxml/xsltext.pxi +242 -242
  84. {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/LICENSE.txt +29 -29
  85. {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/LICENSES.txt +29 -29
  86. {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/METADATA +9 -17
  87. {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/RECORD +89 -89
  88. {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/WHEEL +0 -0
  89. {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()