lxml 6.0.0__cp310-cp310-win_arm64.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 (177) hide show
  1. lxml/ElementInclude.py +244 -0
  2. lxml/__init__.py +22 -0
  3. lxml/_elementpath.cp310-win_arm64.pyd +0 -0
  4. lxml/_elementpath.py +343 -0
  5. lxml/apihelpers.pxi +1801 -0
  6. lxml/builder.cp310-win_arm64.pyd +0 -0
  7. lxml/builder.py +243 -0
  8. lxml/classlookup.pxi +580 -0
  9. lxml/cleanup.pxi +215 -0
  10. lxml/cssselect.py +101 -0
  11. lxml/debug.pxi +36 -0
  12. lxml/docloader.pxi +178 -0
  13. lxml/doctestcompare.py +488 -0
  14. lxml/dtd.pxi +479 -0
  15. lxml/etree.cp310-win_arm64.pyd +0 -0
  16. lxml/etree.h +244 -0
  17. lxml/etree.pyx +3853 -0
  18. lxml/etree_api.h +204 -0
  19. lxml/extensions.pxi +830 -0
  20. lxml/html/ElementSoup.py +10 -0
  21. lxml/html/__init__.py +1927 -0
  22. lxml/html/_diffcommand.py +86 -0
  23. lxml/html/_difflib.cp310-win_arm64.pyd +0 -0
  24. lxml/html/_difflib.py +2106 -0
  25. lxml/html/_html5builder.py +100 -0
  26. lxml/html/_setmixin.py +56 -0
  27. lxml/html/builder.py +173 -0
  28. lxml/html/clean.py +21 -0
  29. lxml/html/defs.py +135 -0
  30. lxml/html/diff.cp310-win_arm64.pyd +0 -0
  31. lxml/html/diff.py +972 -0
  32. lxml/html/formfill.py +299 -0
  33. lxml/html/html5parser.py +260 -0
  34. lxml/html/soupparser.py +314 -0
  35. lxml/html/usedoctest.py +13 -0
  36. lxml/includes/__init__.pxd +0 -0
  37. lxml/includes/__init__.py +0 -0
  38. lxml/includes/c14n.pxd +25 -0
  39. lxml/includes/config.pxd +3 -0
  40. lxml/includes/dtdvalid.pxd +18 -0
  41. lxml/includes/etree_defs.h +379 -0
  42. lxml/includes/etreepublic.pxd +237 -0
  43. lxml/includes/extlibs/__init__.py +0 -0
  44. lxml/includes/extlibs/zconf.h +543 -0
  45. lxml/includes/extlibs/zlib.h +1938 -0
  46. lxml/includes/htmlparser.pxd +56 -0
  47. lxml/includes/libexslt/__init__.py +0 -0
  48. lxml/includes/libexslt/exslt.h +108 -0
  49. lxml/includes/libexslt/exsltconfig.h +70 -0
  50. lxml/includes/libexslt/exsltexports.h +63 -0
  51. lxml/includes/libexslt/libexslt.h +29 -0
  52. lxml/includes/libxml/HTMLparser.h +320 -0
  53. lxml/includes/libxml/HTMLtree.h +147 -0
  54. lxml/includes/libxml/SAX.h +204 -0
  55. lxml/includes/libxml/SAX2.h +173 -0
  56. lxml/includes/libxml/__init__.py +0 -0
  57. lxml/includes/libxml/c14n.h +128 -0
  58. lxml/includes/libxml/catalog.h +182 -0
  59. lxml/includes/libxml/chvalid.h +230 -0
  60. lxml/includes/libxml/debugXML.h +217 -0
  61. lxml/includes/libxml/dict.h +81 -0
  62. lxml/includes/libxml/encoding.h +233 -0
  63. lxml/includes/libxml/entities.h +151 -0
  64. lxml/includes/libxml/globals.h +529 -0
  65. lxml/includes/libxml/hash.h +236 -0
  66. lxml/includes/libxml/list.h +137 -0
  67. lxml/includes/libxml/nanoftp.h +186 -0
  68. lxml/includes/libxml/nanohttp.h +81 -0
  69. lxml/includes/libxml/parser.h +1265 -0
  70. lxml/includes/libxml/parserInternals.h +662 -0
  71. lxml/includes/libxml/pattern.h +100 -0
  72. lxml/includes/libxml/relaxng.h +218 -0
  73. lxml/includes/libxml/schemasInternals.h +958 -0
  74. lxml/includes/libxml/schematron.h +142 -0
  75. lxml/includes/libxml/threads.h +94 -0
  76. lxml/includes/libxml/tree.h +1314 -0
  77. lxml/includes/libxml/uri.h +94 -0
  78. lxml/includes/libxml/valid.h +448 -0
  79. lxml/includes/libxml/xinclude.h +129 -0
  80. lxml/includes/libxml/xlink.h +189 -0
  81. lxml/includes/libxml/xmlIO.h +369 -0
  82. lxml/includes/libxml/xmlautomata.h +146 -0
  83. lxml/includes/libxml/xmlerror.h +919 -0
  84. lxml/includes/libxml/xmlexports.h +50 -0
  85. lxml/includes/libxml/xmlmemory.h +228 -0
  86. lxml/includes/libxml/xmlmodule.h +57 -0
  87. lxml/includes/libxml/xmlreader.h +428 -0
  88. lxml/includes/libxml/xmlregexp.h +222 -0
  89. lxml/includes/libxml/xmlsave.h +88 -0
  90. lxml/includes/libxml/xmlschemas.h +246 -0
  91. lxml/includes/libxml/xmlschemastypes.h +152 -0
  92. lxml/includes/libxml/xmlstring.h +140 -0
  93. lxml/includes/libxml/xmlunicode.h +202 -0
  94. lxml/includes/libxml/xmlversion.h +526 -0
  95. lxml/includes/libxml/xmlwriter.h +488 -0
  96. lxml/includes/libxml/xpath.h +575 -0
  97. lxml/includes/libxml/xpathInternals.h +632 -0
  98. lxml/includes/libxml/xpointer.h +137 -0
  99. lxml/includes/libxslt/__init__.py +0 -0
  100. lxml/includes/libxslt/attributes.h +39 -0
  101. lxml/includes/libxslt/documents.h +93 -0
  102. lxml/includes/libxslt/extensions.h +262 -0
  103. lxml/includes/libxslt/extra.h +72 -0
  104. lxml/includes/libxslt/functions.h +78 -0
  105. lxml/includes/libxslt/imports.h +75 -0
  106. lxml/includes/libxslt/keys.h +53 -0
  107. lxml/includes/libxslt/libxslt.h +36 -0
  108. lxml/includes/libxslt/namespaces.h +68 -0
  109. lxml/includes/libxslt/numbersInternals.h +73 -0
  110. lxml/includes/libxslt/preproc.h +43 -0
  111. lxml/includes/libxslt/security.h +104 -0
  112. lxml/includes/libxslt/templates.h +77 -0
  113. lxml/includes/libxslt/transform.h +207 -0
  114. lxml/includes/libxslt/trio.h +216 -0
  115. lxml/includes/libxslt/triodef.h +220 -0
  116. lxml/includes/libxslt/variables.h +118 -0
  117. lxml/includes/libxslt/win32config.h +51 -0
  118. lxml/includes/libxslt/xslt.h +110 -0
  119. lxml/includes/libxslt/xsltInternals.h +1992 -0
  120. lxml/includes/libxslt/xsltconfig.h +179 -0
  121. lxml/includes/libxslt/xsltexports.h +64 -0
  122. lxml/includes/libxslt/xsltlocale.h +44 -0
  123. lxml/includes/libxslt/xsltutils.h +343 -0
  124. lxml/includes/lxml-version.h +3 -0
  125. lxml/includes/relaxng.pxd +64 -0
  126. lxml/includes/schematron.pxd +34 -0
  127. lxml/includes/tree.pxd +492 -0
  128. lxml/includes/uri.pxd +5 -0
  129. lxml/includes/xinclude.pxd +22 -0
  130. lxml/includes/xmlerror.pxd +852 -0
  131. lxml/includes/xmlparser.pxd +303 -0
  132. lxml/includes/xmlschema.pxd +35 -0
  133. lxml/includes/xpath.pxd +136 -0
  134. lxml/includes/xslt.pxd +190 -0
  135. lxml/isoschematron/__init__.py +348 -0
  136. lxml/isoschematron/resources/rng/iso-schematron.rng +709 -0
  137. lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl +75 -0
  138. lxml/isoschematron/resources/xsl/XSD2Schtrn.xsl +77 -0
  139. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl +313 -0
  140. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl +1160 -0
  141. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl +55 -0
  142. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl +1796 -0
  143. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl +588 -0
  144. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/readme.txt +84 -0
  145. lxml/iterparse.pxi +438 -0
  146. lxml/lxml.etree.h +244 -0
  147. lxml/lxml.etree_api.h +204 -0
  148. lxml/nsclasses.pxi +281 -0
  149. lxml/objectify.cp310-win_arm64.pyd +0 -0
  150. lxml/objectify.pyx +2149 -0
  151. lxml/objectpath.pxi +332 -0
  152. lxml/parser.pxi +2059 -0
  153. lxml/parsertarget.pxi +180 -0
  154. lxml/proxy.pxi +619 -0
  155. lxml/public-api.pxi +178 -0
  156. lxml/pyclasslookup.py +3 -0
  157. lxml/readonlytree.pxi +565 -0
  158. lxml/relaxng.pxi +165 -0
  159. lxml/sax.cp310-win_arm64.pyd +0 -0
  160. lxml/sax.py +286 -0
  161. lxml/saxparser.pxi +875 -0
  162. lxml/schematron.pxi +173 -0
  163. lxml/serializer.pxi +1849 -0
  164. lxml/usedoctest.py +13 -0
  165. lxml/xinclude.pxi +67 -0
  166. lxml/xmlerror.pxi +1654 -0
  167. lxml/xmlid.pxi +179 -0
  168. lxml/xmlschema.pxi +215 -0
  169. lxml/xpath.pxi +487 -0
  170. lxml/xslt.pxi +957 -0
  171. lxml/xsltext.pxi +242 -0
  172. lxml-6.0.0.dist-info/METADATA +163 -0
  173. lxml-6.0.0.dist-info/RECORD +177 -0
  174. lxml-6.0.0.dist-info/WHEEL +5 -0
  175. lxml-6.0.0.dist-info/licenses/LICENSE.txt +31 -0
  176. lxml-6.0.0.dist-info/licenses/LICENSES.txt +29 -0
  177. lxml-6.0.0.dist-info/top_level.txt +1 -0
