lxml 5.3.2__cp310-cp310-win32.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 (175) hide show
  1. lxml/ElementInclude.py +244 -0
  2. lxml/__init__.py +22 -0
  3. lxml/_elementpath.cp310-win32.pyd +0 -0
  4. lxml/_elementpath.py +341 -0
  5. lxml/apihelpers.pxi +1793 -0
  6. lxml/builder.cp310-win32.pyd +0 -0
  7. lxml/builder.py +232 -0
  8. lxml/classlookup.pxi +580 -0
  9. lxml/cleanup.pxi +215 -0
  10. lxml/cssselect.py +101 -0
  11. lxml/debug.pxi +90 -0
  12. lxml/docloader.pxi +178 -0
  13. lxml/doctestcompare.py +488 -0
  14. lxml/dtd.pxi +479 -0
  15. lxml/etree.cp310-win32.pyd +0 -0
  16. lxml/etree.h +248 -0
  17. lxml/etree.pyx +3732 -0
  18. lxml/etree_api.h +195 -0
  19. lxml/extensions.pxi +833 -0
  20. lxml/html/ElementSoup.py +10 -0
  21. lxml/html/__init__.py +1923 -0
  22. lxml/html/_diffcommand.py +86 -0
  23. lxml/html/_html5builder.py +100 -0
  24. lxml/html/_setmixin.py +56 -0
  25. lxml/html/builder.py +133 -0
  26. lxml/html/clean.py +21 -0
  27. lxml/html/defs.py +135 -0
  28. lxml/html/diff.cp310-win32.pyd +0 -0
  29. lxml/html/diff.py +878 -0
  30. lxml/html/formfill.py +299 -0
  31. lxml/html/html5parser.py +260 -0
  32. lxml/html/soupparser.py +314 -0
  33. lxml/html/usedoctest.py +13 -0
  34. lxml/includes/__init__.pxd +0 -0
  35. lxml/includes/__init__.py +0 -0
  36. lxml/includes/c14n.pxd +25 -0
  37. lxml/includes/config.pxd +3 -0
  38. lxml/includes/dtdvalid.pxd +18 -0
  39. lxml/includes/etree_defs.h +379 -0
  40. lxml/includes/etreepublic.pxd +237 -0
  41. lxml/includes/extlibs/__init__.py +0 -0
  42. lxml/includes/extlibs/zconf.h +543 -0
  43. lxml/includes/extlibs/zlib.h +1938 -0
  44. lxml/includes/htmlparser.pxd +56 -0
  45. lxml/includes/libexslt/__init__.py +0 -0
  46. lxml/includes/libexslt/exslt.h +108 -0
  47. lxml/includes/libexslt/exsltconfig.h +70 -0
  48. lxml/includes/libexslt/exsltexports.h +63 -0
  49. lxml/includes/libexslt/libexslt.h +29 -0
  50. lxml/includes/libxml/HTMLparser.h +320 -0
  51. lxml/includes/libxml/HTMLtree.h +147 -0
  52. lxml/includes/libxml/SAX.h +204 -0
  53. lxml/includes/libxml/SAX2.h +173 -0
  54. lxml/includes/libxml/__init__.py +0 -0
  55. lxml/includes/libxml/c14n.h +128 -0
  56. lxml/includes/libxml/catalog.h +182 -0
  57. lxml/includes/libxml/chvalid.h +230 -0
  58. lxml/includes/libxml/debugXML.h +217 -0
  59. lxml/includes/libxml/dict.h +81 -0
  60. lxml/includes/libxml/encoding.h +233 -0
  61. lxml/includes/libxml/entities.h +151 -0
  62. lxml/includes/libxml/globals.h +529 -0
  63. lxml/includes/libxml/hash.h +236 -0
  64. lxml/includes/libxml/list.h +137 -0
  65. lxml/includes/libxml/nanoftp.h +186 -0
  66. lxml/includes/libxml/nanohttp.h +81 -0
  67. lxml/includes/libxml/parser.h +1265 -0
  68. lxml/includes/libxml/parserInternals.h +662 -0
  69. lxml/includes/libxml/pattern.h +100 -0
  70. lxml/includes/libxml/relaxng.h +218 -0
  71. lxml/includes/libxml/schemasInternals.h +958 -0
  72. lxml/includes/libxml/schematron.h +142 -0
  73. lxml/includes/libxml/threads.h +94 -0
  74. lxml/includes/libxml/tree.h +1314 -0
  75. lxml/includes/libxml/uri.h +94 -0
  76. lxml/includes/libxml/valid.h +448 -0
  77. lxml/includes/libxml/xinclude.h +129 -0
  78. lxml/includes/libxml/xlink.h +189 -0
  79. lxml/includes/libxml/xmlIO.h +369 -0
  80. lxml/includes/libxml/xmlautomata.h +146 -0
  81. lxml/includes/libxml/xmlerror.h +919 -0
  82. lxml/includes/libxml/xmlexports.h +50 -0
  83. lxml/includes/libxml/xmlmemory.h +228 -0
  84. lxml/includes/libxml/xmlmodule.h +57 -0
  85. lxml/includes/libxml/xmlreader.h +428 -0
  86. lxml/includes/libxml/xmlregexp.h +222 -0
  87. lxml/includes/libxml/xmlsave.h +88 -0
  88. lxml/includes/libxml/xmlschemas.h +246 -0
  89. lxml/includes/libxml/xmlschemastypes.h +152 -0
  90. lxml/includes/libxml/xmlstring.h +140 -0
  91. lxml/includes/libxml/xmlunicode.h +202 -0
  92. lxml/includes/libxml/xmlversion.h +526 -0
  93. lxml/includes/libxml/xmlwriter.h +488 -0
  94. lxml/includes/libxml/xpath.h +575 -0
  95. lxml/includes/libxml/xpathInternals.h +632 -0
  96. lxml/includes/libxml/xpointer.h +137 -0
  97. lxml/includes/libxslt/__init__.py +0 -0
  98. lxml/includes/libxslt/attributes.h +39 -0
  99. lxml/includes/libxslt/documents.h +93 -0
  100. lxml/includes/libxslt/extensions.h +262 -0
  101. lxml/includes/libxslt/extra.h +72 -0
  102. lxml/includes/libxslt/functions.h +78 -0
  103. lxml/includes/libxslt/imports.h +75 -0
  104. lxml/includes/libxslt/keys.h +53 -0
  105. lxml/includes/libxslt/libxslt.h +36 -0
  106. lxml/includes/libxslt/namespaces.h +68 -0
  107. lxml/includes/libxslt/numbersInternals.h +73 -0
  108. lxml/includes/libxslt/preproc.h +43 -0
  109. lxml/includes/libxslt/security.h +104 -0
  110. lxml/includes/libxslt/templates.h +77 -0
  111. lxml/includes/libxslt/transform.h +207 -0
  112. lxml/includes/libxslt/trio.h +216 -0
  113. lxml/includes/libxslt/triodef.h +220 -0
  114. lxml/includes/libxslt/variables.h +118 -0
  115. lxml/includes/libxslt/win32config.h +51 -0
  116. lxml/includes/libxslt/xslt.h +110 -0
  117. lxml/includes/libxslt/xsltInternals.h +1992 -0
  118. lxml/includes/libxslt/xsltconfig.h +179 -0
  119. lxml/includes/libxslt/xsltexports.h +64 -0
  120. lxml/includes/libxslt/xsltlocale.h +44 -0
  121. lxml/includes/libxslt/xsltutils.h +343 -0
  122. lxml/includes/lxml-version.h +3 -0
  123. lxml/includes/relaxng.pxd +64 -0
  124. lxml/includes/schematron.pxd +34 -0
  125. lxml/includes/tree.pxd +494 -0
  126. lxml/includes/uri.pxd +5 -0
  127. lxml/includes/xinclude.pxd +22 -0
  128. lxml/includes/xmlerror.pxd +852 -0
  129. lxml/includes/xmlparser.pxd +265 -0
  130. lxml/includes/xmlschema.pxd +35 -0
  131. lxml/includes/xpath.pxd +136 -0
  132. lxml/includes/xslt.pxd +190 -0
  133. lxml/isoschematron/__init__.py +348 -0
  134. lxml/isoschematron/resources/rng/iso-schematron.rng +709 -0
  135. lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl +75 -0
  136. lxml/isoschematron/resources/xsl/XSD2Schtrn.xsl +77 -0
  137. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl +313 -0
  138. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl +1160 -0
  139. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl +55 -0
  140. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl +1796 -0
  141. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl +588 -0
  142. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/readme.txt +84 -0
  143. lxml/iterparse.pxi +438 -0
  144. lxml/lxml.etree.h +248 -0
  145. lxml/lxml.etree_api.h +195 -0
  146. lxml/nsclasses.pxi +281 -0
  147. lxml/objectify.cp310-win32.pyd +0 -0
  148. lxml/objectify.pyx +2145 -0
  149. lxml/objectpath.pxi +332 -0
  150. lxml/parser.pxi +2000 -0
  151. lxml/parsertarget.pxi +180 -0
  152. lxml/proxy.pxi +619 -0
  153. lxml/public-api.pxi +178 -0
  154. lxml/pyclasslookup.py +3 -0
  155. lxml/readonlytree.pxi +565 -0
  156. lxml/relaxng.pxi +165 -0
  157. lxml/sax.cp310-win32.pyd +0 -0
  158. lxml/sax.py +275 -0
  159. lxml/saxparser.pxi +875 -0
  160. lxml/schematron.pxi +168 -0
  161. lxml/serializer.pxi +1781 -0
  162. lxml/usedoctest.py +13 -0
  163. lxml/xinclude.pxi +67 -0
  164. lxml/xmlerror.pxi +1654 -0
  165. lxml/xmlid.pxi +179 -0
  166. lxml/xmlschema.pxi +215 -0
  167. lxml/xpath.pxi +487 -0
  168. lxml/xslt.pxi +950 -0
  169. lxml/xsltext.pxi +242 -0
  170. lxml-5.3.2.dist-info/METADATA +100 -0
  171. lxml-5.3.2.dist-info/RECORD +175 -0
  172. lxml-5.3.2.dist-info/WHEEL +5 -0
  173. lxml-5.3.2.dist-info/licenses/LICENSE.txt +29 -0
  174. lxml-5.3.2.dist-info/licenses/LICENSES.txt +29 -0
  175. lxml-5.3.2.dist-info/top_level.txt +1 -0
