diff options
author | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
---|---|---|
committer | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
commit | 4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch) | |
tree | ee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/lxml/xsltext.pxi | |
parent | cc961e04ba734dd72309fb548a2f97d67d578813 (diff) | |
download | gn-ai-master.tar.gz |
Diffstat (limited to '.venv/lib/python3.12/site-packages/lxml/xsltext.pxi')
-rw-r--r-- | .venv/lib/python3.12/site-packages/lxml/xsltext.pxi | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/lxml/xsltext.pxi b/.venv/lib/python3.12/site-packages/lxml/xsltext.pxi new file mode 100644 index 00000000..21894b9e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/lxml/xsltext.pxi @@ -0,0 +1,242 @@ +# XSLT extension elements + +cdef class XSLTExtension: + """Base class of an XSLT extension element. + """ + def execute(self, context, self_node, input_node, output_parent): + """execute(self, context, self_node, input_node, output_parent) + Execute this extension element. + + Subclasses must override this method. They may append + elements to the `output_parent` element here, or set its text + content. To this end, the `input_node` provides read-only + access to the current node in the input document, and the + `self_node` points to the extension element in the stylesheet. + + Note that the `output_parent` parameter may be `None` if there + is no parent element in the current context (e.g. no content + was added to the output tree yet). + """ + pass + + def apply_templates(self, _XSLTContext context not None, node, output_parent=None, + *, elements_only=False, remove_blank_text=False): + """apply_templates(self, context, node, output_parent=None, elements_only=False, remove_blank_text=False) + + Call this method to retrieve the result of applying templates + to an element. + + The return value is a list of elements or text strings that + were generated by the XSLT processor. If you pass + ``elements_only=True``, strings will be discarded from the result + list. The option ``remove_blank_text=True`` will only discard + strings that consist entirely of whitespace (e.g. formatting). + These options do not apply to Elements, only to bare string results. + + If you pass an Element as `output_parent` parameter, the result + will instead be appended to the element (including attributes + etc.) and the return value will be `None`. This is a safe way + to generate content into the output document directly, without + having to take care of special values like text or attributes. + Note that the string discarding options will be ignored in this + case. + """ + cdef xmlNode* c_parent + cdef xmlNode* c_node + cdef xmlNode* c_context_node + assert context._xsltCtxt is not NULL, "XSLT context not initialised" + c_context_node = _roNodeOf(node) + #assert c_context_node.doc is context._xsltContext.node.doc, \ + # "switching input documents during transformation is not currently supported" + + if output_parent is not None: + c_parent = _nonRoNodeOf(output_parent) + else: + c_parent = tree.xmlNewDocNode( + context._xsltCtxt.output, NULL, <unsigned char*>"fake-parent", NULL) + + c_node = context._xsltCtxt.insert + context._xsltCtxt.insert = c_parent + xslt.xsltProcessOneNode( + context._xsltCtxt, c_context_node, NULL) + context._xsltCtxt.insert = c_node + + if output_parent is not None: + return None + + try: + return self._collectXSLTResultContent( + context, c_parent, elements_only, remove_blank_text) + finally: + # free all intermediate nodes that will not be freed by proxies + tree.xmlFreeNode(c_parent) + + def process_children(self, _XSLTContext context not None, output_parent=None, + *, elements_only=False, remove_blank_text=False): + """process_children(self, context, output_parent=None, elements_only=False, remove_blank_text=False) + + Call this method to process the XSLT content of the extension + element itself. + + The return value is a list of elements or text strings that + were generated by the XSLT processor. If you pass + ``elements_only=True``, strings will be discarded from the result + list. The option ``remove_blank_text=True`` will only discard + strings that consist entirely of whitespace (e.g. formatting). + These options do not apply to Elements, only to bare string results. + + If you pass an Element as `output_parent` parameter, the result + will instead be appended to the element (including attributes + etc.) and the return value will be `None`. This is a safe way + to generate content into the output document directly, without + having to take care of special values like text or attributes. + Note that the string discarding options will be ignored in this + case. + """ + cdef xmlNode* c_parent + cdef xslt.xsltTransformContext* c_ctxt = context._xsltCtxt + cdef xmlNode* c_old_output_parent = c_ctxt.insert + assert context._xsltCtxt is not NULL, "XSLT context not initialised" + + # output_parent node is used for adding results instead of + # elements list used in apply_templates, that's easier and allows to + # use attributes added to extension element with <xsl:attribute>. + + if output_parent is not None: + c_parent = _nonRoNodeOf(output_parent) + else: + c_parent = tree.xmlNewDocNode( + context._xsltCtxt.output, NULL, <unsigned char*>"fake-parent", NULL) + + c_ctxt.insert = c_parent + xslt.xsltApplyOneTemplate(c_ctxt, + c_ctxt.node, c_ctxt.inst.children, NULL, NULL) + c_ctxt.insert = c_old_output_parent + + if output_parent is not None: + return None + + try: + return self._collectXSLTResultContent( + context, c_parent, elements_only, remove_blank_text) + finally: + # free all intermediate nodes that will not be freed by proxies + tree.xmlFreeNode(c_parent) + + cdef _collectXSLTResultContent(self, _XSLTContext context, xmlNode* c_parent, + bint elements_only, bint remove_blank_text): + cdef xmlNode* c_node + cdef xmlNode* c_next + cdef _ReadOnlyProxy proxy + cdef list results = [] # or maybe _collectAttributes(c_parent, 2) ? + c_node = c_parent.children + while c_node is not NULL: + c_next = c_node.next + if c_node.type == tree.XML_TEXT_NODE: + if not elements_only: + s = funicode(c_node.content) + if not remove_blank_text or s.strip(): + results.append(s) + s = None + elif c_node.type == tree.XML_ELEMENT_NODE: + proxy = _newReadOnlyProxy( + context._extension_element_proxy, c_node) + results.append(proxy) + # unlink node and make sure it will be freed later on + tree.xmlUnlinkNode(c_node) + proxy.free_after_use() + else: + raise TypeError, \ + f"unsupported XSLT result type: {c_node.type}" + c_node = c_next + return results + + +cdef _registerXSLTExtensions(xslt.xsltTransformContext* c_ctxt, + extension_dict): + for ns_utf, name_utf in extension_dict: + xslt.xsltRegisterExtElement( + c_ctxt, _xcstr(name_utf), _xcstr(ns_utf), + <xslt.xsltTransformFunction>_callExtensionElement) + +cdef void _callExtensionElement(xslt.xsltTransformContext* c_ctxt, + xmlNode* c_context_node, + xmlNode* c_inst_node, + void* dummy) noexcept with gil: + cdef _XSLTContext context + cdef XSLTExtension extension + cdef python.PyObject* dict_result + cdef xmlNode* c_node + cdef _ReadOnlyProxy context_node = None, self_node = None + cdef object output_parent # not restricted to ro-nodes + c_uri = _getNs(c_inst_node) + if c_uri is NULL: + # not allowed, and should never happen + return + if c_ctxt.xpathCtxt.userData is NULL: + # just for safety, should never happen + return + context = <_XSLTContext>c_ctxt.xpathCtxt.userData + try: + try: + dict_result = python.PyDict_GetItem( + context._extension_elements, (c_uri, c_inst_node.name)) + if dict_result is NULL: + raise KeyError, f"extension element {funicode(c_inst_node.name)} not found" + extension = <object>dict_result + + try: + # build the context proxy nodes + self_node = _newReadOnlyProxy(None, c_inst_node) + if _isElement(c_ctxt.insert): + output_parent = _newAppendOnlyProxy(self_node, c_ctxt.insert) + else: + # may be the document node or other stuff + output_parent = _newOpaqueAppendOnlyNodeWrapper(c_ctxt.insert) + if c_context_node.type in (tree.XML_DOCUMENT_NODE, + tree.XML_HTML_DOCUMENT_NODE): + c_node = tree.xmlDocGetRootElement(<xmlDoc*>c_context_node) + if c_node is not NULL: + context_node = _newReadOnlyProxy(self_node, c_node) + else: + context_node = None + elif c_context_node.type in (tree.XML_ATTRIBUTE_NODE, + tree.XML_TEXT_NODE, + tree.XML_CDATA_SECTION_NODE): + # this isn't easy to support using read-only + # nodes, as the smart-string factory must + # instantiate the parent proxy somehow... + raise TypeError(f"Unsupported element type: {c_context_node.type}") + else: + context_node = _newReadOnlyProxy(self_node, c_context_node) + + # run the XSLT extension + context._extension_element_proxy = self_node + extension.execute(context, self_node, context_node, output_parent) + finally: + context._extension_element_proxy = None + if self_node is not None: + _freeReadOnlyProxies(self_node) + except Exception as e: + try: + e = unicode(e).encode("UTF-8") + except: + e = repr(e).encode("UTF-8") + message = python.PyBytes_FromFormat( + "Error executing extension element '%s': %s", + c_inst_node.name, _cstr(e)) + xslt.xsltTransformError(c_ctxt, NULL, c_inst_node, "%s", message) + context._exc._store_raised() + except: + # just in case + message = python.PyBytes_FromFormat( + "Error executing extension element '%s'", c_inst_node.name) + xslt.xsltTransformError(c_ctxt, NULL, c_inst_node, "%s", message) + context._exc._store_raised() + except: + # no Python functions here - everything can fail... + xslt.xsltTransformError(c_ctxt, NULL, c_inst_node, + "Error during XSLT extension element evaluation") + context._exc._store_raised() + finally: + return # swallow any further exceptions |