lxml 6.0.0__cp39-cp39-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.cp39-win_arm64.pyd +0 -0
  4. lxml/_elementpath.py +343 -0
  5. lxml/apihelpers.pxi +1801 -0
  6. lxml/builder.cp39-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.cp39-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.cp39-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.cp39-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.cp39-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.cp39-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/xslt.pxi ADDED
@@ -0,0 +1,957 @@
1
+ # XSLT
2
+ from lxml.includes cimport xslt
3
+
4
+
5
+ cdef class XSLTError(LxmlError):
6
+ """Base class of all XSLT errors.
7
+ """
8
+
9
+ cdef class XSLTParseError(XSLTError):
10
+ """Error parsing a stylesheet document.
11
+ """
12
+
13
+ cdef class XSLTApplyError(XSLTError):
14
+ """Error running an XSL transformation.
15
+ """
16
+
17
+ class XSLTSaveError(XSLTError, SerialisationError):
18
+ """Error serialising an XSLT result.
19
+ """
20
+
21
+ cdef class XSLTExtensionError(XSLTError):
22
+ """Error registering an XSLT extension.
23
+ """
24
+
25
+
26
+ # version information
27
+ LIBXSLT_COMPILED_VERSION = __unpackIntVersion(xslt.LIBXSLT_VERSION)
28
+ LIBXSLT_VERSION = __unpackIntVersion(xslt.xsltLibxsltVersion)
29
+
30
+
31
+ ################################################################################
32
+ # Where do we store what?
33
+ #
34
+ # xsltStylesheet->doc->_private
35
+ # == _XSLTResolverContext for XSL stylesheet
36
+ #
37
+ # xsltTransformContext->_private
38
+ # == _XSLTResolverContext for transformed document
39
+ #
40
+ ################################################################################
41
+
42
+
43
+ ################################################################################
44
+ # XSLT document loaders
45
+
46
+ @cython.final
47
+ @cython.internal
48
+ cdef class _XSLTResolverContext(_ResolverContext):
49
+ cdef xmlDoc* _c_style_doc
50
+ cdef _BaseParser _parser
51
+
52
+ cdef _XSLTResolverContext _copy(self):
53
+ cdef _XSLTResolverContext context
54
+ context = _XSLTResolverContext()
55
+ _initXSLTResolverContext(context, self._parser)
56
+ context._c_style_doc = self._c_style_doc
57
+ return context
58
+
59
+ cdef _initXSLTResolverContext(_XSLTResolverContext context,
60
+ _BaseParser parser):
61
+ _initResolverContext(context, parser.resolvers)
62
+ context._parser = parser
63
+ context._c_style_doc = NULL
64
+
65
+ cdef xmlDoc* _xslt_resolve_from_python(const_xmlChar* c_uri, void* c_context,
66
+ int parse_options, int* error) with gil:
67
+ # call the Python document loaders
68
+ cdef _XSLTResolverContext context
69
+ cdef _ResolverRegistry resolvers
70
+ cdef _InputDocument doc_ref
71
+ cdef xmlDoc* c_doc
72
+ cdef xmlDoc* c_return_doc = NULL
73
+
74
+ error[0] = 0
75
+ context = <_XSLTResolverContext>c_context
76
+
77
+ # shortcut if we resolve the stylesheet itself
78
+ c_doc = context._c_style_doc
79
+ try:
80
+ if c_doc is not NULL and c_doc.URL is not NULL:
81
+ if tree.xmlStrcmp(c_uri, c_doc.URL) == 0:
82
+ c_return_doc = _copyDoc(c_doc, 1)
83
+ return c_return_doc # 'goto', see 'finally' below
84
+
85
+ # delegate to the Python resolvers
86
+ resolvers = context._resolvers
87
+ if tree.xmlStrncmp(<unsigned char*>'string://__STRING__XSLT__/', c_uri, 26) == 0:
88
+ c_uri += 26
89
+ uri = _decodeFilename(c_uri)
90
+ doc_ref = resolvers.resolve(uri, None, context)
91
+
92
+ if doc_ref is not None:
93
+ if doc_ref._type == PARSER_DATA_STRING:
94
+ c_return_doc = _parseDoc(
95
+ doc_ref._data_bytes, doc_ref._filename, context._parser)
96
+ elif doc_ref._type == PARSER_DATA_FILENAME:
97
+ c_return_doc = _parseDocFromFile(
98
+ doc_ref._filename, context._parser)
99
+ elif doc_ref._type == PARSER_DATA_FILE:
100
+ c_return_doc = _parseDocFromFilelike(
101
+ doc_ref._file, doc_ref._filename, context._parser)
102
+ elif doc_ref._type == PARSER_DATA_EMPTY:
103
+ c_return_doc = _newXMLDoc()
104
+ if c_return_doc is not NULL and c_return_doc.URL is NULL:
105
+ c_return_doc.URL = tree.xmlStrdup(c_uri)
106
+ except:
107
+ error[0] = 1
108
+ context._store_raised()
109
+ finally:
110
+ return c_return_doc # and swallow any further exceptions
111
+
112
+
113
+ cdef void _xslt_store_resolver_exception(const_xmlChar* c_uri, void* context,
114
+ xslt.xsltLoadType c_type) noexcept with gil:
115
+ try:
116
+ message = f"Cannot resolve URI {_decodeFilename(c_uri)}"
117
+ if c_type == xslt.XSLT_LOAD_DOCUMENT:
118
+ exception = XSLTApplyError(message)
119
+ else:
120
+ exception = XSLTParseError(message)
121
+ (<_XSLTResolverContext>context)._store_exception(exception)
122
+ except BaseException as e:
123
+ (<_XSLTResolverContext>context)._store_exception(e)
124
+ finally:
125
+ return # and swallow any further exceptions
126
+
127
+
128
+ cdef xmlDoc* _xslt_doc_loader(const_xmlChar* c_uri, tree.xmlDict* c_dict,
129
+ int parse_options, void* c_ctxt,
130
+ xslt.xsltLoadType c_type) noexcept nogil:
131
+ # nogil => no Python objects here, may be called without thread context !
132
+ cdef xmlDoc* c_doc
133
+ cdef xmlDoc* result
134
+ cdef void* c_pcontext
135
+ cdef int error = 0
136
+ # find resolver contexts of stylesheet and transformed doc
137
+ if c_type == xslt.XSLT_LOAD_DOCUMENT:
138
+ # transformation time
139
+ c_pcontext = (<xslt.xsltTransformContext*>c_ctxt)._private
140
+ elif c_type == xslt.XSLT_LOAD_STYLESHEET:
141
+ # include/import resolution while parsing
142
+ c_pcontext = (<xslt.xsltStylesheet*>c_ctxt).doc._private
143
+ else:
144
+ c_pcontext = NULL
145
+
146
+ if c_pcontext is NULL:
147
+ # can't call Python without context, fall back to default loader
148
+ return XSLT_DOC_DEFAULT_LOADER(
149
+ c_uri, c_dict, parse_options, c_ctxt, c_type)
150
+
151
+ c_doc = _xslt_resolve_from_python(c_uri, c_pcontext, parse_options, &error)
152
+ if c_doc is NULL and not error:
153
+ c_doc = XSLT_DOC_DEFAULT_LOADER(
154
+ c_uri, c_dict, parse_options, c_ctxt, c_type)
155
+ if c_doc is NULL:
156
+ _xslt_store_resolver_exception(c_uri, c_pcontext, c_type)
157
+
158
+ if c_doc is not NULL and c_type == xslt.XSLT_LOAD_STYLESHEET:
159
+ c_doc._private = c_pcontext
160
+ return c_doc
161
+
162
+ cdef xslt.xsltDocLoaderFunc XSLT_DOC_DEFAULT_LOADER = xslt.xsltDocDefaultLoader
163
+ xslt.xsltSetLoaderFunc(<xslt.xsltDocLoaderFunc>_xslt_doc_loader)
164
+
165
+ ################################################################################
166
+ # XSLT file/network access control
167
+
168
+ cdef class XSLTAccessControl:
169
+ """XSLTAccessControl(self, read_file=True, write_file=True, create_dir=True, read_network=True, write_network=True)
170
+
171
+ Access control for XSLT: reading/writing files, directories and
172
+ network I/O. Access to a type of resource is granted or denied by
173
+ passing any of the following boolean keyword arguments. All of
174
+ them default to True to allow access.
175
+
176
+ - read_file
177
+ - write_file
178
+ - create_dir
179
+ - read_network
180
+ - write_network
181
+
182
+ For convenience, there is also a class member `DENY_ALL` that
183
+ provides an XSLTAccessControl instance that is readily configured
184
+ to deny everything, and a `DENY_WRITE` member that denies all
185
+ write access but allows read access.
186
+
187
+ See `XSLT`.
188
+ """
189
+ cdef xslt.xsltSecurityPrefs* _prefs
190
+ def __cinit__(self):
191
+ self._prefs = xslt.xsltNewSecurityPrefs()
192
+ if self._prefs is NULL:
193
+ raise MemoryError()
194
+
195
+ def __init__(self, *, bint read_file=True, bint write_file=True, bint create_dir=True,
196
+ bint read_network=True, bint write_network=True):
197
+ self._setAccess(xslt.XSLT_SECPREF_READ_FILE, read_file)
198
+ self._setAccess(xslt.XSLT_SECPREF_WRITE_FILE, write_file)
199
+ self._setAccess(xslt.XSLT_SECPREF_CREATE_DIRECTORY, create_dir)
200
+ self._setAccess(xslt.XSLT_SECPREF_READ_NETWORK, read_network)
201
+ self._setAccess(xslt.XSLT_SECPREF_WRITE_NETWORK, write_network)
202
+
203
+ DENY_ALL = XSLTAccessControl(
204
+ read_file=False, write_file=False, create_dir=False,
205
+ read_network=False, write_network=False)
206
+
207
+ DENY_WRITE = XSLTAccessControl(
208
+ read_file=True, write_file=False, create_dir=False,
209
+ read_network=True, write_network=False)
210
+
211
+ def __dealloc__(self):
212
+ if self._prefs is not NULL:
213
+ xslt.xsltFreeSecurityPrefs(self._prefs)
214
+
215
+ @cython.final
216
+ cdef _setAccess(self, xslt.xsltSecurityOption option, bint allow):
217
+ cdef xslt.xsltSecurityCheck function
218
+ if allow:
219
+ function = xslt.xsltSecurityAllow
220
+ else:
221
+ function = xslt.xsltSecurityForbid
222
+ xslt.xsltSetSecurityPrefs(self._prefs, option, function)
223
+
224
+ @cython.final
225
+ cdef void _register_in_context(self, xslt.xsltTransformContext* ctxt) noexcept:
226
+ xslt.xsltSetCtxtSecurityPrefs(self._prefs, ctxt)
227
+
228
+ @property
229
+ def options(self):
230
+ """The access control configuration as a map of options."""
231
+ return {
232
+ 'read_file': self._optval(xslt.XSLT_SECPREF_READ_FILE),
233
+ 'write_file': self._optval(xslt.XSLT_SECPREF_WRITE_FILE),
234
+ 'create_dir': self._optval(xslt.XSLT_SECPREF_CREATE_DIRECTORY),
235
+ 'read_network': self._optval(xslt.XSLT_SECPREF_READ_NETWORK),
236
+ 'write_network': self._optval(xslt.XSLT_SECPREF_WRITE_NETWORK),
237
+ }
238
+
239
+ @cython.final
240
+ cdef _optval(self, xslt.xsltSecurityOption option):
241
+ cdef xslt.xsltSecurityCheck function
242
+ function = xslt.xsltGetSecurityPrefs(self._prefs, option)
243
+ if function is <xslt.xsltSecurityCheck>xslt.xsltSecurityAllow:
244
+ return True
245
+ elif function is <xslt.xsltSecurityCheck>xslt.xsltSecurityForbid:
246
+ return False
247
+ else:
248
+ return None
249
+
250
+ def __repr__(self):
251
+ items = sorted(self.options.items())
252
+ return "%s(%s)" % (
253
+ python._fqtypename(self).decode('UTF-8').split('.')[-1],
254
+ ', '.join(["%s=%r" % item for item in items]))
255
+
256
+ ################################################################################
257
+ # XSLT
258
+
259
+ cdef int _register_xslt_function(void* ctxt, name_utf, ns_utf) noexcept:
260
+ if ns_utf is None:
261
+ return 0
262
+ # libxml2 internalises the strings if ctxt has a dict
263
+ return xslt.xsltRegisterExtFunction(
264
+ <xslt.xsltTransformContext*>ctxt, _xcstr(name_utf), _xcstr(ns_utf),
265
+ <xslt.xmlXPathFunction>_xpath_function_call)
266
+
267
+ cdef dict EMPTY_DICT = {}
268
+
269
+ @cython.final
270
+ @cython.internal
271
+ cdef class _XSLTContext(_BaseContext):
272
+ cdef xslt.xsltTransformContext* _xsltCtxt
273
+ cdef _ReadOnlyElementProxy _extension_element_proxy
274
+ cdef dict _extension_elements
275
+ def __cinit__(self):
276
+ self._xsltCtxt = NULL
277
+ self._extension_elements = EMPTY_DICT
278
+
279
+ def __init__(self, namespaces, extensions, error_log, enable_regexp,
280
+ build_smart_strings):
281
+ if extensions is not None and extensions:
282
+ for ns_name_tuple, extension in extensions.items():
283
+ if ns_name_tuple[0] is None:
284
+ raise XSLTExtensionError, \
285
+ "extensions must not have empty namespaces"
286
+ if isinstance(extension, XSLTExtension):
287
+ if self._extension_elements is EMPTY_DICT:
288
+ self._extension_elements = {}
289
+ extensions = extensions.copy()
290
+ ns_utf = _utf8(ns_name_tuple[0])
291
+ name_utf = _utf8(ns_name_tuple[1])
292
+ self._extension_elements[(ns_utf, name_utf)] = extension
293
+ del extensions[ns_name_tuple]
294
+ _BaseContext.__init__(self, namespaces, extensions, error_log, enable_regexp,
295
+ build_smart_strings)
296
+
297
+ cdef _BaseContext _copy(self):
298
+ cdef _XSLTContext context
299
+ context = <_XSLTContext>_BaseContext._copy(self)
300
+ context._extension_elements = self._extension_elements
301
+ return context
302
+
303
+ cdef register_context(self, xslt.xsltTransformContext* xsltCtxt,
304
+ _Document doc):
305
+ self._xsltCtxt = xsltCtxt
306
+ self._set_xpath_context(xsltCtxt.xpathCtxt)
307
+ self._register_context(doc)
308
+ self.registerLocalFunctions(xsltCtxt, _register_xslt_function)
309
+ self.registerGlobalFunctions(xsltCtxt, _register_xslt_function)
310
+ _registerXSLTExtensions(xsltCtxt, self._extension_elements)
311
+
312
+ cdef free_context(self):
313
+ self._cleanup_context()
314
+ self._release_context()
315
+ if self._xsltCtxt is not NULL:
316
+ xslt.xsltFreeTransformContext(self._xsltCtxt)
317
+ self._xsltCtxt = NULL
318
+ self._release_temp_refs()
319
+
320
+
321
+ @cython.final
322
+ @cython.internal
323
+ @cython.freelist(8)
324
+ cdef class _XSLTQuotedStringParam:
325
+ """A wrapper class for literal XSLT string parameters that require
326
+ quote escaping.
327
+ """
328
+ cdef bytes strval
329
+ def __cinit__(self, strval):
330
+ self.strval = _utf8(strval)
331
+
332
+
333
+ @cython.no_gc_clear
334
+ cdef class XSLT:
335
+ """XSLT(self, xslt_input, extensions=None, regexp=True, access_control=None)
336
+
337
+ Turn an XSL document into an XSLT object.
338
+
339
+ Calling this object on a tree or Element will execute the XSLT::
340
+
341
+ transform = etree.XSLT(xsl_tree)
342
+ result = transform(xml_tree)
343
+
344
+ Keyword arguments of the constructor:
345
+
346
+ - extensions: a dict mapping ``(namespace, name)`` pairs to
347
+ extension functions or extension elements
348
+ - regexp: enable exslt regular expression support in XPath
349
+ (default: True)
350
+ - access_control: access restrictions for network or file
351
+ system (see `XSLTAccessControl`)
352
+
353
+ Keyword arguments of the XSLT call:
354
+
355
+ - profile_run: enable XSLT profiling and make the profile available
356
+ as XML document in ``result.xslt_profile`` (default: False)
357
+
358
+ Other keyword arguments of the call are passed to the stylesheet
359
+ as parameters.
360
+ """
361
+ cdef _XSLTContext _context
362
+ cdef xslt.xsltStylesheet* _c_style
363
+ cdef _XSLTResolverContext _xslt_resolver_context
364
+ cdef XSLTAccessControl _access_control
365
+ cdef _ErrorLog _error_log
366
+
367
+ def __cinit__(self):
368
+ self._c_style = NULL
369
+
370
+ def __init__(self, xslt_input, *, extensions=None, regexp=True,
371
+ access_control=None):
372
+ cdef xslt.xsltStylesheet* c_style = NULL
373
+ cdef xmlDoc* c_doc
374
+ cdef _Document doc
375
+ cdef _Element root_node
376
+
377
+ doc = _documentOrRaise(xslt_input)
378
+ root_node = _rootNodeOrRaise(xslt_input)
379
+
380
+ # set access control or raise TypeError
381
+ self._access_control = access_control
382
+
383
+ # make a copy of the document as stylesheet parsing modifies it
384
+ c_doc = _copyDocRoot(doc._c_doc, root_node._c_node)
385
+
386
+ # make sure we always have a stylesheet URL
387
+ if c_doc.URL is NULL:
388
+ doc_url_utf = python.PyUnicode_AsASCIIString(
389
+ f"string://__STRING__XSLT__/{id(self)}.xslt")
390
+ c_doc.URL = tree.xmlStrdup(_xcstr(doc_url_utf))
391
+
392
+ self._error_log = _ErrorLog()
393
+ self._xslt_resolver_context = _XSLTResolverContext()
394
+ _initXSLTResolverContext(self._xslt_resolver_context, doc._parser)
395
+ # keep a copy in case we need to access the stylesheet via 'document()'
396
+ self._xslt_resolver_context._c_style_doc = _copyDoc(c_doc, 1)
397
+ c_doc._private = <python.PyObject*>self._xslt_resolver_context
398
+
399
+ with self._error_log:
400
+ orig_loader = _register_document_loader()
401
+ c_style = xslt.xsltParseStylesheetDoc(c_doc)
402
+ _reset_document_loader(orig_loader)
403
+
404
+ if c_style is NULL or c_style.errors:
405
+ tree.xmlFreeDoc(c_doc)
406
+ if c_style is not NULL:
407
+ xslt.xsltFreeStylesheet(c_style)
408
+ self._xslt_resolver_context._raise_if_stored()
409
+ # last error seems to be the most accurate here
410
+ if self._error_log.last_error is not None and \
411
+ self._error_log.last_error.message:
412
+ raise XSLTParseError(self._error_log.last_error.message,
413
+ self._error_log)
414
+ else:
415
+ raise XSLTParseError(
416
+ self._error_log._buildExceptionMessage(
417
+ "Cannot parse stylesheet"),
418
+ self._error_log)
419
+
420
+ c_doc._private = NULL # no longer used!
421
+ self._c_style = c_style
422
+ self._context = _XSLTContext(None, extensions, self._error_log, regexp, True)
423
+
424
+ def __dealloc__(self):
425
+ if self._xslt_resolver_context is not None and \
426
+ self._xslt_resolver_context._c_style_doc is not NULL:
427
+ tree.xmlFreeDoc(self._xslt_resolver_context._c_style_doc)
428
+ # this cleans up the doc copy as well
429
+ if self._c_style is not NULL:
430
+ xslt.xsltFreeStylesheet(self._c_style)
431
+
432
+ @property
433
+ def error_log(self):
434
+ """The log of errors and warnings of an XSLT execution."""
435
+ return self._error_log.copy()
436
+
437
+ @staticmethod
438
+ def strparam(strval):
439
+ """strparam(strval)
440
+
441
+ Mark an XSLT string parameter that requires quote escaping
442
+ before passing it into the transformation. Use it like this::
443
+
444
+ result = transform(doc, some_strval = XSLT.strparam(
445
+ '''it's \"Monty Python's\" ...'''))
446
+
447
+ Escaped string parameters can be reused without restriction.
448
+ """
449
+ return _XSLTQuotedStringParam(strval)
450
+
451
+ @staticmethod
452
+ def set_global_max_depth(int max_depth):
453
+ """set_global_max_depth(max_depth)
454
+
455
+ The maximum traversal depth that the stylesheet engine will allow.
456
+ This does not only count the template recursion depth but also takes
457
+ the number of variables/parameters into account. The required setting
458
+ for a run depends on both the stylesheet and the input data.
459
+
460
+ Example::
461
+
462
+ XSLT.set_global_max_depth(5000)
463
+
464
+ Note that this is currently a global, module-wide setting because
465
+ libxslt does not support it at a per-stylesheet level.
466
+ """
467
+ if max_depth < 0:
468
+ raise ValueError("cannot set a maximum stylesheet traversal depth < 0")
469
+ xslt.xsltMaxDepth = max_depth
470
+
471
+ def tostring(self, _ElementTree result_tree):
472
+ """tostring(self, result_tree)
473
+
474
+ Save result doc to string based on stylesheet output method.
475
+
476
+ :deprecated: use str(result_tree) instead.
477
+ """
478
+ return str(result_tree)
479
+
480
+ def __deepcopy__(self, memo):
481
+ return self.__copy__()
482
+
483
+ def __copy__(self):
484
+ return _copyXSLT(self)
485
+
486
+ def __call__(self, _input, *, profile_run=False, **kw):
487
+ """__call__(self, _input, profile_run=False, **kw)
488
+
489
+ Execute the XSL transformation on a tree or Element.
490
+
491
+ Pass the ``profile_run`` option to get profile information
492
+ about the XSLT. The result of the XSLT will have a property
493
+ xslt_profile that holds an XML tree with profiling data.
494
+ """
495
+ cdef _XSLTContext context = None
496
+ cdef _XSLTResolverContext resolver_context
497
+ cdef _Document input_doc
498
+ cdef _Element root_node
499
+ cdef _Document result_doc
500
+ cdef _Document profile_doc = None
501
+ cdef xmlDoc* c_profile_doc
502
+ cdef xslt.xsltTransformContext* transform_ctxt
503
+ cdef xmlDoc* c_result = NULL
504
+ cdef xmlDoc* c_doc
505
+ cdef tree.xmlDict* c_dict
506
+ cdef const_char** params = NULL
507
+
508
+ assert self._c_style is not NULL, "XSLT stylesheet not initialised"
509
+ input_doc = _documentOrRaise(_input)
510
+ root_node = _rootNodeOrRaise(_input)
511
+
512
+ c_doc = _fakeRootDoc(input_doc._c_doc, root_node._c_node)
513
+
514
+ transform_ctxt = xslt.xsltNewTransformContext(self._c_style, c_doc)
515
+ if transform_ctxt is NULL:
516
+ _destroyFakeDoc(input_doc._c_doc, c_doc)
517
+ raise MemoryError()
518
+
519
+ # using the stylesheet dict is safer than using a possibly
520
+ # unrelated dict from the current thread. Almost all
521
+ # non-input tag/attr names will come from the stylesheet
522
+ # anyway.
523
+ if transform_ctxt.dict is not NULL:
524
+ xmlparser.xmlDictFree(transform_ctxt.dict)
525
+ if kw:
526
+ # parameter values are stored in the dict
527
+ # => avoid unnecessarily cluttering the global dict
528
+ transform_ctxt.dict = xmlparser.xmlDictCreateSub(self._c_style.doc.dict)
529
+ if transform_ctxt.dict is NULL:
530
+ xslt.xsltFreeTransformContext(transform_ctxt)
531
+ raise MemoryError()
532
+ else:
533
+ transform_ctxt.dict = self._c_style.doc.dict
534
+ xmlparser.xmlDictReference(transform_ctxt.dict)
535
+
536
+ xslt.xsltSetCtxtParseOptions(
537
+ transform_ctxt, input_doc._parser._parse_options)
538
+
539
+ if profile_run:
540
+ transform_ctxt.profile = 1
541
+
542
+ try:
543
+ context = self._context._copy()
544
+ context.register_context(transform_ctxt, input_doc)
545
+
546
+ resolver_context = self._xslt_resolver_context._copy()
547
+ transform_ctxt._private = <python.PyObject*>resolver_context
548
+
549
+ _convert_xslt_parameters(transform_ctxt, kw, &params)
550
+ c_result = self._run_transform(
551
+ c_doc, params, context, transform_ctxt)
552
+ if params is not NULL:
553
+ # deallocate space for parameters
554
+ python.lxml_free(params)
555
+
556
+ if transform_ctxt.state != xslt.XSLT_STATE_OK:
557
+ if c_result is not NULL:
558
+ tree.xmlFreeDoc(c_result)
559
+ c_result = NULL
560
+
561
+ if transform_ctxt.profile:
562
+ c_profile_doc = xslt.xsltGetProfileInformation(transform_ctxt)
563
+ if c_profile_doc is not NULL:
564
+ profile_doc = _documentFactory(
565
+ c_profile_doc, input_doc._parser)
566
+ finally:
567
+ if context is not None:
568
+ context.free_context()
569
+ _destroyFakeDoc(input_doc._c_doc, c_doc)
570
+
571
+ try:
572
+ if resolver_context is not None and resolver_context._has_raised():
573
+ if c_result is not NULL:
574
+ tree.xmlFreeDoc(c_result)
575
+ c_result = NULL
576
+ resolver_context._raise_if_stored()
577
+
578
+ if context._exc._has_raised():
579
+ if c_result is not NULL:
580
+ tree.xmlFreeDoc(c_result)
581
+ c_result = NULL
582
+ context._exc._raise_if_stored()
583
+
584
+ if c_result is NULL:
585
+ # last error seems to be the most accurate here
586
+ error = self._error_log.last_error
587
+ if error is not None and error.message:
588
+ if error.line > 0:
589
+ message = f"{error.message}, line {error.line}"
590
+ else:
591
+ message = error.message
592
+ elif error is not None and error.line > 0:
593
+ message = f"Error applying stylesheet, line {error.line}"
594
+ else:
595
+ message = "Error applying stylesheet"
596
+ raise XSLTApplyError(message, self._error_log)
597
+ finally:
598
+ if resolver_context is not None:
599
+ resolver_context.clear()
600
+
601
+ result_doc = _documentFactory(c_result, input_doc._parser)
602
+
603
+ c_dict = c_result.dict
604
+ xmlparser.xmlDictReference(c_dict)
605
+ __GLOBAL_PARSER_CONTEXT.initThreadDictRef(&c_result.dict)
606
+ if c_dict is not c_result.dict or \
607
+ self._c_style.doc.dict is not c_result.dict or \
608
+ input_doc._c_doc.dict is not c_result.dict:
609
+ with nogil:
610
+ if c_dict is not c_result.dict:
611
+ fixThreadDictNames(<xmlNode*>c_result,
612
+ c_dict, c_result.dict)
613
+ if self._c_style.doc.dict is not c_result.dict:
614
+ fixThreadDictNames(<xmlNode*>c_result,
615
+ self._c_style.doc.dict, c_result.dict)
616
+ if input_doc._c_doc.dict is not c_result.dict:
617
+ fixThreadDictNames(<xmlNode*>c_result,
618
+ input_doc._c_doc.dict, c_result.dict)
619
+ xmlparser.xmlDictFree(c_dict)
620
+
621
+ return _xsltResultTreeFactory(result_doc, self, profile_doc)
622
+
623
+ cdef xmlDoc* _run_transform(self, xmlDoc* c_input_doc,
624
+ const_char** params, _XSLTContext context,
625
+ xslt.xsltTransformContext* transform_ctxt):
626
+ cdef xmlDoc* c_result
627
+ xslt.xsltSetTransformErrorFunc(transform_ctxt, <void*>self._error_log,
628
+ <xmlerror.xmlGenericErrorFunc>_receiveXSLTError)
629
+ if self._access_control is not None:
630
+ self._access_control._register_in_context(transform_ctxt)
631
+ with self._error_log, nogil:
632
+ orig_loader = _register_document_loader()
633
+ c_result = xslt.xsltApplyStylesheetUser(
634
+ self._c_style, c_input_doc, params, NULL, NULL, transform_ctxt)
635
+ _reset_document_loader(orig_loader)
636
+ return c_result
637
+
638
+
639
+ cdef _convert_xslt_parameters(xslt.xsltTransformContext* transform_ctxt,
640
+ dict parameters, const_char*** params_ptr):
641
+ cdef Py_ssize_t i, parameter_count
642
+ cdef const_char** params
643
+ cdef tree.xmlDict* c_dict = transform_ctxt.dict
644
+ params_ptr[0] = NULL
645
+ parameter_count = len(parameters)
646
+ if parameter_count == 0:
647
+ return
648
+ # allocate space for parameters
649
+ # * 2 as we want an entry for both key and value,
650
+ # and + 1 as array is NULL terminated
651
+ params = <const_char**>python.lxml_malloc(parameter_count * 2 + 1, sizeof(const_char*))
652
+ if not params:
653
+ raise MemoryError()
654
+ try:
655
+ i = 0
656
+ for key, value in parameters.iteritems():
657
+ k = _utf8(key)
658
+ if isinstance(value, _XSLTQuotedStringParam):
659
+ v = (<_XSLTQuotedStringParam>value).strval
660
+ xslt.xsltQuoteOneUserParam(
661
+ transform_ctxt, _xcstr(k), _xcstr(v))
662
+ else:
663
+ if isinstance(value, XPath):
664
+ v = (<XPath>value)._path
665
+ else:
666
+ v = _utf8(value)
667
+
668
+ c_len = len(k)
669
+ if c_len > limits.INT_MAX:
670
+ raise ValueError("Parameter name too long")
671
+ params[i] = <const_char*> tree.xmlDictLookup(c_dict, _xcstr(k), <int> c_len)
672
+ i += 1
673
+ c_len = len(v)
674
+ if c_len > limits.INT_MAX:
675
+ raise ValueError("Parameter value too long")
676
+ params[i] = <const_char*> tree.xmlDictLookup(c_dict, _xcstr(v), <int> c_len)
677
+ i += 1
678
+ except:
679
+ python.lxml_free(params)
680
+ raise
681
+ params[i] = NULL
682
+ params_ptr[0] = params
683
+
684
+ cdef XSLT _copyXSLT(XSLT stylesheet):
685
+ cdef XSLT new_xslt
686
+ cdef xmlDoc* c_doc
687
+ assert stylesheet._c_style is not NULL, "XSLT stylesheet not initialised"
688
+ new_xslt = XSLT.__new__(XSLT)
689
+ new_xslt._access_control = stylesheet._access_control
690
+ new_xslt._error_log = _ErrorLog()
691
+ new_xslt._context = stylesheet._context._copy()
692
+
693
+ new_xslt._xslt_resolver_context = stylesheet._xslt_resolver_context._copy()
694
+ new_xslt._xslt_resolver_context._c_style_doc = _copyDoc(
695
+ stylesheet._xslt_resolver_context._c_style_doc, 1)
696
+
697
+ c_doc = _copyDoc(stylesheet._c_style.doc, 1)
698
+ new_xslt._c_style = xslt.xsltParseStylesheetDoc(c_doc)
699
+ if new_xslt._c_style is NULL:
700
+ tree.xmlFreeDoc(c_doc)
701
+ raise MemoryError()
702
+
703
+ return new_xslt
704
+
705
+ @cython.final
706
+ cdef class _XSLTResultTree(_ElementTree):
707
+ """The result of an XSLT evaluation.
708
+
709
+ Use ``str()`` or ``bytes()`` (or ``unicode()`` in Python 2.x) to serialise to a string,
710
+ and the ``.write_output()`` method to write serialise to a file.
711
+ """
712
+ cdef XSLT _xslt
713
+ cdef _Document _profile
714
+ cdef xmlChar* _buffer
715
+ cdef Py_ssize_t _buffer_len
716
+ cdef Py_ssize_t _buffer_refcnt
717
+
718
+ def write_output(self, file, *, compression=0):
719
+ """write_output(self, file, *, compression=0)
720
+
721
+ Serialise the XSLT output to a file or file-like object.
722
+
723
+ As opposed to the generic ``.write()`` method, ``.write_output()`` serialises
724
+ the result as defined by the ``<xsl:output>`` tag.
725
+ """
726
+ cdef _FilelikeWriter writer = None
727
+ cdef _Document doc
728
+ cdef int r, rclose, c_compression
729
+ cdef const_xmlChar* c_encoding = NULL
730
+ cdef tree.xmlOutputBuffer* c_buffer
731
+
732
+ if self._context_node is not None:
733
+ doc = self._context_node._doc
734
+ else:
735
+ doc = None
736
+ if doc is None:
737
+ doc = self._doc
738
+ if doc is None:
739
+ raise XSLTSaveError("No document to serialise")
740
+ c_compression = compression or 0
741
+ xslt.LXML_GET_XSLT_ENCODING(c_encoding, self._xslt._c_style)
742
+ writer = _create_output_buffer(file, <const_char*>c_encoding, c_compression, &c_buffer, close=False)
743
+ if writer is None:
744
+ with nogil:
745
+ r = xslt.xsltSaveResultTo(c_buffer, doc._c_doc, self._xslt._c_style)
746
+ rclose = tree.xmlOutputBufferClose(c_buffer)
747
+ else:
748
+ r = xslt.xsltSaveResultTo(c_buffer, doc._c_doc, self._xslt._c_style)
749
+ rclose = tree.xmlOutputBufferClose(c_buffer)
750
+ if writer is not None:
751
+ writer._exc_context._raise_if_stored()
752
+ if r < 0 or rclose == -1:
753
+ python.PyErr_SetFromErrno(IOError) # raises IOError
754
+
755
+ cdef _saveToStringAndSize(self, xmlChar** s, int* l):
756
+ cdef _Document doc
757
+ cdef int r
758
+ if self._context_node is not None:
759
+ doc = self._context_node._doc
760
+ else:
761
+ doc = None
762
+ if doc is None:
763
+ doc = self._doc
764
+ if doc is None:
765
+ s[0] = NULL
766
+ return
767
+ with nogil:
768
+ r = xslt.xsltSaveResultToString(s, l, doc._c_doc,
769
+ self._xslt._c_style)
770
+ if r == -1:
771
+ raise MemoryError()
772
+
773
+ def __str__(self):
774
+ cdef xmlChar* encoding
775
+ cdef xmlChar* s = NULL
776
+ cdef int l = 0
777
+ self._saveToStringAndSize(&s, &l)
778
+ if s is NULL:
779
+ return ''
780
+ encoding = self._xslt._c_style.encoding
781
+ try:
782
+ if encoding is NULL:
783
+ result = s[:l].decode('UTF-8')
784
+ else:
785
+ result = s[:l].decode(encoding)
786
+ finally:
787
+ tree.xmlFree(s)
788
+ return _stripEncodingDeclaration(result)
789
+
790
+ def __getbuffer__(self, Py_buffer* buffer, int flags):
791
+ cdef int l = 0
792
+ if buffer is NULL:
793
+ return
794
+ if self._buffer is NULL or flags & python.PyBUF_WRITABLE:
795
+ self._saveToStringAndSize(<xmlChar**>&buffer.buf, &l)
796
+ buffer.len = l
797
+ if self._buffer is NULL and not flags & python.PyBUF_WRITABLE:
798
+ self._buffer = <xmlChar*>buffer.buf
799
+ self._buffer_len = l
800
+ self._buffer_refcnt = 1
801
+ else:
802
+ buffer.buf = self._buffer
803
+ buffer.len = self._buffer_len
804
+ self._buffer_refcnt += 1
805
+ if flags & python.PyBUF_WRITABLE:
806
+ buffer.readonly = 0
807
+ else:
808
+ buffer.readonly = 1
809
+ if flags & python.PyBUF_FORMAT:
810
+ buffer.format = "B"
811
+ else:
812
+ buffer.format = NULL
813
+ buffer.ndim = 0
814
+ buffer.shape = NULL
815
+ buffer.strides = NULL
816
+ buffer.suboffsets = NULL
817
+ buffer.itemsize = 1
818
+ buffer.internal = NULL
819
+ if buffer.obj is not self: # set by Cython?
820
+ buffer.obj = self
821
+
822
+ def __releasebuffer__(self, Py_buffer* buffer):
823
+ if buffer is NULL:
824
+ return
825
+ if <xmlChar*>buffer.buf is self._buffer:
826
+ self._buffer_refcnt -= 1
827
+ if self._buffer_refcnt == 0:
828
+ tree.xmlFree(<char*>self._buffer)
829
+ self._buffer = NULL
830
+ else:
831
+ tree.xmlFree(<char*>buffer.buf)
832
+ buffer.buf = NULL
833
+
834
+ property xslt_profile:
835
+ """Return an ElementTree with profiling data for the stylesheet run.
836
+ """
837
+ def __get__(self):
838
+ cdef object root
839
+ if self._profile is None:
840
+ return None
841
+ root = self._profile.getroot()
842
+ if root is None:
843
+ return None
844
+ return ElementTree(root)
845
+
846
+ def __del__(self):
847
+ self._profile = None
848
+
849
+ cdef _xsltResultTreeFactory(_Document doc, XSLT xslt, _Document profile):
850
+ cdef _XSLTResultTree result
851
+ result = <_XSLTResultTree>_newElementTree(doc, None, _XSLTResultTree)
852
+ result._xslt = xslt
853
+ result._profile = profile
854
+ return result
855
+
856
+ # functions like "output" and "write" are a potential security risk, but we
857
+ # rely on the user to configure XSLTAccessControl as needed
858
+ xslt.xsltRegisterAllExtras()
859
+
860
+ # enable EXSLT support for XSLT
861
+ xslt.exsltRegisterAll()
862
+
863
+
864
+ ################################################################################
865
+ # XSLT PI support
866
+
867
+ cdef object _RE_PI_HREF = re.compile(r'\s+href\s*=\s*(?:\'([^\']*)\'|"([^"]*)")')
868
+ cdef object _FIND_PI_HREF = _RE_PI_HREF.findall
869
+ cdef object _REPLACE_PI_HREF = _RE_PI_HREF.sub
870
+ cdef XPath __findStylesheetByID = None
871
+
872
+ cdef _findStylesheetByID(_Document doc, id):
873
+ global __findStylesheetByID
874
+ if __findStylesheetByID is None:
875
+ __findStylesheetByID = XPath(
876
+ "//xsl:stylesheet[@xml:id = $id]",
877
+ namespaces={"xsl" : "http://www.w3.org/1999/XSL/Transform"})
878
+ return __findStylesheetByID(doc, id=id)
879
+
880
+ cdef class _XSLTProcessingInstruction(PIBase):
881
+ def parseXSL(self, parser=None):
882
+ """parseXSL(self, parser=None)
883
+
884
+ Try to parse the stylesheet referenced by this PI and return
885
+ an ElementTree for it. If the stylesheet is embedded in the
886
+ same document (referenced via xml:id), find and return an
887
+ ElementTree for the stylesheet Element.
888
+
889
+ The optional ``parser`` keyword argument can be passed to specify the
890
+ parser used to read from external stylesheet URLs.
891
+ """
892
+ cdef _Document result_doc
893
+ cdef _Element result_node
894
+ cdef bytes href_utf
895
+ cdef const_xmlChar* c_href
896
+ cdef xmlAttr* c_attr
897
+ _assertValidNode(self)
898
+ if self._c_node.content is NULL:
899
+ raise ValueError, "PI lacks content"
900
+ hrefs = _FIND_PI_HREF(' ' + (<unsigned char*>self._c_node.content).decode('UTF-8'))
901
+ if len(hrefs) != 1:
902
+ raise ValueError, "malformed PI attributes"
903
+ hrefs = hrefs[0]
904
+ href_utf = utf8(hrefs[0] or hrefs[1])
905
+ c_href = _xcstr(href_utf)
906
+
907
+ if c_href[0] != c'#':
908
+ # normal URL, try to parse from it
909
+ c_href = tree.xmlBuildURI(
910
+ c_href,
911
+ tree.xmlNodeGetBase(self._c_node.doc, self._c_node))
912
+ if c_href is not NULL:
913
+ try:
914
+ href_utf = <unsigned char*>c_href
915
+ finally:
916
+ tree.xmlFree(<char*>c_href)
917
+ result_doc = _parseDocumentFromURL(href_utf, parser)
918
+ return _elementTreeFactory(result_doc, None)
919
+
920
+ # ID reference to embedded stylesheet
921
+ # try XML:ID lookup
922
+ _assertValidDoc(self._doc)
923
+ c_href += 1 # skip leading '#'
924
+ c_attr = tree.xmlGetID(self._c_node.doc, c_href)
925
+ if c_attr is not NULL and c_attr.doc is self._c_node.doc:
926
+ result_node = _elementFactory(self._doc, c_attr.parent)
927
+ return _elementTreeFactory(result_node._doc, result_node)
928
+
929
+ # try XPath search
930
+ root = _findStylesheetByID(self._doc, funicode(c_href))
931
+ if not root:
932
+ raise ValueError, "reference to non-existing embedded stylesheet"
933
+ elif len(root) > 1:
934
+ raise ValueError, "ambiguous reference to embedded stylesheet"
935
+ result_node = root[0]
936
+ return _elementTreeFactory(result_node._doc, result_node)
937
+
938
+ def set(self, key, value):
939
+ """set(self, key, value)
940
+
941
+ Supports setting the 'href' pseudo-attribute in the text of
942
+ the processing instruction.
943
+ """
944
+ if key != "href":
945
+ raise AttributeError, \
946
+ "only setting the 'href' attribute is supported on XSLT-PIs"
947
+ if value is None:
948
+ attrib = ""
949
+ elif '"' in value or '>' in value:
950
+ raise ValueError, "Invalid URL, must not contain '\"' or '>'"
951
+ else:
952
+ attrib = f' href="{value}"'
953
+ text = ' ' + self.text
954
+ if _FIND_PI_HREF(text):
955
+ self.text = _REPLACE_PI_HREF(attrib, text)
956
+ else:
957
+ self.text = text + attrib