lxml/etree.pyx ADDED
@@ -0,0 +1,3853 @@
1
+ # cython: binding=True
2
+ # cython: auto_pickle=False
3
+ # cython: language_level=3
4
+
5
+ """
6
+ The ``lxml.etree`` module implements the extended ElementTree API for XML.
7
+ """
8
+
9
+ __docformat__ = "restructuredtext en"
10
+
11
+ __all__ = [
12
+ 'AttributeBasedElementClassLookup', 'C14NError', 'C14NWriterTarget', 'CDATA',
13
+ 'Comment', 'CommentBase', 'CustomElementClassLookup', 'DEBUG',
14
+ 'DTD', 'DTDError', 'DTDParseError', 'DTDValidateError',
15
+ 'DocumentInvalid', 'ETCompatXMLParser', 'ETXPath', 'Element',
16
+ 'ElementBase', 'ElementClassLookup', 'ElementDefaultClassLookup',
17
+ 'ElementNamespaceClassLookup', 'ElementTree', 'Entity', 'EntityBase',
18
+ 'Error', 'ErrorDomains', 'ErrorLevels', 'ErrorTypes', 'Extension',
19
+ 'FallbackElementClassLookup', 'FunctionNamespace', 'HTML', 'HTMLParser',
20
+ 'ICONV_COMPILED_VERSION',
21
+ 'LIBXML_COMPILED_VERSION', 'LIBXML_VERSION',
22
+ 'LIBXML_FEATURES',
23
+ 'LIBXSLT_COMPILED_VERSION', 'LIBXSLT_VERSION',
24
+ 'LXML_VERSION',
25
+ 'LxmlError', 'LxmlRegistryError', 'LxmlSyntaxError',
26
+ 'NamespaceRegistryError', 'PI', 'PIBase', 'ParseError',
27
+ 'ParserBasedElementClassLookup', 'ParserError', 'ProcessingInstruction',
28
+ 'PyErrorLog', 'PythonElementClassLookup', 'QName', 'RelaxNG',
29
+ 'RelaxNGError', 'RelaxNGErrorTypes', 'RelaxNGParseError',
30
+ 'RelaxNGValidateError', 'Resolver', 'Schematron', 'SchematronError',
31
+ 'SchematronParseError', 'SchematronValidateError', 'SerialisationError',
32
+ 'SubElement', 'TreeBuilder', 'XInclude', 'XIncludeError', 'XML',
33
+ 'XMLDTDID', 'XMLID', 'XMLParser', 'XMLSchema', 'XMLSchemaError',
34
+ 'XMLSchemaParseError', 'XMLSchemaValidateError', 'XMLSyntaxError',
35
+ 'XMLTreeBuilder', 'XPath', 'XPathDocumentEvaluator', 'XPathError',
36
+ 'XPathEvalError', 'XPathEvaluator', 'XPathFunctionError', 'XPathResultError',
37
+ 'XPathSyntaxError', 'XSLT', 'XSLTAccessControl', 'XSLTApplyError',
38
+ 'XSLTError', 'XSLTExtension', 'XSLTExtensionError', 'XSLTParseError',
39
+ 'XSLTSaveError', 'canonicalize',
40
+ 'cleanup_namespaces', 'clear_error_log', 'dump',
41
+ 'fromstring', 'fromstringlist', 'get_default_parser', 'iselement',
42
+ 'iterparse', 'iterwalk', 'parse', 'parseid', 'register_namespace',
43
+ 'set_default_parser', 'set_element_class_lookup', 'strip_attributes',
44
+ 'strip_elements', 'strip_tags', 'tostring', 'tostringlist', 'tounicode',
45
+ 'use_global_python_log'
46
+ ]
47
+
48
+ cimport cython
49
+
50
+ from lxml cimport python
51
+ from lxml.includes cimport tree, config
52
+ from lxml.includes.tree cimport xmlDoc, xmlNode, xmlAttr, xmlNs, _isElement, _getNs
53
+ from lxml.includes.tree cimport const_xmlChar, xmlChar, _xcstr
54
+ from lxml.python cimport _cstr, _isString
55
+ from lxml.includes cimport xpath
56
+ from lxml.includes cimport c14n
57
+
58
+ # Cython's standard declarations
59
+ cimport cpython.mem
60
+ cimport cpython.ref
61
+ from libc cimport limits, stdio, stdlib
62
+ from libc cimport string as cstring_h # not to be confused with stdlib 'string'
63
+ from libc.string cimport const_char
64
+
65
+ cdef object os_path_abspath
66
+ from os.path import abspath as os_path_abspath
67
+
68
+ cdef object BytesIO, StringIO
69
+ from io import BytesIO, StringIO
70
+
71
+ cdef object OrderedDict
72
+ from collections import OrderedDict
73
+
74
+ cdef object _elementpath
75
+ from lxml import _elementpath
76
+
77
+ cdef object sys
78
+ import sys
79
+
80
+ cdef object re
81
+ import re
82
+
83
+ cdef object partial
84
+ from functools import partial
85
+
86
+ cdef object islice
87
+ from itertools import islice
88
+
89
+ cdef object ITER_EMPTY = iter(())
90
+
91
+ cdef object MutableMapping
92
+ from collections.abc import MutableMapping
93
+
94
+ class _ImmutableMapping(MutableMapping):
95
+ def __getitem__(self, key):
96
+ raise KeyError, key
97
+
98
+ def __setitem__(self, key, value):
99
+ raise KeyError, key
100
+
101
+ def __delitem__(self, key):
102
+ raise KeyError, key
103
+
104
+ def __contains__(self, key):
105
+ return False
106
+
107
+ def __len__(self):
108
+ return 0
109
+
110
+ def __iter__(self):
111
+ return ITER_EMPTY
112
+ iterkeys = itervalues = iteritems = __iter__
113
+
114
+ cdef object IMMUTABLE_EMPTY_MAPPING = _ImmutableMapping()
115
+ del _ImmutableMapping
116
+
117
+
118
+ # the rules
119
+ # ---------
120
+ # any libxml C argument/variable is prefixed with c_
121
+ # any non-public function/class is prefixed with an underscore
122
+ # instance creation is always through factories
123
+
124
+ # what to do with libxml2/libxslt error messages?
125
+ # 0 : drop
126
+ # 1 : use log
127
+ DEF __DEBUG = 1
128
+
129
+ # maximum number of lines in the libxml2/xslt log if __DEBUG == 1
130
+ DEF __MAX_LOG_SIZE = 100
131
+
132
+ # make the compiled-in debug state publicly available
133
+ DEBUG = __DEBUG
134
+
135
+ # A struct to store a cached qualified tag name+href pair.
136
+ # While we can borrow the c_name from the document dict,
137
+ # PyPy requires us to store a Python reference for the
138
+ # namespace in order to keep the byte buffer alive.
139
+ cdef struct qname:
140
+ const_xmlChar* c_name
141
+ python.PyObject* href
142
+
143
+ # initialize parser (and threading)
144
+ xmlparser.xmlInitParser()
145
+
146
+ # global per-thread setup
147
+ tree.xmlThrDefIndentTreeOutput(1)
148
+ tree.xmlThrDefLineNumbersDefaultValue(1)
149
+
150
+ _initThreadLogging()
151
+
152
+ # filename encoding
153
+ cdef bytes _FILENAME_ENCODING = (sys.getfilesystemencoding() or sys.getdefaultencoding() or 'ascii').encode("UTF-8")
154
+ cdef char* _C_FILENAME_ENCODING = _cstr(_FILENAME_ENCODING)
155
+
156
+ # set up some default namespace prefixes
157
+ cdef dict _DEFAULT_NAMESPACE_PREFIXES = {
158
+ b"http://www.w3.org/XML/1998/namespace": b'xml',
159
+ b"http://www.w3.org/1999/xhtml": b"html",
160
+ b"http://www.w3.org/1999/XSL/Transform": b"xsl",
161
+ b"http://www.w3.org/1999/02/22-rdf-syntax-ns#": b"rdf",
162
+ b"http://schemas.xmlsoap.org/wsdl/": b"wsdl",
163
+ # xml schema
164
+ b"http://www.w3.org/2001/XMLSchema": b"xs",
165
+ b"http://www.w3.org/2001/XMLSchema-instance": b"xsi",
166
+ # dublin core
167
+ b"http://purl.org/dc/elements/1.1/": b"dc",
168
+ # objectify
169
+ b"http://codespeak.net/lxml/objectify/pytype" : b"py",
170
+ }
171
+
172
+ # To avoid runtime encoding overhead, we keep a Unicode copy
173
+ # of the uri-prefix mapping as (str, str) items view.
174
+ cdef object _DEFAULT_NAMESPACE_PREFIXES_ITEMS = []
175
+
176
+ cdef _update_default_namespace_prefixes_items():
177
+ cdef bytes ns, prefix
178
+ global _DEFAULT_NAMESPACE_PREFIXES_ITEMS
179
+ _DEFAULT_NAMESPACE_PREFIXES_ITEMS = {
180
+ ns.decode('utf-8') : prefix.decode('utf-8')
181
+ for ns, prefix in _DEFAULT_NAMESPACE_PREFIXES.items()
182
+ }.items()
183
+
184
+ _update_default_namespace_prefixes_items()
185
+
186
+ cdef object _check_internal_prefix = re.compile(br"ns\d+$").match
187
+
188
+ def register_namespace(prefix, uri):
189
+ """Registers a namespace prefix that newly created Elements in that
190
+ namespace will use. The registry is global, and any existing
191
+ mapping for either the given prefix or the namespace URI will be
192
+ removed.
193
+ """
194
+ prefix_utf, uri_utf = _utf8(prefix), _utf8(uri)
195
+ if _check_internal_prefix(prefix_utf):
196
+ raise ValueError("Prefix format reserved for internal use")
197
+ _tagValidOrRaise(prefix_utf)
198
+ _uriValidOrRaise(uri_utf)
199
+ if (uri_utf == b"http://www.w3.org/XML/1998/namespace" and prefix_utf != b'xml'
200
+ or prefix_utf == b'xml' and uri_utf != b"http://www.w3.org/XML/1998/namespace"):
201
+ raise ValueError("Cannot change the 'xml' prefix of the XML namespace")
202
+ for k, v in list(_DEFAULT_NAMESPACE_PREFIXES.items()):
203
+ if k == uri_utf or v == prefix_utf:
204
+ del _DEFAULT_NAMESPACE_PREFIXES[k]
205
+ _DEFAULT_NAMESPACE_PREFIXES[uri_utf] = prefix_utf
206
+ _update_default_namespace_prefixes_items()
207
+
208
+
209
+ # Error superclass for ElementTree compatibility
210
+ cdef class Error(Exception):
211
+ pass
212
+
213
+ # module level superclass for all exceptions
214
+ cdef class LxmlError(Error):
215
+ """Main exception base class for lxml. All other exceptions inherit from
216
+ this one.
217
+ """
218
+ def __init__(self, message, error_log=None):
219
+ super(_Error, self).__init__(message)
220
+ if error_log is None:
221
+ self.error_log = __copyGlobalErrorLog()
222
+ else:
223
+ self.error_log = error_log.copy()
224
+
225
+ cdef object _Error = Error
226
+
227
+
228
+ # superclass for all syntax errors
229
+ class LxmlSyntaxError(LxmlError, SyntaxError):
230
+ """Base class for all syntax errors.
231
+ """
232
+
233
+ cdef class C14NError(LxmlError):
234
+ """Error during C14N serialisation.
235
+ """
236
+
237
+ # version information
238
+ cdef tuple __unpackDottedVersion(version):
239
+ version_list = []
240
+ l = (version.decode("ascii").replace('-', '.').split('.') + [0]*4)[:4]
241
+ for item in l:
242
+ try:
243
+ item = int(item)
244
+ except ValueError:
245
+ if item.startswith('dev'):
246
+ count = item[3:]
247
+ item = -300
248
+ elif item.startswith('alpha'):
249
+ count = item[5:]
250
+ item = -200
251
+ elif item.startswith('beta'):
252
+ count = item[4:]
253
+ item = -100
254
+ else:
255
+ count = 0
256
+ if count:
257
+ item += int(count)
258
+ version_list.append(item)
259
+ return tuple(version_list)
260
+
261
+ cdef tuple __unpackIntVersion(int c_version, int base=100):
262
+ return (
263
+ ((c_version // (base*base)) % base),
264
+ ((c_version // base) % base),
265
+ (c_version % base)
266
+ )
267
+
268
+ cdef int _LIBXML_VERSION_INT
269
+ try:
270
+ _LIBXML_VERSION_INT = int(
271
+ re.match('[0-9]+', (<unsigned char*>tree.xmlParserVersion).decode("ascii")).group(0))
272
+ except Exception:
273
+ print("Unknown libxml2 version: " + (<unsigned char*>tree.xmlParserVersion).decode("latin1"))
274
+ _LIBXML_VERSION_INT = 0
275
+
276
+ LIBXML_VERSION = __unpackIntVersion(_LIBXML_VERSION_INT)
277
+ LIBXML_COMPILED_VERSION = __unpackIntVersion(tree.LIBXML_VERSION)
278
+ LXML_VERSION = __unpackDottedVersion(tree.LXML_VERSION_STRING)
279
+
280
+ __version__ = tree.LXML_VERSION_STRING.decode("ascii")
281
+
282
+ cdef extern from *:
283
+ """
284
+ #ifdef ZLIB_VERNUM
285
+ #define __lxml_zlib_version (ZLIB_VERNUM >> 4)
286
+ #else
287
+ #define __lxml_zlib_version 0
288
+ #endif
289
+ #ifdef _LIBICONV_VERSION
290
+ #define __lxml_iconv_version (_LIBICONV_VERSION << 8)
291
+ #else
292
+ #define __lxml_iconv_version 0
293
+ #endif
294
+ """
295
+ # zlib isn't included automatically by libxml2's headers
296
+ #long ZLIB_HEX_VERSION "__lxml_zlib_version"
297
+ long LIBICONV_HEX_VERSION "__lxml_iconv_version"
298
+
299
+ #ZLIB_COMPILED_VERSION = __unpackIntVersion(ZLIB_HEX_VERSION, base=0x10)
300
+ ICONV_COMPILED_VERSION = __unpackIntVersion(LIBICONV_HEX_VERSION, base=0x100)[:2]
301
+
302
+
303
+ cdef extern from "libxml/xmlversion.h":
304
+ """
305
+ static const char* const _lxml_lib_features[] = {
306
+ #ifdef LIBXML_HTML_ENABLED
307
+ "html",
308
+ #endif
309
+ #ifdef LIBXML_FTP_ENABLED
310
+ "ftp",
311
+ #endif
312
+ #ifdef LIBXML_HTTP_ENABLED
313
+ "http",
314
+ #endif
315
+ #ifdef LIBXML_CATALOG_ENABLED
316
+ "catalog",
317
+ #endif
318
+ #ifdef LIBXML_XPATH_ENABLED
319
+ "xpath",
320
+ #endif
321
+ #ifdef LIBXML_ICONV_ENABLED
322
+ "iconv",
323
+ #endif
324
+ #ifdef LIBXML_ICU_ENABLED
325
+ "icu",
326
+ #endif
327
+ #ifdef LIBXML_REGEXP_ENABLED
328
+ "regexp",
329
+ #endif
330
+ #ifdef LIBXML_SCHEMAS_ENABLED
331
+ "xmlschema",
332
+ #endif
333
+ #ifdef LIBXML_SCHEMATRON_ENABLED
334
+ "schematron",
335
+ #endif
336
+ #ifdef LIBXML_ZLIB_ENABLED
337
+ "zlib",
338
+ #endif
339
+ #ifdef LIBXML_LZMA_ENABLED
340
+ "lzma",
341
+ #endif
342
+ 0
343
+ };
344
+ """
345
+ const char* const* _LXML_LIB_FEATURES "_lxml_lib_features"
346
+
347
+
348
+ cdef set _copy_lib_features():
349
+ features = set()
350
+ feature = _LXML_LIB_FEATURES
351
+ while feature[0]:
352
+ features.add(feature[0].decode('ASCII'))
353
+ feature += 1
354
+ return features
355
+
356
+ LIBXML_COMPILED_FEATURES = _copy_lib_features()
357
+ LIBXML_FEATURES = {
358
+ feature_name for feature_id, feature_name in [
359
+ #XML_WITH_THREAD = 1
360
+ #XML_WITH_TREE = 2
361
+ #XML_WITH_OUTPUT = 3
362
+ #XML_WITH_PUSH = 4
363
+ #XML_WITH_READER = 5
364
+ #XML_WITH_PATTERN = 6
365
+ #XML_WITH_WRITER = 7
366
+ #XML_WITH_SAX1 = 8
367
+ (xmlparser.XML_WITH_FTP, "ftp"), # XML_WITH_FTP = 9
368
+ (xmlparser.XML_WITH_HTTP, "http"), # XML_WITH_HTTP = 10
369
+ #XML_WITH_VALID = 11
370
+ (xmlparser.XML_WITH_HTML, "html"), # XML_WITH_HTML = 12
371
+ #XML_WITH_LEGACY = 13
372
+ #XML_WITH_C14N = 14
373
+ (xmlparser.XML_WITH_CATALOG, "catalog"), # XML_WITH_CATALOG = 15
374
+ (xmlparser.XML_WITH_XPATH, "xpath"), # XML_WITH_XPATH = 16
375
+ #XML_WITH_XPTR = 17
376
+ #XML_WITH_XINCLUDE = 18
377
+ (xmlparser.XML_WITH_ICONV, "iconv"), # XML_WITH_ICONV = 19
378
+ #XML_WITH_ISO8859X = 20
379
+ #XML_WITH_UNICODE = 21
380
+ (xmlparser.XML_WITH_REGEXP, "regexp"), # XML_WITH_REGEXP = 22
381
+ #XML_WITH_AUTOMATA = 23
382
+ #XML_WITH_EXPR = 24
383
+ (xmlparser.XML_WITH_SCHEMAS, "xmlschema"), # XML_WITH_SCHEMAS = 25
384
+ (xmlparser.XML_WITH_SCHEMATRON, "schematron"), # XML_WITH_SCHEMATRON = 26
385
+ #XML_WITH_MODULES = 27
386
+ #XML_WITH_DEBUG = 28
387
+ #XML_WITH_DEBUG_MEM = 29
388
+ #XML_WITH_DEBUG_RUN = 30 # unused
389
+ (xmlparser.XML_WITH_ZLIB, "zlib"), # XML_WITH_ZLIB = 31
390
+ (xmlparser.XML_WITH_ICU, "icu"), # XML_WITH_ICU = 32
391
+ (xmlparser.XML_WITH_LZMA, "lzma"), # XML_WITH_LZMA = 33
392
+ ] if xmlparser.xmlHasFeature(feature_id)
393
+ }
394
+
395
+ cdef bint HAS_ZLIB_COMPRESSION = xmlparser.xmlHasFeature(xmlparser.XML_WITH_ZLIB)
396
+
397
+
398
+ # class for temporary storage of Python references,
399
+ # used e.g. for XPath results
400
+ @cython.final
401
+ @cython.internal
402
+ cdef class _TempStore:
403
+ cdef list _storage
404
+ def __init__(self):
405
+ self._storage = []
406
+
407
+ cdef int add(self, obj) except -1:
408
+ self._storage.append(obj)
409
+ return 0
410
+
411
+ cdef int clear(self) except -1:
412
+ del self._storage[:]
413
+ return 0
414
+
415
+
416
+ # class for temporarily storing exceptions raised in extensions
417
+ @cython.internal
418
+ cdef class _ExceptionContext:
419
+ cdef object _exc_info
420
+ cdef int clear(self) except -1:
421
+ self._exc_info = None
422
+ return 0
423
+
424
+ cdef void _store_raised(self) noexcept:
425
+ try:
426
+ self._exc_info = sys.exc_info()
427
+ except BaseException as e:
428
+ self._store_exception(e)
429
+ finally:
430
+ return # and swallow any further exceptions
431
+
432
+ cdef int _store_exception(self, exception) except -1:
433
+ self._exc_info = (exception, None, None)
434
+ return 0
435
+
436
+ cdef bint _has_raised(self) except -1:
437
+ return self._exc_info is not None
438
+
439
+ cdef int _raise_if_stored(self) except -1:
440
+ if self._exc_info is None:
441
+ return 0
442
+ type, value, traceback = self._exc_info
443
+ self._exc_info = None
444
+ if value is None and traceback is None:
445
+ raise type
446
+ else:
447
+ raise type, value, traceback
448
+
449
+
450
+ # type of a function that steps from node to node
451
+ ctypedef public xmlNode* (*_node_to_node_function)(xmlNode*)
452
+
453
+
454
+ ################################################################################
455
+ # Include submodules
456
+
457
+ include "proxy.pxi" # Proxy handling (element backpointers/memory/etc.)
458
+ include "apihelpers.pxi" # Private helper functions
459
+ include "xmlerror.pxi" # Error and log handling
460
+
461
+
462
+ ################################################################################
463
+ # Public Python API
464
+
465
+ @cython.final
466
+ @cython.freelist(8)
467
+ cdef public class _Document [ type LxmlDocumentType, object LxmlDocument ]:
468
+ """Internal base class to reference a libxml document.
469
+
470
+ When instances of this class are garbage collected, the libxml
471
+ document is cleaned up.
472
+ """
473
+ cdef int _ns_counter
474
+ cdef bytes _prefix_tail
475
+ cdef xmlDoc* _c_doc
476
+ cdef _BaseParser _parser
477
+
478
+ def __dealloc__(self):
479
+ # if there are no more references to the document, it is safe
480
+ # to clean the whole thing up, as all nodes have a reference to
481
+ # the document
482
+ tree.xmlFreeDoc(self._c_doc)
483
+
484
+ @cython.final
485
+ cdef getroot(self):
486
+ # return an element proxy for the document root
487
+ cdef xmlNode* c_node
488
+ c_node = tree.xmlDocGetRootElement(self._c_doc)
489
+ if c_node is NULL:
490
+ return None
491
+ return _elementFactory(self, c_node)
492
+
493
+ @cython.final
494
+ cdef bint hasdoctype(self) noexcept:
495
+ # DOCTYPE gets parsed into internal subset (xmlDTD*)
496
+ return self._c_doc is not NULL and self._c_doc.intSubset is not NULL
497
+
498
+ @cython.final
499
+ cdef getdoctype(self):
500
+ # get doctype info: root tag, public/system ID (or None if not known)
501
+ cdef tree.xmlDtd* c_dtd
502
+ cdef xmlNode* c_root_node
503
+ public_id = None
504
+ sys_url = None
505
+ c_dtd = self._c_doc.intSubset
506
+ if c_dtd is not NULL:
507
+ if c_dtd.ExternalID is not NULL:
508
+ public_id = funicode(c_dtd.ExternalID)
509
+ if c_dtd.SystemID is not NULL:
510
+ sys_url = funicode(c_dtd.SystemID)
511
+ c_dtd = self._c_doc.extSubset
512
+ if c_dtd is not NULL:
513
+ if not public_id and c_dtd.ExternalID is not NULL:
514
+ public_id = funicode(c_dtd.ExternalID)
515
+ if not sys_url and c_dtd.SystemID is not NULL:
516
+ sys_url = funicode(c_dtd.SystemID)
517
+ c_root_node = tree.xmlDocGetRootElement(self._c_doc)
518
+ if c_root_node is NULL:
519
+ root_name = None
520
+ else:
521
+ root_name = funicode(c_root_node.name)
522
+ return root_name, public_id, sys_url
523
+
524
+ @cython.final
525
+ cdef getxmlinfo(self):
526
+ # return XML version and encoding (or None if not known)
527
+ cdef xmlDoc* c_doc = self._c_doc
528
+ if c_doc.version is NULL:
529
+ version = None
530
+ else:
531
+ version = funicode(c_doc.version)
532
+ if c_doc.encoding is NULL:
533
+ encoding = None
534
+ else:
535
+ encoding = funicode(c_doc.encoding)
536
+ return version, encoding
537
+
538
+ @cython.final
539
+ cdef isstandalone(self):
540
+ # returns True for "standalone=true",
541
+ # False for "standalone=false", None if not provided
542
+ if self._c_doc.standalone == -1:
543
+ return None
544
+ else:
545
+ return <bint>(self._c_doc.standalone == 1)
546
+
547
+ @cython.final
548
+ cdef bytes buildNewPrefix(self):
549
+ # get a new unique prefix ("nsX") for this document
550
+ cdef bytes ns
551
+ if self._ns_counter < len(_PREFIX_CACHE):
552
+ ns = _PREFIX_CACHE[self._ns_counter]
553
+ else:
554
+ ns = python.PyBytes_FromFormat("ns%d", self._ns_counter)
555
+ if self._prefix_tail is not None:
556
+ ns += self._prefix_tail
557
+ self._ns_counter += 1
558
+ if self._ns_counter < 0:
559
+ # overflow!
560
+ self._ns_counter = 0
561
+ if self._prefix_tail is None:
562
+ self._prefix_tail = b"A"
563
+ else:
564
+ self._prefix_tail += b"A"
565
+ return ns
566
+
567
+ @cython.final
568
+ cdef xmlNs* _findOrBuildNodeNs(self, xmlNode* c_node,
569
+ const_xmlChar* c_href, const_xmlChar* c_prefix,
570
+ bint is_attribute) except NULL:
571
+ """Get or create namespace structure for a node. Reuses the prefix if
572
+ possible.
573
+ """
574
+ cdef xmlNs* c_ns
575
+ cdef xmlNs* c_doc_ns
576
+ cdef python.PyObject* dict_result
577
+ if c_node.type != tree.XML_ELEMENT_NODE:
578
+ assert c_node.type == tree.XML_ELEMENT_NODE, \
579
+ "invalid node type %d, expected %d" % (
580
+ c_node.type, tree.XML_ELEMENT_NODE)
581
+ # look for existing ns declaration
582
+ c_ns = _searchNsByHref(c_node, c_href, is_attribute)
583
+ if c_ns is not NULL:
584
+ if is_attribute and c_ns.prefix is NULL:
585
+ # do not put namespaced attributes into the default
586
+ # namespace as this would break serialisation
587
+ pass
588
+ else:
589
+ return c_ns
590
+
591
+ # none found => determine a suitable new prefix
592
+ if c_prefix is NULL:
593
+ dict_result = python.PyDict_GetItem(
594
+ _DEFAULT_NAMESPACE_PREFIXES, <unsigned char*>c_href)
595
+ if dict_result is not NULL:
596
+ prefix = <object>dict_result
597
+ else:
598
+ prefix = self.buildNewPrefix()
599
+ c_prefix = _xcstr(prefix)
600
+
601
+ # make sure the prefix is not in use already
602
+ while tree.xmlSearchNs(self._c_doc, c_node, c_prefix) is not NULL:
603
+ prefix = self.buildNewPrefix()
604
+ c_prefix = _xcstr(prefix)
605
+
606
+ # declare the namespace and return it
607
+ c_ns = tree.xmlNewNs(c_node, c_href, c_prefix)
608
+ if c_ns is NULL:
609
+ raise MemoryError()
610
+ return c_ns
611
+
612
+ @cython.final
613
+ cdef int _setNodeNs(self, xmlNode* c_node, const_xmlChar* c_href) except -1:
614
+ "Lookup namespace structure and set it for the node."
615
+ c_ns = self._findOrBuildNodeNs(c_node, c_href, NULL, 0)
616
+ tree.xmlSetNs(c_node, c_ns)
617
+
618
+
619
+ cdef tuple __initPrefixCache():
620
+ cdef int i
621
+ return tuple([ python.PyBytes_FromFormat("ns%d", i)
622
+ for i in range(26) ])
623
+
624
+ cdef tuple _PREFIX_CACHE = __initPrefixCache()
625
+
626
+
627
+ cdef _Document _documentFactory(xmlDoc* c_doc, _BaseParser parser):
628
+ cdef _Document result
629
+ result = _Document.__new__(_Document)
630
+ result._c_doc = c_doc
631
+ result._ns_counter = 0
632
+ result._prefix_tail = None
633
+ if parser is None:
634
+ parser = __GLOBAL_PARSER_CONTEXT.getDefaultParser()
635
+ result._parser = parser
636
+ return result
637
+
638
+
639
+ cdef object _find_invalid_public_id_characters = re.compile(
640
+ ur"[^\x20\x0D\x0Aa-zA-Z0-9'()+,./:=?;!*#@$_%-]+").search
641
+
642
+
643
+ cdef class DocInfo:
644
+ "Document information provided by parser and DTD."
645
+ cdef _Document _doc
646
+ def __cinit__(self, tree):
647
+ "Create a DocInfo object for an ElementTree object or root Element."
648
+ self._doc = _documentOrRaise(tree)
649
+ root_name, public_id, system_url = self._doc.getdoctype()
650
+ if not root_name and (public_id or system_url):
651
+ raise ValueError, "Could not find root node"
652
+
653
+ @property
654
+ def root_name(self):
655
+ """Returns the name of the root node as defined by the DOCTYPE."""
656
+ root_name, public_id, system_url = self._doc.getdoctype()
657
+ return root_name
658
+
659
+ @cython.final
660
+ cdef tree.xmlDtd* _get_c_dtd(self):
661
+ """"Return the DTD. Create it if it does not yet exist."""
662
+ cdef xmlDoc* c_doc = self._doc._c_doc
663
+ cdef xmlNode* c_root_node
664
+ cdef const_xmlChar* c_name
665
+
666
+ if c_doc.intSubset:
667
+ return c_doc.intSubset
668
+
669
+ c_root_node = tree.xmlDocGetRootElement(c_doc)
670
+ c_name = c_root_node.name if c_root_node else NULL
671
+ return tree.xmlCreateIntSubset(c_doc, c_name, NULL, NULL)
672
+
673
+ def clear(self):
674
+ """Removes DOCTYPE and internal subset from the document."""
675
+ cdef xmlDoc* c_doc = self._doc._c_doc
676
+ cdef tree.xmlNode* c_dtd = <xmlNode*>c_doc.intSubset
677
+ if c_dtd is NULL:
678
+ return
679
+ tree.xmlUnlinkNode(c_dtd)
680
+ tree.xmlFreeNode(c_dtd)
681
+
682
+ property public_id:
683
+ """Public ID of the DOCTYPE.
684
+
685
+ Mutable. May be set to a valid string or None. If a DTD does not
686
+ exist, setting this variable (even to None) will create one.
687
+ """
688
+ def __get__(self):
689
+ root_name, public_id, system_url = self._doc.getdoctype()
690
+ return public_id
691
+
692
+ def __set__(self, value):
693
+ cdef xmlChar* c_value = NULL
694
+ if value is not None:
695
+ match = _find_invalid_public_id_characters(value)
696
+ if match:
697
+ raise ValueError, f'Invalid character(s) {match.group(0)!r} in public_id.'
698
+ value = _utf8(value)
699
+ c_value = tree.xmlStrdup(_xcstr(value))
700
+ if not c_value:
701
+ raise MemoryError()
702
+
703
+ c_dtd = self._get_c_dtd()
704
+ if not c_dtd:
705
+ tree.xmlFree(c_value)
706
+ raise MemoryError()
707
+ if c_dtd.ExternalID:
708
+ tree.xmlFree(<void*>c_dtd.ExternalID)
709
+ c_dtd.ExternalID = c_value
710
+
711
+ property system_url:
712
+ """System ID of the DOCTYPE.
713
+
714
+ Mutable. May be set to a valid string or None. If a DTD does not
715
+ exist, setting this variable (even to None) will create one.
716
+ """
717
+ def __get__(self):
718
+ root_name, public_id, system_url = self._doc.getdoctype()
719
+ return system_url
720
+
721
+ def __set__(self, value):
722
+ cdef xmlChar* c_value = NULL
723
+ if value is not None:
724
+ bvalue = _utf8(value)
725
+ # sys_url may be any valid unicode string that can be
726
+ # enclosed in single quotes or quotes.
727
+ if b"'" in bvalue and b'"' in bvalue:
728
+ raise ValueError(
729
+ 'System URL may not contain both single (\') and double quotes (").')
730
+ c_value = tree.xmlStrdup(_xcstr(bvalue))
731
+ if not c_value:
732
+ raise MemoryError()
733
+
734
+ c_dtd = self._get_c_dtd()
735
+ if not c_dtd:
736
+ tree.xmlFree(c_value)
737
+ raise MemoryError()
738
+ if c_dtd.SystemID:
739
+ tree.xmlFree(<void*>c_dtd.SystemID)
740
+ c_dtd.SystemID = c_value
741
+
742
+ @property
743
+ def xml_version(self):
744
+ """Returns the XML version as declared by the document."""
745
+ xml_version, encoding = self._doc.getxmlinfo()
746
+ return xml_version
747
+
748
+ @property
749
+ def encoding(self):
750
+ """Returns the encoding name as declared by the document."""
751
+ xml_version, encoding = self._doc.getxmlinfo()
752
+ return encoding
753
+
754
+ @property
755
+ def standalone(self):
756
+ """Returns the standalone flag as declared by the document. The possible
757
+ values are True (``standalone='yes'``), False
758
+ (``standalone='no'`` or flag not provided in the declaration),
759
+ and None (unknown or no declaration found). Note that a
760
+ normal truth test on this value will always tell if the
761
+ ``standalone`` flag was set to ``'yes'`` or not.
762
+ """
763
+ return self._doc.isstandalone()
764
+
765
+ property URL:
766
+ "The source URL of the document (or None if unknown)."
767
+ def __get__(self):
768
+ if self._doc._c_doc.URL is NULL:
769
+ return None
770
+ return _decodeFilename(self._doc._c_doc.URL)
771
+ def __set__(self, url):
772
+ url = _encodeFilename(url)
773
+ c_oldurl = self._doc._c_doc.URL
774
+ if url is None:
775
+ self._doc._c_doc.URL = NULL
776
+ else:
777
+ self._doc._c_doc.URL = tree.xmlStrdup(_xcstr(url))
778
+ if c_oldurl is not NULL:
779
+ tree.xmlFree(<void*>c_oldurl)
780
+
781
+ @property
782
+ def doctype(self):
783
+ """Returns a DOCTYPE declaration string for the document."""
784
+ root_name, public_id, system_url = self._doc.getdoctype()
785
+ if system_url:
786
+ # If '"' in system_url, we must escape it with single
787
+ # quotes, otherwise escape with double quotes. If url
788
+ # contains both a single quote and a double quote, XML
789
+ # standard is being violated.
790
+ if '"' in system_url:
791
+ quoted_system_url = f"'{system_url}'"
792
+ else:
793
+ quoted_system_url = f'"{system_url}"'
794
+ if public_id:
795
+ if system_url:
796
+ return f'<!DOCTYPE {root_name} PUBLIC "{public_id}" {quoted_system_url}>'
797
+ else:
798
+ return f'<!DOCTYPE {root_name} PUBLIC "{public_id}">'
799
+ elif system_url:
800
+ return f'<!DOCTYPE {root_name} SYSTEM {quoted_system_url}>'
801
+ elif self._doc.hasdoctype():
802
+ return f'<!DOCTYPE {root_name}>'
803
+ else:
804
+ return ''
805
+
806
+ @property
807
+ def internalDTD(self):
808
+ """Returns a DTD validator based on the internal subset of the document."""
809
+ return _dtdFactory(self._doc._c_doc.intSubset)
810
+
811
+ @property
812
+ def externalDTD(self):
813
+ """Returns a DTD validator based on the external subset of the document."""
814
+ return _dtdFactory(self._doc._c_doc.extSubset)
815
+
816
+
817
+ @cython.no_gc_clear
818
+ cdef public class _Element [ type LxmlElementType, object LxmlElement ]:
819
+ """Element class.
820
+
821
+ References a document object and a libxml node.
822
+
823
+ By pointing to a Document instance, a reference is kept to
824
+ _Document as long as there is some pointer to a node in it.
825
+ """
826
+ cdef _Document _doc
827
+ cdef xmlNode* _c_node
828
+ cdef object _tag
829
+
830
+ def _init(self):
831
+ """_init(self)
832
+
833
+ Called after object initialisation. Custom subclasses may override
834
+ this if they recursively call _init() in the superclasses.
835
+ """
836
+
837
+ @cython.linetrace(False)
838
+ @cython.profile(False)
839
+ def __dealloc__(self):
840
+ #print("trying to free node:", <int>self._c_node)
841
+ #displayNode(self._c_node, 0)
842
+ if self._c_node is not NULL:
843
+ _unregisterProxy(self)
844
+ attemptDeallocation(self._c_node)
845
+
846
+ # MANIPULATORS
847
+
848
+ def __setitem__(self, x, value):
849
+ """__setitem__(self, x, value)
850
+
851
+ Replaces the given subelement index or slice.
852
+ """
853
+ cdef xmlNode* c_node = NULL
854
+ cdef xmlNode* c_next
855
+ cdef xmlDoc* c_source_doc
856
+ cdef _Element element
857
+ cdef bint left_to_right
858
+ cdef Py_ssize_t slicelength = 0, step = 0
859
+ _assertValidNode(self)
860
+ if value is None:
861
+ raise ValueError, "cannot assign None"
862
+ if isinstance(x, slice):
863
+ # slice assignment
864
+ _findChildSlice(<slice>x, self._c_node, &c_node, &step, &slicelength)
865
+ if step > 0:
866
+ left_to_right = 1
867
+ else:
868
+ left_to_right = 0
869
+ step = -step
870
+ _replaceSlice(self, c_node, slicelength, step, left_to_right, value)
871
+ return
872
+ else:
873
+ # otherwise: normal item assignment
874
+ element = value
875
+ _assertValidNode(element)
876
+ c_node = _findChild(self._c_node, x)
877
+ if c_node is NULL:
878
+ raise IndexError, "list index out of range"
879
+ c_source_doc = element._c_node.doc
880
+ c_next = element._c_node.next
881
+ _removeText(c_node.next)
882
+ tree.xmlReplaceNode(c_node, element._c_node)
883
+ _moveTail(c_next, element._c_node)
884
+ moveNodeToDocument(self._doc, c_source_doc, element._c_node)
885
+ if not attemptDeallocation(c_node):
886
+ moveNodeToDocument(self._doc, c_node.doc, c_node)
887
+
888
+ def __delitem__(self, x):
889
+ """__delitem__(self, x)
890
+
891
+ Deletes the given subelement or a slice.
892
+ """
893
+ cdef xmlNode* c_node = NULL
894
+ cdef xmlNode* c_next
895
+ cdef Py_ssize_t step = 0, slicelength = 0
896
+ _assertValidNode(self)
897
+ if isinstance(x, slice):
898
+ # slice deletion
899
+ if _isFullSlice(<slice>x):
900
+ c_node = self._c_node.children
901
+ if c_node is not NULL:
902
+ if not _isElement(c_node):
903
+ c_node = _nextElement(c_node)
904
+ while c_node is not NULL:
905
+ c_next = _nextElement(c_node)
906
+ _removeNode(self._doc, c_node)
907
+ c_node = c_next
908
+ else:
909
+ _findChildSlice(<slice>x, self._c_node, &c_node, &step, &slicelength)
910
+ _deleteSlice(self._doc, c_node, slicelength, step)
911
+ else:
912
+ # item deletion
913
+ c_node = _findChild(self._c_node, x)
914
+ if c_node is NULL:
915
+ raise IndexError, f"index out of range: {x}"
916
+ _removeNode(self._doc, c_node)
917
+
918
+ def __deepcopy__(self, memo):
919
+ "__deepcopy__(self, memo)"
920
+ return self.__copy__()
921
+
922
+ def __copy__(self):
923
+ "__copy__(self)"
924
+ cdef xmlDoc* c_doc
925
+ cdef xmlNode* c_node
926
+ cdef _Document new_doc
927
+ _assertValidNode(self)
928
+ c_doc = _copyDocRoot(self._doc._c_doc, self._c_node) # recursive
929
+ new_doc = _documentFactory(c_doc, self._doc._parser)
930
+ root = new_doc.getroot()
931
+ if root is not None:
932
+ return root
933
+ # Comment/PI
934
+ c_node = c_doc.children
935
+ while c_node is not NULL and c_node.type != self._c_node.type:
936
+ c_node = c_node.next
937
+ if c_node is NULL:
938
+ return None
939
+ return _elementFactory(new_doc, c_node)
940
+
941
+ def set(self, key, value):
942
+ """set(self, key, value)
943
+
944
+ Sets an element attribute.
945
+ In HTML documents (not XML or XHTML), the value None is allowed and creates
946
+ an attribute without value (just the attribute name).
947
+ """
948
+ _assertValidNode(self)
949
+ _setAttributeValue(self, key, value)
950
+
951
+ def append(self, _Element element not None):
952
+ """append(self, element)
953
+
954
+ Adds a subelement to the end of this element.
955
+ """
956
+ _assertValidNode(self)
957
+ _assertValidNode(element)
958
+ _appendChild(self, element)
959
+
960
+ def addnext(self, _Element element not None):
961
+ """addnext(self, element)
962
+
963
+ Adds the element as a following sibling directly after this
964
+ element.
965
+
966
+ This is normally used to set a processing instruction or comment after
967
+ the root node of a document. Note that tail text is automatically
968
+ discarded when adding at the root level.
969
+ """
970
+ _assertValidNode(self)
971
+ _assertValidNode(element)
972
+ if self._c_node.parent != NULL and not _isElement(self._c_node.parent):
973
+ if element._c_node.type not in (tree.XML_PI_NODE, tree.XML_COMMENT_NODE):
974
+ raise TypeError, "Only processing instructions and comments can be siblings of the root element"
975
+ element.tail = None
976
+ _appendSibling(self, element)
977
+
978
+ def addprevious(self, _Element element not None):
979
+ """addprevious(self, element)
980
+
981
+ Adds the element as a preceding sibling directly before this
982
+ element.
983
+
984
+ This is normally used to set a processing instruction or comment
985
+ before the root node of a document. Note that tail text is
986
+ automatically discarded when adding at the root level.
987
+ """
988
+ _assertValidNode(self)
989
+ _assertValidNode(element)
990
+ if self._c_node.parent != NULL and not _isElement(self._c_node.parent):
991
+ if element._c_node.type != tree.XML_PI_NODE:
992
+ if element._c_node.type != tree.XML_COMMENT_NODE:
993
+ raise TypeError, "Only processing instructions and comments can be siblings of the root element"
994
+ element.tail = None
995
+ _prependSibling(self, element)
996
+
997
+ def extend(self, elements):
998
+ """extend(self, elements)
999
+
1000
+ Extends the current children by the elements in the iterable.
1001
+ """
1002
+ cdef _Element element
1003
+ _assertValidNode(self)
1004
+ for element in elements:
1005
+ if element is None:
1006
+ raise TypeError, "Node must not be None"
1007
+ _assertValidNode(element)
1008
+ _appendChild(self, element)
1009
+
1010
+ def clear(self, bint keep_tail=False):
1011
+ """clear(self, keep_tail=False)
1012
+
1013
+ Resets an element. This function removes all subelements, clears
1014
+ all attributes and sets the text and tail properties to None.
1015
+
1016
+ Pass ``keep_tail=True`` to leave the tail text untouched.
1017
+ """
1018
+ cdef xmlAttr* c_attr
1019
+ cdef xmlAttr* c_attr_next
1020
+ cdef xmlNode* c_node
1021
+ cdef xmlNode* c_node_next
1022
+ _assertValidNode(self)
1023
+ c_node = self._c_node
1024
+ # remove self.text and self.tail
1025
+ _removeText(c_node.children)
1026
+ if not keep_tail:
1027
+ _removeText(c_node.next)
1028
+ # remove all attributes
1029
+ c_attr = c_node.properties
1030
+ if c_attr:
1031
+ c_node.properties = NULL
1032
+ tree.xmlFreePropList(c_attr)
1033
+ # remove all subelements
1034
+ c_node = c_node.children
1035
+ if c_node and not _isElement(c_node):
1036
+ c_node = _nextElement(c_node)
1037
+ while c_node is not NULL:
1038
+ c_node_next = _nextElement(c_node)
1039
+ _removeNode(self._doc, c_node)
1040
+ c_node = c_node_next
1041
+
1042
+ def insert(self, index: int, _Element element not None):
1043
+ """insert(self, index, element)
1044
+
1045
+ Inserts a subelement at the given position in this element
1046
+ """
1047
+ cdef xmlNode* c_node
1048
+ cdef xmlNode* c_next
1049
+ cdef xmlDoc* c_source_doc
1050
+ _assertValidNode(self)
1051
+ _assertValidNode(element)
1052
+ c_node = _findChild(self._c_node, index)
1053
+ if c_node is NULL:
1054
+ _appendChild(self, element)
1055
+ return
1056
+ # prevent cycles
1057
+ if _isAncestorOrSame(element._c_node, self._c_node):
1058
+ raise ValueError("cannot append parent to itself")
1059
+ c_source_doc = element._c_node.doc
1060
+ c_next = element._c_node.next
1061
+ tree.xmlAddPrevSibling(c_node, element._c_node)
1062
+ _moveTail(c_next, element._c_node)
1063
+ moveNodeToDocument(self._doc, c_source_doc, element._c_node)
1064
+
1065
+ def remove(self, _Element element not None):
1066
+ """remove(self, element)
1067
+
1068
+ Removes a matching subelement. Unlike the find methods, this
1069
+ method compares elements based on identity, not on tag value
1070
+ or contents.
1071
+ """
1072
+ cdef xmlNode* c_node
1073
+ cdef xmlNode* c_next
1074
+ _assertValidNode(self)
1075
+ _assertValidNode(element)
1076
+ c_node = element._c_node
1077
+ if c_node.parent is not self._c_node:
1078
+ raise ValueError, "Element is not a child of this node."
1079
+ c_next = element._c_node.next
1080
+ tree.xmlUnlinkNode(c_node)
1081
+ _moveTail(c_next, c_node)
1082
+ # fix namespace declarations
1083
+ moveNodeToDocument(self._doc, c_node.doc, c_node)
1084
+
1085
+ def replace(self, _Element old_element not None,
1086
+ _Element new_element not None):
1087
+ """replace(self, old_element, new_element)
1088
+
1089
+ Replaces a subelement with the element passed as second argument.
1090
+ """
1091
+ cdef xmlNode* c_old_node
1092
+ cdef xmlNode* c_old_next
1093
+ cdef xmlNode* c_new_node
1094
+ cdef xmlNode* c_new_next
1095
+ cdef xmlDoc* c_source_doc
1096
+ _assertValidNode(self)
1097
+ _assertValidNode(old_element)
1098
+ _assertValidNode(new_element)
1099
+ c_old_node = old_element._c_node
1100
+ if c_old_node.parent is not self._c_node:
1101
+ raise ValueError, "Element is not a child of this node."
1102
+ c_new_node = new_element._c_node
1103
+ # prevent cycles
1104
+ if _isAncestorOrSame(c_new_node, self._c_node):
1105
+ raise ValueError("cannot append parent to itself")
1106
+ # replace node
1107
+ c_old_next = c_old_node.next
1108
+ c_new_next = c_new_node.next
1109
+ c_source_doc = c_new_node.doc
1110
+ tree.xmlReplaceNode(c_old_node, c_new_node)
1111
+ _moveTail(c_new_next, c_new_node)
1112
+ _moveTail(c_old_next, c_old_node)
1113
+ moveNodeToDocument(self._doc, c_source_doc, c_new_node)
1114
+ # fix namespace declarations
1115
+ moveNodeToDocument(self._doc, c_old_node.doc, c_old_node)
1116
+
1117
+ # PROPERTIES
1118
+ property tag:
1119
+ """Element tag
1120
+ """
1121
+ def __get__(self):
1122
+ if self._tag is not None:
1123
+ return self._tag
1124
+ _assertValidNode(self)
1125
+ self._tag = _namespacedName(self._c_node)
1126
+ return self._tag
1127
+
1128
+ def __set__(self, value):
1129
+ cdef _BaseParser parser
1130
+ _assertValidNode(self)
1131
+ ns, name = _getNsTag(value)
1132
+ parser = self._doc._parser
1133
+ if parser is not None and parser._for_html:
1134
+ _htmlTagValidOrRaise(name)
1135
+ else:
1136
+ _tagValidOrRaise(name)
1137
+ self._tag = value
1138
+ tree.xmlNodeSetName(self._c_node, _xcstr(name))
1139
+ if ns is None:
1140
+ self._c_node.ns = NULL
1141
+ else:
1142
+ self._doc._setNodeNs(self._c_node, _xcstr(ns))
1143
+
1144
+ @property
1145
+ def attrib(self):
1146
+ """Element attribute dictionary. Where possible, use get(), set(),
1147
+ keys(), values() and items() to access element attributes.
1148
+ """
1149
+ return _Attrib.__new__(_Attrib, self)
1150
+
1151
+ property text:
1152
+ """Text before the first subelement. This is either a string or
1153
+ the value None, if there was no text.
1154
+ """
1155
+ def __get__(self):
1156
+ _assertValidNode(self)
1157
+ return _collectText(self._c_node.children)
1158
+
1159
+ def __set__(self, value):
1160
+ _assertValidNode(self)
1161
+ if isinstance(value, QName):
1162
+ value = _resolveQNameText(self, value).decode('utf8')
1163
+ _setNodeText(self._c_node, value)
1164
+
1165
+ # using 'del el.text' is the wrong thing to do
1166
+ #def __del__(self):
1167
+ # _setNodeText(self._c_node, None)
1168
+
1169
+ property tail:
1170
+ """Text after this element's end tag, but before the next sibling
1171
+ element's start tag. This is either a string or the value None, if
1172
+ there was no text.
1173
+ """
1174
+ def __get__(self):
1175
+ _assertValidNode(self)
1176
+ return _collectText(self._c_node.next)
1177
+
1178
+ def __set__(self, value):
1179
+ _assertValidNode(self)
1180
+ _setTailText(self._c_node, value)
1181
+
1182
+ # using 'del el.tail' is the wrong thing to do
1183
+ #def __del__(self):
1184
+ # _setTailText(self._c_node, None)
1185
+
1186
+ # not in ElementTree, read-only
1187
+ @property
1188
+ def prefix(self):
1189
+ """Namespace prefix or None.
1190
+ """
1191
+ if self._c_node.ns is not NULL:
1192
+ if self._c_node.ns.prefix is not NULL:
1193
+ return funicode(self._c_node.ns.prefix)
1194
+ return None
1195
+
1196
+ # not in ElementTree, read-only
1197
+ property sourceline:
1198
+ """Original line number as found by the parser or None if unknown.
1199
+ """
1200
+ def __get__(self):
1201
+ cdef long line
1202
+ _assertValidNode(self)
1203
+ line = tree.xmlGetLineNo(self._c_node)
1204
+ return line if line > 0 else None
1205
+
1206
+ def __set__(self, line):
1207
+ _assertValidNode(self)
1208
+ if line <= 0:
1209
+ self._c_node.line = 0
1210
+ else:
1211
+ self._c_node.line = line
1212
+
1213
+ # not in ElementTree, read-only
1214
+ @property
1215
+ def nsmap(self):
1216
+ """Namespace prefix->URI mapping known in the context of this
1217
+ Element. This includes all namespace declarations of the
1218
+ parents.
1219
+
1220
+ Note that changing the returned dict has no effect on the Element.
1221
+ """
1222
+ _assertValidNode(self)
1223
+ return _build_nsmap(self._c_node)
1224
+
1225
+ # not in ElementTree, read-only
1226
+ property base:
1227
+ """The base URI of the Element (xml:base or HTML base URL).
1228
+ None if the base URI is unknown.
1229
+
1230
+ Note that the value depends on the URL of the document that
1231
+ holds the Element if there is no xml:base attribute on the
1232
+ Element or its ancestors.
1233
+
1234
+ Setting this property will set an xml:base attribute on the
1235
+ Element, regardless of the document type (XML or HTML).
1236
+ """
1237
+ def __get__(self):
1238
+ _assertValidNode(self)
1239
+ c_base = tree.xmlNodeGetBase(self._doc._c_doc, self._c_node)
1240
+ if c_base is NULL:
1241
+ if self._doc._c_doc.URL is NULL:
1242
+ return None
1243
+ return _decodeFilename(self._doc._c_doc.URL)
1244
+ try:
1245
+ base = _decodeFilename(c_base)
1246
+ finally:
1247
+ tree.xmlFree(c_base)
1248
+ return base
1249
+
1250
+ def __set__(self, url):
1251
+ _assertValidNode(self)
1252
+ if url is None:
1253
+ c_base = <const_xmlChar*>NULL
1254
+ else:
1255
+ url = _encodeFilename(url)
1256
+ c_base = _xcstr(url)
1257
+ tree.xmlNodeSetBase(self._c_node, c_base)
1258
+
1259
+ # ACCESSORS
1260
+ def __repr__(self):
1261
+ "__repr__(self)"
1262
+ return "<Element %s at 0x%x>" % (self.tag, id(self))
1263
+
1264
+ def __getitem__(self, x):
1265
+ """Returns the subelement at the given position or the requested
1266
+ slice.
1267
+ """
1268
+ cdef xmlNode* c_node = NULL
1269
+ cdef Py_ssize_t step = 0, slicelength = 0
1270
+ cdef Py_ssize_t c, i
1271
+ cdef _node_to_node_function next_element
1272
+ cdef list result
1273
+ _assertValidNode(self)
1274
+ if isinstance(x, slice):
1275
+ # slicing
1276
+ if _isFullSlice(<slice>x):
1277
+ return _collectChildren(self)
1278
+ _findChildSlice(<slice>x, self._c_node, &c_node, &step, &slicelength)
1279
+ if c_node is NULL:
1280
+ return []
1281
+ if step > 0:
1282
+ next_element = _nextElement
1283
+ else:
1284
+ step = -step
1285
+ next_element = _previousElement
1286
+ result = []
1287
+ c = 0
1288
+ while c_node is not NULL and c < slicelength:
1289
+ result.append(_elementFactory(self._doc, c_node))
1290
+ c += 1
1291
+ for i in range(step):
1292
+ c_node = next_element(c_node)
1293
+ if c_node is NULL:
1294
+ break
1295
+ return result
1296
+ else:
1297
+ # indexing
1298
+ c_node = _findChild(self._c_node, x)
1299
+ if c_node is NULL:
1300
+ raise IndexError, "list index out of range"
1301
+ return _elementFactory(self._doc, c_node)
1302
+
1303
+ def __len__(self):
1304
+ """__len__(self)
1305
+
1306
+ Returns the number of subelements.
1307
+ """
1308
+ _assertValidNode(self)
1309
+ return _countElements(self._c_node.children)
1310
+
1311
+ def __bool__(self):
1312
+ """__bool__(self)"""
1313
+ import warnings
1314
+ warnings.warn(
1315
+ "Truth-testing of elements was a source of confusion and will always "
1316
+ "return True in future versions. "
1317
+ "Use specific 'len(elem)' or 'elem is not None' test instead.",
1318
+ FutureWarning
1319
+ )
1320
+ # emulate old behaviour
1321
+ _assertValidNode(self)
1322
+ return _hasChild(self._c_node)
1323
+
1324
+ def __contains__(self, element):
1325
+ "__contains__(self, element)"
1326
+ cdef xmlNode* c_node
1327
+ _assertValidNode(self)
1328
+ if not isinstance(element, _Element):
1329
+ return 0
1330
+ c_node = (<_Element>element)._c_node
1331
+ return c_node is not NULL and c_node.parent is self._c_node
1332
+
1333
+ def __iter__(self):
1334
+ "__iter__(self)"
1335
+ return ElementChildIterator(self)
1336
+
1337
+ def __reversed__(self):
1338
+ "__reversed__(self)"
1339
+ return ElementChildIterator(self, reversed=True)
1340
+
1341
+ def index(self, child: _Element, start: int = None, stop: int = None):
1342
+ """index(self, child, start=None, stop=None)
1343
+
1344
+ Find the position of the child within the parent.
1345
+
1346
+ This method is not part of the original ElementTree API.
1347
+ """
1348
+ cdef Py_ssize_t k, l
1349
+ cdef Py_ssize_t c_start, c_stop
1350
+ cdef xmlNode* c_child
1351
+ cdef xmlNode* c_start_node
1352
+ _assertValidNode(self)
1353
+ _assertValidNode(child)
1354
+ c_child = child._c_node
1355
+ if c_child.parent is not self._c_node:
1356
+ raise ValueError, "Element is not a child of this node."
1357
+
1358
+ # handle the unbounded search straight away (normal case)
1359
+ if stop is None and (start is None or start == 0):
1360
+ k = 0
1361
+ c_child = c_child.prev
1362
+ while c_child is not NULL:
1363
+ if _isElement(c_child):
1364
+ k += 1
1365
+ c_child = c_child.prev
1366
+ return k
1367
+
1368
+ # check indices
1369
+ if start is None:
1370
+ c_start = 0
1371
+ else:
1372
+ c_start = start
1373
+ if stop is None:
1374
+ c_stop = 0
1375
+ else:
1376
+ c_stop = stop
1377
+ if c_stop == 0 or \
1378
+ c_start >= c_stop and (c_stop > 0 or c_start < 0):
1379
+ raise ValueError, "list.index(x): x not in slice"
1380
+
1381
+ # for negative slice indices, check slice before searching index
1382
+ if c_start < 0 or c_stop < 0:
1383
+ # start from right, at most up to leftmost(c_start, c_stop)
1384
+ if c_start < c_stop:
1385
+ k = -c_start
1386
+ else:
1387
+ k = -c_stop
1388
+ c_start_node = self._c_node.last
1389
+ l = 1
1390
+ while c_start_node != c_child and l < k:
1391
+ if _isElement(c_start_node):
1392
+ l += 1
1393
+ c_start_node = c_start_node.prev
1394
+ if c_start_node == c_child:
1395
+ # found! before slice end?
1396
+ if c_stop < 0 and l <= -c_stop:
1397
+ raise ValueError, "list.index(x): x not in slice"
1398
+ elif c_start < 0:
1399
+ raise ValueError, "list.index(x): x not in slice"
1400
+
1401
+ # now determine the index backwards from child
1402
+ c_child = c_child.prev
1403
+ k = 0
1404
+ if c_stop > 0:
1405
+ # we can optimize: stop after c_stop elements if not found
1406
+ while c_child != NULL and k < c_stop:
1407
+ if _isElement(c_child):
1408
+ k += 1
1409
+ c_child = c_child.prev
1410
+ if k < c_stop:
1411
+ return k
1412
+ else:
1413
+ # traverse all
1414
+ while c_child != NULL:
1415
+ if _isElement(c_child):
1416
+ k = k + 1
1417
+ c_child = c_child.prev
1418
+ if c_start > 0:
1419
+ if k >= c_start:
1420
+ return k
1421
+ else:
1422
+ return k
1423
+ if c_start != 0 or c_stop != 0:
1424
+ raise ValueError, "list.index(x): x not in slice"
1425
+ else:
1426
+ raise ValueError, "list.index(x): x not in list"
1427
+
1428
+ def get(self, key, default=None):
1429
+ """get(self, key, default=None)
1430
+
1431
+ Gets an element attribute.
1432
+ """
1433
+ _assertValidNode(self)
1434
+ return _getAttributeValue(self, key, default)
1435
+
1436
+ def keys(self):
1437
+ """keys(self)
1438
+
1439
+ Gets a list of attribute names. The names are returned in an
1440
+ arbitrary order (just like for an ordinary Python dictionary).
1441
+ """
1442
+ _assertValidNode(self)
1443
+ return _collectAttributes(self._c_node, 1)
1444
+
1445
+ def values(self):
1446
+ """values(self)
1447
+
1448
+ Gets element attribute values as a sequence of strings. The
1449
+ attributes are returned in an arbitrary order.
1450
+ """
1451
+ _assertValidNode(self)
1452
+ return _collectAttributes(self._c_node, 2)
1453
+
1454
+ def items(self):
1455
+ """items(self)
1456
+
1457
+ Gets element attributes, as a sequence. The attributes are returned in
1458
+ an arbitrary order.
1459
+ """
1460
+ _assertValidNode(self)
1461
+ return _collectAttributes(self._c_node, 3)
1462
+
1463
+ def getchildren(self):
1464
+ """getchildren(self)
1465
+
1466
+ Returns all direct children. The elements are returned in document
1467
+ order.
1468
+
1469
+ :deprecated: Note that this method has been deprecated as of
1470
+ ElementTree 1.3 and lxml 2.0. New code should use
1471
+ ``list(element)`` or simply iterate over elements.
1472
+ """
1473
+ _assertValidNode(self)
1474
+ return _collectChildren(self)
1475
+
1476
+ def getparent(self):
1477
+ """getparent(self)
1478
+
1479
+ Returns the parent of this element or None for the root element.
1480
+ """
1481
+ cdef xmlNode* c_node
1482
+ #_assertValidNode(self) # not needed
1483
+ c_node = _parentElement(self._c_node)
1484
+ if c_node is NULL:
1485
+ return None
1486
+ return _elementFactory(self._doc, c_node)
1487
+
1488
+ def getnext(self):
1489
+ """getnext(self)
1490
+
1491
+ Returns the following sibling of this element or None.
1492
+ """
1493
+ cdef xmlNode* c_node
1494
+ #_assertValidNode(self) # not needed
1495
+ c_node = _nextElement(self._c_node)
1496
+ if c_node is NULL:
1497
+ return None
1498
+ return _elementFactory(self._doc, c_node)
1499
+
1500
+ def getprevious(self):
1501
+ """getprevious(self)
1502
+
1503
+ Returns the preceding sibling of this element or None.
1504
+ """
1505
+ cdef xmlNode* c_node
1506
+ #_assertValidNode(self) # not needed
1507
+ c_node = _previousElement(self._c_node)
1508
+ if c_node is NULL:
1509
+ return None
1510
+ return _elementFactory(self._doc, c_node)
1511
+
1512
+ def itersiblings(self, tag=None, *tags, preceding=False):
1513
+ """itersiblings(self, tag=None, *tags, preceding=False)
1514
+
1515
+ Iterate over the following or preceding siblings of this element.
1516
+
1517
+ The direction is determined by the 'preceding' keyword which
1518
+ defaults to False, i.e. forward iteration over the following
1519
+ siblings. When True, the iterator yields the preceding
1520
+ siblings in reverse document order, i.e. starting right before
1521
+ the current element and going backwards.
1522
+
1523
+ Can be restricted to find only elements with specific tags,
1524
+ see `iter`.
1525
+ """
1526
+ if preceding:
1527
+ if self._c_node and not self._c_node.prev:
1528
+ return ITER_EMPTY
1529
+ elif self._c_node and not self._c_node.next:
1530
+ return ITER_EMPTY
1531
+ if tag is not None:
1532
+ tags += (tag,)
1533
+ return SiblingsIterator(self, tags, preceding=preceding)
1534
+
1535
+ def iterancestors(self, tag=None, *tags):
1536
+ """iterancestors(self, tag=None, *tags)
1537
+
1538
+ Iterate over the ancestors of this element (from parent to parent).
1539
+
1540
+ Can be restricted to find only elements with specific tags,
1541
+ see `iter`.
1542
+ """
1543
+ if self._c_node and not self._c_node.parent:
1544
+ return ITER_EMPTY
1545
+ if tag is not None:
1546
+ tags += (tag,)
1547
+ return AncestorsIterator(self, tags)
1548
+
1549
+ def iterdescendants(self, tag=None, *tags):
1550
+ """iterdescendants(self, tag=None, *tags)
1551
+
1552
+ Iterate over the descendants of this element in document order.
1553
+
1554
+ As opposed to ``el.iter()``, this iterator does not yield the element
1555
+ itself. The returned elements can be restricted to find only elements
1556
+ with specific tags, see `iter`.
1557
+ """
1558
+ if self._c_node and not self._c_node.children:
1559
+ return ITER_EMPTY
1560
+ if tag is not None:
1561
+ tags += (tag,)
1562
+ return ElementDepthFirstIterator(self, tags, inclusive=False)
1563
+
1564
+ def iterchildren(self, tag=None, *tags, reversed=False):
1565
+ """iterchildren(self, tag=None, *tags, reversed=False)
1566
+
1567
+ Iterate over the children of this element.
1568
+
1569
+ As opposed to using normal iteration on this element, the returned
1570
+ elements can be reversed with the 'reversed' keyword and restricted
1571
+ to find only elements with specific tags, see `iter`.
1572
+ """
1573
+ if self._c_node and not self._c_node.children:
1574
+ return ITER_EMPTY
1575
+ if tag is not None:
1576
+ tags += (tag,)
1577
+ return ElementChildIterator(self, tags, reversed=reversed)
1578
+
1579
+ def getroottree(self):
1580
+ """getroottree(self)
1581
+
1582
+ Return an ElementTree for the root node of the document that
1583
+ contains this element.
1584
+
1585
+ This is the same as following element.getparent() up the tree until it
1586
+ returns None (for the root element) and then build an ElementTree for
1587
+ the last parent that was returned."""
1588
+ _assertValidDoc(self._doc)
1589
+ return _elementTreeFactory(self._doc, None)
1590
+
1591
+ def getiterator(self, tag=None, *tags):
1592
+ """getiterator(self, tag=None, *tags)
1593
+
1594
+ Returns a sequence or iterator of all elements in the subtree in
1595
+ document order (depth first pre-order), starting with this
1596
+ element.
1597
+
1598
+ Can be restricted to find only elements with specific tags,
1599
+ see `iter`.
1600
+
1601
+ :deprecated: Note that this method is deprecated as of
1602
+ ElementTree 1.3 and lxml 2.0. It returns an iterator in
1603
+ lxml, which diverges from the original ElementTree
1604
+ behaviour. If you want an efficient iterator, use the
1605
+ ``element.iter()`` method instead. You should only use this
1606
+ method in new code if you require backwards compatibility
1607
+ with older versions of lxml or ElementTree.
1608
+ """
1609
+ if tag is not None:
1610
+ tags += (tag,)
1611
+ return ElementDepthFirstIterator(self, tags)
1612
+
1613
+ def iter(self, tag=None, *tags):
1614
+ """iter(self, tag=None, *tags)
1615
+
1616
+ Iterate over all elements in the subtree in document order (depth
1617
+ first pre-order), starting with this element.
1618
+
1619
+ Can be restricted to find only elements with specific tags:
1620
+ pass ``"{ns}localname"`` as tag. Either or both of ``ns`` and
1621
+ ``localname`` can be ``*`` for a wildcard; ``ns`` can be empty
1622
+ for no namespace. ``"localname"`` is equivalent to ``"{}localname"``
1623
+ (i.e. no namespace) but ``"*"`` is ``"{*}*"`` (any or no namespace),
1624
+ not ``"{}*"``.
1625
+
1626
+ You can also pass the Element, Comment, ProcessingInstruction and
1627
+ Entity factory functions to look only for the specific element type.
1628
+
1629
+ Passing multiple tags (or a sequence of tags) instead of a single tag
1630
+ will let the iterator return all elements matching any of these tags,
1631
+ in document order.
1632
+ """
1633
+ if tag is not None:
1634
+ tags += (tag,)
1635
+ return ElementDepthFirstIterator(self, tags)
1636
+
1637
+ def itertext(self, tag=None, *tags, with_tail=True):
1638
+ """itertext(self, tag=None, *tags, with_tail=True)
1639
+
1640
+ Iterates over the text content of a subtree.
1641
+
1642
+ You can pass tag names to restrict text content to specific elements,
1643
+ see `iter`.
1644
+
1645
+ You can set the ``with_tail`` keyword argument to ``False`` to skip
1646
+ over tail text.
1647
+ """
1648
+ if tag is not None:
1649
+ tags += (tag,)
1650
+ return ElementTextIterator(self, tags, with_tail=with_tail)
1651
+
1652
+ def makeelement(self, _tag, attrib=None, nsmap=None, **_extra):
1653
+ """makeelement(self, _tag, attrib=None, nsmap=None, **_extra)
1654
+
1655
+ Creates a new element associated with the same document.
1656
+ """
1657
+ _assertValidDoc(self._doc)
1658
+ return _makeElement(_tag, NULL, self._doc, None, None, None,
1659
+ attrib, nsmap, _extra)
1660
+
1661
+ def find(self, path, namespaces=None):
1662
+ """find(self, path, namespaces=None)
1663
+
1664
+ Finds the first matching subelement, by tag name or path.
1665
+
1666
+ The optional ``namespaces`` argument accepts a
1667
+ prefix-to-namespace mapping that allows the usage of XPath
1668
+ prefixes in the path expression.
1669
+ """
1670
+ if isinstance(path, QName):
1671
+ path = (<QName>path).text
1672
+ return _elementpath.find(self, path, namespaces, with_prefixes=not _isHtmlDocument(self))
1673
+
1674
+ def findtext(self, path, default=None, namespaces=None):
1675
+ """findtext(self, path, default=None, namespaces=None)
1676
+
1677
+ Finds text for the first matching subelement, by tag name or path.
1678
+
1679
+ The optional ``namespaces`` argument accepts a
1680
+ prefix-to-namespace mapping that allows the usage of XPath
1681
+ prefixes in the path expression.
1682
+ """
1683
+ if isinstance(path, QName):
1684
+ path = (<QName>path).text
1685
+ return _elementpath.findtext(self, path, default, namespaces, with_prefixes=not _isHtmlDocument(self))
1686
+
1687
+ def findall(self, path, namespaces=None):
1688
+ """findall(self, path, namespaces=None)
1689
+
1690
+ Finds all matching subelements, by tag name or path.
1691
+
1692
+ The optional ``namespaces`` argument accepts a
1693
+ prefix-to-namespace mapping that allows the usage of XPath
1694
+ prefixes in the path expression.
1695
+ """
1696
+ if isinstance(path, QName):
1697
+ path = (<QName>path).text
1698
+ return _elementpath.findall(self, path, namespaces, with_prefixes=not _isHtmlDocument(self))
1699
+
1700
+ def iterfind(self, path, namespaces=None):
1701
+ """iterfind(self, path, namespaces=None)
1702
+
1703
+ Iterates over all matching subelements, by tag name or path.
1704
+
1705
+ The optional ``namespaces`` argument accepts a
1706
+ prefix-to-namespace mapping that allows the usage of XPath
1707
+ prefixes in the path expression.
1708
+ """
1709
+ if isinstance(path, QName):
1710
+ path = (<QName>path).text
1711
+ return _elementpath.iterfind(self, path, namespaces, with_prefixes=not _isHtmlDocument(self))
1712
+
1713
+ def xpath(self, _path, *, namespaces=None, extensions=None,
1714
+ smart_strings=True, **_variables):
1715
+ """xpath(self, _path, namespaces=None, extensions=None, smart_strings=True, **_variables)
1716
+
1717
+ Evaluate an xpath expression using the element as context node.
1718
+ """
1719
+ evaluator = XPathElementEvaluator(self, namespaces=namespaces,
1720
+ extensions=extensions,
1721
+ smart_strings=smart_strings)
1722
+ return evaluator(_path, **_variables)
1723
+
1724
+ def cssselect(self, expr, *, translator='xml'):
1725
+ """
1726
+ Run the CSS expression on this element and its children,
1727
+ returning a list of the results.
1728
+
1729
+ Equivalent to lxml.cssselect.CSSSelect(expr)(self) -- note
1730
+ that pre-compiling the expression can provide a substantial
1731
+ speedup.
1732
+ """
1733
+ # Do the import here to make the dependency optional.
1734
+ from lxml.cssselect import CSSSelector
1735
+ return CSSSelector(expr, translator=translator)(self)
1736
+
1737
+
1738
+ @cython.linetrace(False)
1739
+ cdef _Element _elementFactory(_Document doc, xmlNode* c_node):
1740
+ cdef _Element result
1741
+ result = getProxy(c_node)
1742
+ if result is not None:
1743
+ return result
1744
+ if c_node is NULL:
1745
+ return None
1746
+
1747
+ element_class = <type> LOOKUP_ELEMENT_CLASS(
1748
+ ELEMENT_CLASS_LOOKUP_STATE, doc, c_node)
1749
+ if type(element_class) is not type:
1750
+ if not isinstance(element_class, type):
1751
+ raise TypeError(f"Element class is not a type, got {type(element_class)}")
1752
+ if hasProxy(c_node):
1753
+ # prevent re-entry race condition - we just called into Python
1754
+ return getProxy(c_node)
1755
+ result = element_class.__new__(element_class)
1756
+ if hasProxy(c_node):
1757
+ # prevent re-entry race condition - we just called into Python
1758
+ result._c_node = NULL
1759
+ return getProxy(c_node)
1760
+
1761
+ _registerProxy(result, doc, c_node)
1762
+ if element_class is not _Element:
1763
+ result._init()
1764
+ return result
1765
+
1766
+
1767
+ @cython.internal
1768
+ cdef class __ContentOnlyElement(_Element):
1769
+ cdef int _raiseImmutable(self) except -1:
1770
+ raise TypeError, "this element does not have children or attributes"
1771
+
1772
+ def set(self, key, value):
1773
+ "set(self, key, value)"
1774
+ self._raiseImmutable()
1775
+
1776
+ def append(self, value):
1777
+ "append(self, value)"
1778
+ self._raiseImmutable()
1779
+
1780
+ def insert(self, index, value):
1781
+ "insert(self, index, value)"
1782
+ self._raiseImmutable()
1783
+
1784
+ def __setitem__(self, index, value):
1785
+ "__setitem__(self, index, value)"
1786
+ self._raiseImmutable()
1787
+
1788
+ @property
1789
+ def attrib(self):
1790
+ return IMMUTABLE_EMPTY_MAPPING
1791
+
1792
+ property text:
1793
+ def __get__(self):
1794
+ _assertValidNode(self)
1795
+ return funicodeOrEmpty(self._c_node.content)
1796
+
1797
+ def __set__(self, value):
1798
+ cdef tree.xmlDict* c_dict
1799
+ _assertValidNode(self)
1800
+ if value is None:
1801
+ c_text = <const_xmlChar*>NULL
1802
+ else:
1803
+ value = _utf8(value)
1804
+ c_text = _xcstr(value)
1805
+ tree.xmlNodeSetContent(self._c_node, c_text)
1806
+
1807
+ # ACCESSORS
1808
+ def __getitem__(self, x):
1809
+ "__getitem__(self, x)"
1810
+ if isinstance(x, slice):
1811
+ return []
1812
+ else:
1813
+ raise IndexError, "list index out of range"
1814
+
1815
+ def __len__(self):
1816
+ "__len__(self)"
1817
+ return 0
1818
+
1819
+ def get(self, key, default=None):
1820
+ "get(self, key, default=None)"
1821
+ return None
1822
+
1823
+ def keys(self):
1824
+ "keys(self)"
1825
+ return []
1826
+
1827
+ def items(self):
1828
+ "items(self)"
1829
+ return []
1830
+
1831
+ def values(self):
1832
+ "values(self)"
1833
+ return []
1834
+
1835
+ cdef class _Comment(__ContentOnlyElement):
1836
+ @property
1837
+ def tag(self):
1838
+ return Comment
1839
+
1840
+ def __repr__(self):
1841
+ return "<!--%s-->" % self.text
1842
+
1843
+ cdef class _ProcessingInstruction(__ContentOnlyElement):
1844
+ @property
1845
+ def tag(self):
1846
+ return ProcessingInstruction
1847
+
1848
+ property target:
1849
+ # not in ElementTree
1850
+ def __get__(self):
1851
+ _assertValidNode(self)
1852
+ return funicode(self._c_node.name)
1853
+
1854
+ def __set__(self, value):
1855
+ _assertValidNode(self)
1856
+ value = _utf8(value)
1857
+ c_text = _xcstr(value)
1858
+ tree.xmlNodeSetName(self._c_node, c_text)
1859
+
1860
+ def __repr__(self):
1861
+ text = self.text
1862
+ if text:
1863
+ return "<?%s %s?>" % (self.target, text)
1864
+ else:
1865
+ return "<?%s?>" % self.target
1866
+
1867
+ def get(self, key, default=None):
1868
+ """get(self, key, default=None)
1869
+
1870
+ Try to parse pseudo-attributes from the text content of the
1871
+ processing instruction, search for one with the given key as
1872
+ name and return its associated value.
1873
+
1874
+ Note that this is only a convenience method for the most
1875
+ common case that all text content is structured in
1876
+ attribute-like name-value pairs with properly quoted values.
1877
+ It is not guaranteed to work for all possible text content.
1878
+ """
1879
+ return self.attrib.get(key, default)
1880
+
1881
+ @property
1882
+ def attrib(self):
1883
+ """Returns a dict containing all pseudo-attributes that can be
1884
+ parsed from the text content of this processing instruction.
1885
+ Note that modifying the dict currently has no effect on the
1886
+ XML node, although this is not guaranteed to stay this way.
1887
+ """
1888
+ return { attr : (value1 or value2)
1889
+ for attr, value1, value2 in _FIND_PI_ATTRIBUTES(' ' + self.text) }
1890
+
1891
+ cdef object _FIND_PI_ATTRIBUTES = re.compile(r'\s+(\w+)\s*=\s*(?:\'([^\']*)\'|"([^"]*)")', re.U).findall
1892
+
1893
+ cdef class _Entity(__ContentOnlyElement):
1894
+ @property
1895
+ def tag(self):
1896
+ return Entity
1897
+
1898
+ property name:
1899
+ # not in ElementTree
1900
+ def __get__(self):
1901
+ _assertValidNode(self)
1902
+ return funicode(self._c_node.name)
1903
+
1904
+ def __set__(self, value):
1905
+ _assertValidNode(self)
1906
+ value_utf = _utf8(value)
1907
+ if b'&' in value_utf or b';' in value_utf:
1908
+ raise ValueError, f"Invalid entity name '{value}'"
1909
+ tree.xmlNodeSetName(self._c_node, _xcstr(value_utf))
1910
+
1911
+ @property
1912
+ def text(self):
1913
+ # FIXME: should this be None or '&[VALUE];' or the resolved
1914
+ # entity value ?
1915
+ _assertValidNode(self)
1916
+ return f'&{funicode(self._c_node.name)};'
1917
+
1918
+ def __repr__(self):
1919
+ return "&%s;" % self.name
1920
+
1921
+
1922
+ cdef class QName:
1923
+ """QName(text_or_uri_or_element, tag=None)
1924
+
1925
+ QName wrapper for qualified XML names.
1926
+
1927
+ Pass a tag name by itself or a namespace URI and a tag name to
1928
+ create a qualified name. Alternatively, pass an Element to
1929
+ extract its tag name. ``None`` as first argument is ignored in
1930
+ order to allow for generic 2-argument usage.
1931
+
1932
+ The ``text`` property holds the qualified name in
1933
+ ``{namespace}tagname`` notation. The ``namespace`` and
1934
+ ``localname`` properties hold the respective parts of the tag
1935
+ name.
1936
+
1937
+ You can pass QName objects wherever a tag name is expected. Also,
1938
+ setting Element text from a QName will resolve the namespace prefix
1939
+ on assignment and set a qualified text value. This is helpful in XML
1940
+ languages like SOAP or XML-Schema that use prefixed tag names in
1941
+ their text content.
1942
+ """
1943
+ cdef readonly unicode text
1944
+ cdef readonly unicode localname
1945
+ cdef readonly unicode namespace
1946
+ def __init__(self, text_or_uri_or_element, tag=None):
1947
+ if text_or_uri_or_element is None:
1948
+ # Allow None as no namespace.
1949
+ text_or_uri_or_element, tag = tag, None
1950
+ if not _isString(text_or_uri_or_element):
1951
+ if isinstance(text_or_uri_or_element, _Element):
1952
+ text_or_uri_or_element = (<_Element>text_or_uri_or_element).tag
1953
+ if not _isString(text_or_uri_or_element):
1954
+ raise ValueError, f"Invalid input tag of type {type(text_or_uri_or_element)!r}"
1955
+ elif isinstance(text_or_uri_or_element, QName):
1956
+ text_or_uri_or_element = (<QName>text_or_uri_or_element).text
1957
+ elif text_or_uri_or_element is not None:
1958
+ text_or_uri_or_element = unicode(text_or_uri_or_element)
1959
+ else:
1960
+ raise ValueError, f"Invalid input tag of type {type(text_or_uri_or_element)!r}"
1961
+
1962
+ ns_utf, tag_utf = _getNsTag(text_or_uri_or_element)
1963
+ if tag is not None:
1964
+ # either ('ns', 'tag') or ('{ns}oldtag', 'newtag')
1965
+ if ns_utf is None:
1966
+ ns_utf = tag_utf # case 1: namespace ended up as tag name
1967
+ tag_utf = _utf8(tag)
1968
+ _tagValidOrRaise(tag_utf)
1969
+ self.localname = (<bytes>tag_utf).decode('utf8')
1970
+ if ns_utf is None:
1971
+ self.namespace = None
1972
+ self.text = self.localname
1973
+ else:
1974
+ self.namespace = (<bytes>ns_utf).decode('utf8')
1975
+ self.text = "{%s}%s" % (self.namespace, self.localname)
1976
+ def __str__(self):
1977
+ return self.text
1978
+ def __hash__(self):
1979
+ return hash(self.text)
1980
+ def __richcmp__(self, other, int op):
1981
+ try:
1982
+ if type(other) is QName:
1983
+ other = (<QName>other).text
1984
+ elif not isinstance(other, unicode):
1985
+ other = unicode(other)
1986
+ except (ValueError, UnicodeDecodeError):
1987
+ return NotImplemented
1988
+ return python.PyObject_RichCompare(self.text, other, op)
1989
+
1990
+
1991
+ cdef public class _ElementTree [ type LxmlElementTreeType,
1992
+ object LxmlElementTree ]:
1993
+ cdef _Document _doc
1994
+ cdef _Element _context_node
1995
+
1996
+ # Note that _doc is only used to store the original document if we do not
1997
+ # have a _context_node. All methods should prefer self._context_node._doc
1998
+ # to honour tree restructuring. _doc can happily be None!
1999
+
2000
+ @cython.final
2001
+ cdef int _assertHasRoot(self) except -1:
2002
+ """We have to take care here: the document may not have a root node!
2003
+ This can happen if ElementTree() is called without any argument and
2004
+ the caller 'forgets' to call parse() afterwards, so this is a bug in
2005
+ the caller program.
2006
+ """
2007
+ assert self._context_node is not None, \
2008
+ "ElementTree not initialized, missing root"
2009
+ return 0
2010
+
2011
+ def parse(self, source, _BaseParser parser=None, *, base_url=None):
2012
+ """parse(self, source, parser=None, base_url=None)
2013
+
2014
+ Updates self with the content of source and returns its root.
2015
+ """
2016
+ cdef _Document doc = None
2017
+ try:
2018
+ doc = _parseDocument(source, parser, base_url)
2019
+ except _TargetParserResult as result_container:
2020
+ # raises a TypeError if we don't get an _Element
2021
+ self._context_node = result_container.result
2022
+ else:
2023
+ self._context_node = doc.getroot()
2024
+ self._doc = None if self._context_node is not None else doc
2025
+ return self._context_node
2026
+
2027
+ def _setroot(self, _Element root not None):
2028
+ """_setroot(self, root)
2029
+
2030
+ Relocate the ElementTree to a new root node.
2031
+ """
2032
+ _assertValidNode(root)
2033
+ if root._c_node.type != tree.XML_ELEMENT_NODE:
2034
+ raise TypeError, "Only elements can be the root of an ElementTree"
2035
+ self._context_node = root
2036
+ self._doc = None
2037
+
2038
+ def getroot(self):
2039
+ """getroot(self)
2040
+
2041
+ Gets the root element for this tree.
2042
+ """
2043
+ return self._context_node
2044
+
2045
+ def __copy__(self):
2046
+ return _elementTreeFactory(self._doc, self._context_node)
2047
+
2048
+ def __deepcopy__(self, memo):
2049
+ cdef _Element root
2050
+ cdef _Document doc
2051
+ cdef xmlDoc* c_doc
2052
+ if self._context_node is not None:
2053
+ root = self._context_node.__copy__()
2054
+ assert root is not None
2055
+ _assertValidNode(root)
2056
+ _copyNonElementSiblings(self._context_node._c_node, root._c_node)
2057
+ return _elementTreeFactory(None, root)
2058
+ elif self._doc is not None:
2059
+ _assertValidDoc(self._doc)
2060
+ c_doc = tree.xmlCopyDoc(self._doc._c_doc, 1)
2061
+ if c_doc is NULL:
2062
+ raise MemoryError()
2063
+ doc = _documentFactory(c_doc, self._doc._parser)
2064
+ return _elementTreeFactory(doc, None)
2065
+ else:
2066
+ # so what ...
2067
+ return self
2068
+
2069
+ # not in ElementTree
2070
+ @property
2071
+ def docinfo(self) -> DocInfo:
2072
+ """Information about the document provided by parser and DTD."""
2073
+ self._assertHasRoot()
2074
+ return DocInfo(self._context_node._doc)
2075
+
2076
+ # not in ElementTree, read-only
2077
+ @property
2078
+ def parser(self):
2079
+ """The parser that was used to parse the document in this ElementTree.
2080
+ """
2081
+ if self._context_node is not None and \
2082
+ self._context_node._doc is not None:
2083
+ return self._context_node._doc._parser
2084
+ if self._doc is not None:
2085
+ return self._doc._parser
2086
+ return None
2087
+
2088
+ def write(self, file, *, encoding=None, method="xml",
2089
+ bint pretty_print=False, xml_declaration=None, bint with_tail=True,
2090
+ standalone=None, doctype=None, compression=0,
2091
+ bint exclusive=False, inclusive_ns_prefixes=None,
2092
+ bint with_comments=True, bint strip_text=False,
2093
+ docstring=None):
2094
+ """write(self, file, encoding=None, method="xml",
2095
+ pretty_print=False, xml_declaration=None, with_tail=True,
2096
+ standalone=None, doctype=None, compression=0,
2097
+ exclusive=False, inclusive_ns_prefixes=None,
2098
+ with_comments=True, strip_text=False)
2099
+
2100
+ Write the tree to a filename, file or file-like object.
2101
+
2102
+ Defaults to ASCII encoding and writing a declaration as needed.
2103
+
2104
+ The keyword argument 'method' selects the output method:
2105
+ 'xml', 'html', 'text', 'c14n' or 'c14n2'. Default is 'xml'.
2106
+
2107
+ With ``method="c14n"`` (C14N version 1), the options ``exclusive``,
2108
+ ``with_comments`` and ``inclusive_ns_prefixes`` request exclusive
2109
+ C14N, include comments, and list the inclusive prefixes respectively.
2110
+
2111
+ With ``method="c14n2"`` (C14N version 2), the ``with_comments`` and
2112
+ ``strip_text`` options control the output of comments and text space
2113
+ according to C14N 2.0.
2114
+
2115
+ Passing a boolean value to the ``standalone`` option will
2116
+ output an XML declaration with the corresponding
2117
+ ``standalone`` flag.
2118
+
2119
+ The ``doctype`` option allows passing in a plain string that will
2120
+ be serialised before the XML tree. Note that passing in non
2121
+ well-formed content here will make the XML output non well-formed.
2122
+ Also, an existing doctype in the document tree will not be removed
2123
+ when serialising an ElementTree instance.
2124
+
2125
+ The ``compression`` option enables GZip compression level 1-9.
2126
+
2127
+ The ``inclusive_ns_prefixes`` should be a list of namespace strings
2128
+ (i.e. ['xs', 'xsi']) that will be promoted to the top-level element
2129
+ during exclusive C14N serialisation. This parameter is ignored if
2130
+ exclusive mode=False.
2131
+
2132
+ If exclusive=True and no list is provided, a namespace will only be
2133
+ rendered if it is used by the immediate parent or one of its attributes
2134
+ and its prefix and values have not already been rendered by an ancestor
2135
+ of the namespace node's parent element.
2136
+ """
2137
+ cdef bint write_declaration
2138
+ cdef int is_standalone
2139
+
2140
+ self._assertHasRoot()
2141
+ _assertValidNode(self._context_node)
2142
+ if compression is None or compression < 0:
2143
+ compression = 0
2144
+
2145
+ # C14N serialisation
2146
+ if method in ('c14n', 'c14n2'):
2147
+ if encoding is not None:
2148
+ raise ValueError("Cannot specify encoding with C14N")
2149
+ if xml_declaration:
2150
+ raise ValueError("Cannot enable XML declaration in C14N")
2151
+
2152
+ if method == 'c14n':
2153
+ _tofilelikeC14N(file, self._context_node, exclusive, with_comments,
2154
+ compression, inclusive_ns_prefixes)
2155
+ else: # c14n2
2156
+ with _open_utf8_file(file, compression=compression) as f:
2157
+ target = C14NWriterTarget(
2158
+ f.write, with_comments=with_comments, strip_text=strip_text)
2159
+ _tree_to_target(self, target)
2160
+ return
2161
+
2162
+ if not with_comments:
2163
+ raise ValueError("Can only discard comments in C14N serialisation")
2164
+ # suppress decl. in default case (purely for ElementTree compatibility)
2165
+ if xml_declaration is not None:
2166
+ write_declaration = xml_declaration
2167
+ if encoding is None:
2168
+ encoding = 'ASCII'
2169
+ else:
2170
+ encoding = encoding.upper()
2171
+ elif encoding is None:
2172
+ encoding = 'ASCII'
2173
+ write_declaration = 0
2174
+ else:
2175
+ encoding = encoding.upper()
2176
+ write_declaration = encoding not in (
2177
+ 'US-ASCII', 'ASCII', 'UTF8', 'UTF-8')
2178
+ if standalone is None:
2179
+ is_standalone = -1
2180
+ elif standalone:
2181
+ write_declaration = 1
2182
+ is_standalone = 1
2183
+ else:
2184
+ write_declaration = 1
2185
+ is_standalone = 0
2186
+
2187
+ if docstring is not None and doctype is None:
2188
+ import warnings
2189
+ warnings.warn(
2190
+ "The 'docstring' option is deprecated. Use 'doctype' instead.",
2191
+ DeprecationWarning)
2192
+ doctype = docstring
2193
+
2194
+ _tofilelike(file, self._context_node, encoding, doctype, method,
2195
+ write_declaration, 1, pretty_print, with_tail,
2196
+ is_standalone, compression)
2197
+
2198
+ def getpath(self, _Element element not None):
2199
+ """getpath(self, element)
2200
+
2201
+ Returns a structural, absolute XPath expression to find the element.
2202
+
2203
+ For namespaced elements, the expression uses prefixes from the
2204
+ document, which therefore need to be provided in order to make any
2205
+ use of the expression in XPath.
2206
+
2207
+ Also see the method getelementpath(self, element), which returns a
2208
+ self-contained ElementPath expression.
2209
+ """
2210
+ cdef _Document doc
2211
+ cdef _Element root
2212
+ cdef xmlDoc* c_doc
2213
+ _assertValidNode(element)
2214
+ if self._context_node is not None:
2215
+ root = self._context_node
2216
+ doc = root._doc
2217
+ elif self._doc is not None:
2218
+ doc = self._doc
2219
+ root = doc.getroot()
2220
+ else:
2221
+ raise ValueError, "Element is not in this tree."
2222
+ _assertValidDoc(doc)
2223
+ _assertValidNode(root)
2224
+ if element._doc is not doc:
2225
+ raise ValueError, "Element is not in this tree."
2226
+
2227
+ c_doc = _fakeRootDoc(doc._c_doc, root._c_node)
2228
+ c_path = tree.xmlGetNodePath(element._c_node)
2229
+ _destroyFakeDoc(doc._c_doc, c_doc)
2230
+ if c_path is NULL:
2231
+ raise MemoryError()
2232
+ path = funicode(c_path)
2233
+ tree.xmlFree(c_path)
2234
+ return path
2235
+
2236
+ def getelementpath(self, _Element element not None):
2237
+ """getelementpath(self, element)
2238
+
2239
+ Returns a structural, absolute ElementPath expression to find the
2240
+ element. This path can be used in the .find() method to look up
2241
+ the element, provided that the elements along the path and their
2242
+ list of immediate children were not modified in between.
2243
+
2244
+ ElementPath has the advantage over an XPath expression (as returned
2245
+ by the .getpath() method) that it does not require additional prefix
2246
+ declarations. It is always self-contained.
2247
+ """
2248
+ cdef _Element root
2249
+ cdef Py_ssize_t count
2250
+ _assertValidNode(element)
2251
+ if element._c_node.type != tree.XML_ELEMENT_NODE:
2252
+ raise ValueError, "input is not an Element"
2253
+ if self._context_node is not None:
2254
+ root = self._context_node
2255
+ elif self._doc is not None:
2256
+ root = self._doc.getroot()
2257
+ else:
2258
+ raise ValueError, "Element is not in this tree"
2259
+ _assertValidNode(root)
2260
+ if element._doc is not root._doc:
2261
+ raise ValueError, "Element is not in this tree"
2262
+
2263
+ path = []
2264
+ c_element = element._c_node
2265
+ while c_element is not root._c_node:
2266
+ c_name = c_element.name
2267
+ c_href = _getNs(c_element)
2268
+ tag = _namespacedNameFromNsName(c_href, c_name)
2269
+ if c_href is NULL:
2270
+ c_href = <const_xmlChar*>b'' # no namespace (NULL is wildcard)
2271
+ # use tag[N] if there are preceding siblings with the same tag
2272
+ count = 0
2273
+ c_node = c_element.prev
2274
+ while c_node is not NULL:
2275
+ if c_node.type == tree.XML_ELEMENT_NODE:
2276
+ if _tagMatches(c_node, c_href, c_name):
2277
+ count += 1
2278
+ c_node = c_node.prev
2279
+ if count:
2280
+ tag = f'{tag}[{count+1}]'
2281
+ else:
2282
+ # use tag[1] if there are following siblings with the same tag
2283
+ c_node = c_element.next
2284
+ while c_node is not NULL:
2285
+ if c_node.type == tree.XML_ELEMENT_NODE:
2286
+ if _tagMatches(c_node, c_href, c_name):
2287
+ tag += '[1]'
2288
+ break
2289
+ c_node = c_node.next
2290
+
2291
+ path.append(tag)
2292
+ c_element = c_element.parent
2293
+ if c_element is NULL or c_element.type != tree.XML_ELEMENT_NODE:
2294
+ raise ValueError, "Element is not in this tree."
2295
+ if not path:
2296
+ return '.'
2297
+ path.reverse()
2298
+ return '/'.join(path)
2299
+
2300
+ def getiterator(self, tag=None, *tags):
2301
+ """getiterator(self, *tags, tag=None)
2302
+
2303
+ Returns a sequence or iterator of all elements in document order
2304
+ (depth first pre-order), starting with the root element.
2305
+
2306
+ Can be restricted to find only elements with specific tags,
2307
+ see `_Element.iter`.
2308
+
2309
+ :deprecated: Note that this method is deprecated as of
2310
+ ElementTree 1.3 and lxml 2.0. It returns an iterator in
2311
+ lxml, which diverges from the original ElementTree
2312
+ behaviour. If you want an efficient iterator, use the
2313
+ ``tree.iter()`` method instead. You should only use this
2314
+ method in new code if you require backwards compatibility
2315
+ with older versions of lxml or ElementTree.
2316
+ """
2317
+ root = self.getroot()
2318
+ if root is None:
2319
+ return ITER_EMPTY
2320
+ if tag is not None:
2321
+ tags += (tag,)
2322
+ return root.getiterator(*tags)
2323
+
2324
+ def iter(self, tag=None, *tags):
2325
+ """iter(self, tag=None, *tags)
2326
+
2327
+ Creates an iterator for the root element. The iterator loops over
2328
+ all elements in this tree, in document order. Note that siblings
2329
+ of the root element (comments or processing instructions) are not
2330
+ returned by the iterator.
2331
+
2332
+ Can be restricted to find only elements with specific tags,
2333
+ see `_Element.iter`.
2334
+ """
2335
+ root = self.getroot()
2336
+ if root is None:
2337
+ return ITER_EMPTY
2338
+ if tag is not None:
2339
+ tags += (tag,)
2340
+ return root.iter(*tags)
2341
+
2342
+ def find(self, path, namespaces=None):
2343
+ """find(self, path, namespaces=None)
2344
+
2345
+ Finds the first toplevel element with given tag. Same as
2346
+ ``tree.getroot().find(path)``.
2347
+
2348
+ The optional ``namespaces`` argument accepts a
2349
+ prefix-to-namespace mapping that allows the usage of XPath
2350
+ prefixes in the path expression.
2351
+ """
2352
+ self._assertHasRoot()
2353
+ root = self.getroot()
2354
+ if _isString(path):
2355
+ if path[:1] == "/":
2356
+ path = "." + path
2357
+ from warnings import warn
2358
+ warn(
2359
+ "This search incorrectly ignores the root element, and will be "
2360
+ "fixed in a future version. If you rely on the current "
2361
+ f"behaviour, change it to {path!r}",
2362
+ FutureWarning, stacklevel=1
2363
+ )
2364
+ return root.find(path, namespaces)
2365
+
2366
+ def findtext(self, path, default=None, namespaces=None):
2367
+ """findtext(self, path, default=None, namespaces=None)
2368
+
2369
+ Finds the text for the first element matching the ElementPath
2370
+ expression. Same as getroot().findtext(path)
2371
+
2372
+ The optional ``namespaces`` argument accepts a
2373
+ prefix-to-namespace mapping that allows the usage of XPath
2374
+ prefixes in the path expression.
2375
+ """
2376
+ self._assertHasRoot()
2377
+ root = self.getroot()
2378
+ if _isString(path):
2379
+ if path[:1] == "/":
2380
+ path = "." + path
2381
+ from warnings import warn
2382
+ warn(
2383
+ "This search incorrectly ignores the root element, and will be "
2384
+ "fixed in a future version. If you rely on the current "
2385
+ f"behaviour, change it to {path!r}",
2386
+ FutureWarning, stacklevel=1
2387
+ )
2388
+ return root.findtext(path, default, namespaces)
2389
+
2390
+ def findall(self, path, namespaces=None):
2391
+ """findall(self, path, namespaces=None)
2392
+
2393
+ Finds all elements matching the ElementPath expression. Same as
2394
+ getroot().findall(path).
2395
+
2396
+ The optional ``namespaces`` argument accepts a
2397
+ prefix-to-namespace mapping that allows the usage of XPath
2398
+ prefixes in the path expression.
2399
+ """
2400
+ self._assertHasRoot()
2401
+ root = self.getroot()
2402
+ if _isString(path):
2403
+ if path[:1] == "/":
2404
+ path = "." + path
2405
+ from warnings import warn
2406
+ warn(
2407
+ "This search incorrectly ignores the root element, and will be "
2408
+ "fixed in a future version. If you rely on the current "
2409
+ f"behaviour, change it to {path!r}",
2410
+ FutureWarning, stacklevel=1
2411
+ )
2412
+ return root.findall(path, namespaces)
2413
+
2414
+ def iterfind(self, path, namespaces=None):
2415
+ """iterfind(self, path, namespaces=None)
2416
+
2417
+ Iterates over all elements matching the ElementPath expression.
2418
+ Same as getroot().iterfind(path).
2419
+
2420
+ The optional ``namespaces`` argument accepts a
2421
+ prefix-to-namespace mapping that allows the usage of XPath
2422
+ prefixes in the path expression.
2423
+ """
2424
+ self._assertHasRoot()
2425
+ root = self.getroot()
2426
+ if _isString(path):
2427
+ if path[:1] == "/":
2428
+ path = "." + path
2429
+ from warnings import warn
2430
+ warn(
2431
+ "This search incorrectly ignores the root element, and will be "
2432
+ "fixed in a future version. If you rely on the current "
2433
+ f"behaviour, change it to {path!r}",
2434
+ FutureWarning, stacklevel=1
2435
+ )
2436
+ return root.iterfind(path, namespaces)
2437
+
2438
+ def xpath(self, _path, *, namespaces=None, extensions=None,
2439
+ smart_strings=True, **_variables):
2440
+ """xpath(self, _path, namespaces=None, extensions=None, smart_strings=True, **_variables)
2441
+
2442
+ XPath evaluate in context of document.
2443
+
2444
+ ``namespaces`` is an optional dictionary with prefix to namespace URI
2445
+ mappings, used by XPath. ``extensions`` defines additional extension
2446
+ functions.
2447
+
2448
+ Returns a list (nodeset), or bool, float or string.
2449
+
2450
+ In case of a list result, return Element for element nodes,
2451
+ string for text and attribute values.
2452
+
2453
+ Note: if you are going to apply multiple XPath expressions
2454
+ against the same document, it is more efficient to use
2455
+ XPathEvaluator directly.
2456
+ """
2457
+ self._assertHasRoot()
2458
+ evaluator = XPathDocumentEvaluator(self, namespaces=namespaces,
2459
+ extensions=extensions,
2460
+ smart_strings=smart_strings)
2461
+ return evaluator(_path, **_variables)
2462
+
2463
+ def xslt(self, _xslt, extensions=None, access_control=None, **_kw):
2464
+ """xslt(self, _xslt, extensions=None, access_control=None, **_kw)
2465
+
2466
+ Transform this document using other document.
2467
+
2468
+ xslt is a tree that should be XSLT
2469
+ keyword parameters are XSLT transformation parameters.
2470
+
2471
+ Returns the transformed tree.
2472
+
2473
+ Note: if you are going to apply the same XSLT stylesheet against
2474
+ multiple documents, it is more efficient to use the XSLT
2475
+ class directly.
2476
+ """
2477
+ self._assertHasRoot()
2478
+ style = XSLT(_xslt, extensions=extensions,
2479
+ access_control=access_control)
2480
+ return style(self, **_kw)
2481
+
2482
+ def relaxng(self, relaxng):
2483
+ """relaxng(self, relaxng)
2484
+
2485
+ Validate this document using other document.
2486
+
2487
+ The relaxng argument is a tree that should contain a Relax NG schema.
2488
+
2489
+ Returns True or False, depending on whether validation
2490
+ succeeded.
2491
+
2492
+ Note: if you are going to apply the same Relax NG schema against
2493
+ multiple documents, it is more efficient to use the RelaxNG
2494
+ class directly.
2495
+ """
2496
+ self._assertHasRoot()
2497
+ schema = RelaxNG(relaxng)
2498
+ return schema.validate(self)
2499
+
2500
+ def xmlschema(self, xmlschema):
2501
+ """xmlschema(self, xmlschema)
2502
+
2503
+ Validate this document using other document.
2504
+
2505
+ The xmlschema argument is a tree that should contain an XML Schema.
2506
+
2507
+ Returns True or False, depending on whether validation
2508
+ succeeded.
2509
+
2510
+ Note: If you are going to apply the same XML Schema against
2511
+ multiple documents, it is more efficient to use the XMLSchema
2512
+ class directly.
2513
+ """
2514
+ self._assertHasRoot()
2515
+ schema = XMLSchema(xmlschema)
2516
+ return schema.validate(self)
2517
+
2518
+ def xinclude(self):
2519
+ """xinclude(self)
2520
+
2521
+ Process the XInclude nodes in this document and include the
2522
+ referenced XML fragments.
2523
+
2524
+ There is support for loading files through the file system, HTTP and
2525
+ FTP.
2526
+
2527
+ Note that XInclude does not support custom resolvers in Python space
2528
+ due to restrictions of libxml2 <= 2.6.29.
2529
+ """
2530
+ self._assertHasRoot()
2531
+ XInclude()(self._context_node)
2532
+
2533
+ def write_c14n(self, file, *, bint exclusive=False, bint with_comments=True,
2534
+ compression=0, inclusive_ns_prefixes=None):
2535
+ """write_c14n(self, file, exclusive=False, with_comments=True,
2536
+ compression=0, inclusive_ns_prefixes=None)
2537
+
2538
+ C14N write of document. Always writes UTF-8.
2539
+
2540
+ The ``compression`` option enables GZip compression level 1-9.
2541
+
2542
+ The ``inclusive_ns_prefixes`` should be a list of namespace strings
2543
+ (i.e. ['xs', 'xsi']) that will be promoted to the top-level element
2544
+ during exclusive C14N serialisation. This parameter is ignored if
2545
+ exclusive mode=False.
2546
+
2547
+ If exclusive=True and no list is provided, a namespace will only be
2548
+ rendered if it is used by the immediate parent or one of its attributes
2549
+ and its prefix and values have not already been rendered by an ancestor
2550
+ of the namespace node's parent element.
2551
+
2552
+ NOTE: This method is deprecated as of lxml 4.4 and will be removed in a
2553
+ future release. Use ``.write(f, method="c14n")`` instead.
2554
+ """
2555
+ self._assertHasRoot()
2556
+ _assertValidNode(self._context_node)
2557
+ if compression is None or compression < 0:
2558
+ compression = 0
2559
+
2560
+ _tofilelikeC14N(file, self._context_node, exclusive, with_comments,
2561
+ compression, inclusive_ns_prefixes)
2562
+
2563
+ cdef _ElementTree _elementTreeFactory(_Document doc, _Element context_node):
2564
+ return _newElementTree(doc, context_node, _ElementTree)
2565
+
2566
+ cdef _ElementTree _newElementTree(_Document doc, _Element context_node,
2567
+ object baseclass):
2568
+ cdef _ElementTree result
2569
+ result = baseclass()
2570
+ if context_node is None and doc is not None:
2571
+ context_node = doc.getroot()
2572
+ if context_node is None:
2573
+ _assertValidDoc(doc)
2574
+ result._doc = doc
2575
+ else:
2576
+ _assertValidNode(context_node)
2577
+ result._context_node = context_node
2578
+ return result
2579
+
2580
+
2581
+ @cython.final
2582
+ @cython.freelist(16)
2583
+ cdef class _Attrib:
2584
+ """A dict-like proxy for the ``Element.attrib`` property.
2585
+ """
2586
+ cdef _Element _element
2587
+ def __cinit__(self, _Element element not None):
2588
+ _assertValidNode(element)
2589
+ self._element = element
2590
+
2591
+ # MANIPULATORS
2592
+ def __setitem__(self, key, value):
2593
+ _assertValidNode(self._element)
2594
+ _setAttributeValue(self._element, key, value)
2595
+
2596
+ def __delitem__(self, key):
2597
+ _assertValidNode(self._element)
2598
+ _delAttribute(self._element, key)
2599
+
2600
+ def update(self, sequence_or_dict):
2601
+ _assertValidNode(self._element)
2602
+ if isinstance(sequence_or_dict, (dict, _Attrib)):
2603
+ sequence_or_dict = sequence_or_dict.items()
2604
+ for key, value in sequence_or_dict:
2605
+ _setAttributeValue(self._element, key, value)
2606
+
2607
+ def pop(self, key, *default):
2608
+ if len(default) > 1:
2609
+ raise TypeError, f"pop expected at most 2 arguments, got {len(default)+1}"
2610
+ _assertValidNode(self._element)
2611
+ result = _getAttributeValue(self._element, key, None)
2612
+ if result is None:
2613
+ if not default:
2614
+ raise KeyError, key
2615
+ result = default[0]
2616
+ else:
2617
+ _delAttribute(self._element, key)
2618
+ return result
2619
+
2620
+ def clear(self):
2621
+ _assertValidNode(self._element)
2622
+ c_attrs = self._element._c_node.properties
2623
+ if c_attrs:
2624
+ self._element._c_node.properties = NULL
2625
+ tree.xmlFreePropList(c_attrs)
2626
+
2627
+ # ACCESSORS
2628
+ def __repr__(self):
2629
+ _assertValidNode(self._element)
2630
+ return repr(dict( _collectAttributes(self._element._c_node, 3) ))
2631
+
2632
+ def __copy__(self):
2633
+ _assertValidNode(self._element)
2634
+ return dict(_collectAttributes(self._element._c_node, 3))
2635
+
2636
+ def __deepcopy__(self, memo):
2637
+ _assertValidNode(self._element)
2638
+ return dict(_collectAttributes(self._element._c_node, 3))
2639
+
2640
+ def __getitem__(self, key):
2641
+ _assertValidNode(self._element)
2642
+ result = _getAttributeValue(self._element, key, None)
2643
+ if result is None:
2644
+ raise KeyError, key
2645
+ return result
2646
+
2647
+ def __bool__(self):
2648
+ _assertValidNode(self._element)
2649
+ cdef xmlAttr* c_attr = self._element._c_node.properties
2650
+ while c_attr is not NULL:
2651
+ if c_attr.type == tree.XML_ATTRIBUTE_NODE:
2652
+ return 1
2653
+ c_attr = c_attr.next
2654
+ return 0
2655
+
2656
+ def __len__(self):
2657
+ _assertValidNode(self._element)
2658
+ cdef xmlAttr* c_attr = self._element._c_node.properties
2659
+ cdef Py_ssize_t c = 0
2660
+ while c_attr is not NULL:
2661
+ if c_attr.type == tree.XML_ATTRIBUTE_NODE:
2662
+ c += 1
2663
+ c_attr = c_attr.next
2664
+ return c
2665
+
2666
+ def get(self, key, default=None):
2667
+ _assertValidNode(self._element)
2668
+ return _getAttributeValue(self._element, key, default)
2669
+
2670
+ def keys(self):
2671
+ _assertValidNode(self._element)
2672
+ return _collectAttributes(self._element._c_node, 1)
2673
+
2674
+ def __iter__(self):
2675
+ _assertValidNode(self._element)
2676
+ return iter(_collectAttributes(self._element._c_node, 1))
2677
+
2678
+ def iterkeys(self):
2679
+ _assertValidNode(self._element)
2680
+ return iter(_collectAttributes(self._element._c_node, 1))
2681
+
2682
+ def values(self):
2683
+ _assertValidNode(self._element)
2684
+ return _collectAttributes(self._element._c_node, 2)
2685
+
2686
+ def itervalues(self):
2687
+ _assertValidNode(self._element)
2688
+ return iter(_collectAttributes(self._element._c_node, 2))
2689
+
2690
+ def items(self):
2691
+ _assertValidNode(self._element)
2692
+ return _collectAttributes(self._element._c_node, 3)
2693
+
2694
+ def iteritems(self):
2695
+ _assertValidNode(self._element)
2696
+ return iter(_collectAttributes(self._element._c_node, 3))
2697
+
2698
+ def has_key(self, key):
2699
+ _assertValidNode(self._element)
2700
+ return key in self
2701
+
2702
+ def __contains__(self, key):
2703
+ _assertValidNode(self._element)
2704
+ cdef xmlNode* c_node
2705
+ ns, tag = _getNsTag(key)
2706
+ c_node = self._element._c_node
2707
+ c_href = <const_xmlChar*>NULL if ns is None else _xcstr(ns)
2708
+ return 1 if tree.xmlHasNsProp(c_node, _xcstr(tag), c_href) else 0
2709
+
2710
+ def __richcmp__(self, other, int op):
2711
+ try:
2712
+ one = dict(self.items())
2713
+ if not isinstance(other, dict):
2714
+ other = dict(other)
2715
+ except (TypeError, ValueError):
2716
+ return NotImplemented
2717
+ return python.PyObject_RichCompare(one, other, op)
2718
+
2719
+ MutableMapping.register(_Attrib)
2720
+
2721
+
2722
+ @cython.final
2723
+ @cython.internal
2724
+ cdef class _AttribIterator:
2725
+ """Attribute iterator - for internal use only!
2726
+ """
2727
+ # XML attributes must not be removed while running!
2728
+ cdef _Element _node
2729
+ cdef xmlAttr* _c_attr
2730
+ cdef int _keysvalues # 1 - keys, 2 - values, 3 - items (key, value)
2731
+ def __iter__(self):
2732
+ return self
2733
+
2734
+ def __next__(self):
2735
+ cdef xmlAttr* c_attr
2736
+ if self._node is None:
2737
+ raise StopIteration
2738
+ c_attr = self._c_attr
2739
+ while c_attr is not NULL and c_attr.type != tree.XML_ATTRIBUTE_NODE:
2740
+ c_attr = c_attr.next
2741
+ if c_attr is NULL:
2742
+ self._node = None
2743
+ raise StopIteration
2744
+
2745
+ self._c_attr = c_attr.next
2746
+ if self._keysvalues == 1:
2747
+ return _namespacedName(<xmlNode*>c_attr)
2748
+ elif self._keysvalues == 2:
2749
+ return _attributeValue(self._node._c_node, c_attr)
2750
+ else:
2751
+ return (_namespacedName(<xmlNode*>c_attr),
2752
+ _attributeValue(self._node._c_node, c_attr))
2753
+
2754
+ cdef object _attributeIteratorFactory(_Element element, int keysvalues):
2755
+ cdef _AttribIterator attribs
2756
+ if element._c_node.properties is NULL:
2757
+ return ITER_EMPTY
2758
+ attribs = _AttribIterator()
2759
+ attribs._node = element
2760
+ attribs._c_attr = element._c_node.properties
2761
+ attribs._keysvalues = keysvalues
2762
+ return attribs
2763
+
2764
+
2765
+ cdef public class _ElementTagMatcher [ object LxmlElementTagMatcher,
2766
+ type LxmlElementTagMatcherType ]:
2767
+ """
2768
+ Dead but public. :)
2769
+ """
2770
+ cdef object _pystrings
2771
+ cdef int _node_type
2772
+ cdef char* _href
2773
+ cdef char* _name
2774
+ cdef _initTagMatch(self, tag):
2775
+ self._href = NULL
2776
+ self._name = NULL
2777
+ if tag is None:
2778
+ self._node_type = 0
2779
+ elif tag is Comment:
2780
+ self._node_type = tree.XML_COMMENT_NODE
2781
+ elif tag is ProcessingInstruction:
2782
+ self._node_type = tree.XML_PI_NODE
2783
+ elif tag is Entity:
2784
+ self._node_type = tree.XML_ENTITY_REF_NODE
2785
+ elif tag is Element:
2786
+ self._node_type = tree.XML_ELEMENT_NODE
2787
+ else:
2788
+ self._node_type = tree.XML_ELEMENT_NODE
2789
+ self._pystrings = _getNsTag(tag)
2790
+ if self._pystrings[0] is not None:
2791
+ self._href = _cstr(self._pystrings[0])
2792
+ self._name = _cstr(self._pystrings[1])
2793
+ if self._name[0] == c'*' and self._name[1] == c'\0':
2794
+ self._name = NULL
2795
+
2796
+ cdef public class _ElementIterator(_ElementTagMatcher) [
2797
+ object LxmlElementIterator, type LxmlElementIteratorType ]:
2798
+ """
2799
+ Dead but public. :)
2800
+ """
2801
+ # we keep Python references here to control GC
2802
+ cdef _Element _node
2803
+ cdef _node_to_node_function _next_element
2804
+ def __iter__(self):
2805
+ return self
2806
+
2807
+ cdef void _storeNext(self, _Element node):
2808
+ cdef xmlNode* c_node
2809
+ c_node = self._next_element(node._c_node)
2810
+ while c_node is not NULL and \
2811
+ self._node_type != 0 and \
2812
+ (<tree.xmlElementType>self._node_type != c_node.type or
2813
+ not _tagMatches(c_node, <const_xmlChar*>self._href, <const_xmlChar*>self._name)):
2814
+ c_node = self._next_element(c_node)
2815
+ if c_node is NULL:
2816
+ self._node = None
2817
+ else:
2818
+ # Python ref:
2819
+ self._node = _elementFactory(node._doc, c_node)
2820
+
2821
+ def __next__(self):
2822
+ cdef xmlNode* c_node
2823
+ cdef _Element current_node
2824
+ if self._node is None:
2825
+ raise StopIteration
2826
+ # Python ref:
2827
+ current_node = self._node
2828
+ self._storeNext(current_node)
2829
+ return current_node
2830
+
2831
+ @cython.final
2832
+ @cython.internal
2833
+ cdef class _MultiTagMatcher:
2834
+ """
2835
+ Match an xmlNode against a list of tags.
2836
+ """
2837
+ cdef list _py_tags
2838
+ cdef qname* _cached_tags
2839
+ cdef size_t _tag_count
2840
+ cdef size_t _cached_size
2841
+ cdef _Document _cached_doc
2842
+ cdef int _node_types
2843
+
2844
+ def __cinit__(self, tags):
2845
+ self._py_tags = []
2846
+ self.initTagMatch(tags)
2847
+
2848
+ def __dealloc__(self):
2849
+ self._clear()
2850
+
2851
+ cdef bint rejectsAll(self) noexcept:
2852
+ return not self._tag_count and not self._node_types
2853
+
2854
+ cdef bint rejectsAllAttributes(self) noexcept:
2855
+ return not self._tag_count
2856
+
2857
+ cdef bint matchesType(self, int node_type) noexcept:
2858
+ if node_type == tree.XML_ELEMENT_NODE and self._tag_count:
2859
+ return True
2860
+ return self._node_types & (1 << node_type)
2861
+
2862
+ cdef void _clear(self) noexcept:
2863
+ cdef size_t i, count
2864
+ count = self._tag_count
2865
+ self._tag_count = 0
2866
+ if self._cached_tags:
2867
+ for i in range(count):
2868
+ cpython.ref.Py_XDECREF(self._cached_tags[i].href)
2869
+ python.lxml_free(self._cached_tags)
2870
+ self._cached_tags = NULL
2871
+
2872
+ cdef initTagMatch(self, tags):
2873
+ self._cached_doc = None
2874
+ del self._py_tags[:]
2875
+ self._clear()
2876
+ if tags is None or tags == ():
2877
+ # no selection in tags argument => match anything
2878
+ self._node_types = (
2879
+ 1 << tree.XML_COMMENT_NODE |
2880
+ 1 << tree.XML_PI_NODE |
2881
+ 1 << tree.XML_ENTITY_REF_NODE |
2882
+ 1 << tree.XML_ELEMENT_NODE)
2883
+ else:
2884
+ self._node_types = 0
2885
+ self._storeTags(tags, set())
2886
+
2887
+ cdef _storeTags(self, tag, set seen):
2888
+ if tag is Comment:
2889
+ self._node_types |= 1 << tree.XML_COMMENT_NODE
2890
+ elif tag is ProcessingInstruction:
2891
+ self._node_types |= 1 << tree.XML_PI_NODE
2892
+ elif tag is Entity:
2893
+ self._node_types |= 1 << tree.XML_ENTITY_REF_NODE
2894
+ elif tag is Element:
2895
+ self._node_types |= 1 << tree.XML_ELEMENT_NODE
2896
+ elif python._isString(tag):
2897
+ if tag in seen:
2898
+ return
2899
+ seen.add(tag)
2900
+ if tag in ('*', '{*}*'):
2901
+ self._node_types |= 1 << tree.XML_ELEMENT_NODE
2902
+ else:
2903
+ href, name = _getNsTag(tag)
2904
+ if name == b'*':
2905
+ name = None
2906
+ if href is None:
2907
+ href = b'' # no namespace
2908
+ elif href == b'*':
2909
+ href = None # wildcard: any namespace, including none
2910
+ self._py_tags.append((href, name))
2911
+ elif isinstance(tag, QName):
2912
+ self._storeTags(tag.text, seen)
2913
+ else:
2914
+ # support a sequence of tags
2915
+ for item in tag:
2916
+ self._storeTags(item, seen)
2917
+
2918
+ cdef inline int cacheTags(self, _Document doc, bint force_into_dict=False) except -1:
2919
+ """
2920
+ Look up the tag names in the doc dict to enable string pointer comparisons.
2921
+ """
2922
+ cdef size_t dict_size = tree.xmlDictSize(doc._c_doc.dict)
2923
+ if doc is self._cached_doc and dict_size == self._cached_size:
2924
+ # doc and dict didn't change => names already cached
2925
+ return 0
2926
+ self._tag_count = 0
2927
+ if not self._py_tags:
2928
+ self._cached_doc = doc
2929
+ self._cached_size = dict_size
2930
+ return 0
2931
+ if not self._cached_tags:
2932
+ self._cached_tags = <qname*>python.lxml_malloc(len(self._py_tags), sizeof(qname))
2933
+ if not self._cached_tags:
2934
+ self._cached_doc = None
2935
+ raise MemoryError()
2936
+ self._tag_count = <size_t>_mapTagsToQnameMatchArray(
2937
+ doc._c_doc, self._py_tags, self._cached_tags, force_into_dict)
2938
+ self._cached_doc = doc
2939
+ self._cached_size = dict_size
2940
+ return 0
2941
+
2942
+ cdef inline bint matches(self, xmlNode* c_node) noexcept:
2943
+ cdef qname* c_qname
2944
+ if self._node_types & (1 << c_node.type):
2945
+ return True
2946
+ elif c_node.type == tree.XML_ELEMENT_NODE:
2947
+ for c_qname in self._cached_tags[:self._tag_count]:
2948
+ if _tagMatchesExactly(c_node, c_qname):
2949
+ return True
2950
+ return False
2951
+
2952
+ cdef inline bint matchesNsTag(self, const_xmlChar* c_href,
2953
+ const_xmlChar* c_name) noexcept:
2954
+ cdef qname* c_qname
2955
+ if self._node_types & (1 << tree.XML_ELEMENT_NODE):
2956
+ return True
2957
+ for c_qname in self._cached_tags[:self._tag_count]:
2958
+ if _nsTagMatchesExactly(c_href, c_name, c_qname):
2959
+ return True
2960
+ return False
2961
+
2962
+ cdef inline bint matchesAttribute(self, xmlAttr* c_attr) noexcept:
2963
+ """Attribute matches differ from Element matches in that they do
2964
+ not care about node types.
2965
+ """
2966
+ cdef qname* c_qname
2967
+ for c_qname in self._cached_tags[:self._tag_count]:
2968
+ if _tagMatchesExactly(<xmlNode*>c_attr, c_qname):
2969
+ return True
2970
+ return False
2971
+
2972
+ cdef class _ElementMatchIterator:
2973
+ cdef _Element _node
2974
+ cdef _node_to_node_function _next_element
2975
+ cdef _MultiTagMatcher _matcher
2976
+
2977
+ @cython.final
2978
+ cdef _initTagMatcher(self, tags):
2979
+ self._matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, tags)
2980
+
2981
+ def __iter__(self):
2982
+ return self
2983
+
2984
+ @cython.final
2985
+ cdef int _storeNext(self, _Element node) except -1:
2986
+ self._matcher.cacheTags(node._doc)
2987
+ c_node = self._next_element(node._c_node)
2988
+ while c_node is not NULL and not self._matcher.matches(c_node):
2989
+ c_node = self._next_element(c_node)
2990
+ # store Python ref to next node to make sure it's kept alive
2991
+ self._node = _elementFactory(node._doc, c_node) if c_node is not NULL else None
2992
+ return 0
2993
+
2994
+ def __next__(self):
2995
+ cdef _Element current_node = self._node
2996
+ if current_node is None:
2997
+ raise StopIteration
2998
+ self._storeNext(current_node)
2999
+ return current_node
3000
+
3001
+ cdef class ElementChildIterator(_ElementMatchIterator):
3002
+ """ElementChildIterator(self, node, tag=None, reversed=False)
3003
+ Iterates over the children of an element.
3004
+ """
3005
+ def __cinit__(self, _Element node not None, tag=None, *, bint reversed=False):
3006
+ cdef xmlNode* c_node
3007
+ _assertValidNode(node)
3008
+ self._initTagMatcher(tag)
3009
+ if reversed:
3010
+ c_node = _findChildBackwards(node._c_node, 0)
3011
+ self._next_element = _previousElement
3012
+ else:
3013
+ c_node = _findChildForwards(node._c_node, 0)
3014
+ self._next_element = _nextElement
3015
+ self._matcher.cacheTags(node._doc)
3016
+ while c_node is not NULL and not self._matcher.matches(c_node):
3017
+ c_node = self._next_element(c_node)
3018
+ # store Python ref to next node to make sure it's kept alive
3019
+ self._node = _elementFactory(node._doc, c_node) if c_node is not NULL else None
3020
+
3021
+ cdef class SiblingsIterator(_ElementMatchIterator):
3022
+ """SiblingsIterator(self, node, tag=None, preceding=False)
3023
+ Iterates over the siblings of an element.
3024
+
3025
+ You can pass the boolean keyword ``preceding`` to specify the direction.
3026
+ """
3027
+ def __cinit__(self, _Element node not None, tag=None, *, bint preceding=False):
3028
+ _assertValidNode(node)
3029
+ self._initTagMatcher(tag)
3030
+ if preceding:
3031
+ self._next_element = _previousElement
3032
+ else:
3033
+ self._next_element = _nextElement
3034
+ self._storeNext(node)
3035
+
3036
+ cdef class AncestorsIterator(_ElementMatchIterator):
3037
+ """AncestorsIterator(self, node, tag=None)
3038
+ Iterates over the ancestors of an element (from parent to parent).
3039
+ """
3040
+ def __cinit__(self, _Element node not None, tag=None):
3041
+ _assertValidNode(node)
3042
+ self._initTagMatcher(tag)
3043
+ self._next_element = _parentElement
3044
+ self._storeNext(node)
3045
+
3046
+ cdef class ElementDepthFirstIterator:
3047
+ """ElementDepthFirstIterator(self, node, tag=None, inclusive=True)
3048
+ Iterates over an element and its sub-elements in document order (depth
3049
+ first pre-order).
3050
+
3051
+ Note that this also includes comments, entities and processing
3052
+ instructions. To filter them out, check if the ``tag`` property
3053
+ of the returned element is a string (i.e. not None and not a
3054
+ factory function), or pass the ``Element`` factory for the ``tag``
3055
+ argument to receive only Elements.
3056
+
3057
+ If the optional ``tag`` argument is not None, the iterator returns only
3058
+ the elements that match the respective name and namespace.
3059
+
3060
+ The optional boolean argument 'inclusive' defaults to True and can be set
3061
+ to False to exclude the start element itself.
3062
+
3063
+ Note that the behaviour of this iterator is completely undefined if the
3064
+ tree it traverses is modified during iteration.
3065
+ """
3066
+ # we keep Python references here to control GC
3067
+ # keep the next Element after the one we return, and the (s)top node
3068
+ cdef _Element _next_node
3069
+ cdef _Element _top_node
3070
+ cdef _MultiTagMatcher _matcher
3071
+ def __cinit__(self, _Element node not None, tag=None, *, bint inclusive=True):
3072
+ _assertValidNode(node)
3073
+ self._top_node = node
3074
+ self._next_node = node
3075
+ self._matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, tag)
3076
+ self._matcher.cacheTags(node._doc)
3077
+ if not inclusive or not self._matcher.matches(node._c_node):
3078
+ # find start node (this cannot raise StopIteration, self._next_node != None)
3079
+ next(self)
3080
+
3081
+ def __iter__(self):
3082
+ return self
3083
+
3084
+ def __next__(self):
3085
+ cdef xmlNode* c_node
3086
+ cdef _Element current_node = self._next_node
3087
+ if current_node is None:
3088
+ raise StopIteration
3089
+ c_node = current_node._c_node
3090
+ self._matcher.cacheTags(current_node._doc)
3091
+ if not self._matcher._tag_count:
3092
+ # no tag name was found in the dict => not in document either
3093
+ # try to match by node type
3094
+ c_node = self._nextNodeAnyTag(c_node)
3095
+ else:
3096
+ c_node = self._nextNodeMatchTag(c_node)
3097
+ if c_node is NULL:
3098
+ self._next_node = None
3099
+ else:
3100
+ self._next_node = _elementFactory(current_node._doc, c_node)
3101
+ return current_node
3102
+
3103
+ @cython.final
3104
+ cdef xmlNode* _nextNodeAnyTag(self, xmlNode* c_node) noexcept:
3105
+ cdef int node_types = self._matcher._node_types
3106
+ if not node_types:
3107
+ return NULL
3108
+ tree.BEGIN_FOR_EACH_ELEMENT_FROM(self._top_node._c_node, c_node, 0)
3109
+ if node_types & (1 << c_node.type):
3110
+ return c_node
3111
+ tree.END_FOR_EACH_ELEMENT_FROM(c_node)
3112
+ return NULL
3113
+
3114
+ @cython.final
3115
+ cdef xmlNode* _nextNodeMatchTag(self, xmlNode* c_node) noexcept:
3116
+ tree.BEGIN_FOR_EACH_ELEMENT_FROM(self._top_node._c_node, c_node, 0)
3117
+ if self._matcher.matches(c_node):
3118
+ return c_node
3119
+ tree.END_FOR_EACH_ELEMENT_FROM(c_node)
3120
+ return NULL
3121
+
3122
+
3123
+ cdef class ElementTextIterator:
3124
+ """ElementTextIterator(self, element, tag=None, with_tail=True)
3125
+ Iterates over the text content of a subtree.
3126
+
3127
+ You can pass the ``tag`` keyword argument to restrict text content to a
3128
+ specific tag name.
3129
+
3130
+ You can set the ``with_tail`` keyword argument to ``False`` to skip over
3131
+ tail text (e.g. if you know that it's only whitespace from pretty-printing).
3132
+ """
3133
+ cdef object _events
3134
+ cdef _Element _start_element
3135
+ def __cinit__(self, _Element element not None, tag=None, *, bint with_tail=True):
3136
+ _assertValidNode(element)
3137
+ if with_tail:
3138
+ events = ("start", "comment", "pi", "end")
3139
+ else:
3140
+ events = ("start",)
3141
+ self._start_element = element
3142
+ self._events = iterwalk(element, events=events, tag=tag)
3143
+
3144
+ def __iter__(self):
3145
+ return self
3146
+
3147
+ def __next__(self):
3148
+ cdef _Element element
3149
+ result = None
3150
+ while result is None:
3151
+ event, element = next(self._events) # raises StopIteration
3152
+ if event == "start":
3153
+ result = element.text
3154
+ elif element is not self._start_element:
3155
+ result = element.tail
3156
+ return result
3157
+
3158
+
3159
+ cdef xmlNode* _createElement(xmlDoc* c_doc, object name_utf) except NULL:
3160
+ cdef xmlNode* c_node
3161
+ c_node = tree.xmlNewDocNode(c_doc, NULL, _xcstr(name_utf), NULL)
3162
+ return c_node
3163
+
3164
+ cdef xmlNode* _createComment(xmlDoc* c_doc, const_xmlChar* text) noexcept:
3165
+ cdef xmlNode* c_node
3166
+ c_node = tree.xmlNewDocComment(c_doc, text)
3167
+ return c_node
3168
+
3169
+ cdef xmlNode* _createPI(xmlDoc* c_doc, const_xmlChar* target, const_xmlChar* text) noexcept:
3170
+ cdef xmlNode* c_node
3171
+ c_node = tree.xmlNewDocPI(c_doc, target, text)
3172
+ return c_node
3173
+
3174
+ cdef xmlNode* _createEntity(xmlDoc* c_doc, const_xmlChar* name) noexcept:
3175
+ cdef xmlNode* c_node
3176
+ c_node = tree.xmlNewReference(c_doc, name)
3177
+ return c_node
3178
+
3179
+ # module-level API for ElementTree
3180
+
3181
+ from abc import ABC
3182
+
3183
+ class Element(ABC):
3184
+ """Element(_tag, attrib=None, nsmap=None, **_extra)
3185
+
3186
+ Element factory, as a class.
3187
+
3188
+ An instance of this class is an object implementing the
3189
+ Element interface.
3190
+
3191
+ >>> element = Element("test")
3192
+ >>> type(element)
3193
+ <class 'lxml.etree._Element'>
3194
+ >>> isinstance(element, Element)
3195
+ True
3196
+ >>> issubclass(_Element, Element)
3197
+ True
3198
+
3199
+ Also look at the `_Element.makeelement()` and
3200
+ `_BaseParser.makeelement()` methods, which provide a faster way to
3201
+ create an Element within a specific document or parser context.
3202
+ """
3203
+ def __new__(cls, _tag, attrib=None, nsmap=None, **_extra):
3204
+ return _makeElement(_tag, NULL, None, None, None, None,
3205
+ attrib, nsmap, _extra)
3206
+
3207
+ # Register _Element as a virtual subclass of Element
3208
+ Element.register(_Element)
3209
+
3210
+
3211
+ def Comment(text=None):
3212
+ """Comment(text=None)
3213
+
3214
+ Comment element factory. This factory function creates a special element that will
3215
+ be serialized as an XML comment.
3216
+ """
3217
+ cdef _Document doc
3218
+ cdef xmlNode* c_node
3219
+ cdef xmlDoc* c_doc
3220
+
3221
+ if text is None:
3222
+ text = b''
3223
+ else:
3224
+ text = _utf8(text)
3225
+ if b'--' in text or text.endswith(b'-'):
3226
+ raise ValueError("Comment may not contain '--' or end with '-'")
3227
+
3228
+ c_doc = _newXMLDoc()
3229
+ doc = _documentFactory(c_doc, None)
3230
+ c_node = _createComment(c_doc, _xcstr(text))
3231
+ tree.xmlAddChild(<xmlNode*>c_doc, c_node)
3232
+ return _elementFactory(doc, c_node)
3233
+
3234
+
3235
+ def ProcessingInstruction(target, text=None):
3236
+ """ProcessingInstruction(target, text=None)
3237
+
3238
+ ProcessingInstruction element factory. This factory function creates a
3239
+ special element that will be serialized as an XML processing instruction.
3240
+ """
3241
+ cdef _Document doc
3242
+ cdef xmlNode* c_node
3243
+ cdef xmlDoc* c_doc
3244
+
3245
+ target = _utf8(target)
3246
+ _tagValidOrRaise(target)
3247
+ if target.lower() == b'xml':
3248
+ raise ValueError, f"Invalid PI name '{target}'"
3249
+
3250
+ if text is None:
3251
+ text = b''
3252
+ else:
3253
+ text = _utf8(text)
3254
+ if b'?>' in text:
3255
+ raise ValueError, "PI text must not contain '?>'"
3256
+
3257
+ c_doc = _newXMLDoc()
3258
+ doc = _documentFactory(c_doc, None)
3259
+ c_node = _createPI(c_doc, _xcstr(target), _xcstr(text))
3260
+ tree.xmlAddChild(<xmlNode*>c_doc, c_node)
3261
+ return _elementFactory(doc, c_node)
3262
+
3263
+ PI = ProcessingInstruction
3264
+
3265
+
3266
+ cdef class CDATA:
3267
+ """CDATA(data)
3268
+
3269
+ CDATA factory. This factory creates an opaque data object that
3270
+ can be used to set Element text. The usual way to use it is::
3271
+
3272
+ >>> el = Element('content')
3273
+ >>> el.text = CDATA('a string')
3274
+
3275
+ >>> print(el.text)
3276
+ a string
3277
+ >>> print(tostring(el, encoding="unicode"))
3278
+ <content><![CDATA[a string]]></content>
3279
+ """
3280
+ cdef bytes _utf8_data
3281
+ def __cinit__(self, data):
3282
+ self._utf8_data = _utf8(data)
3283
+
3284
+
3285
+ def Entity(name):
3286
+ """Entity(name)
3287
+
3288
+ Entity factory. This factory function creates a special element
3289
+ that will be serialized as an XML entity reference or character
3290
+ reference. Note, however, that entities will not be automatically
3291
+ declared in the document. A document that uses entity references
3292
+ requires a DTD to define the entities.
3293
+ """
3294
+ cdef _Document doc
3295
+ cdef xmlNode* c_node
3296
+ cdef xmlDoc* c_doc
3297
+ name_utf = _utf8(name)
3298
+ c_name = _xcstr(name_utf)
3299
+ if c_name[0] == c'#':
3300
+ if not _characterReferenceIsValid(c_name + 1):
3301
+ raise ValueError, f"Invalid character reference: '{name}'"
3302
+ elif not _xmlNameIsValid(c_name):
3303
+ raise ValueError, f"Invalid entity reference: '{name}'"
3304
+ c_doc = _newXMLDoc()
3305
+ doc = _documentFactory(c_doc, None)
3306
+ c_node = _createEntity(c_doc, c_name)
3307
+ tree.xmlAddChild(<xmlNode*>c_doc, c_node)
3308
+ return _elementFactory(doc, c_node)
3309
+
3310
+
3311
+ def SubElement(_Element _parent not None, _tag,
3312
+ attrib=None, nsmap=None, **_extra):
3313
+ """SubElement(_parent, _tag, attrib=None, nsmap=None, **_extra)
3314
+
3315
+ Subelement factory. This function creates an element instance, and
3316
+ appends it to an existing element.
3317
+ """
3318
+ return _makeSubElement(_parent, _tag, None, None, attrib, nsmap, _extra)
3319
+
3320
+ from typing import Generic, TypeVar
3321
+
3322
+ T = TypeVar("T")
3323
+
3324
+ class ElementTree(ABC, Generic[T]):
3325
+ def __new__(cls, _Element element=None, *, file=None, _BaseParser parser=None):
3326
+ """ElementTree(element=None, file=None, parser=None)
3327
+
3328
+ ElementTree wrapper class.
3329
+ """
3330
+ cdef xmlNode* c_next
3331
+ cdef xmlNode* c_node
3332
+ cdef xmlNode* c_node_copy
3333
+ cdef xmlDoc* c_doc
3334
+ cdef _ElementTree etree
3335
+ cdef _Document doc
3336
+
3337
+ if element is not None:
3338
+ doc = element._doc
3339
+ elif file is not None:
3340
+ try:
3341
+ doc = _parseDocument(file, parser, None)
3342
+ except _TargetParserResult as result_container:
3343
+ return result_container.result
3344
+ else:
3345
+ c_doc = _newXMLDoc()
3346
+ doc = _documentFactory(c_doc, parser)
3347
+
3348
+ return _elementTreeFactory(doc, element)
3349
+
3350
+ # Register _ElementTree as a virtual subclass of ElementTree
3351
+ ElementTree.register(_ElementTree)
3352
+
3353
+ # Remove "ABC" and typing helpers from module dict
3354
+ del ABC, Generic, TypeVar, T
3355
+
3356
+ def HTML(text, _BaseParser parser=None, *, base_url=None):
3357
+ """HTML(text, parser=None, base_url=None)
3358
+
3359
+ Parses an HTML document from a string constant. Returns the root
3360
+ node (or the result returned by a parser target). This function
3361
+ can be used to embed "HTML literals" in Python code.
3362
+
3363
+ To override the parser with a different ``HTMLParser`` you can pass it to
3364
+ the ``parser`` keyword argument.
3365
+
3366
+ The ``base_url`` keyword argument allows to set the original base URL of
3367
+ the document to support relative Paths when looking up external entities
3368
+ (DTD, XInclude, ...).
3369
+ """
3370
+ cdef _Document doc
3371
+ if parser is None:
3372
+ parser = __GLOBAL_PARSER_CONTEXT.getDefaultParser()
3373
+ if not isinstance(parser, HTMLParser):
3374
+ parser = __DEFAULT_HTML_PARSER
3375
+ try:
3376
+ doc = _parseMemoryDocument(text, base_url, parser)
3377
+ return doc.getroot()
3378
+ except _TargetParserResult as result_container:
3379
+ return result_container.result
3380
+
3381
+
3382
+ def XML(text, _BaseParser parser=None, *, base_url=None):
3383
+ """XML(text, parser=None, base_url=None)
3384
+
3385
+ Parses an XML document or fragment from a string constant.
3386
+ Returns the root node (or the result returned by a parser target).
3387
+ This function can be used to embed "XML literals" in Python code,
3388
+ like in
3389
+
3390
+ >>> root = XML("<root><test/></root>")
3391
+ >>> print(root.tag)
3392
+ root
3393
+
3394
+ To override the parser with a different ``XMLParser`` you can pass it to
3395
+ the ``parser`` keyword argument.
3396
+
3397
+ The ``base_url`` keyword argument allows to set the original base URL of
3398
+ the document to support relative Paths when looking up external entities
3399
+ (DTD, XInclude, ...).
3400
+ """
3401
+ cdef _Document doc
3402
+ if parser is None:
3403
+ parser = __GLOBAL_PARSER_CONTEXT.getDefaultParser()
3404
+ if not isinstance(parser, XMLParser):
3405
+ parser = __DEFAULT_XML_PARSER
3406
+ try:
3407
+ doc = _parseMemoryDocument(text, base_url, parser)
3408
+ return doc.getroot()
3409
+ except _TargetParserResult as result_container:
3410
+ return result_container.result
3411
+
3412
+
3413
+ def fromstring(text, _BaseParser parser=None, *, base_url=None):
3414
+ """fromstring(text, parser=None, base_url=None)
3415
+
3416
+ Parses an XML document or fragment from a string. Returns the
3417
+ root node (or the result returned by a parser target).
3418
+
3419
+ To override the default parser with a different parser you can pass it to
3420
+ the ``parser`` keyword argument.
3421
+
3422
+ The ``base_url`` keyword argument allows to set the original base URL of
3423
+ the document to support relative Paths when looking up external entities
3424
+ (DTD, XInclude, ...).
3425
+ """
3426
+ cdef _Document doc
3427
+ try:
3428
+ doc = _parseMemoryDocument(text, base_url, parser)
3429
+ return doc.getroot()
3430
+ except _TargetParserResult as result_container:
3431
+ return result_container.result
3432
+
3433
+
3434
+ def fromstringlist(strings, _BaseParser parser=None):
3435
+ """fromstringlist(strings, parser=None)
3436
+
3437
+ Parses an XML document from a sequence of strings. Returns the
3438
+ root node (or the result returned by a parser target).
3439
+
3440
+ To override the default parser with a different parser you can pass it to
3441
+ the ``parser`` keyword argument.
3442
+ """
3443
+ cdef _Document doc
3444
+ if isinstance(strings, (bytes, unicode)):
3445
+ raise ValueError("passing a single string into fromstringlist() is not"
3446
+ " efficient, use fromstring() instead")
3447
+ if parser is None:
3448
+ parser = __GLOBAL_PARSER_CONTEXT.getDefaultParser()
3449
+ feed = parser.feed
3450
+ for data in strings:
3451
+ feed(data)
3452
+ return parser.close()
3453
+
3454
+
3455
+ def iselement(element):
3456
+ """iselement(element)
3457
+
3458
+ Checks if an object appears to be a valid element object.
3459
+ """
3460
+ return isinstance(element, _Element) and (<_Element>element)._c_node is not NULL
3461
+
3462
+
3463
+ def indent(tree, space=" ", *, Py_ssize_t level=0):
3464
+ """indent(tree, space=" ", level=0)
3465
+
3466
+ Indent an XML document by inserting newlines and indentation space
3467
+ after elements.
3468
+
3469
+ *tree* is the ElementTree or Element to modify. The (root) element
3470
+ itself will not be changed, but the tail text of all elements in its
3471
+ subtree will be adapted.
3472
+
3473
+ *space* is the whitespace to insert for each indentation level, two
3474
+ space characters by default.
3475
+
3476
+ *level* is the initial indentation level. Setting this to a higher
3477
+ value than 0 can be used for indenting subtrees that are more deeply
3478
+ nested inside of a document.
3479
+ """
3480
+ root = _rootNodeOrRaise(tree)
3481
+ if level < 0:
3482
+ raise ValueError(f"Initial indentation level must be >= 0, got {level}")
3483
+ if _hasChild(root._c_node):
3484
+ space = _utf8(space)
3485
+ indent = b"\n" + level * space
3486
+ _indent_children(root._c_node, 1, space, [indent, indent + space])
3487
+
3488
+
3489
+ cdef int _indent_children(xmlNode* c_node, Py_ssize_t level, bytes one_space, list indentations) except -1:
3490
+ # Reuse indentation strings for speed.
3491
+ if len(indentations) <= level:
3492
+ indentations.append(indentations[-1] + one_space)
3493
+
3494
+ # Start a new indentation level for the first child.
3495
+ child_indentation = indentations[level]
3496
+ if not _hasNonWhitespaceText(c_node):
3497
+ _setNodeText(c_node, child_indentation)
3498
+
3499
+ # Recursively indent all children.
3500
+ cdef xmlNode* c_child = _findChildForwards(c_node, 0)
3501
+ while c_child is not NULL:
3502
+ if _hasChild(c_child):
3503
+ _indent_children(c_child, level+1, one_space, indentations)
3504
+ c_next_child = _nextElement(c_child)
3505
+ if not _hasNonWhitespaceTail(c_child):
3506
+ if c_next_child is NULL:
3507
+ # Dedent after the last child.
3508
+ child_indentation = indentations[level-1]
3509
+ _setTailText(c_child, child_indentation)
3510
+ c_child = c_next_child
3511
+ return 0
3512
+
3513
+
3514
+ def dump(_Element elem not None, *, bint pretty_print=True, bint with_tail=True):
3515
+ """dump(elem, pretty_print=True, with_tail=True)
3516
+
3517
+ Writes an element tree or element structure to sys.stdout. This function
3518
+ should be used for debugging only.
3519
+ """
3520
+ xml = tostring(elem, pretty_print=pretty_print, with_tail=with_tail, encoding='unicode')
3521
+ if not pretty_print:
3522
+ xml += '\n'
3523
+ sys.stdout.write(xml)
3524
+
3525
+
3526
+ def tostring(element_or_tree, *, encoding=None, method="xml",
3527
+ xml_declaration=None, bint pretty_print=False, bint with_tail=True,
3528
+ standalone=None, doctype=None,
3529
+ # method='c14n'
3530
+ bint exclusive=False, inclusive_ns_prefixes=None,
3531
+ # method='c14n2'
3532
+ bint with_comments=True, bint strip_text=False,
3533
+ ):
3534
+ """tostring(element_or_tree, encoding=None, method="xml",
3535
+ xml_declaration=None, pretty_print=False, with_tail=True,
3536
+ standalone=None, doctype=None,
3537
+ exclusive=False, inclusive_ns_prefixes=None,
3538
+ with_comments=True, strip_text=False,
3539
+ )
3540
+
3541
+ Serialize an element to an encoded string representation of its XML
3542
+ tree.
3543
+
3544
+ Defaults to ASCII encoding without XML declaration. This
3545
+ behaviour can be configured with the keyword arguments 'encoding'
3546
+ (string) and 'xml_declaration' (bool). Note that changing the
3547
+ encoding to a non UTF-8 compatible encoding will enable a
3548
+ declaration by default.
3549
+
3550
+ You can also serialise to a Unicode string without declaration by
3551
+ passing the name ``'unicode'`` as encoding (or the ``str`` function
3552
+ in Py3 or ``unicode`` in Py2). This changes the return value from
3553
+ a byte string to an unencoded unicode string.
3554
+
3555
+ The keyword argument 'pretty_print' (bool) enables formatted XML.
3556
+
3557
+ The keyword argument 'method' selects the output method: 'xml',
3558
+ 'html', plain 'text' (text content without tags), 'c14n' or 'c14n2'.
3559
+ Default is 'xml'.
3560
+
3561
+ With ``method="c14n"`` (C14N version 1), the options ``exclusive``,
3562
+ ``with_comments`` and ``inclusive_ns_prefixes`` request exclusive
3563
+ C14N, include comments, and list the inclusive prefixes respectively.
3564
+
3565
+ With ``method="c14n2"`` (C14N version 2), the ``with_comments`` and
3566
+ ``strip_text`` options control the output of comments and text space
3567
+ according to C14N 2.0.
3568
+
3569
+ Passing a boolean value to the ``standalone`` option will output
3570
+ an XML declaration with the corresponding ``standalone`` flag.
3571
+
3572
+ The ``doctype`` option allows passing in a plain string that will
3573
+ be serialised before the XML tree. Note that passing in non
3574
+ well-formed content here will make the XML output non well-formed.
3575
+ Also, an existing doctype in the document tree will not be removed
3576
+ when serialising an ElementTree instance.
3577
+
3578
+ You can prevent the tail text of the element from being serialised
3579
+ by passing the boolean ``with_tail`` option. This has no impact
3580
+ on the tail text of children, which will always be serialised.
3581
+ """
3582
+ cdef bint write_declaration
3583
+ cdef int is_standalone
3584
+ # C14N serialisation
3585
+ if method in ('c14n', 'c14n2'):
3586
+ if encoding is not None:
3587
+ raise ValueError("Cannot specify encoding with C14N")
3588
+ if xml_declaration:
3589
+ raise ValueError("Cannot enable XML declaration in C14N")
3590
+ if method == 'c14n':
3591
+ return _tostringC14N(element_or_tree, exclusive, with_comments, inclusive_ns_prefixes)
3592
+ else:
3593
+ out = BytesIO()
3594
+ target = C14NWriterTarget(
3595
+ utf8_writer(out).write,
3596
+ with_comments=with_comments, strip_text=strip_text)
3597
+ _tree_to_target(element_or_tree, target)
3598
+ return out.getvalue()
3599
+ if not with_comments:
3600
+ raise ValueError("Can only discard comments in C14N serialisation")
3601
+ if strip_text:
3602
+ raise ValueError("Can only strip text in C14N 2.0 serialisation")
3603
+ if encoding is unicode or (encoding is not None and encoding.lower() == 'unicode'):
3604
+ if xml_declaration:
3605
+ raise ValueError, \
3606
+ "Serialisation to unicode must not request an XML declaration"
3607
+ write_declaration = 0
3608
+ encoding = unicode
3609
+ elif xml_declaration is None:
3610
+ # by default, write an XML declaration only for non-standard encodings
3611
+ write_declaration = encoding is not None and encoding.upper() not in \
3612
+ ('ASCII', 'UTF-8', 'UTF8', 'US-ASCII')
3613
+ else:
3614
+ write_declaration = xml_declaration
3615
+ if encoding is None:
3616
+ encoding = 'ASCII'
3617
+ if standalone is None:
3618
+ is_standalone = -1
3619
+ elif standalone:
3620
+ write_declaration = 1
3621
+ is_standalone = 1
3622
+ else:
3623
+ write_declaration = 1
3624
+ is_standalone = 0
3625
+
3626
+ if isinstance(element_or_tree, _Element):
3627
+ return _tostring(<_Element>element_or_tree, encoding, doctype, method,
3628
+ write_declaration, 0, pretty_print, with_tail,
3629
+ is_standalone)
3630
+ elif isinstance(element_or_tree, _ElementTree):
3631
+ return _tostring((<_ElementTree>element_or_tree)._context_node,
3632
+ encoding, doctype, method, write_declaration, 1,
3633
+ pretty_print, with_tail, is_standalone)
3634
+ else:
3635
+ raise TypeError, f"Type '{python._fqtypename(element_or_tree).decode('utf8')}' cannot be serialized."
3636
+
3637
+
3638
+
3639
+ def tostringlist(element_or_tree, *args, **kwargs):
3640
+ """tostringlist(element_or_tree, *args, **kwargs)
3641
+
3642
+ Serialize an element to an encoded string representation of its XML
3643
+ tree, stored in a list of partial strings.
3644
+
3645
+ This is purely for ElementTree 1.3 compatibility. The result is a
3646
+ single string wrapped in a list.
3647
+ """
3648
+ return [tostring(element_or_tree, *args, **kwargs)]
3649
+
3650
+
3651
+ def tounicode(element_or_tree, *, method="xml", bint pretty_print=False,
3652
+ bint with_tail=True, doctype=None):
3653
+ """tounicode(element_or_tree, method="xml", pretty_print=False,
3654
+ with_tail=True, doctype=None)
3655
+
3656
+ Serialize an element to the Python unicode representation of its XML
3657
+ tree.
3658
+
3659
+ :deprecated: use ``tostring(el, encoding='unicode')`` instead.
3660
+
3661
+ Note that the result does not carry an XML encoding declaration and is
3662
+ therefore not necessarily suited for serialization to byte streams without
3663
+ further treatment.
3664
+
3665
+ The boolean keyword argument 'pretty_print' enables formatted XML.
3666
+
3667
+ The keyword argument 'method' selects the output method: 'xml',
3668
+ 'html' or plain 'text'.
3669
+
3670
+ You can prevent the tail text of the element from being serialised
3671
+ by passing the boolean ``with_tail`` option. This has no impact
3672
+ on the tail text of children, which will always be serialised.
3673
+ """
3674
+ if isinstance(element_or_tree, _Element):
3675
+ return _tostring(<_Element>element_or_tree, unicode, doctype, method,
3676
+ 0, 0, pretty_print, with_tail, -1)
3677
+ elif isinstance(element_or_tree, _ElementTree):
3678
+ return _tostring((<_ElementTree>element_or_tree)._context_node,
3679
+ unicode, doctype, method, 0, 1, pretty_print,
3680
+ with_tail, -1)
3681
+ else:
3682
+ raise TypeError, f"Type '{type(element_or_tree)}' cannot be serialized."
3683
+
3684
+
3685
+ def parse(source, _BaseParser parser=None, *, base_url=None):
3686
+ """parse(source, parser=None, base_url=None)
3687
+
3688
+ Return an ElementTree object loaded with source elements. If no parser
3689
+ is provided as second argument, the default parser is used.
3690
+
3691
+ The ``source`` can be any of the following:
3692
+
3693
+ - a file name/path
3694
+ - a file object
3695
+ - a file-like object
3696
+ - a URL using the HTTP or FTP protocol
3697
+
3698
+ To parse from a string, use the ``fromstring()`` function instead.
3699
+
3700
+ Note that it is generally faster to parse from a file path or URL
3701
+ than from an open file object or file-like object. Transparent
3702
+ decompression from gzip compressed sources is supported (unless
3703
+ explicitly disabled in libxml2).
3704
+
3705
+ The ``base_url`` keyword allows setting a URL for the document
3706
+ when parsing from a file-like object. This is needed when looking
3707
+ up external entities (DTD, XInclude, ...) with relative paths.
3708
+ """
3709
+ cdef _Document doc
3710
+ try:
3711
+ doc = _parseDocument(source, parser, base_url)
3712
+ return _elementTreeFactory(doc, None)
3713
+ except _TargetParserResult as result_container:
3714
+ return result_container.result
3715
+
3716
+
3717
+ def adopt_external_document(capsule, _BaseParser parser=None):
3718
+ """adopt_external_document(capsule, parser=None)
3719
+
3720
+ Unpack a libxml2 document pointer from a PyCapsule and wrap it in an
3721
+ lxml ElementTree object.
3722
+
3723
+ This allows external libraries to build XML/HTML trees using libxml2
3724
+ and then pass them efficiently into lxml for further processing.
3725
+
3726
+ If a ``parser`` is provided, it will be used for configuring the
3727
+ lxml document. No parsing will be done.
3728
+
3729
+ The capsule must have the name ``"libxml2:xmlDoc"`` and its pointer
3730
+ value must reference a correct libxml2 document of type ``xmlDoc*``.
3731
+ The creator of the capsule must take care to correctly clean up the
3732
+ document using an appropriate capsule destructor. By default, the
3733
+ libxml2 document will be copied to let lxml safely own the memory
3734
+ of the internal tree that it uses.
3735
+
3736
+ If the capsule context is non-NULL, it must point to a C string that
3737
+ can be compared using ``strcmp()``. If the context string equals
3738
+ ``"destructor:xmlFreeDoc"``, the libxml2 document will not be copied
3739
+ but the capsule invalidated instead by clearing its destructor and
3740
+ name. That way, lxml takes ownership of the libxml2 document in memory
3741
+ without creating a copy first, and the capsule destructor will not be
3742
+ called. The document will then eventually be cleaned up by lxml using
3743
+ the libxml2 API function ``xmlFreeDoc()`` once it is no longer used.
3744
+
3745
+ If no copy is made, later modifications of the tree outside of lxml
3746
+ should not be attempted after transferring the ownership.
3747
+ """
3748
+ cdef xmlDoc* c_doc
3749
+ cdef bint is_owned = False
3750
+ c_doc = <xmlDoc*> python.lxml_unpack_xmldoc_capsule(capsule, &is_owned)
3751
+ doc = _adoptForeignDoc(c_doc, parser, is_owned)
3752
+ return _elementTreeFactory(doc, None)
3753
+
3754
+
3755
+ ################################################################################
3756
+ # Include submodules
3757
+
3758
+ include "readonlytree.pxi" # Read-only implementation of Element proxies
3759
+ include "classlookup.pxi" # Element class lookup mechanisms
3760
+ include "nsclasses.pxi" # Namespace implementation and registry
3761
+ include "docloader.pxi" # Support for custom document loaders
3762
+ include "parser.pxi" # XML and HTML parsers
3763
+ include "saxparser.pxi" # SAX-like Parser interface and tree builder
3764
+ include "parsertarget.pxi" # ET Parser target
3765
+ include "serializer.pxi" # XML output functions
3766
+ include "iterparse.pxi" # incremental XML parsing
3767
+ include "xmlid.pxi" # XMLID and IDDict
3768
+ include "xinclude.pxi" # XInclude
3769
+ include "cleanup.pxi" # Cleanup and recursive element removal functions
3770
+
3771
+
3772
+ ################################################################################
3773
+ # Include submodules for XPath and XSLT
3774
+
3775
+ include "extensions.pxi" # XPath/XSLT extension functions
3776
+ include "xpath.pxi" # XPath evaluation
3777
+ include "xslt.pxi" # XSL transformations
3778
+ include "xsltext.pxi" # XSL extension elements
3779
+
3780
+
3781
+ ################################################################################
3782
+ # Validation
3783
+
3784
+ cdef class DocumentInvalid(LxmlError):
3785
+ """Validation error.
3786
+
3787
+ Raised by all document validators when their ``assertValid(tree)``
3788
+ method fails.
3789
+ """
3790
+
3791
+
3792
+ cdef class _Validator:
3793
+ "Base class for XML validators."
3794
+ cdef _ErrorLog _error_log
3795
+ def __cinit__(self):
3796
+ self._error_log = _ErrorLog()
3797
+
3798
+ def validate(self, etree):
3799
+ """validate(self, etree)
3800
+
3801
+ Validate the document using this schema.
3802
+
3803
+ Returns true if document is valid, false if not.
3804
+ """
3805
+ return self(etree)
3806
+
3807
+ def assertValid(self, etree):
3808
+ """assertValid(self, etree)
3809
+
3810
+ Raises `DocumentInvalid` if the document does not comply with the schema.
3811
+ """
3812
+ if not self(etree):
3813
+ raise DocumentInvalid(self._error_log._buildExceptionMessage(
3814
+ "Document does not comply with schema"),
3815
+ self._error_log)
3816
+
3817
+ def assert_(self, etree):
3818
+ """assert_(self, etree)
3819
+
3820
+ Raises `AssertionError` if the document does not comply with the schema.
3821
+ """
3822
+ if not self(etree):
3823
+ raise AssertionError, self._error_log._buildExceptionMessage(
3824
+ "Document does not comply with schema")
3825
+
3826
+ cpdef _append_log_message(self, int domain, int type, int level, int line,
3827
+ message, filename):
3828
+ self._error_log._receiveGeneric(domain, type, level, line, message,
3829
+ filename)
3830
+
3831
+ cpdef _clear_error_log(self):
3832
+ self._error_log.clear()
3833
+
3834
+ @property
3835
+ def error_log(self):
3836
+ """The log of validation errors and warnings."""
3837
+ assert self._error_log is not None, "XPath evaluator not initialised"
3838
+ return self._error_log.copy()
3839
+
3840
+ include "dtd.pxi" # DTD
3841
+ include "relaxng.pxi" # RelaxNG
3842
+ include "xmlschema.pxi" # XMLSchema
3843
+ include "schematron.pxi" # Schematron (requires libxml2 2.6.21+)
3844
+
3845
+ ################################################################################
3846
+ # Public C API
3847
+
3848
+ include "public-api.pxi"
3849
+
3850
+ ################################################################################
3851
+ # Other stuff
3852
+
3853
+ include "debug.pxi"