5 class XSLTError(LxmlError):
6 u"""Base class of all XSLT errors.
10 class XSLTParseError(XSLTError):
11 u"""Error parsing a stylesheet document.
15 class XSLTApplyError(XSLTError):
16 u"""Error running an XSL transformation.
20 class XSLTSaveError(XSLTError):
21 u"""Error serialising an XSLT result.
25 class XSLTExtensionError(XSLTError):
26 u"""Error registering an XSLT extension.
31 LIBXSLT_COMPILED_VERSION = __unpackIntVersion(xslt.LIBXSLT_VERSION)
32 LIBXSLT_VERSION = __unpackIntVersion(xslt.xsltLibxsltVersion)
35 ################################################################################
36 # Where do we store what?
38 # xsltStylesheet->doc->_private
39 # == _XSLTResolverContext for XSL stylesheet
41 # xsltTransformContext->_private
42 # == _XSLTResolverContext for transformed document
44 ################################################################################
47 ################################################################################
48 # XSLT document loaders
50 cdef class _XSLTResolverContext(_ResolverContext):
51 cdef xmlDoc* _c_style_doc
52 cdef _BaseParser _parser
54 cdef _XSLTResolverContext _copy(self):
55 cdef _XSLTResolverContext context
56 context = _XSLTResolverContext()
57 _initXSLTResolverContext(context, self._parser)
58 context._c_style_doc = self._c_style_doc
61 cdef _initXSLTResolverContext(_XSLTResolverContext context,
63 _initResolverContext(context, parser.resolvers)
64 context._parser = parser
65 context._c_style_doc = NULL
67 cdef xmlDoc* _xslt_resolve_from_python(char* c_uri, void* c_context,
68 int parse_options, int* error) with gil:
69 # call the Python document loaders
70 cdef _XSLTResolverContext context
71 cdef _ResolverRegistry resolvers
72 cdef _InputDocument doc_ref
76 context = <_XSLTResolverContext>c_context
78 # shortcut if we resolve the stylesheet itself
79 c_doc = context._c_style_doc
80 if c_doc is not NULL and c_doc.URL is not NULL:
81 if cstd.strcmp(c_uri, c_doc.URL) == 0:
82 return _copyDoc(c_doc, 1)
84 # delegate to the Python resolvers
86 resolvers = context._resolvers
87 if cstd.strncmp('string://__STRING__XSLT__/', c_uri, 26) == 0:
89 uri = _decodeFilename(c_uri)
90 doc_ref = resolvers.resolve(uri, None, context)
93 if doc_ref is not None:
94 if doc_ref._type == PARSER_DATA_STRING:
96 doc_ref._data_bytes, doc_ref._filename, context._parser)
97 elif doc_ref._type == PARSER_DATA_FILENAME:
98 c_doc = _parseDocFromFile(doc_ref._filename, context._parser)
99 elif doc_ref._type == PARSER_DATA_FILE:
100 c_doc = _parseDocFromFilelike(
101 doc_ref._file, doc_ref._filename, context._parser)
102 elif doc_ref._type == PARSER_DATA_EMPTY:
104 if c_doc is not NULL and c_doc.URL is NULL:
105 c_doc.URL = tree.xmlStrdup(c_uri)
108 context._store_raised()
112 cdef void _xslt_store_resolver_exception(char* c_uri, void* context,
113 xslt.xsltLoadType c_type) with gil:
115 message = u"Cannot resolve URI %s" % _decodeFilename(c_uri)
116 if c_type == xslt.XSLT_LOAD_DOCUMENT:
117 exception = XSLTApplyError(message)
119 exception = XSLTParseError(message)
120 (<_XSLTResolverContext>context)._store_exception(exception)
122 (<_XSLTResolverContext>context)._store_exception(e)
124 cdef xmlDoc* _xslt_doc_loader(char* c_uri, tree.xmlDict* c_dict,
125 int parse_options, void* c_ctxt,
126 xslt.xsltLoadType c_type) nogil:
127 # nogil => no Python objects here, may be called without thread context !
130 cdef void* c_pcontext
132 # find resolver contexts of stylesheet and transformed doc
133 if c_type == xslt.XSLT_LOAD_DOCUMENT:
134 # transformation time
135 c_pcontext = (<xslt.xsltTransformContext*>c_ctxt)._private
136 elif c_type == xslt.XSLT_LOAD_STYLESHEET:
137 # include/import resolution while parsing
138 c_pcontext = (<xslt.xsltStylesheet*>c_ctxt).doc._private
142 if c_pcontext is NULL:
143 # can't call Python without context, fall back to default loader
144 return XSLT_DOC_DEFAULT_LOADER(
145 c_uri, c_dict, parse_options, c_ctxt, c_type)
147 c_doc = _xslt_resolve_from_python(c_uri, c_pcontext, parse_options, &error)
148 if c_doc is NULL and not error:
149 c_doc = XSLT_DOC_DEFAULT_LOADER(
150 c_uri, c_dict, parse_options, c_ctxt, c_type)
152 _xslt_store_resolver_exception(c_uri, c_pcontext, c_type)
154 if c_doc is not NULL and c_type == xslt.XSLT_LOAD_STYLESHEET:
155 c_doc._private = c_pcontext
158 cdef xslt.xsltDocLoaderFunc XSLT_DOC_DEFAULT_LOADER
159 XSLT_DOC_DEFAULT_LOADER = xslt.xsltDocDefaultLoader
161 xslt.xsltSetLoaderFunc(_xslt_doc_loader)
163 ################################################################################
164 # XSLT file/network access control
166 cdef class XSLTAccessControl:
167 u"""XSLTAccessControl(self, read_file=True, write_file=True, create_dir=True, read_network=True, write_network=True)
169 Access control for XSLT: reading/writing files, directories and
170 network I/O. Access to a type of resource is granted or denied by
171 passing any of the following boolean keyword arguments. All of
172 them default to True to allow access.
180 For convenience, there is also a class member `DENY_ALL` that
181 provides an XSLTAccessControl instance that is readily configured
182 to deny everything, and a `DENY_WRITE` member that denies all
183 write access but allows read access.
187 cdef xslt.xsltSecurityPrefs* _prefs
188 def __cinit__(self, *, read_file=True, write_file=True, create_dir=True,
189 read_network=True, write_network=True):
190 self._prefs = xslt.xsltNewSecurityPrefs()
191 if self._prefs is NULL:
192 python.PyErr_NoMemory()
193 self._setAccess(xslt.XSLT_SECPREF_READ_FILE, read_file)
194 self._setAccess(xslt.XSLT_SECPREF_WRITE_FILE, write_file)
195 self._setAccess(xslt.XSLT_SECPREF_CREATE_DIRECTORY, create_dir)
196 self._setAccess(xslt.XSLT_SECPREF_READ_NETWORK, read_network)
197 self._setAccess(xslt.XSLT_SECPREF_WRITE_NETWORK, write_network)
199 DENY_ALL = XSLTAccessControl(
200 read_file=False, write_file=False, create_dir=False,
201 read_network=False, write_network=False)
203 DENY_WRITE = XSLTAccessControl(
204 read_file=True, write_file=False, create_dir=False,
205 read_network=True, write_network=False)
207 def __dealloc__(self):
208 if self._prefs is not NULL:
209 xslt.xsltFreeSecurityPrefs(self._prefs)
211 cdef _setAccess(self, xslt.xsltSecurityOption option, allow):
212 cdef xslt.xsltSecurityCheck function
214 function = xslt.xsltSecurityAllow
216 function = xslt.xsltSecurityForbid
217 xslt.xsltSetSecurityPrefs(self._prefs, option, function)
219 cdef void _register_in_context(self, xslt.xsltTransformContext* ctxt):
220 xslt.xsltSetCtxtSecurityPrefs(self._prefs, ctxt)
223 u"The access control configuration as a map of options."
226 u'read_file': self._optval(xslt.XSLT_SECPREF_READ_FILE),
227 u'write_file': self._optval(xslt.XSLT_SECPREF_WRITE_FILE),
228 u'create_dir': self._optval(xslt.XSLT_SECPREF_CREATE_DIRECTORY),
229 u'read_network': self._optval(xslt.XSLT_SECPREF_READ_NETWORK),
230 u'write_network': self._optval(xslt.XSLT_SECPREF_WRITE_NETWORK),
233 cdef _optval(self, xslt.xsltSecurityOption option):
234 cdef xslt.xsltSecurityCheck function
235 function = xslt.xsltGetSecurityPrefs(self._prefs, option)
236 if function is <xslt.xsltSecurityCheck>xslt.xsltSecurityAllow:
238 elif function is <xslt.xsltSecurityCheck>xslt.xsltSecurityForbid:
244 items = self.options.items()
247 funicode(python._fqtypename(self)).split(u'.')[-1],
248 u', '.join([u"%s=%r" % item for item in items]))
250 ################################################################################
253 cdef int _register_xslt_function(void* ctxt, name_utf, ns_utf):
256 return xslt.xsltRegisterExtFunction(
257 <xslt.xsltTransformContext*>ctxt, _cstr(name_utf), _cstr(ns_utf),
258 _xpath_function_call)
260 cdef int _unregister_xslt_function(void* ctxt, name_utf, ns_utf):
263 return xslt.xsltRegisterExtFunction(
264 <xslt.xsltTransformContext*>ctxt, _cstr(name_utf), _cstr(ns_utf),
267 cdef dict EMPTY_DICT = {}
269 cdef class _XSLTContext(_BaseContext):
270 cdef xslt.xsltTransformContext* _xsltCtxt
271 cdef _ReadOnlyElementProxy _extension_element_proxy
272 cdef dict _extension_elements
274 self._xsltCtxt = NULL
275 self._extension_elements = EMPTY_DICT
277 def __init__(self, namespaces, extensions, enable_regexp,
278 build_smart_strings):
279 if extensions is not None and extensions:
280 for ns_name_tuple, extension in extensions.items():
281 if ns_name_tuple[0] is None:
282 raise XSLTExtensionError, \
283 u"extensions must not have empty namespaces"
284 if isinstance(extension, XSLTExtension):
285 if self._extension_elements is EMPTY_DICT:
286 self._extension_elements = {}
287 extensions = extensions.copy()
288 ns_utf = _utf8(ns_name_tuple[0])
289 name_utf = _utf8(ns_name_tuple[1])
290 self._extension_elements[(ns_utf, name_utf)] = extension
291 del extensions[ns_name_tuple]
292 _BaseContext.__init__(self, namespaces, extensions, enable_regexp,
295 cdef _BaseContext _copy(self):
296 cdef _XSLTContext context
297 context = <_XSLTContext>_BaseContext._copy(self)
298 context._extension_elements = self._extension_elements
301 cdef register_context(self, xslt.xsltTransformContext* xsltCtxt,
303 self._xsltCtxt = xsltCtxt
304 self._set_xpath_context(xsltCtxt.xpathCtxt)
305 self._register_context(doc)
306 self.registerLocalFunctions(xsltCtxt, _register_xslt_function)
307 self.registerGlobalFunctions(xsltCtxt, _register_xslt_function)
308 _registerXSLTExtensions(xsltCtxt, self._extension_elements)
310 cdef free_context(self):
311 self._cleanup_context()
312 self._release_context()
313 if self._xsltCtxt is not NULL:
314 xslt.xsltFreeTransformContext(self._xsltCtxt)
315 self._xsltCtxt = NULL
316 self._release_temp_refs()
319 cdef class _XSLTQuotedStringParam:
320 u"""A wrapper class for literal XSLT string parameters that require
324 def __cinit__(self, strval):
325 self.strval = _utf8(strval)
329 u"""XSLT(self, xslt_input, extensions=None, regexp=True, access_control=None)
331 Turn an XSL document into an XSLT object.
333 Calling this object on a tree or Element will execute the XSLT::
335 >>> transform = etree.XSLT(xsl_tree)
336 >>> result = transform(xml_tree)
338 Keyword arguments of the constructor:
340 - extensions: a dict mapping ``(namespace, name)`` pairs to
341 extension functions or extension elements
342 - regexp: enable exslt regular expression support in XPath
344 - access_control: access restrictions for network or file
345 system (see `XSLTAccessControl`)
347 Keyword arguments of the XSLT call:
349 - profile_run: enable XSLT profiling (default: False)
351 Other keyword arguments of the call are passed to the stylesheet
354 cdef _XSLTContext _context
355 cdef xslt.xsltStylesheet* _c_style
356 cdef _XSLTResolverContext _xslt_resolver_context
357 cdef XSLTAccessControl _access_control
358 cdef _ErrorLog _error_log
363 def __init__(self, xslt_input, *, extensions=None, regexp=True,
364 access_control=None):
365 cdef xslt.xsltStylesheet* c_style
368 cdef _Element root_node
370 doc = _documentOrRaise(xslt_input)
371 root_node = _rootNodeOrRaise(xslt_input)
373 # set access control or raise TypeError
374 self._access_control = access_control
376 # make a copy of the document as stylesheet parsing modifies it
377 c_doc = _copyDocRoot(doc._c_doc, root_node._c_node)
379 # make sure we always have a stylesheet URL
380 if c_doc.URL is NULL:
381 doc_url_utf = python.PyUnicode_AsASCIIString(
382 u"string://__STRING__XSLT__/%d.xslt" % id(self))
383 c_doc.URL = tree.xmlStrdup(_cstr(doc_url_utf))
385 self._error_log = _ErrorLog()
386 self._xslt_resolver_context = _XSLTResolverContext()
387 _initXSLTResolverContext(self._xslt_resolver_context, doc._parser)
388 # keep a copy in case we need to access the stylesheet via 'document()'
389 self._xslt_resolver_context._c_style_doc = _copyDoc(c_doc, 1)
390 c_doc._private = <python.PyObject*>self._xslt_resolver_context
392 self._error_log.connect()
393 c_style = xslt.xsltParseStylesheetDoc(c_doc)
394 self._error_log.disconnect()
396 if c_style is NULL or c_style.errors:
397 tree.xmlFreeDoc(c_doc)
398 if c_style is not NULL:
399 xslt.xsltFreeStylesheet(c_style)
400 self._xslt_resolver_context._raise_if_stored()
401 # last error seems to be the most accurate here
402 if self._error_log.last_error is not None and \
403 self._error_log.last_error.message:
404 raise XSLTParseError(self._error_log.last_error.message,
407 raise XSLTParseError(
408 self._error_log._buildExceptionMessage(
409 u"Cannot parse stylesheet"),
412 c_doc._private = NULL # no longer used!
413 self._c_style = c_style
414 self._context = _XSLTContext(None, extensions, regexp, True)
416 def __dealloc__(self):
417 if self._xslt_resolver_context is not None and \
418 self._xslt_resolver_context._c_style_doc is not NULL:
419 tree.xmlFreeDoc(self._xslt_resolver_context._c_style_doc)
420 # this cleans up the doc copy as well
421 if self._c_style is not NULL:
422 xslt.xsltFreeStylesheet(self._c_style)
425 u"The log of errors and warnings of an XSLT execution."
427 return self._error_log.copy()
430 def strparam(_, strval):
433 Mark an XSLT string parameter that requires quote escaping
434 before passing it into the transformation. Use it like this::
436 result = transform(doc, some_strval = XSLT.strparam(
437 '''it's \"Monty Python's\" ...'''))
439 Escaped string parameters can be reused without restriction.
441 return _XSLTQuotedStringParam(strval)
443 def apply(self, _input, *, profile_run=False, **kw):
444 u"""apply(self, _input, profile_run=False, **kw)
446 :deprecated: call the object, not this method."""
447 return self(_input, profile_run=profile_run, **kw)
449 def tostring(self, _ElementTree result_tree):
450 u"""tostring(self, result_tree)
452 Save result doc to string based on stylesheet output method.
454 :deprecated: use str(result_tree) instead.
456 return str(result_tree)
458 def __deepcopy__(self, memo):
459 return self.__copy__()
462 return _copyXSLT(self)
464 def __call__(self, _input, *, profile_run=False, **kw):
465 u"""__call__(self, _input, profile_run=False, **kw)
467 Execute the XSL transformation on a tree or Element.
469 Pass the ``profile_run`` option to get profile information
470 about the XSLT. The result of the XSLT will have a property
471 xslt_profile that holds an XML tree with profiling data.
473 cdef _XSLTContext context = None
474 cdef _XSLTResolverContext resolver_context
475 cdef _Document input_doc
476 cdef _Element root_node
477 cdef _Document result_doc
478 cdef _Document profile_doc = None
479 cdef xmlDoc* c_profile_doc
480 cdef xslt.xsltTransformContext* transform_ctxt
481 cdef xmlDoc* c_result = NULL
483 cdef tree.xmlDict* c_dict
484 cdef char** params = NULL
486 assert self._c_style is not NULL, "XSLT stylesheet not initialised"
487 input_doc = _documentOrRaise(_input)
488 root_node = _rootNodeOrRaise(_input)
490 c_doc = _fakeRootDoc(input_doc._c_doc, root_node._c_node)
492 transform_ctxt = xslt.xsltNewTransformContext(self._c_style, c_doc)
493 if transform_ctxt is NULL:
494 _destroyFakeDoc(input_doc._c_doc, c_doc)
495 python.PyErr_NoMemory()
497 # using the stylesheet dict is safer than using a possibly
498 # unrelated dict from the current thread. Almost all
499 # non-input tag/attr names will come from the stylesheet
501 if transform_ctxt.dict is not NULL:
502 xmlparser.xmlDictFree(transform_ctxt.dict)
503 transform_ctxt.dict = self._c_style.doc.dict
504 xmlparser.xmlDictReference(transform_ctxt.dict)
506 xslt.xsltSetCtxtParseOptions(
507 transform_ctxt, input_doc._parser._parse_options)
510 transform_ctxt.profile = 1
513 context = self._context._copy()
514 context.register_context(transform_ctxt, input_doc)
516 resolver_context = self._xslt_resolver_context._copy()
517 transform_ctxt._private = <python.PyObject*>resolver_context
519 live_refs = _convert_xslt_parameters(transform_ctxt, kw, ¶ms)
520 c_result = self._run_transform(
521 c_doc, params, context, transform_ctxt)
522 if params is not NULL:
523 # deallocate space for parameters
524 python.PyMem_Free(params)
527 if transform_ctxt.state != xslt.XSLT_STATE_OK:
528 if c_result is not NULL:
529 tree.xmlFreeDoc(c_result)
532 if transform_ctxt.profile:
533 c_profile_doc = xslt.xsltGetProfileInformation(transform_ctxt)
534 if c_profile_doc is not NULL:
535 profile_doc = _documentFactory(
536 c_profile_doc, input_doc._parser)
538 if context is not None:
539 context.free_context()
540 _destroyFakeDoc(input_doc._c_doc, c_doc)
543 if resolver_context is not None and resolver_context._has_raised():
544 if c_result is not NULL:
545 tree.xmlFreeDoc(c_result)
547 resolver_context._raise_if_stored()
549 if context._exc._has_raised():
550 if c_result is not NULL:
551 tree.xmlFreeDoc(c_result)
553 context._exc._raise_if_stored()
556 # last error seems to be the most accurate here
557 error = self._error_log.last_error
558 if error is not None and error.message:
560 message = u"%s, line %d" % (error.message, error.line)
562 message = error.message
563 elif error is not None and error.line > 0:
564 message = u"Error applying stylesheet, line %d" % error.line
566 message = u"Error applying stylesheet"
567 raise XSLTApplyError(message, self._error_log)
569 if resolver_context is not None:
570 resolver_context.clear()
572 result_doc = _documentFactory(c_result, input_doc._parser)
574 c_dict = c_result.dict
575 __GLOBAL_PARSER_CONTEXT.initThreadDictRef(&c_result.dict)
576 if c_dict is not c_result.dict or \
577 self._c_style.doc.dict is not c_result.dict or \
578 input_doc._c_doc.dict is not c_result.dict:
580 if c_dict is not c_result.dict:
581 fixThreadDictNames(<xmlNode*>c_result,
582 c_dict, c_result.dict)
583 if self._c_style.doc.dict is not c_result.dict:
584 fixThreadDictNames(<xmlNode*>c_result,
585 self._c_style.doc.dict, c_result.dict)
586 if input_doc._c_doc.dict is not c_result.dict:
587 fixThreadDictNames(<xmlNode*>c_result,
588 input_doc._c_doc.dict, c_result.dict)
590 return _xsltResultTreeFactory(result_doc, self, profile_doc)
592 cdef xmlDoc* _run_transform(self, xmlDoc* c_input_doc,
593 char** params, _XSLTContext context,
594 xslt.xsltTransformContext* transform_ctxt):
595 cdef xmlDoc* c_result
596 xslt.xsltSetTransformErrorFunc(transform_ctxt, <void*>self._error_log,
598 if self._access_control is not None:
599 self._access_control._register_in_context(transform_ctxt)
601 c_result = xslt.xsltApplyStylesheetUser(
602 self._c_style, c_input_doc, params, NULL, NULL, transform_ctxt)
605 cdef _convert_xslt_parameters(xslt.xsltTransformContext* transform_ctxt,
606 dict parameters, char*** params_ptr):
607 cdef Py_ssize_t i, parameter_count
611 parameter_count = len(parameters)
612 if parameter_count == 0:
614 # allocate space for parameters
615 # * 2 as we want an entry for both key and value,
616 # and + 1 as array is NULL terminated
617 params = <char**>python.PyMem_Malloc(
618 sizeof(char*) * (parameter_count * 2 + 1))
622 for key, value in parameters.iteritems():
625 if isinstance(value, _XSLTQuotedStringParam):
626 v = (<_XSLTQuotedStringParam>value).strval
627 xslt.xsltQuoteOneUserParam(
628 transform_ctxt, _cstr(k), _cstr(v))
630 if isinstance(value, XPath):
631 v = (<XPath>value)._path
640 python.PyMem_Free(params)
643 params_ptr[0] = params
646 cdef XSLT _copyXSLT(XSLT stylesheet):
649 assert stylesheet._c_style is not NULL, "XSLT stylesheet not initialised"
650 new_xslt = XSLT.__new__(XSLT)
651 new_xslt._access_control = stylesheet._access_control
652 new_xslt._error_log = _ErrorLog()
653 new_xslt._context = stylesheet._context._copy()
655 new_xslt._xslt_resolver_context = stylesheet._xslt_resolver_context._copy()
656 new_xslt._xslt_resolver_context._c_style_doc = _copyDoc(
657 stylesheet._xslt_resolver_context._c_style_doc, 1)
659 c_doc = _copyDoc(stylesheet._c_style.doc, 1)
660 new_xslt._c_style = xslt.xsltParseStylesheetDoc(c_doc)
661 if new_xslt._c_style is NULL:
662 tree.xmlFreeDoc(c_doc)
663 python.PyErr_NoMemory()
667 cdef class _XSLTResultTree(_ElementTree):
669 cdef _Document _profile
671 cdef Py_ssize_t _buffer_len
672 cdef Py_ssize_t _buffer_refcnt
676 self._buffer_refcnt = 0
678 cdef _saveToStringAndSize(self, char** s, int* l):
681 if self._context_node is not None:
682 doc = self._context_node._doc
691 r = xslt.xsltSaveResultToString(s, l, doc._c_doc,
694 python.PyErr_NoMemory()
699 if python.IS_PYTHON3:
700 return self.__unicode__()
701 self._saveToStringAndSize(&s, &l)
704 # we must not use 'funicode' here as this is not always UTF-8
706 result = <bytes>s[:l]
711 def __unicode__(self):
715 self._saveToStringAndSize(&s, &l)
718 encoding = self._xslt._c_style.encoding
721 result = s[:l].decode('UTF-8')
723 result = python.PyUnicode_Decode(s, l, encoding, 'strict')
726 return _stripEncodingDeclaration(result)
728 def __getbuffer__(self, Py_buffer* buffer, int flags):
732 if self._buffer is NULL or flags & python.PyBUF_WRITABLE:
733 self._saveToStringAndSize(<char**>&buffer.buf, &l)
735 if self._buffer is NULL and not flags & python.PyBUF_WRITABLE:
736 self._buffer = <char*>buffer.buf
738 self._buffer_refcnt = 1
740 buffer.buf = <char*>self._buffer
741 buffer.len = self._buffer_len
742 self._buffer_refcnt += 1
743 if flags & python.PyBUF_WRITABLE:
747 if flags & python.PyBUF_FORMAT:
753 buffer.strides = NULL
754 buffer.suboffsets = NULL
756 buffer.internal = NULL
758 def __releasebuffer__(self, Py_buffer* buffer):
761 if <char*>buffer.buf is self._buffer:
762 self._buffer_refcnt -= 1
763 if self._buffer_refcnt == 0:
764 tree.xmlFree(self._buffer)
767 tree.xmlFree(<char*>buffer.buf)
770 property xslt_profile:
771 u"""Return an ElementTree with profiling data for the stylesheet run.
775 if self._profile is None:
777 root = self._profile.getroot()
780 return ElementTree(root)
785 cdef _xsltResultTreeFactory(_Document doc, XSLT xslt, _Document profile):
786 cdef _XSLTResultTree result
787 result = <_XSLTResultTree>_newElementTree(doc, None, _XSLTResultTree)
789 result._profile = profile
792 # functions like "output" and "write" are a potential security risk, but we
793 # rely on the user to configure XSLTAccessControl as needed
794 xslt.xsltRegisterAllExtras()
796 # enable EXSLT support for XSLT
797 xslt.exsltRegisterAll()
800 ################################################################################
803 cdef object _RE_PI_HREF = re.compile(ur'\s+href\s*=\s*(?:\'([^\']*)\'|"([^"]*)")')
804 cdef object _FIND_PI_HREF = _RE_PI_HREF.findall
805 cdef object _REPLACE_PI_HREF = _RE_PI_HREF.sub
806 cdef XPath __findStylesheetByID = None
808 cdef _findStylesheetByID(_Document doc, id):
809 global __findStylesheetByID
810 if __findStylesheetByID is None:
811 __findStylesheetByID = XPath(
812 u"//xsl:stylesheet[@xml:id = $id]",
813 namespaces={u"xsl" : u"http://www.w3.org/1999/XSL/Transform"})
814 return __findStylesheetByID(doc, id=id)
816 cdef class _XSLTProcessingInstruction(PIBase):
817 def parseXSL(self, parser=None):
818 u"""parseXSL(self, parser=None)
820 Try to parse the stylesheet referenced by this PI and return
821 an ElementTree for it. If the stylesheet is embedded in the
822 same document (referenced via xml:id), find and return an
823 ElementTree for the stylesheet Element.
825 The optional ``parser`` keyword argument can be passed to specify the
826 parser used to read from external stylesheet URLs.
828 cdef _Document result_doc
829 cdef _Element result_node
832 _assertValidNode(self)
833 if self._c_node.content is NULL:
834 raise ValueError, u"PI lacks content"
835 hrefs = _FIND_PI_HREF(u' ' + self._c_node.content.decode('UTF-8'))
837 raise ValueError, u"malformed PI attributes"
839 href_utf = utf8(hrefs[0] or hrefs[1])
840 c_href = _cstr(href_utf)
842 if c_href[0] != c'#':
843 # normal URL, try to parse from it
844 c_href = tree.xmlBuildURI(
846 tree.xmlNodeGetBase(self._c_node.doc, self._c_node))
847 if c_href is not NULL:
850 result_doc = _parseDocumentFromURL(href_utf, parser)
851 return _elementTreeFactory(result_doc, None)
853 # ID reference to embedded stylesheet
855 _assertValidDoc(self._doc)
856 c_href += 1 # skip leading '#'
857 c_attr = tree.xmlGetID(self._c_node.doc, c_href)
858 if c_attr is not NULL and c_attr.doc is self._c_node.doc:
859 result_node = _elementFactory(self._doc, c_attr.parent)
860 return _elementTreeFactory(result_node._doc, result_node)
863 root = _findStylesheetByID(self._doc, funicode(c_href))
865 raise ValueError, u"reference to non-existing embedded stylesheet"
867 raise ValueError, u"ambiguous reference to embedded stylesheet"
868 result_node = root[0]
869 return _elementTreeFactory(result_node._doc, result_node)
871 def set(self, key, value):
872 u"""set(self, key, value)
874 Supports setting the 'href' pseudo-attribute in the text of
875 the processing instruction.
878 raise AttributeError, \
879 u"only setting the 'href' attribute is supported on XSLT-PIs"
882 elif u'"' in value or u'>' in value:
883 raise ValueError, u"Invalid URL, must not contain '\"' or '>'"
885 attrib = u' href="%s"' % value
886 text = u' ' + self.text
887 if _FIND_PI_HREF(text):
888 self.text = _REPLACE_PI_HREF(attrib, text)
890 self.text = text + attrib