lxml/objectify.pyx ADDED
@@ -0,0 +1,2145 @@
1
+ # cython: binding=True
2
+ # cython: auto_pickle=False
3
+ # cython: language_level=3
4
+
5
+ """
6
+ The ``lxml.objectify`` module implements a Python object API for XML.
7
+ It is based on `lxml.etree`.
8
+ """
9
+
10
+ cimport cython
11
+
12
+ from lxml.includes.etreepublic cimport _Document, _Element, ElementBase, ElementClassLookup
13
+ from lxml.includes.etreepublic cimport elementFactory, import_lxml__etree, textOf, pyunicode
14
+ from lxml.includes.tree cimport const_xmlChar, _xcstr
15
+ from lxml cimport python
16
+ from lxml.includes cimport tree
17
+
18
+ cimport lxml.includes.etreepublic as cetree
19
+ cimport libc.string as cstring_h # not to be confused with stdlib 'string'
20
+ from libc.string cimport const_char
21
+
22
+ __all__ = ['BoolElement', 'DataElement', 'E', 'Element', 'ElementMaker',
23
+ 'FloatElement', 'IntElement', 'NoneElement',
24
+ 'NumberElement', 'ObjectPath', 'ObjectifiedDataElement',
25
+ 'ObjectifiedElement', 'ObjectifyElementClassLookup',
26
+ 'PYTYPE_ATTRIBUTE', 'PyType', 'StringElement', 'SubElement',
27
+ 'XML', 'annotate', 'deannotate', 'dump', 'enable_recursive_str',
28
+ 'fromstring', 'getRegisteredTypes', 'makeparser', 'parse',
29
+ 'pyannotate', 'pytypename', 'set_default_parser',
30
+ 'set_pytype_attribute_tag', 'xsiannotate']
31
+
32
+ cdef object etree
33
+ from lxml import etree
34
+ # initialize C-API of lxml.etree
35
+ import_lxml__etree()
36
+
37
+ __version__ = etree.__version__
38
+
39
+ cdef object _float_is_inf, _float_is_nan
40
+ from math import isinf as _float_is_inf, isnan as _float_is_nan
41
+
42
+ cdef object re
43
+ import re
44
+
45
+ cdef tuple IGNORABLE_ERRORS = (ValueError, TypeError)
46
+ cdef object is_special_method = re.compile('__.*__$').match
47
+
48
+
49
+ cdef object _typename(object t):
50
+ cdef const_char* c_name
51
+ c_name = python._fqtypename(t)
52
+ s = cstring_h.strrchr(c_name, c'.')
53
+ if s is not NULL:
54
+ c_name = s + 1
55
+ return pyunicode(<const_xmlChar*>c_name)
56
+
57
+
58
+ # namespace/name for "pytype" hint attribute
59
+ cdef object PYTYPE_NAMESPACE
60
+ cdef bytes PYTYPE_NAMESPACE_UTF8
61
+ cdef const_xmlChar* _PYTYPE_NAMESPACE
62
+
63
+ cdef object PYTYPE_ATTRIBUTE_NAME
64
+ cdef bytes PYTYPE_ATTRIBUTE_NAME_UTF8
65
+ cdef const_xmlChar* _PYTYPE_ATTRIBUTE_NAME
66
+
67
+ PYTYPE_ATTRIBUTE = None
68
+
69
+ cdef unicode TREE_PYTYPE_NAME = "TREE"
70
+
71
+ cdef tuple _unicodeAndUtf8(s):
72
+ return s, python.PyUnicode_AsUTF8String(s)
73
+
74
+ def set_pytype_attribute_tag(attribute_tag=None):
75
+ """set_pytype_attribute_tag(attribute_tag=None)
76
+ Change name and namespace of the XML attribute that holds Python type
77
+ information.
78
+
79
+ Do not use this unless you know what you are doing.
80
+
81
+ Reset by calling without argument.
82
+
83
+ Default: "{http://codespeak.net/lxml/objectify/pytype}pytype"
84
+ """
85
+ global PYTYPE_ATTRIBUTE, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME
86
+ global PYTYPE_NAMESPACE, PYTYPE_NAMESPACE_UTF8
87
+ global PYTYPE_ATTRIBUTE_NAME, PYTYPE_ATTRIBUTE_NAME_UTF8
88
+ if attribute_tag is None:
89
+ PYTYPE_NAMESPACE, PYTYPE_NAMESPACE_UTF8 = \
90
+ _unicodeAndUtf8("http://codespeak.net/lxml/objectify/pytype")
91
+ PYTYPE_ATTRIBUTE_NAME, PYTYPE_ATTRIBUTE_NAME_UTF8 = \
92
+ _unicodeAndUtf8("pytype")
93
+ else:
94
+ PYTYPE_NAMESPACE_UTF8, PYTYPE_ATTRIBUTE_NAME_UTF8 = \
95
+ cetree.getNsTag(attribute_tag)
96
+ PYTYPE_NAMESPACE = PYTYPE_NAMESPACE_UTF8.decode('utf8')
97
+ PYTYPE_ATTRIBUTE_NAME = PYTYPE_ATTRIBUTE_NAME_UTF8.decode('utf8')
98
+
99
+ _PYTYPE_NAMESPACE = PYTYPE_NAMESPACE_UTF8
100
+ _PYTYPE_ATTRIBUTE_NAME = PYTYPE_ATTRIBUTE_NAME_UTF8
101
+ PYTYPE_ATTRIBUTE = cetree.namespacedNameFromNsName(
102
+ _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
103
+
104
+ set_pytype_attribute_tag()
105
+
106
+
107
+ # namespaces for XML Schema
108
+ cdef object XML_SCHEMA_NS, XML_SCHEMA_NS_UTF8
109
+ XML_SCHEMA_NS, XML_SCHEMA_NS_UTF8 = \
110
+ _unicodeAndUtf8("http://www.w3.org/2001/XMLSchema")
111
+ cdef const_xmlChar* _XML_SCHEMA_NS = _xcstr(XML_SCHEMA_NS_UTF8)
112
+
113
+ cdef object XML_SCHEMA_INSTANCE_NS, XML_SCHEMA_INSTANCE_NS_UTF8
114
+ XML_SCHEMA_INSTANCE_NS, XML_SCHEMA_INSTANCE_NS_UTF8 = \
115
+ _unicodeAndUtf8("http://www.w3.org/2001/XMLSchema-instance")
116
+ cdef const_xmlChar* _XML_SCHEMA_INSTANCE_NS = _xcstr(XML_SCHEMA_INSTANCE_NS_UTF8)
117
+
118
+ cdef object XML_SCHEMA_INSTANCE_NIL_ATTR = "{%s}nil" % XML_SCHEMA_INSTANCE_NS
119
+ cdef object XML_SCHEMA_INSTANCE_TYPE_ATTR = "{%s}type" % XML_SCHEMA_INSTANCE_NS
120
+
121
+
122
+ ################################################################################
123
+ # Element class for the main API
124
+
125
+ cdef class ObjectifiedElement(ElementBase):
126
+ """Main XML Element class.
127
+
128
+ Element children are accessed as object attributes. Multiple children
129
+ with the same name are available through a list index. Example::
130
+
131
+ >>> root = XML("<root><c1><c2>0</c2><c2>1</c2></c1></root>")
132
+ >>> second_c2 = root.c1.c2[1]
133
+ >>> print(second_c2.text)
134
+ 1
135
+
136
+ Note that you cannot (and must not) instantiate this class or its
137
+ subclasses.
138
+ """
139
+ def __iter__(self):
140
+ """Iterate over self and all siblings with the same tag.
141
+ """
142
+ parent = self.getparent()
143
+ if parent is None:
144
+ return iter([self])
145
+ return etree.ElementChildIterator(parent, tag=self.tag)
146
+
147
+ def __str__(self):
148
+ if __RECURSIVE_STR:
149
+ return _dump(self, 0)
150
+ else:
151
+ return textOf(self._c_node) or ''
152
+
153
+ # pickle support for objectified Element
154
+ def __reduce__(self):
155
+ return fromstring, (etree.tostring(self),)
156
+
157
+ @property
158
+ def text(self):
159
+ return textOf(self._c_node)
160
+
161
+ @property
162
+ def __dict__(self):
163
+ """A fake implementation for __dict__ to support dir() etc.
164
+
165
+ Note that this only considers the first child with a given name.
166
+ """
167
+ cdef _Element child
168
+ cdef dict children
169
+ c_ns = tree._getNs(self._c_node)
170
+ tag = "{%s}*" % pyunicode(c_ns) if c_ns is not NULL else None
171
+ children = {}
172
+ for child in etree.ElementChildIterator(self, tag=tag):
173
+ if c_ns is NULL and tree._getNs(child._c_node) is not NULL:
174
+ continue
175
+ name = pyunicode(child._c_node.name)
176
+ if name not in children:
177
+ children[name] = child
178
+ return children
179
+
180
+ def __len__(self):
181
+ """Count self and siblings with the same tag.
182
+ """
183
+ return _countSiblings(self._c_node)
184
+
185
+ def countchildren(self):
186
+ """countchildren(self)
187
+
188
+ Return the number of children of this element, regardless of their
189
+ name.
190
+ """
191
+ # copied from etree
192
+ cdef Py_ssize_t c
193
+ cdef tree.xmlNode* c_node
194
+ c = 0
195
+ c_node = self._c_node.children
196
+ while c_node is not NULL:
197
+ if tree._isElement(c_node):
198
+ c += 1
199
+ c_node = c_node.next
200
+ return c
201
+
202
+ def getchildren(self):
203
+ """getchildren(self)
204
+
205
+ Returns a sequence of all direct children. The elements are
206
+ returned in document order.
207
+ """
208
+ cdef tree.xmlNode* c_node
209
+ result = []
210
+ c_node = self._c_node.children
211
+ while c_node is not NULL:
212
+ if tree._isElement(c_node):
213
+ result.append(cetree.elementFactory(self._doc, c_node))
214
+ c_node = c_node.next
215
+ return result
216
+
217
+ def __getattr__(self, tag):
218
+ """Return the (first) child with the given tag name. If no namespace
219
+ is provided, the child will be looked up in the same one as self.
220
+ """
221
+ return _lookupChildOrRaise(self, tag)
222
+
223
+ def __setattr__(self, tag, value):
224
+ """Set the value of the (first) child with the given tag name. If no
225
+ namespace is provided, the child will be looked up in the same one as
226
+ self.
227
+ """
228
+ cdef _Element element
229
+ # properties are looked up /after/ __setattr__, so we must emulate them
230
+ if tag == 'text' or tag == 'pyval':
231
+ # read-only !
232
+ raise TypeError, f"attribute '{tag}' of '{_typename(self)}' objects is not writable"
233
+ elif tag == 'tail':
234
+ cetree.setTailText(self._c_node, value)
235
+ return
236
+ elif tag == 'tag':
237
+ ElementBase.tag.__set__(self, value)
238
+ return
239
+ elif tag == 'base':
240
+ ElementBase.base.__set__(self, value)
241
+ return
242
+ tag = _buildChildTag(self, tag)
243
+ element = _lookupChild(self, tag)
244
+ if element is None:
245
+ _appendValue(self, tag, value)
246
+ else:
247
+ _replaceElement(element, value)
248
+
249
+ def __delattr__(self, tag):
250
+ child = _lookupChildOrRaise(self, tag)
251
+ self.remove(child)
252
+
253
+ def addattr(self, tag, value):
254
+ """addattr(self, tag, value)
255
+
256
+ Add a child value to the element.
257
+
258
+ As opposed to append(), it sets a data value, not an element.
259
+ """
260
+ _appendValue(self, _buildChildTag(self, tag), value)
261
+
262
+ def __getitem__(self, key):
263
+ """Return a sibling, counting from the first child of the parent. The
264
+ method behaves like both a dict and a sequence.
265
+
266
+ * If argument is an integer, returns the sibling at that position.
267
+
268
+ * If argument is a string, does the same as getattr(). This can be
269
+ used to provide namespaces for element lookup, or to look up
270
+ children with special names (``text`` etc.).
271
+
272
+ * If argument is a slice object, returns the matching slice.
273
+ """
274
+ cdef tree.xmlNode* c_self_node
275
+ cdef tree.xmlNode* c_parent
276
+ cdef tree.xmlNode* c_node
277
+ cdef Py_ssize_t c_index
278
+ if python._isString(key):
279
+ return _lookupChildOrRaise(self, key)
280
+ elif isinstance(key, slice):
281
+ return list(self)[key]
282
+ # normal item access
283
+ c_index = key # raises TypeError if necessary
284
+ c_self_node = self._c_node
285
+ c_parent = c_self_node.parent
286
+ if c_parent is NULL:
287
+ if c_index == 0 or c_index == -1:
288
+ return self
289
+ raise IndexError, unicode(key)
290
+ if c_index < 0:
291
+ c_node = c_parent.last
292
+ else:
293
+ c_node = c_parent.children
294
+ c_node = _findFollowingSibling(
295
+ c_node, tree._getNs(c_self_node), c_self_node.name, c_index)
296
+ if c_node is NULL:
297
+ raise IndexError, unicode(key)
298
+ return elementFactory(self._doc, c_node)
299
+
300
+ def __setitem__(self, key, value):
301
+ """Set the value of a sibling, counting from the first child of the
302
+ parent. Implements key assignment, item assignment and slice
303
+ assignment.
304
+
305
+ * If argument is an integer, sets the sibling at that position.
306
+
307
+ * If argument is a string, does the same as setattr(). This is used
308
+ to provide namespaces for element lookup.
309
+
310
+ * If argument is a sequence (list, tuple, etc.), assign the contained
311
+ items to the siblings.
312
+ """
313
+ cdef _Element element
314
+ cdef tree.xmlNode* c_node
315
+ if python._isString(key):
316
+ key = _buildChildTag(self, key)
317
+ element = _lookupChild(self, key)
318
+ if element is None:
319
+ _appendValue(self, key, value)
320
+ else:
321
+ _replaceElement(element, value)
322
+ return
323
+
324
+ if self._c_node.parent is NULL:
325
+ # the 'root[i] = ...' case
326
+ raise TypeError, "assignment to root element is invalid"
327
+
328
+ if isinstance(key, slice):
329
+ # slice assignment
330
+ _setSlice(key, self, value)
331
+ else:
332
+ # normal index assignment
333
+ if key < 0:
334
+ c_node = self._c_node.parent.last
335
+ else:
336
+ c_node = self._c_node.parent.children
337
+ c_node = _findFollowingSibling(
338
+ c_node, tree._getNs(self._c_node), self._c_node.name, key)
339
+ if c_node is NULL:
340
+ raise IndexError, unicode(key)
341
+ element = elementFactory(self._doc, c_node)
342
+ _replaceElement(element, value)
343
+
344
+ def __delitem__(self, key):
345
+ parent = self.getparent()
346
+ if parent is None:
347
+ raise TypeError, "deleting items not supported by root element"
348
+ if isinstance(key, slice):
349
+ # slice deletion
350
+ del_items = list(self)[key]
351
+ remove = parent.remove
352
+ for el in del_items:
353
+ remove(el)
354
+ else:
355
+ # normal index deletion
356
+ sibling = self.__getitem__(key)
357
+ parent.remove(sibling)
358
+
359
+ def descendantpaths(self, prefix=None):
360
+ """descendantpaths(self, prefix=None)
361
+
362
+ Returns a list of object path expressions for all descendants.
363
+ """
364
+ if prefix is not None and not python._isString(prefix):
365
+ prefix = '.'.join(prefix)
366
+ return _build_descendant_paths(self._c_node, prefix)
367
+
368
+
369
+ cdef inline bint _tagMatches(tree.xmlNode* c_node, const_xmlChar* c_href, const_xmlChar* c_name):
370
+ if c_node.name != c_name:
371
+ return 0
372
+ if c_href == NULL:
373
+ return 1
374
+ c_node_href = tree._getNs(c_node)
375
+ if c_node_href == NULL:
376
+ return c_href[0] == c'\0'
377
+ return tree.xmlStrcmp(c_node_href, c_href) == 0
378
+
379
+
380
+ cdef Py_ssize_t _countSiblings(tree.xmlNode* c_start_node):
381
+ cdef tree.xmlNode* c_node
382
+ cdef Py_ssize_t count
383
+ c_tag = c_start_node.name
384
+ c_href = tree._getNs(c_start_node)
385
+ count = 1
386
+ c_node = c_start_node.next
387
+ while c_node is not NULL:
388
+ if c_node.type == tree.XML_ELEMENT_NODE and \
389
+ _tagMatches(c_node, c_href, c_tag):
390
+ count += 1
391
+ c_node = c_node.next
392
+ c_node = c_start_node.prev
393
+ while c_node is not NULL:
394
+ if c_node.type == tree.XML_ELEMENT_NODE and \
395
+ _tagMatches(c_node, c_href, c_tag):
396
+ count += 1
397
+ c_node = c_node.prev
398
+ return count
399
+
400
+ cdef tree.xmlNode* _findFollowingSibling(tree.xmlNode* c_node,
401
+ const_xmlChar* href, const_xmlChar* name,
402
+ Py_ssize_t index):
403
+ cdef tree.xmlNode* (*next)(tree.xmlNode*)
404
+ if index >= 0:
405
+ next = cetree.nextElement
406
+ else:
407
+ index = -1 - index
408
+ next = cetree.previousElement
409
+ while c_node is not NULL:
410
+ if c_node.type == tree.XML_ELEMENT_NODE and \
411
+ _tagMatches(c_node, href, name):
412
+ index = index - 1
413
+ if index < 0:
414
+ return c_node
415
+ c_node = next(c_node)
416
+ return NULL
417
+
418
+ cdef object _lookupChild(_Element parent, tag):
419
+ cdef tree.xmlNode* c_result
420
+ cdef tree.xmlNode* c_node
421
+ c_node = parent._c_node
422
+ ns, tag = cetree.getNsTagWithEmptyNs(tag)
423
+ c_tag = tree.xmlDictExists(
424
+ c_node.doc.dict, _xcstr(tag), python.PyBytes_GET_SIZE(tag))
425
+ if c_tag is NULL:
426
+ return None # not in the hash map => not in the tree
427
+ if ns is None:
428
+ # either inherit ns from parent or use empty (i.e. no) namespace
429
+ c_href = tree._getNs(c_node) or <const_xmlChar*>''
430
+ else:
431
+ c_href = _xcstr(ns)
432
+ c_result = _findFollowingSibling(c_node.children, c_href, c_tag, 0)
433
+ if c_result is NULL:
434
+ return None
435
+ return elementFactory(parent._doc, c_result)
436
+
437
+ cdef object _lookupChildOrRaise(_Element parent, tag):
438
+ element = _lookupChild(parent, tag)
439
+ if element is None:
440
+ raise AttributeError, "no such child: " + _buildChildTag(parent, tag)
441
+ return element
442
+
443
+ cdef object _buildChildTag(_Element parent, tag):
444
+ ns, tag = cetree.getNsTag(tag)
445
+ c_tag = _xcstr(tag)
446
+ c_href = tree._getNs(parent._c_node) if ns is None else _xcstr(ns)
447
+ return cetree.namespacedNameFromNsName(c_href, c_tag)
448
+
449
+ cdef _replaceElement(_Element element, value):
450
+ cdef _Element new_element
451
+ if isinstance(value, _Element):
452
+ # deep copy the new element
453
+ new_element = cetree.deepcopyNodeToDocument(
454
+ element._doc, (<_Element>value)._c_node)
455
+ new_element.tag = element.tag
456
+ elif isinstance(value, (list, tuple)):
457
+ element[:] = value
458
+ return
459
+ else:
460
+ new_element = element.makeelement(element.tag)
461
+ _setElementValue(new_element, value)
462
+ element.getparent().replace(element, new_element)
463
+
464
+ cdef _appendValue(_Element parent, tag, value):
465
+ cdef _Element new_element
466
+ if isinstance(value, _Element):
467
+ # deep copy the new element
468
+ new_element = cetree.deepcopyNodeToDocument(
469
+ parent._doc, (<_Element>value)._c_node)
470
+ new_element.tag = tag
471
+ cetree.appendChildToElement(parent, new_element)
472
+ elif isinstance(value, (list, tuple)):
473
+ for item in value:
474
+ _appendValue(parent, tag, item)
475
+ else:
476
+ new_element = cetree.makeElement(
477
+ tag, parent._doc, None, None, None, None, None)
478
+ _setElementValue(new_element, value)
479
+ cetree.appendChildToElement(parent, new_element)
480
+
481
+ cdef _setElementValue(_Element element, value):
482
+ if value is None:
483
+ cetree.setAttributeValue(
484
+ element, XML_SCHEMA_INSTANCE_NIL_ATTR, "true")
485
+ elif isinstance(value, _Element):
486
+ _replaceElement(element, value)
487
+ return
488
+ else:
489
+ cetree.delAttributeFromNsName(
490
+ element._c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>"nil")
491
+ if python._isString(value):
492
+ pytype_name = "str"
493
+ py_type = <PyType>_PYTYPE_DICT.get(pytype_name)
494
+ else:
495
+ pytype_name = _typename(value)
496
+ py_type = <PyType>_PYTYPE_DICT.get(pytype_name)
497
+ if py_type is not None:
498
+ value = py_type.stringify(value)
499
+ else:
500
+ value = unicode(value)
501
+ if py_type is not None:
502
+ cetree.setAttributeValue(element, PYTYPE_ATTRIBUTE, pytype_name)
503
+ else:
504
+ cetree.delAttributeFromNsName(
505
+ element._c_node, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
506
+ cetree.setNodeText(element._c_node, value)
507
+
508
+ cdef _setSlice(sliceobject, _Element target, items):
509
+ cdef _Element parent
510
+ cdef tree.xmlNode* c_node
511
+ cdef Py_ssize_t c_step, c_start, pos
512
+ # collect existing slice
513
+ if (<slice>sliceobject).step is None:
514
+ c_step = 1
515
+ else:
516
+ c_step = (<slice>sliceobject).step
517
+ if c_step == 0:
518
+ raise ValueError, "Invalid slice"
519
+ cdef list del_items = target[sliceobject]
520
+
521
+ # collect new values
522
+ new_items = []
523
+ tag = target.tag
524
+ for item in items:
525
+ if isinstance(item, _Element):
526
+ # deep copy the new element
527
+ new_element = cetree.deepcopyNodeToDocument(
528
+ target._doc, (<_Element>item)._c_node)
529
+ new_element.tag = tag
530
+ else:
531
+ new_element = cetree.makeElement(
532
+ tag, target._doc, None, None, None, None, None)
533
+ _setElementValue(new_element, item)
534
+ new_items.append(new_element)
535
+
536
+ # sanity check - raise what a list would raise
537
+ if c_step != 1 and len(del_items) != len(new_items):
538
+ raise ValueError, \
539
+ f"attempt to assign sequence of size {len(new_items)} to extended slice of size {len(del_items)}"
540
+
541
+ # replace existing items
542
+ pos = 0
543
+ parent = target.getparent()
544
+ replace = parent.replace
545
+ while pos < len(new_items) and pos < len(del_items):
546
+ replace(del_items[pos], new_items[pos])
547
+ pos += 1
548
+ # remove leftover items
549
+ if pos < len(del_items):
550
+ remove = parent.remove
551
+ while pos < len(del_items):
552
+ remove(del_items[pos])
553
+ pos += 1
554
+ # append remaining new items
555
+ if pos < len(new_items):
556
+ # the sanity check above guarantees (step == 1)
557
+ if pos > 0:
558
+ item = new_items[pos-1]
559
+ else:
560
+ if (<slice>sliceobject).start > 0:
561
+ c_node = parent._c_node.children
562
+ else:
563
+ c_node = parent._c_node.last
564
+ c_node = _findFollowingSibling(
565
+ c_node, tree._getNs(target._c_node), target._c_node.name,
566
+ (<slice>sliceobject).start - 1)
567
+ if c_node is NULL:
568
+ while pos < len(new_items):
569
+ cetree.appendChildToElement(parent, new_items[pos])
570
+ pos += 1
571
+ return
572
+ item = cetree.elementFactory(parent._doc, c_node)
573
+ while pos < len(new_items):
574
+ add = item.addnext
575
+ item = new_items[pos]
576
+ add(item)
577
+ pos += 1
578
+
579
+ ################################################################################
580
+ # Data type support in subclasses
581
+
582
+ cdef class ObjectifiedDataElement(ObjectifiedElement):
583
+ """This is the base class for all data type Elements. Subclasses should
584
+ override the 'pyval' property and possibly the __str__ method.
585
+ """
586
+ @property
587
+ def pyval(self):
588
+ return textOf(self._c_node)
589
+
590
+ def __str__(self):
591
+ return textOf(self._c_node) or ''
592
+
593
+ def __repr__(self):
594
+ return textOf(self._c_node) or ''
595
+
596
+ def _setText(self, s):
597
+ """For use in subclasses only. Don't use unless you know what you are
598
+ doing.
599
+ """
600
+ cetree.setNodeText(self._c_node, s)
601
+
602
+
603
+ cdef class NumberElement(ObjectifiedDataElement):
604
+ cdef object _parse_value
605
+
606
+ def _setValueParser(self, function):
607
+ """Set the function that parses the Python value from a string.
608
+
609
+ Do not use this unless you know what you are doing.
610
+ """
611
+ self._parse_value = function
612
+
613
+ @property
614
+ def pyval(self):
615
+ return _parseNumber(self)
616
+
617
+ def __int__(self):
618
+ return int(_parseNumber(self))
619
+
620
+ def __float__(self):
621
+ return float(_parseNumber(self))
622
+
623
+ def __complex__(self):
624
+ return complex(_parseNumber(self))
625
+
626
+ def __str__(self):
627
+ return unicode(_parseNumber(self))
628
+
629
+ def __repr__(self):
630
+ return repr(_parseNumber(self))
631
+
632
+ def __oct__(self):
633
+ return oct(_parseNumber(self))
634
+
635
+ def __hex__(self):
636
+ return hex(_parseNumber(self))
637
+
638
+ def __richcmp__(self, other, int op):
639
+ return _richcmpPyvals(self, other, op)
640
+
641
+ def __hash__(self):
642
+ return hash(_parseNumber(self))
643
+
644
+ def __add__(self, other):
645
+ return _numericValueOf(self) + _numericValueOf(other)
646
+
647
+ def __radd__(self, other):
648
+ return _numericValueOf(other) + _numericValueOf(self)
649
+
650
+ def __sub__(self, other):
651
+ return _numericValueOf(self) - _numericValueOf(other)
652
+
653
+ def __rsub__(self, other):
654
+ return _numericValueOf(other) - _numericValueOf(self)
655
+
656
+ def __mul__(self, other):
657
+ return _numericValueOf(self) * _numericValueOf(other)
658
+
659
+ def __rmul__(self, other):
660
+ return _numericValueOf(other) * _numericValueOf(self)
661
+
662
+ def __div__(self, other):
663
+ return _numericValueOf(self) / _numericValueOf(other)
664
+
665
+ def __rdiv__(self, other):
666
+ return _numericValueOf(other) / _numericValueOf(self)
667
+
668
+ def __truediv__(self, other):
669
+ return _numericValueOf(self) / _numericValueOf(other)
670
+
671
+ def __rtruediv__(self, other):
672
+ return _numericValueOf(other) / _numericValueOf(self)
673
+
674
+ def __floordiv__(self, other):
675
+ return _numericValueOf(self) // _numericValueOf(other)
676
+
677
+ def __rfloordiv__(self, other):
678
+ return _numericValueOf(other) // _numericValueOf(self)
679
+
680
+ def __mod__(self, other):
681
+ return _numericValueOf(self) % _numericValueOf(other)
682
+
683
+ def __rmod__(self, other):
684
+ return _numericValueOf(other) % _numericValueOf(self)
685
+
686
+ def __divmod__(self, other):
687
+ return divmod(_numericValueOf(self), _numericValueOf(other))
688
+
689
+ def __rdivmod__(self, other):
690
+ return divmod(_numericValueOf(other), _numericValueOf(self))
691
+
692
+ def __pow__(self, other, modulo):
693
+ if modulo is None:
694
+ return _numericValueOf(self) ** _numericValueOf(other)
695
+ else:
696
+ return pow(_numericValueOf(self), _numericValueOf(other), modulo)
697
+
698
+ def __rpow__(self, other, modulo):
699
+ if modulo is None:
700
+ return _numericValueOf(other) ** _numericValueOf(self)
701
+ else:
702
+ return pow(_numericValueOf(other), _numericValueOf(self), modulo)
703
+
704
+ def __neg__(self):
705
+ return - _numericValueOf(self)
706
+
707
+ def __pos__(self):
708
+ return + _numericValueOf(self)
709
+
710
+ def __abs__(self):
711
+ return abs( _numericValueOf(self) )
712
+
713
+ def __bool__(self):
714
+ return bool(_numericValueOf(self))
715
+
716
+ def __invert__(self):
717
+ return ~ _numericValueOf(self)
718
+
719
+ def __lshift__(self, other):
720
+ return _numericValueOf(self) << _numericValueOf(other)
721
+
722
+ def __rlshift__(self, other):
723
+ return _numericValueOf(other) << _numericValueOf(self)
724
+
725
+ def __rshift__(self, other):
726
+ return _numericValueOf(self) >> _numericValueOf(other)
727
+
728
+ def __rrshift__(self, other):
729
+ return _numericValueOf(other) >> _numericValueOf(self)
730
+
731
+ def __and__(self, other):
732
+ return _numericValueOf(self) & _numericValueOf(other)
733
+
734
+ def __rand__(self, other):
735
+ return _numericValueOf(other) & _numericValueOf(self)
736
+
737
+ def __or__(self, other):
738
+ return _numericValueOf(self) | _numericValueOf(other)
739
+
740
+ def __ror__(self, other):
741
+ return _numericValueOf(other) | _numericValueOf(self)
742
+
743
+ def __xor__(self, other):
744
+ return _numericValueOf(self) ^ _numericValueOf(other)
745
+
746
+ def __rxor__(self, other):
747
+ return _numericValueOf(other) ^ _numericValueOf(self)
748
+
749
+
750
+ cdef class IntElement(NumberElement):
751
+ def _init(self):
752
+ self._parse_value = int
753
+
754
+ def __index__(self):
755
+ return int(_parseNumber(self))
756
+
757
+
758
+ cdef class FloatElement(NumberElement):
759
+ def _init(self):
760
+ self._parse_value = float
761
+
762
+
763
+ cdef class StringElement(ObjectifiedDataElement):
764
+ """String data class.
765
+
766
+ Note that this class does *not* support the sequence protocol of strings:
767
+ len(), iter(), str_attr[0], str_attr[0:1], etc. are *not* supported.
768
+ Instead, use the .text attribute to get a 'real' string.
769
+ """
770
+ @property
771
+ def pyval(self):
772
+ return textOf(self._c_node) or ''
773
+
774
+ def __repr__(self):
775
+ return repr(textOf(self._c_node) or '')
776
+
777
+ def strlen(self):
778
+ text = textOf(self._c_node)
779
+ if text is None:
780
+ return 0
781
+ else:
782
+ return len(text)
783
+
784
+ def __bool__(self):
785
+ return bool(textOf(self._c_node))
786
+
787
+ def __richcmp__(self, other, int op):
788
+ return _richcmpPyvals(self, other, op)
789
+
790
+ def __hash__(self):
791
+ return hash(textOf(self._c_node) or '')
792
+
793
+ def __add__(self, other):
794
+ text = _strValueOf(self)
795
+ other = _strValueOf(other)
796
+ return text + other
797
+
798
+ def __radd__(self, other):
799
+ text = _strValueOf(self)
800
+ other = _strValueOf(other)
801
+ return other + text
802
+
803
+ def __mul__(self, other):
804
+ if isinstance(self, StringElement):
805
+ return (textOf((<StringElement>self)._c_node) or '') * _numericValueOf(other)
806
+ elif isinstance(other, StringElement):
807
+ return _numericValueOf(self) * (textOf((<StringElement>other)._c_node) or '')
808
+ else:
809
+ return NotImplemented
810
+
811
+ def __rmul__(self, other):
812
+ return _numericValueOf(other) * (textOf((<StringElement>self)._c_node) or '')
813
+
814
+ def __mod__(self, other):
815
+ return (_strValueOf(self) or '') % other
816
+
817
+ def __int__(self):
818
+ return int(textOf(self._c_node))
819
+
820
+ def __float__(self):
821
+ return float(textOf(self._c_node))
822
+
823
+ def __complex__(self):
824
+ return complex(textOf(self._c_node))
825
+
826
+
827
+ cdef class NoneElement(ObjectifiedDataElement):
828
+ def __str__(self):
829
+ return "None"
830
+
831
+ def __repr__(self):
832
+ return "None"
833
+
834
+ def __bool__(self):
835
+ return False
836
+
837
+ def __richcmp__(self, other, int op):
838
+ if other is None or self is None:
839
+ return python.PyObject_RichCompare(None, None, op)
840
+ if isinstance(self, NoneElement):
841
+ return python.PyObject_RichCompare(None, other, op)
842
+ else:
843
+ return python.PyObject_RichCompare(self, None, op)
844
+
845
+ def __hash__(self):
846
+ return hash(None)
847
+
848
+ @property
849
+ def pyval(self):
850
+ return None
851
+
852
+
853
+ cdef class BoolElement(IntElement):
854
+ """Boolean type base on string values: 'true' or 'false'.
855
+
856
+ Note that this inherits from IntElement to mimic the behaviour of
857
+ Python's bool type.
858
+ """
859
+ def _init(self):
860
+ self._parse_value = _parseBool # wraps as Python callable
861
+
862
+ def __bool__(self):
863
+ return _parseBool(textOf(self._c_node))
864
+
865
+ def __int__(self):
866
+ return 0 + _parseBool(textOf(self._c_node))
867
+
868
+ def __float__(self):
869
+ return 0.0 + _parseBool(textOf(self._c_node))
870
+
871
+ def __richcmp__(self, other, int op):
872
+ return _richcmpPyvals(self, other, op)
873
+
874
+ def __hash__(self):
875
+ return hash(_parseBool(textOf(self._c_node)))
876
+
877
+ def __str__(self):
878
+ return unicode(_parseBool(textOf(self._c_node)))
879
+
880
+ def __repr__(self):
881
+ return repr(_parseBool(textOf(self._c_node)))
882
+
883
+ @property
884
+ def pyval(self):
885
+ return _parseBool(textOf(self._c_node))
886
+
887
+
888
+ cdef _checkBool(s):
889
+ cdef int value = -1
890
+ if s is not None:
891
+ value = __parseBoolAsInt(s)
892
+ if value == -1:
893
+ raise ValueError
894
+
895
+
896
+ cdef bint _parseBool(s) except -1:
897
+ cdef int value
898
+ if s is None:
899
+ return False
900
+ value = __parseBoolAsInt(s)
901
+ if value == -1:
902
+ raise ValueError, f"Invalid boolean value: '{s}'"
903
+ return value
904
+
905
+
906
+ cdef inline int __parseBoolAsInt(text) except -2:
907
+ if text == 'false':
908
+ return 0
909
+ elif text == 'true':
910
+ return 1
911
+ elif text == '0':
912
+ return 0
913
+ elif text == '1':
914
+ return 1
915
+ return -1
916
+
917
+
918
+ cdef object _parseNumber(NumberElement element):
919
+ return element._parse_value(textOf(element._c_node))
920
+
921
+
922
+ cdef enum NumberParserState:
923
+ NPS_SPACE_PRE = 0
924
+ NPS_SIGN = 1
925
+ NPS_DIGITS = 2
926
+ NPS_POINT_LEAD = 3
927
+ NPS_POINT = 4
928
+ NPS_FRACTION = 5
929
+ NPS_EXP = 6
930
+ NPS_EXP_SIGN = 7
931
+ NPS_DIGITS_EXP = 8
932
+ NPS_SPACE_TAIL = 9
933
+ NPS_INF1 = 20
934
+ NPS_INF2 = 21
935
+ NPS_INF3 = 22
936
+ NPS_NAN1 = 23
937
+ NPS_NAN2 = 24
938
+ NPS_NAN3 = 25
939
+ NPS_ERROR = 99
940
+
941
+
942
+ ctypedef fused bytes_unicode:
943
+ bytes
944
+ unicode
945
+
946
+
947
+ cdef _checkNumber(bytes_unicode s, bint allow_float):
948
+ cdef Py_UCS4 c
949
+ cdef NumberParserState state = NPS_SPACE_PRE
950
+
951
+ for c in s:
952
+ if c in '0123456789':
953
+ if state in (NPS_DIGITS, NPS_FRACTION, NPS_DIGITS_EXP):
954
+ pass
955
+ elif state in (NPS_SPACE_PRE, NPS_SIGN):
956
+ state = NPS_DIGITS
957
+ elif state in (NPS_POINT_LEAD, NPS_POINT):
958
+ state = NPS_FRACTION
959
+ elif state in (NPS_EXP, NPS_EXP_SIGN):
960
+ state = NPS_DIGITS_EXP
961
+ else:
962
+ state = NPS_ERROR
963
+ else:
964
+ if c == '.':
965
+ if state in (NPS_SPACE_PRE, NPS_SIGN):
966
+ state = NPS_POINT_LEAD
967
+ elif state == NPS_DIGITS:
968
+ state = NPS_POINT
969
+ else:
970
+ state = NPS_ERROR
971
+ if not allow_float:
972
+ state = NPS_ERROR
973
+ elif c in '-+':
974
+ if state == NPS_SPACE_PRE:
975
+ state = NPS_SIGN
976
+ elif state == NPS_EXP:
977
+ state = NPS_EXP_SIGN
978
+ else:
979
+ state = NPS_ERROR
980
+ elif c == 'E':
981
+ if state in (NPS_DIGITS, NPS_POINT, NPS_FRACTION):
982
+ state = NPS_EXP
983
+ else:
984
+ state = NPS_ERROR
985
+ if not allow_float:
986
+ state = NPS_ERROR
987
+ # Allow INF and NaN. XMLSchema requires case, we don't, like Python.
988
+ elif c in 'iI':
989
+ state = NPS_INF1 if allow_float and state in (NPS_SPACE_PRE, NPS_SIGN) else NPS_ERROR
990
+ elif c in 'fF':
991
+ state = NPS_INF3 if state == NPS_INF2 else NPS_ERROR
992
+ elif c in 'aA':
993
+ state = NPS_NAN2 if state == NPS_NAN1 else NPS_ERROR
994
+ elif c in 'nN':
995
+ # Python also allows [+-]NaN, so let's accept that.
996
+ if state in (NPS_SPACE_PRE, NPS_SIGN):
997
+ state = NPS_NAN1 if allow_float else NPS_ERROR
998
+ elif state == NPS_NAN2:
999
+ state = NPS_NAN3
1000
+ elif state == NPS_INF1:
1001
+ state = NPS_INF2
1002
+ else:
1003
+ state = NPS_ERROR
1004
+ # Allow spaces around text values.
1005
+ else:
1006
+ if c.isspace() if (bytes_unicode is unicode) else c in b'\x09\x0a\x0b\x0c\x0d\x20':
1007
+ if state in (NPS_SPACE_PRE, NPS_SPACE_TAIL):
1008
+ pass
1009
+ elif state in (NPS_DIGITS, NPS_POINT, NPS_FRACTION, NPS_DIGITS_EXP, NPS_INF3, NPS_NAN3):
1010
+ state = NPS_SPACE_TAIL
1011
+ else:
1012
+ state = NPS_ERROR
1013
+ else:
1014
+ state = NPS_ERROR
1015
+
1016
+ if state == NPS_ERROR:
1017
+ break
1018
+
1019
+ if state not in (NPS_DIGITS, NPS_FRACTION, NPS_POINT, NPS_DIGITS_EXP, NPS_INF3, NPS_NAN3, NPS_SPACE_TAIL):
1020
+ raise ValueError
1021
+
1022
+
1023
+ cdef _checkInt(s):
1024
+ return _checkNumber(<unicode>s, allow_float=False)
1025
+
1026
+
1027
+ cdef _checkFloat(s):
1028
+ return _checkNumber(<unicode>s, allow_float=True)
1029
+
1030
+
1031
+ cdef object _strValueOf(obj):
1032
+ if python._isString(obj):
1033
+ return obj
1034
+ if isinstance(obj, _Element):
1035
+ return textOf((<_Element>obj)._c_node) or ''
1036
+ if obj is None:
1037
+ return ''
1038
+ return unicode(obj)
1039
+
1040
+
1041
+ cdef object _numericValueOf(obj):
1042
+ if isinstance(obj, NumberElement):
1043
+ return _parseNumber(<NumberElement>obj)
1044
+ try:
1045
+ # not always numeric, but Python will raise the right exception
1046
+ return obj.pyval
1047
+ except AttributeError:
1048
+ pass
1049
+ return obj
1050
+
1051
+
1052
+ cdef _richcmpPyvals(left, right, int op):
1053
+ left = getattr(left, 'pyval', left)
1054
+ right = getattr(right, 'pyval', right)
1055
+ return python.PyObject_RichCompare(left, right, op)
1056
+
1057
+
1058
+ ################################################################################
1059
+ # Python type registry
1060
+
1061
+ cdef class PyType:
1062
+ """PyType(self, name, type_check, type_class, stringify=None)
1063
+ User defined type.
1064
+
1065
+ Named type that contains a type check function, a type class that
1066
+ inherits from ObjectifiedDataElement and an optional "stringification"
1067
+ function. The type check must take a string as argument and raise
1068
+ ValueError or TypeError if it cannot handle the string value. It may be
1069
+ None in which case it is not considered for type guessing. For registered
1070
+ named types, the 'stringify' function (or unicode() if None) is used to
1071
+ convert a Python object with type name 'name' to the string representation
1072
+ stored in the XML tree.
1073
+
1074
+ Example::
1075
+
1076
+ PyType('int', int, MyIntClass).register()
1077
+
1078
+ Note that the order in which types are registered matters. The first
1079
+ matching type will be used.
1080
+ """
1081
+ cdef readonly object name
1082
+ cdef readonly object type_check
1083
+ cdef readonly object stringify
1084
+ cdef object _type
1085
+ cdef list _schema_types
1086
+ def __init__(self, name, type_check, type_class, stringify=None):
1087
+ if isinstance(name, bytes):
1088
+ name = (<bytes>name).decode('ascii')
1089
+ elif not isinstance(name, unicode):
1090
+ raise TypeError, "Type name must be a string"
1091
+ if type_check is not None and not callable(type_check):
1092
+ raise TypeError, "Type check function must be callable (or None)"
1093
+ if name != TREE_PYTYPE_NAME and \
1094
+ not issubclass(type_class, ObjectifiedDataElement):
1095
+ raise TypeError, \
1096
+ "Data classes must inherit from ObjectifiedDataElement"
1097
+ self.name = name
1098
+ self._type = type_class
1099
+ self.type_check = type_check
1100
+ if stringify is None:
1101
+ stringify = unicode
1102
+ self.stringify = stringify
1103
+ self._schema_types = []
1104
+
1105
+ def __repr__(self):
1106
+ return "PyType(%s, %s)" % (self.name, self._type.__name__)
1107
+
1108
+ def register(self, before=None, after=None):
1109
+ """register(self, before=None, after=None)
1110
+
1111
+ Register the type.
1112
+
1113
+ The additional keyword arguments 'before' and 'after' accept a
1114
+ sequence of type names that must appear before/after the new type in
1115
+ the type list. If any of them is not currently known, it is simply
1116
+ ignored. Raises ValueError if the dependencies cannot be fulfilled.
1117
+ """
1118
+ if self.name == TREE_PYTYPE_NAME:
1119
+ raise ValueError, "Cannot register tree type"
1120
+ if self.type_check is not None:
1121
+ for item in _TYPE_CHECKS:
1122
+ if item[0] is self.type_check:
1123
+ _TYPE_CHECKS.remove(item)
1124
+ break
1125
+ entry = (self.type_check, self)
1126
+ first_pos = 0
1127
+ last_pos = -1
1128
+ if before or after:
1129
+ if before is None:
1130
+ before = ()
1131
+ elif after is None:
1132
+ after = ()
1133
+ for i, (check, pytype) in enumerate(_TYPE_CHECKS):
1134
+ if last_pos == -1 and pytype.name in before:
1135
+ last_pos = i
1136
+ if pytype.name in after:
1137
+ first_pos = i+1
1138
+ if last_pos == -1:
1139
+ _TYPE_CHECKS.append(entry)
1140
+ elif first_pos > last_pos:
1141
+ raise ValueError, "inconsistent before/after dependencies"
1142
+ else:
1143
+ _TYPE_CHECKS.insert(last_pos, entry)
1144
+
1145
+ _PYTYPE_DICT[self.name] = self
1146
+ for xs_type in self._schema_types:
1147
+ _SCHEMA_TYPE_DICT[xs_type] = self
1148
+
1149
+ def unregister(self):
1150
+ "unregister(self)"
1151
+ if _PYTYPE_DICT.get(self.name) is self:
1152
+ del _PYTYPE_DICT[self.name]
1153
+ for xs_type, pytype in list(_SCHEMA_TYPE_DICT.items()):
1154
+ if pytype is self:
1155
+ del _SCHEMA_TYPE_DICT[xs_type]
1156
+ if self.type_check is None:
1157
+ return
1158
+ try:
1159
+ _TYPE_CHECKS.remove( (self.type_check, self) )
1160
+ except ValueError:
1161
+ pass
1162
+
1163
+ property xmlSchemaTypes:
1164
+ """The list of XML Schema datatypes this Python type maps to.
1165
+
1166
+ Note that this must be set before registering the type!
1167
+ """
1168
+ def __get__(self):
1169
+ return self._schema_types
1170
+ def __set__(self, types):
1171
+ self._schema_types = list(map(unicode, types))
1172
+
1173
+
1174
+ cdef dict _PYTYPE_DICT = {}
1175
+ cdef dict _SCHEMA_TYPE_DICT = {}
1176
+ cdef list _TYPE_CHECKS = []
1177
+
1178
+ cdef unicode _xml_bool(value):
1179
+ return "true" if value else "false"
1180
+
1181
+ cdef unicode _xml_float(value):
1182
+ if _float_is_inf(value):
1183
+ if value > 0:
1184
+ return "INF"
1185
+ return "-INF"
1186
+ if _float_is_nan(value):
1187
+ return "NaN"
1188
+ return unicode(repr(value))
1189
+
1190
+ cdef _pytypename(obj):
1191
+ return "str" if python._isString(obj) else _typename(obj)
1192
+
1193
+ def pytypename(obj):
1194
+ """pytypename(obj)
1195
+
1196
+ Find the name of the corresponding PyType for a Python object.
1197
+ """
1198
+ return _pytypename(obj)
1199
+
1200
+ cdef _registerPyTypes():
1201
+ pytype = PyType('int', _checkInt, IntElement) # wraps functions for Python
1202
+ pytype.xmlSchemaTypes = ("integer", "int", "short", "byte", "unsignedShort",
1203
+ "unsignedByte", "nonPositiveInteger",
1204
+ "negativeInteger", "long", "nonNegativeInteger",
1205
+ "unsignedLong", "unsignedInt", "positiveInteger",)
1206
+ pytype.register()
1207
+
1208
+ # 'long' type just for backwards compatibility
1209
+ pytype = PyType('long', None, IntElement)
1210
+ pytype.register()
1211
+
1212
+ pytype = PyType('float', _checkFloat, FloatElement, _xml_float) # wraps functions for Python
1213
+ pytype.xmlSchemaTypes = ("double", "float")
1214
+ pytype.register()
1215
+
1216
+ pytype = PyType('bool', _checkBool, BoolElement, _xml_bool) # wraps functions for Python
1217
+ pytype.xmlSchemaTypes = ("boolean",)
1218
+ pytype.register()
1219
+
1220
+ pytype = PyType('str', None, StringElement)
1221
+ pytype.xmlSchemaTypes = ("string", "normalizedString", "token", "language",
1222
+ "Name", "NCName", "ID", "IDREF", "ENTITY",
1223
+ "NMTOKEN", )
1224
+ pytype.register()
1225
+
1226
+ # since lxml 2.0
1227
+ pytype = PyType('NoneType', None, NoneElement)
1228
+ pytype.register()
1229
+
1230
+ # backwards compatibility
1231
+ pytype = PyType('none', None, NoneElement)
1232
+ pytype.register()
1233
+
1234
+ # non-registered PyType for inner tree elements
1235
+ cdef PyType TREE_PYTYPE = PyType(TREE_PYTYPE_NAME, None, ObjectifiedElement)
1236
+
1237
+ _registerPyTypes()
1238
+
1239
+ def getRegisteredTypes():
1240
+ """getRegisteredTypes()
1241
+
1242
+ Returns a list of the currently registered PyType objects.
1243
+
1244
+ To add a new type, retrieve this list and call unregister() for all
1245
+ entries. Then add the new type at a suitable position (possibly replacing
1246
+ an existing one) and call register() for all entries.
1247
+
1248
+ This is necessary if the new type interferes with the type check functions
1249
+ of existing ones (normally only int/float/bool) and must the tried before
1250
+ other types. To add a type that is not yet parsable by the current type
1251
+ check functions, you can simply register() it, which will append it to the
1252
+ end of the type list.
1253
+ """
1254
+ cdef list types = []
1255
+ cdef set known = set()
1256
+ for check, pytype in _TYPE_CHECKS:
1257
+ name = pytype.name
1258
+ if name not in known:
1259
+ known.add(name)
1260
+ types.append(pytype)
1261
+ for pytype in _PYTYPE_DICT.values():
1262
+ name = pytype.name
1263
+ if name not in known:
1264
+ known.add(name)
1265
+ types.append(pytype)
1266
+ return types
1267
+
1268
+ cdef PyType _guessPyType(value, PyType defaulttype):
1269
+ if value is None:
1270
+ return None
1271
+ for type_check, tested_pytype in _TYPE_CHECKS:
1272
+ try:
1273
+ type_check(value)
1274
+ return <PyType>tested_pytype
1275
+ except IGNORABLE_ERRORS:
1276
+ # could not be parsed as the specified type => ignore
1277
+ pass
1278
+ return defaulttype
1279
+
1280
+ cdef object _guessElementClass(tree.xmlNode* c_node):
1281
+ value = textOf(c_node)
1282
+ if value is None:
1283
+ return None
1284
+ if value == '':
1285
+ return StringElement
1286
+
1287
+ for type_check, pytype in _TYPE_CHECKS:
1288
+ try:
1289
+ type_check(value)
1290
+ return (<PyType>pytype)._type
1291
+ except IGNORABLE_ERRORS:
1292
+ pass
1293
+ return None
1294
+
1295
+ ################################################################################
1296
+ # adapted ElementMaker supports registered PyTypes
1297
+
1298
+ @cython.final
1299
+ @cython.internal
1300
+ cdef class _ObjectifyElementMakerCaller:
1301
+ cdef object _tag
1302
+ cdef object _nsmap
1303
+ cdef object _element_factory
1304
+ cdef bint _annotate
1305
+
1306
+ def __call__(self, *children, **attrib):
1307
+ "__call__(self, *children, **attrib)"
1308
+ cdef _ObjectifyElementMakerCaller elementMaker
1309
+ cdef _Element element
1310
+ cdef _Element childElement
1311
+ cdef bint has_children
1312
+ cdef bint has_string_value
1313
+ if self._element_factory is None:
1314
+ element = _makeElement(self._tag, None, attrib, self._nsmap)
1315
+ else:
1316
+ element = self._element_factory(self._tag, attrib, self._nsmap)
1317
+
1318
+ pytype_name = None
1319
+ has_children = False
1320
+ has_string_value = False
1321
+ for child in children:
1322
+ if child is None:
1323
+ if len(children) == 1:
1324
+ cetree.setAttributeValue(
1325
+ element, XML_SCHEMA_INSTANCE_NIL_ATTR, "true")
1326
+ elif python._isString(child):
1327
+ _add_text(element, child)
1328
+ has_string_value = True
1329
+ elif isinstance(child, _Element):
1330
+ cetree.appendChildToElement(element, <_Element>child)
1331
+ has_children = True
1332
+ elif isinstance(child, _ObjectifyElementMakerCaller):
1333
+ elementMaker = <_ObjectifyElementMakerCaller>child
1334
+ if elementMaker._element_factory is None:
1335
+ cetree.makeSubElement(element, elementMaker._tag,
1336
+ None, None, None, None)
1337
+ else:
1338
+ childElement = elementMaker._element_factory(
1339
+ elementMaker._tag)
1340
+ cetree.appendChildToElement(element, childElement)
1341
+ has_children = True
1342
+ elif isinstance(child, dict):
1343
+ for name, value in child.items():
1344
+ # keyword arguments in attrib take precedence
1345
+ if name in attrib:
1346
+ continue
1347
+ pytype = _PYTYPE_DICT.get(_typename(value))
1348
+ if pytype is not None:
1349
+ value = (<PyType>pytype).stringify(value)
1350
+ elif not python._isString(value):
1351
+ value = unicode(value)
1352
+ cetree.setAttributeValue(element, name, value)
1353
+ else:
1354
+ if pytype_name is not None:
1355
+ # concatenation always makes the result a string
1356
+ has_string_value = True
1357
+ pytype_name = _typename(child)
1358
+ pytype = _PYTYPE_DICT.get(_typename(child))
1359
+ if pytype is not None:
1360
+ _add_text(element, (<PyType>pytype).stringify(child))
1361
+ else:
1362
+ has_string_value = True
1363
+ child = unicode(child)
1364
+ _add_text(element, child)
1365
+
1366
+ if self._annotate and not has_children:
1367
+ if has_string_value:
1368
+ cetree.setAttributeValue(element, PYTYPE_ATTRIBUTE, "str")
1369
+ elif pytype_name is not None:
1370
+ cetree.setAttributeValue(element, PYTYPE_ATTRIBUTE, pytype_name)
1371
+
1372
+ return element
1373
+
1374
+ cdef _add_text(_Element elem, text):
1375
+ # add text to the tree in construction, either as element text or
1376
+ # tail text, depending on the current tree state
1377
+ cdef tree.xmlNode* c_child
1378
+ c_child = cetree.findChildBackwards(elem._c_node, 0)
1379
+ if c_child is not NULL:
1380
+ old = cetree.tailOf(c_child)
1381
+ if old is not None:
1382
+ text = old + text
1383
+ cetree.setTailText(c_child, text)
1384
+ else:
1385
+ old = cetree.textOf(elem._c_node)
1386
+ if old is not None:
1387
+ text = old + text
1388
+ cetree.setNodeText(elem._c_node, text)
1389
+
1390
+ cdef class ElementMaker:
1391
+ """ElementMaker(self, namespace=None, nsmap=None, annotate=True, makeelement=None)
1392
+
1393
+ An ElementMaker that can be used for constructing trees.
1394
+
1395
+ Example::
1396
+
1397
+ >>> M = ElementMaker(annotate=False)
1398
+ >>> attributes = {'class': 'par'}
1399
+ >>> html = M.html( M.body( M.p('hello', attributes, M.br, 'objectify', style="font-weight: bold") ) )
1400
+
1401
+ >>> from lxml.etree import tostring
1402
+ >>> print(tostring(html, method='html').decode('ascii'))
1403
+ <html><body><p style="font-weight: bold" class="par">hello<br>objectify</p></body></html>
1404
+
1405
+ To create tags that are not valid Python identifiers, call the factory
1406
+ directly and pass the tag name as first argument::
1407
+
1408
+ >>> root = M('tricky-tag', 'some text')
1409
+ >>> print(root.tag)
1410
+ tricky-tag
1411
+ >>> print(root.text)
1412
+ some text
1413
+
1414
+ Note that this module has a predefined ElementMaker instance called ``E``.
1415
+ """
1416
+ cdef object _makeelement
1417
+ cdef object _namespace
1418
+ cdef object _nsmap
1419
+ cdef bint _annotate
1420
+ cdef dict _cache
1421
+ def __init__(self, *, namespace=None, nsmap=None, annotate=True,
1422
+ makeelement=None):
1423
+ if nsmap is None:
1424
+ nsmap = _DEFAULT_NSMAP if annotate else {}
1425
+ self._nsmap = nsmap
1426
+ self._namespace = None if namespace is None else "{%s}" % namespace
1427
+ self._annotate = annotate
1428
+ if makeelement is not None:
1429
+ if not callable(makeelement):
1430
+ raise TypeError(
1431
+ f"argument of 'makeelement' parameter must be callable, got {type(makeelement)}")
1432
+ self._makeelement = makeelement
1433
+ else:
1434
+ self._makeelement = None
1435
+ self._cache = {}
1436
+
1437
+ @cython.final
1438
+ cdef _build_element_maker(self, tag, bint caching):
1439
+ cdef _ObjectifyElementMakerCaller element_maker
1440
+ element_maker = _ObjectifyElementMakerCaller.__new__(_ObjectifyElementMakerCaller)
1441
+ if self._namespace is not None and tag[0] != "{":
1442
+ element_maker._tag = self._namespace + tag
1443
+ else:
1444
+ element_maker._tag = tag
1445
+ element_maker._nsmap = self._nsmap
1446
+ element_maker._annotate = self._annotate
1447
+ element_maker._element_factory = self._makeelement
1448
+ if caching:
1449
+ if len(self._cache) > 200:
1450
+ self._cache.clear()
1451
+ self._cache[tag] = element_maker
1452
+ return element_maker
1453
+
1454
+ def __getattr__(self, tag):
1455
+ element_maker = self._cache.get(tag)
1456
+ if element_maker is None:
1457
+ return self._build_element_maker(tag, caching=True)
1458
+ return element_maker
1459
+
1460
+ def __call__(self, tag, *args, **kwargs):
1461
+ element_maker = self._cache.get(tag)
1462
+ if element_maker is None:
1463
+ element_maker = self._build_element_maker(
1464
+ tag, caching=not is_special_method(tag))
1465
+ return element_maker(*args, **kwargs)
1466
+
1467
+ ################################################################################
1468
+ # Recursive element dumping
1469
+
1470
+ cdef bint __RECURSIVE_STR = 0 # default: off
1471
+
1472
+ def enable_recursive_str(on=True):
1473
+ """enable_recursive_str(on=True)
1474
+
1475
+ Enable a recursively generated tree representation for str(element),
1476
+ based on objectify.dump(element).
1477
+ """
1478
+ global __RECURSIVE_STR
1479
+ __RECURSIVE_STR = on
1480
+
1481
+ def dump(_Element element not None):
1482
+ """dump(_Element element not None)
1483
+
1484
+ Return a recursively generated string representation of an element.
1485
+ """
1486
+ return _dump(element, 0)
1487
+
1488
+ cdef object _dump(_Element element, int indent):
1489
+ indentstr = " " * indent
1490
+ if isinstance(element, ObjectifiedDataElement):
1491
+ value = repr(element)
1492
+ else:
1493
+ value = textOf(element._c_node)
1494
+ if value is not None:
1495
+ if not value.strip():
1496
+ value = None
1497
+ else:
1498
+ value = repr(value)
1499
+ result = f"{indentstr}{element.tag} = {value} [{_typename(element)}]\n"
1500
+ xsi_ns = "{%s}" % XML_SCHEMA_INSTANCE_NS
1501
+ pytype_ns = "{%s}" % PYTYPE_NAMESPACE
1502
+ for name, value in sorted(cetree.iterattributes(element, 3)):
1503
+ if '{' in name:
1504
+ if name == PYTYPE_ATTRIBUTE:
1505
+ if value == TREE_PYTYPE_NAME:
1506
+ continue
1507
+ else:
1508
+ name = name.replace(pytype_ns, 'py:')
1509
+ name = name.replace(xsi_ns, 'xsi:')
1510
+ result += f"{indentstr} * {name} = {value!r}\n"
1511
+
1512
+ indent += 1
1513
+ for child in element.iterchildren():
1514
+ result += _dump(child, indent)
1515
+ if indent == 1:
1516
+ return result[:-1] # strip last '\n'
1517
+ else:
1518
+ return result
1519
+
1520
+
1521
+ ################################################################################
1522
+ # Pickle support for objectified ElementTree
1523
+
1524
+ def __unpickleElementTree(data):
1525
+ return etree.ElementTree(fromstring(data))
1526
+
1527
+ cdef _setupPickle(elementTreeReduceFunction):
1528
+ import copyreg
1529
+ copyreg.pickle(etree._ElementTree,
1530
+ elementTreeReduceFunction, __unpickleElementTree)
1531
+
1532
+ def pickleReduceElementTree(obj):
1533
+ return __unpickleElementTree, (etree.tostring(obj),)
1534
+
1535
+ _setupPickle(pickleReduceElementTree)
1536
+ del pickleReduceElementTree
1537
+
1538
+ ################################################################################
1539
+ # Element class lookup
1540
+
1541
+ cdef class ObjectifyElementClassLookup(ElementClassLookup):
1542
+ """ObjectifyElementClassLookup(self, tree_class=None, empty_data_class=None)
1543
+ Element class lookup method that uses the objectify classes.
1544
+ """
1545
+ cdef object empty_data_class
1546
+ cdef object tree_class
1547
+ def __init__(self, tree_class=None, empty_data_class=None):
1548
+ """Lookup mechanism for objectify.
1549
+
1550
+ The default Element classes can be replaced by passing subclasses of
1551
+ ObjectifiedElement and ObjectifiedDataElement as keyword arguments.
1552
+ 'tree_class' defines inner tree classes (defaults to
1553
+ ObjectifiedElement), 'empty_data_class' defines the default class for
1554
+ empty data elements (defaults to StringElement).
1555
+ """
1556
+ self._lookup_function = _lookupElementClass
1557
+ if tree_class is None:
1558
+ tree_class = ObjectifiedElement
1559
+ self.tree_class = tree_class
1560
+ if empty_data_class is None:
1561
+ empty_data_class = StringElement
1562
+ self.empty_data_class = empty_data_class
1563
+
1564
+ cdef object _lookupElementClass(state, _Document doc, tree.xmlNode* c_node):
1565
+ cdef ObjectifyElementClassLookup lookup
1566
+ lookup = <ObjectifyElementClassLookup>state
1567
+ # if element has children => no data class
1568
+ if cetree.hasChild(c_node):
1569
+ return lookup.tree_class
1570
+
1571
+ # if element is defined as xsi:nil, return NoneElement class
1572
+ if "true" == cetree.attributeValueFromNsName(
1573
+ c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>"nil"):
1574
+ return NoneElement
1575
+
1576
+ # check for Python type hint
1577
+ value = cetree.attributeValueFromNsName(
1578
+ c_node, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
1579
+ if value is not None:
1580
+ if value == TREE_PYTYPE_NAME:
1581
+ return lookup.tree_class
1582
+ py_type = <PyType>_PYTYPE_DICT.get(value)
1583
+ if py_type is not None:
1584
+ return py_type._type
1585
+ # unknown 'pyval' => try to figure it out ourself, just go on
1586
+
1587
+ # check for XML Schema type hint
1588
+ value = cetree.attributeValueFromNsName(
1589
+ c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>"type")
1590
+
1591
+ if value is not None:
1592
+ schema_type = <PyType>_SCHEMA_TYPE_DICT.get(value)
1593
+ if schema_type is None and ':' in value:
1594
+ prefix, value = value.split(':', 1)
1595
+ schema_type = <PyType>_SCHEMA_TYPE_DICT.get(value)
1596
+ if schema_type is not None:
1597
+ return schema_type._type
1598
+
1599
+ # otherwise determine class based on text content type
1600
+ el_class = _guessElementClass(c_node)
1601
+ if el_class is not None:
1602
+ return el_class
1603
+
1604
+ # if element is a root node => default to tree node
1605
+ if c_node.parent is NULL or not tree._isElement(c_node.parent):
1606
+ return lookup.tree_class
1607
+
1608
+ return lookup.empty_data_class
1609
+
1610
+
1611
+ ################################################################################
1612
+ # Type annotations
1613
+
1614
+ cdef PyType _check_type(tree.xmlNode* c_node, PyType pytype):
1615
+ if pytype is None:
1616
+ return None
1617
+ value = textOf(c_node)
1618
+ try:
1619
+ pytype.type_check(value)
1620
+ return pytype
1621
+ except IGNORABLE_ERRORS:
1622
+ # could not be parsed as the specified type => ignore
1623
+ pass
1624
+ return None
1625
+
1626
+ def pyannotate(element_or_tree, *, ignore_old=False, ignore_xsi=False,
1627
+ empty_pytype=None):
1628
+ """pyannotate(element_or_tree, ignore_old=False, ignore_xsi=False, empty_pytype=None)
1629
+
1630
+ Recursively annotates the elements of an XML tree with 'pytype'
1631
+ attributes.
1632
+
1633
+ If the 'ignore_old' keyword argument is True (the default), current 'pytype'
1634
+ attributes will be ignored and replaced. Otherwise, they will be checked
1635
+ and only replaced if they no longer fit the current text value.
1636
+
1637
+ Setting the keyword argument ``ignore_xsi`` to True makes the function
1638
+ additionally ignore existing ``xsi:type`` annotations. The default is to
1639
+ use them as a type hint.
1640
+
1641
+ The default annotation of empty elements can be set with the
1642
+ ``empty_pytype`` keyword argument. The default is not to annotate empty
1643
+ elements. Pass 'str', for example, to make string values the default.
1644
+ """
1645
+ cdef _Element element
1646
+ element = cetree.rootNodeOrRaise(element_or_tree)
1647
+ _annotate(element, 0, 1, ignore_xsi, ignore_old, None, empty_pytype)
1648
+
1649
+ def xsiannotate(element_or_tree, *, ignore_old=False, ignore_pytype=False,
1650
+ empty_type=None):
1651
+ """xsiannotate(element_or_tree, ignore_old=False, ignore_pytype=False, empty_type=None)
1652
+
1653
+ Recursively annotates the elements of an XML tree with 'xsi:type'
1654
+ attributes.
1655
+
1656
+ If the 'ignore_old' keyword argument is True (the default), current
1657
+ 'xsi:type' attributes will be ignored and replaced. Otherwise, they will be
1658
+ checked and only replaced if they no longer fit the current text value.
1659
+
1660
+ Note that the mapping from Python types to XSI types is usually ambiguous.
1661
+ Currently, only the first XSI type name in the corresponding PyType
1662
+ definition will be used for annotation. Thus, you should consider naming
1663
+ the widest type first if you define additional types.
1664
+
1665
+ Setting the keyword argument ``ignore_pytype`` to True makes the function
1666
+ additionally ignore existing ``pytype`` annotations. The default is to
1667
+ use them as a type hint.
1668
+
1669
+ The default annotation of empty elements can be set with the
1670
+ ``empty_type`` keyword argument. The default is not to annotate empty
1671
+ elements. Pass 'string', for example, to make string values the default.
1672
+ """
1673
+ cdef _Element element
1674
+ element = cetree.rootNodeOrRaise(element_or_tree)
1675
+ _annotate(element, 1, 0, ignore_old, ignore_pytype, empty_type, None)
1676
+
1677
+ def annotate(element_or_tree, *, ignore_old=True, ignore_xsi=False,
1678
+ empty_pytype=None, empty_type=None, annotate_xsi=0,
1679
+ annotate_pytype=1):
1680
+ """annotate(element_or_tree, ignore_old=True, ignore_xsi=False, empty_pytype=None, empty_type=None, annotate_xsi=0, annotate_pytype=1)
1681
+
1682
+ Recursively annotates the elements of an XML tree with 'xsi:type'
1683
+ and/or 'py:pytype' attributes.
1684
+
1685
+ If the 'ignore_old' keyword argument is True (the default), current
1686
+ 'py:pytype' attributes will be ignored for the type annotation. Set to False
1687
+ if you want reuse existing 'py:pytype' information (iff appropriate for the
1688
+ element text value).
1689
+
1690
+ If the 'ignore_xsi' keyword argument is False (the default), existing
1691
+ 'xsi:type' attributes will be used for the type annotation, if they fit the
1692
+ element text values.
1693
+
1694
+ Note that the mapping from Python types to XSI types is usually ambiguous.
1695
+ Currently, only the first XSI type name in the corresponding PyType
1696
+ definition will be used for annotation. Thus, you should consider naming
1697
+ the widest type first if you define additional types.
1698
+
1699
+ The default 'py:pytype' annotation of empty elements can be set with the
1700
+ ``empty_pytype`` keyword argument. Pass 'str', for example, to make
1701
+ string values the default.
1702
+
1703
+ The default 'xsi:type' annotation of empty elements can be set with the
1704
+ ``empty_type`` keyword argument. The default is not to annotate empty
1705
+ elements. Pass 'string', for example, to make string values the default.
1706
+
1707
+ The keyword arguments 'annotate_xsi' (default: 0) and 'annotate_pytype'
1708
+ (default: 1) control which kind(s) of annotation to use.
1709
+ """
1710
+ cdef _Element element
1711
+ element = cetree.rootNodeOrRaise(element_or_tree)
1712
+ _annotate(element, annotate_xsi, annotate_pytype, ignore_xsi,
1713
+ ignore_old, empty_type, empty_pytype)
1714
+
1715
+
1716
+ cdef _annotate(_Element element, bint annotate_xsi, bint annotate_pytype,
1717
+ bint ignore_xsi, bint ignore_pytype,
1718
+ empty_type_name, empty_pytype_name):
1719
+ cdef _Document doc
1720
+ cdef tree.xmlNode* c_node
1721
+ cdef PyType empty_pytype, StrType, NoneType
1722
+
1723
+ if not annotate_xsi and not annotate_pytype:
1724
+ return
1725
+
1726
+ if empty_type_name is not None:
1727
+ if isinstance(empty_type_name, bytes):
1728
+ empty_type_name = (<bytes>empty_type_name).decode("ascii")
1729
+ empty_pytype = <PyType>_SCHEMA_TYPE_DICT.get(empty_type_name)
1730
+ elif empty_pytype_name is not None:
1731
+ if isinstance(empty_pytype_name, bytes):
1732
+ empty_pytype_name = (<bytes>empty_pytype_name).decode("ascii")
1733
+ empty_pytype = <PyType>_PYTYPE_DICT.get(empty_pytype_name)
1734
+ else:
1735
+ empty_pytype = None
1736
+
1737
+ StrType = <PyType>_PYTYPE_DICT.get('str')
1738
+ NoneType = <PyType>_PYTYPE_DICT.get('NoneType')
1739
+
1740
+ doc = element._doc
1741
+ c_node = element._c_node
1742
+ tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1)
1743
+ if c_node.type == tree.XML_ELEMENT_NODE:
1744
+ _annotate_element(c_node, doc, annotate_xsi, annotate_pytype,
1745
+ ignore_xsi, ignore_pytype,
1746
+ empty_type_name, empty_pytype, StrType, NoneType)
1747
+ tree.END_FOR_EACH_ELEMENT_FROM(c_node)
1748
+
1749
+ cdef int _annotate_element(tree.xmlNode* c_node, _Document doc,
1750
+ bint annotate_xsi, bint annotate_pytype,
1751
+ bint ignore_xsi, bint ignore_pytype,
1752
+ empty_type_name, PyType empty_pytype,
1753
+ PyType StrType, PyType NoneType) except -1:
1754
+ cdef tree.xmlNs* c_ns
1755
+ cdef PyType pytype = None
1756
+ typename = None
1757
+ istree = 0
1758
+
1759
+ # if element is defined as xsi:nil, represent it as None
1760
+ if cetree.attributeValueFromNsName(
1761
+ c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>"nil") == "true":
1762
+ pytype = NoneType
1763
+
1764
+ if pytype is None and not ignore_xsi:
1765
+ # check that old xsi type value is valid
1766
+ typename = cetree.attributeValueFromNsName(
1767
+ c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>"type")
1768
+ if typename is not None:
1769
+ pytype = <PyType>_SCHEMA_TYPE_DICT.get(typename)
1770
+ if pytype is None and ':' in typename:
1771
+ prefix, typename = typename.split(':', 1)
1772
+ pytype = <PyType>_SCHEMA_TYPE_DICT.get(typename)
1773
+ if pytype is not None and pytype is not StrType:
1774
+ # StrType does not have a typecheck but is the default
1775
+ # anyway, so just accept it if given as type
1776
+ # information
1777
+ pytype = _check_type(c_node, pytype)
1778
+ if pytype is None:
1779
+ typename = None
1780
+
1781
+ if pytype is None and not ignore_pytype:
1782
+ # check that old pytype value is valid
1783
+ old_pytypename = cetree.attributeValueFromNsName(
1784
+ c_node, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
1785
+ if old_pytypename is not None:
1786
+ if old_pytypename == TREE_PYTYPE_NAME:
1787
+ if not cetree.hasChild(c_node):
1788
+ # only case where we should keep it,
1789
+ # everything else is clear enough
1790
+ pytype = TREE_PYTYPE
1791
+ else:
1792
+ if old_pytypename == 'none':
1793
+ # transition from lxml 1.x
1794
+ old_pytypename = "NoneType"
1795
+ pytype = <PyType>_PYTYPE_DICT.get(old_pytypename)
1796
+ if pytype is not None and pytype is not StrType:
1797
+ # StrType does not have a typecheck but is the
1798
+ # default anyway, so just accept it if given as
1799
+ # type information
1800
+ pytype = _check_type(c_node, pytype)
1801
+
1802
+ if pytype is None:
1803
+ # try to guess type
1804
+ if not cetree.hasChild(c_node):
1805
+ # element has no children => data class
1806
+ pytype = _guessPyType(textOf(c_node), StrType)
1807
+ else:
1808
+ istree = 1
1809
+
1810
+ if pytype is None:
1811
+ # use default type for empty elements
1812
+ if cetree.hasText(c_node):
1813
+ pytype = StrType
1814
+ else:
1815
+ pytype = empty_pytype
1816
+ if typename is None:
1817
+ typename = empty_type_name
1818
+
1819
+ if pytype is not None:
1820
+ if typename is None:
1821
+ if not istree:
1822
+ if pytype._schema_types:
1823
+ # pytype->xsi:type is a 1:n mapping
1824
+ # simply take the first
1825
+ typename = pytype._schema_types[0]
1826
+ elif typename not in pytype._schema_types:
1827
+ typename = pytype._schema_types[0]
1828
+
1829
+ if annotate_xsi:
1830
+ if typename is None or istree:
1831
+ cetree.delAttributeFromNsName(
1832
+ c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>"type")
1833
+ else:
1834
+ # update or create attribute
1835
+ typename_utf8 = cetree.utf8(typename)
1836
+ c_ns = cetree.findOrBuildNodeNsPrefix(
1837
+ doc, c_node, _XML_SCHEMA_NS, <unsigned char*>'xsd')
1838
+ if c_ns is not NULL:
1839
+ if b':' in typename_utf8:
1840
+ prefix, name = typename_utf8.split(b':', 1)
1841
+ if c_ns.prefix is NULL or c_ns.prefix[0] == c'\0':
1842
+ typename_utf8 = name
1843
+ elif tree.xmlStrcmp(_xcstr(prefix), c_ns.prefix) != 0:
1844
+ typename_utf8 = (<unsigned char*>c_ns.prefix) + b':' + name
1845
+ elif c_ns.prefix is not NULL and c_ns.prefix[0] != c'\0':
1846
+ typename_utf8 = (<unsigned char*>c_ns.prefix) + b':' + typename_utf8
1847
+ c_ns = cetree.findOrBuildNodeNsPrefix(
1848
+ doc, c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>'xsi')
1849
+ tree.xmlSetNsProp(c_node, c_ns, <unsigned char*>"type", _xcstr(typename_utf8))
1850
+
1851
+ if annotate_pytype:
1852
+ if pytype is None:
1853
+ # delete attribute if it exists
1854
+ cetree.delAttributeFromNsName(
1855
+ c_node, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
1856
+ else:
1857
+ # update or create attribute
1858
+ c_ns = cetree.findOrBuildNodeNsPrefix(
1859
+ doc, c_node, _PYTYPE_NAMESPACE, <unsigned char*>'py')
1860
+ pytype_name = cetree.utf8(pytype.name)
1861
+ tree.xmlSetNsProp(c_node, c_ns, _PYTYPE_ATTRIBUTE_NAME,
1862
+ _xcstr(pytype_name))
1863
+ if pytype is NoneType:
1864
+ c_ns = cetree.findOrBuildNodeNsPrefix(
1865
+ doc, c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>'xsi')
1866
+ tree.xmlSetNsProp(c_node, c_ns, <unsigned char*>"nil", <unsigned char*>"true")
1867
+
1868
+ return 0
1869
+
1870
+ cdef object _strip_attributes = etree.strip_attributes
1871
+ cdef object _cleanup_namespaces = etree.cleanup_namespaces
1872
+
1873
+ def deannotate(element_or_tree, *, bint pytype=True, bint xsi=True,
1874
+ bint xsi_nil=False, bint cleanup_namespaces=False):
1875
+ """deannotate(element_or_tree, pytype=True, xsi=True, xsi_nil=False, cleanup_namespaces=False)
1876
+
1877
+ Recursively de-annotate the elements of an XML tree by removing 'py:pytype'
1878
+ and/or 'xsi:type' attributes and/or 'xsi:nil' attributes.
1879
+
1880
+ If the 'pytype' keyword argument is True (the default), 'py:pytype'
1881
+ attributes will be removed. If the 'xsi' keyword argument is True (the
1882
+ default), 'xsi:type' attributes will be removed.
1883
+ If the 'xsi_nil' keyword argument is True (default: False), 'xsi:nil'
1884
+ attributes will be removed.
1885
+
1886
+ Note that this does not touch the namespace declarations by
1887
+ default. If you want to remove unused namespace declarations from
1888
+ the tree, pass the option ``cleanup_namespaces=True``.
1889
+ """
1890
+ cdef list attribute_names = []
1891
+
1892
+ if pytype:
1893
+ attribute_names.append(PYTYPE_ATTRIBUTE)
1894
+ if xsi:
1895
+ attribute_names.append(XML_SCHEMA_INSTANCE_TYPE_ATTR)
1896
+ if xsi_nil:
1897
+ attribute_names.append(XML_SCHEMA_INSTANCE_NIL_ATTR)
1898
+
1899
+ _strip_attributes(element_or_tree, *attribute_names)
1900
+ if cleanup_namespaces:
1901
+ _cleanup_namespaces(element_or_tree)
1902
+
1903
+ ################################################################################
1904
+ # Module level parser setup
1905
+
1906
+ cdef object __DEFAULT_PARSER
1907
+ __DEFAULT_PARSER = etree.XMLParser(remove_blank_text=True)
1908
+ __DEFAULT_PARSER.set_element_class_lookup( ObjectifyElementClassLookup() )
1909
+
1910
+ cdef object objectify_parser
1911
+ objectify_parser = __DEFAULT_PARSER
1912
+
1913
+ def set_default_parser(new_parser = None):
1914
+ """set_default_parser(new_parser = None)
1915
+
1916
+ Replace the default parser used by objectify's Element() and
1917
+ fromstring() functions.
1918
+
1919
+ The new parser must be an etree.XMLParser.
1920
+
1921
+ Call without arguments to reset to the original parser.
1922
+ """
1923
+ global objectify_parser
1924
+ if new_parser is None:
1925
+ objectify_parser = __DEFAULT_PARSER
1926
+ elif isinstance(new_parser, etree.XMLParser):
1927
+ objectify_parser = new_parser
1928
+ else:
1929
+ raise TypeError, "parser must inherit from lxml.etree.XMLParser"
1930
+
1931
+ def makeparser(**kw):
1932
+ """makeparser(remove_blank_text=True, **kw)
1933
+
1934
+ Create a new XML parser for objectify trees.
1935
+
1936
+ You can pass all keyword arguments that are supported by
1937
+ ``etree.XMLParser()``. Note that this parser defaults to removing
1938
+ blank text. You can disable this by passing the
1939
+ ``remove_blank_text`` boolean keyword option yourself.
1940
+ """
1941
+ if 'remove_blank_text' not in kw:
1942
+ kw['remove_blank_text'] = True
1943
+ parser = etree.XMLParser(**kw)
1944
+ parser.set_element_class_lookup( ObjectifyElementClassLookup() )
1945
+ return parser
1946
+
1947
+ cdef _Element _makeElement(tag, text, attrib, nsmap):
1948
+ return cetree.makeElement(tag, None, objectify_parser, text, None, attrib, nsmap)
1949
+
1950
+ ################################################################################
1951
+ # Module level factory functions
1952
+
1953
+ cdef object _fromstring
1954
+ _fromstring = etree.fromstring
1955
+
1956
+ SubElement = etree.SubElement
1957
+
1958
+ def fromstring(xml, parser=None, *, base_url=None):
1959
+ """fromstring(xml, parser=None, base_url=None)
1960
+
1961
+ Objectify specific version of the lxml.etree fromstring() function
1962
+ that uses the objectify parser.
1963
+
1964
+ You can pass a different parser as second argument.
1965
+
1966
+ The ``base_url`` keyword argument allows to set the original base URL of
1967
+ the document to support relative Paths when looking up external entities
1968
+ (DTD, XInclude, ...).
1969
+ """
1970
+ if parser is None:
1971
+ parser = objectify_parser
1972
+ return _fromstring(xml, parser, base_url=base_url)
1973
+
1974
+ def XML(xml, parser=None, *, base_url=None):
1975
+ """XML(xml, parser=None, base_url=None)
1976
+
1977
+ Objectify specific version of the lxml.etree XML() literal factory
1978
+ that uses the objectify parser.
1979
+
1980
+ You can pass a different parser as second argument.
1981
+
1982
+ The ``base_url`` keyword argument allows to set the original base URL of
1983
+ the document to support relative Paths when looking up external entities
1984
+ (DTD, XInclude, ...).
1985
+ """
1986
+ if parser is None:
1987
+ parser = objectify_parser
1988
+ return _fromstring(xml, parser, base_url=base_url)
1989
+
1990
+ cdef object _parse
1991
+ _parse = etree.parse
1992
+
1993
+ def parse(f, parser=None, *, base_url=None):
1994
+ """parse(f, parser=None, base_url=None)
1995
+
1996
+ Parse a file or file-like object with the objectify parser.
1997
+
1998
+ You can pass a different parser as second argument.
1999
+
2000
+ The ``base_url`` keyword allows setting a URL for the document
2001
+ when parsing from a file-like object. This is needed when looking
2002
+ up external entities (DTD, XInclude, ...) with relative paths.
2003
+ """
2004
+ if parser is None:
2005
+ parser = objectify_parser
2006
+ return _parse(f, parser, base_url=base_url)
2007
+
2008
+ cdef dict _DEFAULT_NSMAP = {
2009
+ "py" : PYTYPE_NAMESPACE,
2010
+ "xsi" : XML_SCHEMA_INSTANCE_NS,
2011
+ "xsd" : XML_SCHEMA_NS
2012
+ }
2013
+
2014
+ E = ElementMaker()
2015
+
2016
+ def Element(_tag, attrib=None, nsmap=None, *, _pytype=None, **_attributes):
2017
+ """Element(_tag, attrib=None, nsmap=None, _pytype=None, **_attributes)
2018
+
2019
+ Objectify specific version of the lxml.etree Element() factory that
2020
+ always creates a structural (tree) element.
2021
+
2022
+ NOTE: requires parser based element class lookup activated in lxml.etree!
2023
+ """
2024
+ if attrib is not None:
2025
+ if _attributes:
2026
+ attrib = dict(attrib)
2027
+ attrib.update(_attributes)
2028
+ _attributes = attrib
2029
+ if _pytype is None:
2030
+ _pytype = TREE_PYTYPE_NAME
2031
+ if nsmap is None:
2032
+ nsmap = _DEFAULT_NSMAP
2033
+ _attributes[PYTYPE_ATTRIBUTE] = _pytype
2034
+ return _makeElement(_tag, None, _attributes, nsmap)
2035
+
2036
+ def DataElement(_value, attrib=None, nsmap=None, *, _pytype=None, _xsi=None,
2037
+ **_attributes):
2038
+ """DataElement(_value, attrib=None, nsmap=None, _pytype=None, _xsi=None, **_attributes)
2039
+
2040
+ Create a new element from a Python value and XML attributes taken from
2041
+ keyword arguments or a dictionary passed as second argument.
2042
+
2043
+ Automatically adds a 'pytype' attribute for the Python type of the value,
2044
+ if the type can be identified. If '_pytype' or '_xsi' are among the
2045
+ keyword arguments, they will be used instead.
2046
+
2047
+ If the _value argument is an ObjectifiedDataElement instance, its py:pytype,
2048
+ xsi:type and other attributes and nsmap are reused unless they are redefined
2049
+ in attrib and/or keyword arguments.
2050
+ """
2051
+ if nsmap is None:
2052
+ nsmap = _DEFAULT_NSMAP
2053
+ if attrib is not None and attrib:
2054
+ if _attributes:
2055
+ attrib = dict(attrib)
2056
+ attrib.update(_attributes)
2057
+ _attributes = attrib
2058
+ if isinstance(_value, ObjectifiedElement):
2059
+ if _pytype is None:
2060
+ if _xsi is None and not _attributes and nsmap is _DEFAULT_NSMAP:
2061
+ # special case: no change!
2062
+ return _value.__copy__()
2063
+ if isinstance(_value, ObjectifiedDataElement):
2064
+ # reuse existing nsmap unless redefined in nsmap parameter
2065
+ temp = _value.nsmap
2066
+ if temp is not None and temp:
2067
+ temp = dict(temp)
2068
+ temp.update(nsmap)
2069
+ nsmap = temp
2070
+ # reuse existing attributes unless redefined in attrib/_attributes
2071
+ temp = _value.attrib
2072
+ if temp is not None and temp:
2073
+ temp = dict(temp)
2074
+ temp.update(_attributes)
2075
+ _attributes = temp
2076
+ # reuse existing xsi:type or py:pytype attributes, unless provided as
2077
+ # arguments
2078
+ if _xsi is None and _pytype is None:
2079
+ _xsi = _attributes.get(XML_SCHEMA_INSTANCE_TYPE_ATTR)
2080
+ _pytype = _attributes.get(PYTYPE_ATTRIBUTE)
2081
+
2082
+ if _xsi is not None:
2083
+ if ':' in _xsi:
2084
+ prefix, name = _xsi.split(':', 1)
2085
+ ns = nsmap.get(prefix)
2086
+ if ns != XML_SCHEMA_NS:
2087
+ raise ValueError, "XSD types require the XSD namespace"
2088
+ elif nsmap is _DEFAULT_NSMAP:
2089
+ name = _xsi
2090
+ _xsi = 'xsd:' + _xsi
2091
+ else:
2092
+ name = _xsi
2093
+ for prefix, ns in nsmap.items():
2094
+ if ns == XML_SCHEMA_NS:
2095
+ if prefix is not None and prefix:
2096
+ _xsi = prefix + ':' + _xsi
2097
+ break
2098
+ else:
2099
+ raise ValueError, "XSD types require the XSD namespace"
2100
+ _attributes[XML_SCHEMA_INSTANCE_TYPE_ATTR] = _xsi
2101
+ if _pytype is None:
2102
+ # allow using unregistered or even wrong xsi:type names
2103
+ py_type = <PyType>_SCHEMA_TYPE_DICT.get(_xsi)
2104
+ if py_type is None:
2105
+ py_type = <PyType>_SCHEMA_TYPE_DICT.get(name)
2106
+ if py_type is not None:
2107
+ _pytype = py_type.name
2108
+
2109
+ if _pytype is None:
2110
+ _pytype = _pytypename(_value)
2111
+
2112
+ if _value is None and _pytype != "str":
2113
+ _pytype = _pytype or "NoneType"
2114
+ strval = None
2115
+ elif python._isString(_value):
2116
+ strval = _value
2117
+ elif isinstance(_value, bool):
2118
+ if _value:
2119
+ strval = "true"
2120
+ else:
2121
+ strval = "false"
2122
+ else:
2123
+ py_type = <PyType>_PYTYPE_DICT.get(_pytype)
2124
+ stringify = unicode if py_type is None else py_type.stringify
2125
+ strval = stringify(_value)
2126
+
2127
+ if _pytype is not None:
2128
+ if _pytype == "NoneType" or _pytype == "none":
2129
+ strval = None
2130
+ _attributes[XML_SCHEMA_INSTANCE_NIL_ATTR] = "true"
2131
+ else:
2132
+ # check if type information from arguments is valid
2133
+ py_type = <PyType>_PYTYPE_DICT.get(_pytype)
2134
+ if py_type is not None:
2135
+ if py_type.type_check is not None:
2136
+ py_type.type_check(strval)
2137
+ _attributes[PYTYPE_ATTRIBUTE] = _pytype
2138
+
2139
+ return _makeElement("value", strval, _attributes, nsmap)
2140
+
2141
+
2142
+ ################################################################################
2143
+ # ObjectPath
2144
+
2145
+ include "objectpath.pxi"