lxml 6.0.0__cp39-cp39-musllinux_1_2_armv7l.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.
- lxml/ElementInclude.py +244 -0
- lxml/__init__.py +22 -0
- lxml/_elementpath.cpython-39-arm-linux-gnueabihf.so +0 -0
- lxml/_elementpath.py +343 -0
- lxml/apihelpers.pxi +1801 -0
- lxml/builder.cpython-39-arm-linux-gnueabihf.so +0 -0
- lxml/builder.py +243 -0
- lxml/classlookup.pxi +580 -0
- lxml/cleanup.pxi +215 -0
- lxml/cssselect.py +101 -0
- lxml/debug.pxi +36 -0
- lxml/docloader.pxi +178 -0
- lxml/doctestcompare.py +488 -0
- lxml/dtd.pxi +479 -0
- lxml/etree.cpython-39-arm-linux-gnueabihf.so +0 -0
- lxml/etree.h +244 -0
- lxml/etree.pyx +3853 -0
- lxml/etree_api.h +204 -0
- lxml/extensions.pxi +830 -0
- lxml/html/ElementSoup.py +10 -0
- lxml/html/__init__.py +1927 -0
- lxml/html/_diffcommand.py +86 -0
- lxml/html/_difflib.cpython-39-arm-linux-gnueabihf.so +0 -0
- lxml/html/_difflib.py +2106 -0
- lxml/html/_html5builder.py +100 -0
- lxml/html/_setmixin.py +56 -0
- lxml/html/builder.py +173 -0
- lxml/html/clean.py +21 -0
- lxml/html/defs.py +135 -0
- lxml/html/diff.cpython-39-arm-linux-gnueabihf.so +0 -0
- lxml/html/diff.py +972 -0
- lxml/html/formfill.py +299 -0
- lxml/html/html5parser.py +260 -0
- lxml/html/soupparser.py +314 -0
- lxml/html/usedoctest.py +13 -0
- lxml/includes/__init__.pxd +0 -0
- lxml/includes/__init__.py +0 -0
- lxml/includes/c14n.pxd +25 -0
- lxml/includes/config.pxd +3 -0
- lxml/includes/dtdvalid.pxd +18 -0
- lxml/includes/etree_defs.h +379 -0
- lxml/includes/etreepublic.pxd +237 -0
- lxml/includes/extlibs/__init__.py +0 -0
- lxml/includes/extlibs/libcharset.h +45 -0
- lxml/includes/extlibs/localcharset.h +137 -0
- lxml/includes/extlibs/zconf.h +543 -0
- lxml/includes/extlibs/zlib.h +1938 -0
- lxml/includes/htmlparser.pxd +56 -0
- lxml/includes/libexslt/__init__.py +0 -0
- lxml/includes/libexslt/exslt.h +108 -0
- lxml/includes/libexslt/exsltconfig.h +70 -0
- lxml/includes/libexslt/exsltexports.h +63 -0
- lxml/includes/libxml/HTMLparser.h +339 -0
- lxml/includes/libxml/HTMLtree.h +148 -0
- lxml/includes/libxml/SAX.h +18 -0
- lxml/includes/libxml/SAX2.h +170 -0
- lxml/includes/libxml/__init__.py +0 -0
- lxml/includes/libxml/c14n.h +115 -0
- lxml/includes/libxml/catalog.h +183 -0
- lxml/includes/libxml/chvalid.h +230 -0
- lxml/includes/libxml/debugXML.h +79 -0
- lxml/includes/libxml/dict.h +82 -0
- lxml/includes/libxml/encoding.h +307 -0
- lxml/includes/libxml/entities.h +147 -0
- lxml/includes/libxml/globals.h +25 -0
- lxml/includes/libxml/hash.h +251 -0
- lxml/includes/libxml/list.h +137 -0
- lxml/includes/libxml/nanoftp.h +16 -0
- lxml/includes/libxml/nanohttp.h +98 -0
- lxml/includes/libxml/parser.h +1633 -0
- lxml/includes/libxml/parserInternals.h +591 -0
- lxml/includes/libxml/relaxng.h +224 -0
- lxml/includes/libxml/schemasInternals.h +959 -0
- lxml/includes/libxml/schematron.h +143 -0
- lxml/includes/libxml/threads.h +81 -0
- lxml/includes/libxml/tree.h +1326 -0
- lxml/includes/libxml/uri.h +106 -0
- lxml/includes/libxml/valid.h +485 -0
- lxml/includes/libxml/xinclude.h +141 -0
- lxml/includes/libxml/xlink.h +193 -0
- lxml/includes/libxml/xmlIO.h +419 -0
- lxml/includes/libxml/xmlautomata.h +163 -0
- lxml/includes/libxml/xmlerror.h +962 -0
- lxml/includes/libxml/xmlexports.h +96 -0
- lxml/includes/libxml/xmlmemory.h +188 -0
- lxml/includes/libxml/xmlmodule.h +61 -0
- lxml/includes/libxml/xmlreader.h +444 -0
- lxml/includes/libxml/xmlregexp.h +116 -0
- lxml/includes/libxml/xmlsave.h +111 -0
- lxml/includes/libxml/xmlschemas.h +254 -0
- lxml/includes/libxml/xmlschemastypes.h +152 -0
- lxml/includes/libxml/xmlstring.h +140 -0
- lxml/includes/libxml/xmlunicode.h +15 -0
- lxml/includes/libxml/xmlversion.h +332 -0
- lxml/includes/libxml/xmlwriter.h +489 -0
- lxml/includes/libxml/xpath.h +569 -0
- lxml/includes/libxml/xpathInternals.h +639 -0
- lxml/includes/libxml/xpointer.h +48 -0
- lxml/includes/libxslt/__init__.py +0 -0
- lxml/includes/libxslt/attributes.h +39 -0
- lxml/includes/libxslt/documents.h +93 -0
- lxml/includes/libxslt/extensions.h +262 -0
- lxml/includes/libxslt/extra.h +72 -0
- lxml/includes/libxslt/functions.h +78 -0
- lxml/includes/libxslt/imports.h +75 -0
- lxml/includes/libxslt/keys.h +53 -0
- lxml/includes/libxslt/namespaces.h +68 -0
- lxml/includes/libxslt/numbersInternals.h +73 -0
- lxml/includes/libxslt/pattern.h +84 -0
- lxml/includes/libxslt/preproc.h +43 -0
- lxml/includes/libxslt/security.h +104 -0
- lxml/includes/libxslt/templates.h +77 -0
- lxml/includes/libxslt/transform.h +207 -0
- lxml/includes/libxslt/variables.h +118 -0
- lxml/includes/libxslt/xslt.h +110 -0
- lxml/includes/libxslt/xsltInternals.h +1995 -0
- lxml/includes/libxslt/xsltconfig.h +146 -0
- lxml/includes/libxslt/xsltexports.h +64 -0
- lxml/includes/libxslt/xsltlocale.h +44 -0
- lxml/includes/libxslt/xsltutils.h +343 -0
- lxml/includes/lxml-version.h +3 -0
- lxml/includes/relaxng.pxd +64 -0
- lxml/includes/schematron.pxd +34 -0
- lxml/includes/tree.pxd +492 -0
- lxml/includes/uri.pxd +5 -0
- lxml/includes/xinclude.pxd +22 -0
- lxml/includes/xmlerror.pxd +852 -0
- lxml/includes/xmlparser.pxd +303 -0
- lxml/includes/xmlschema.pxd +35 -0
- lxml/includes/xpath.pxd +136 -0
- lxml/includes/xslt.pxd +190 -0
- lxml/isoschematron/__init__.py +348 -0
- lxml/isoschematron/resources/rng/iso-schematron.rng +709 -0
- lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl +75 -0
- lxml/isoschematron/resources/xsl/XSD2Schtrn.xsl +77 -0
- lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl +313 -0
- lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl +1160 -0
- lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl +55 -0
- lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl +1796 -0
- lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl +588 -0
- lxml/isoschematron/resources/xsl/iso-schematron-xslt1/readme.txt +84 -0
- lxml/iterparse.pxi +438 -0
- lxml/lxml.etree.h +244 -0
- lxml/lxml.etree_api.h +204 -0
- lxml/nsclasses.pxi +281 -0
- lxml/objectify.cpython-39-arm-linux-gnueabihf.so +0 -0
- lxml/objectify.pyx +2149 -0
- lxml/objectpath.pxi +332 -0
- lxml/parser.pxi +2059 -0
- lxml/parsertarget.pxi +180 -0
- lxml/proxy.pxi +619 -0
- lxml/public-api.pxi +178 -0
- lxml/pyclasslookup.py +3 -0
- lxml/readonlytree.pxi +565 -0
- lxml/relaxng.pxi +165 -0
- lxml/sax.cpython-39-arm-linux-gnueabihf.so +0 -0
- lxml/sax.py +286 -0
- lxml/saxparser.pxi +875 -0
- lxml/schematron.pxi +173 -0
- lxml/serializer.pxi +1849 -0
- lxml/usedoctest.py +13 -0
- lxml/xinclude.pxi +67 -0
- lxml/xmlerror.pxi +1654 -0
- lxml/xmlid.pxi +179 -0
- lxml/xmlschema.pxi +215 -0
- lxml/xpath.pxi +487 -0
- lxml/xslt.pxi +957 -0
- lxml/xsltext.pxi +242 -0
- lxml-6.0.0.dist-info/METADATA +163 -0
- lxml-6.0.0.dist-info/RECORD +174 -0
- lxml-6.0.0.dist-info/WHEEL +5 -0
- lxml-6.0.0.dist-info/licenses/LICENSE.txt +31 -0
- lxml-6.0.0.dist-info/licenses/LICENSES.txt +29 -0
- 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, ¶ms)
|
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
|