From scoder at codespeak.net Sat Mar 1 17:59:51 2008
From: scoder at codespeak.net (scoder at codespeak.net)
Date: Sat, 1 Mar 2008 17:59:51 +0100 (CET)
Subject: [Lxml-checkins] r52006 - in lxml/trunk: . doc
Message-ID: <20080301165951.77B341684EC@codespeak.net>
Author: scoder
Date: Sat Mar 1 17:59:49 2008
New Revision: 52006
Modified:
lxml/trunk/ (props changed)
lxml/trunk/doc/FAQ.txt
Log:
r3651 at delle: sbehnel | 2008-03-01 17:00:46 +0100
clarification on MacOS-X crashes
Modified: lxml/trunk/doc/FAQ.txt
==============================================================================
--- lxml/trunk/doc/FAQ.txt (original)
+++ lxml/trunk/doc/FAQ.txt Sat Mar 1 17:59:49 2008
@@ -408,15 +408,15 @@
the FAQ section on threading_ to check if you touch on one of the
potential pitfalls.
-b) If you are on Mac-OS X, make sure lxml uses the correct libraries. If you
- have updated the old system libraries (e.g. through fink), this is best
- achieved by building lxml statically to prevent the different library
- versions from interfering. If you choose to use a dynamically linked
- version, make sure the ``DYLD_LIBRARY_PATH`` environment variable contains
- the directory where you installed the libraries. To make sure the correct
- libraries are used, print the module level version numbers that
- ``lxml.etree`` provides from *within* your application rather than relying
- on what your operating system tells you.
+b) If you are on Mac-OS X, make sure lxml uses the correct libraries.
+ Since the normal system libraries are pretty much outdated, you
+ likely have installed newer versions through a package management
+ system like fink or macports. In this case, please make sure the
+ ``DYLD_LIBRARY_PATH`` environment variable contains the directory
+ where you installed the libraries. There are other Python packages
+ that depend on libxml2, so it is up to you to make sure that *all*
+ packages that dynamically load libxml2 load the *same* library
+ version. Loading conflicting versions *will* lead to a crash.
In any case, try to reproduce the problem with the latest versions of
libxml2 and libxslt. From time to time, bugs and race conditions are found
From scoder at codespeak.net Sat Mar 1 17:59:55 2008
From: scoder at codespeak.net (scoder at codespeak.net)
Date: Sat, 1 Mar 2008 17:59:55 +0100 (CET)
Subject: [Lxml-checkins] r52007 - in lxml/trunk: . doc
Message-ID: <20080301165955.934AB1684EC@codespeak.net>
Author: scoder
Date: Sat Mar 1 17:59:55 2008
New Revision: 52007
Modified:
lxml/trunk/ (props changed)
lxml/trunk/doc/FAQ.txt
Log:
r3652 at delle: sbehnel | 2008-03-01 17:58:57 +0100
more clarifications on MacOS-X crashes
Modified: lxml/trunk/doc/FAQ.txt
==============================================================================
--- lxml/trunk/doc/FAQ.txt (original)
+++ lxml/trunk/doc/FAQ.txt Sat Mar 1 17:59:55 2008
@@ -396,33 +396,53 @@
My application crashes!
-----------------------
-One of the goals of lxml is "no segfaults", so if there is no clear warning in
-the documentation that you were doing something potentially harmful, you have
-found a bug and we would like to hear about it. Please report this bug to the
-`mailing list`_. See the next section on how to do that.
-
-However, there are a few things to try first, to make sure the problem is
-really within lxml (or libxml2 or libxslt):
-
-a) If your application (or e.g. your web container) uses threads, please see
- the FAQ section on threading_ to check if you touch on one of the
- potential pitfalls.
-
-b) If you are on Mac-OS X, make sure lxml uses the correct libraries.
- Since the normal system libraries are pretty much outdated, you
- likely have installed newer versions through a package management
- system like fink or macports. In this case, please make sure the
- ``DYLD_LIBRARY_PATH`` environment variable contains the directory
- where you installed the libraries. There are other Python packages
- that depend on libxml2, so it is up to you to make sure that *all*
- packages that dynamically load libxml2 load the *same* library
- version. Loading conflicting versions *will* lead to a crash.
+One of the goals of lxml is "no segfaults", so if there is no clear
+warning in the documentation that you were doing something potentially
+harmful, you have found a bug and we would like to hear about it.
+Please report this bug to the `mailing list`_. See the section on bug
+reporting to learn how to do that.
+
+If your application (or e.g. your web container) uses threads, please
+see the FAQ section on threading_ to check if you touch on one of the
+potential pitfalls.
In any case, try to reproduce the problem with the latest versions of
libxml2 and libxslt. From time to time, bugs and race conditions are found
in these libraries, so a more recent version might already contain a fix for
your problem.
+Remember: even if you see lxml appear in a crash stack trace, it is
+not necessarily lxml that *caused* the crash.
+
+
+My application crashes on MacOS-X!
+----------------------------------
+
+Since the normal system libraries are pretty much outdated, you likely
+have installed newer versions through a package management system like
+fink or macports in addition to the system libraries. Chances are
+high that your system is confused by the conflicting library versions.
+
+To work around this, please set the ``DYLD_LIBRARY_PATH`` environment
+variable *at runtime* to the directory where you installed the newer
+libraries. There are other Python packages that depend on libxml2, so
+it is up to you to make sure that *all* packages that dynamically load
+libxml2 load the *same* library version. Loading conflicting versions
+*will* lead to a crash and has confused a lot of MacOS users already.
+
+Please understand that if your system uses conflicting library
+versions, there is nothing lxml can do about it. It is up to you as a
+user to make sure you have a sane execution environment.
+
+See `bug 197243`_ for more information.
+
+.. _`bug 197243`: https://bugs.launchpad.net/lxml/+bug/197243
+
+If you want a sane, reliable execution environment, especially for
+production systems, `using a buildout`_ might be a good idea.
+
+.. _`using a buildout`: http://comments.gmane.org/gmane.comp.python.lxml.devel/3297?set_lines=100000
+
I think I have found a bug in lxml. What should I do?
-----------------------------------------------------
@@ -604,11 +624,13 @@
more robust against possible pitfalls. So newer versions might already fix
your problem in a reliable way.
-* make sure the library versions you installed are really used. Do not rely
- on what your operating system tells you! Print the version constants in
- ``lxml.etree`` from within your runtime environment to make sure it is the
- case. This is especially a problem under MacOS-X when newer library
- versions were installed in addition to the outdated system libraries.
+* make sure the library versions you installed are really used. Do
+ not rely on what your operating system tells you! Print the version
+ constants in ``lxml.etree`` from within your runtime environment to
+ make sure it is the case. This is especially a problem under
+ MacOS-X when newer library versions were installed in addition to
+ the outdated system libraries. Please read the bugs section
+ regarding MacOS-X in this FAQ.
* if you use ``mod_python``, try setting this option:
From scoder at codespeak.net Sun Mar 2 09:31:22 2008
From: scoder at codespeak.net (scoder at codespeak.net)
Date: Sun, 2 Mar 2008 09:31:22 +0100 (CET)
Subject: [Lxml-checkins] r52025 - in lxml/trunk: . src/lxml src/lxml/tests
Message-ID: <20080302083122.37468168538@codespeak.net>
Author: scoder
Date: Sun Mar 2 09:31:20 2008
New Revision: 52025
Modified:
lxml/trunk/ (props changed)
lxml/trunk/TODO.txt
lxml/trunk/src/lxml/readonlytree.pxi
lxml/trunk/src/lxml/tests/test_xslt.py
lxml/trunk/src/lxml/xslt.pxd
Log:
r3664 at delle: sbehnel | 2008-03-02 08:56:19 +0100
r3650 at delle: sbehnel | 2008-02-29 21:39:21 +0100
initial import: will use read-only elements to access the XSLT tree, the input tree and the output tree
Modified: lxml/trunk/TODO.txt
==============================================================================
--- lxml/trunk/TODO.txt (original)
+++ lxml/trunk/TODO.txt Sun Mar 2 09:31:20 2008
@@ -45,6 +45,22 @@
by libxml2 (patch exists)
+XSLT extension elements
+-----------------------
+
+* implementation: one base class that represents the result parent
+
+ - .append(), .extend() and .text will add to the result tree (no .tail)
+
+ - difference: Elements should be copied, not moved? (will break
+ later changes, but this just means that Elements in the result
+ tree are immutable, including those that were added)
+
+ - how to make input tree read-only? maybe just document?
+
+ - docs: "once in the result tree, Elements must no longer be changed"?
+
+
lxml 2.0
========
Modified: lxml/trunk/src/lxml/readonlytree.pxi
==============================================================================
--- lxml/trunk/src/lxml/readonlytree.pxi (original)
+++ lxml/trunk/src/lxml/readonlytree.pxi Sun Mar 2 09:31:20 2008
@@ -207,17 +207,21 @@
cdef _ReadOnlyElementProxy NEW_RO_PROXY "PY_NEW" (object t)
cdef _ReadOnlyElementProxy _newReadOnlyProxy(
- _ReadOnlyElementProxy sourceProxy, xmlNode* c_node):
+ _ReadOnlyElementProxy source_proxy, xmlNode* c_node):
cdef _ReadOnlyElementProxy el
el = NEW_RO_PROXY(_ReadOnlyElementProxy)
el._c_node = c_node
- if sourceProxy is None:
+ _initReadOnlyProxy(el, source_proxy)
+ return el
+
+cdef inline _initReadOnlyProxy(_ReadOnlyElementProxy el,
+ _ReadOnlyElementProxy source_proxy):
+ if source_proxy is None:
el._source_proxy = el
el._dependent_proxies = [el]
else:
- el._source_proxy = sourceProxy
- python.PyList_Append(sourceProxy._dependent_proxies, el)
- return el
+ el._source_proxy = source_proxy
+ python.PyList_Append(source_proxy._dependent_proxies, el)
cdef _freeReadOnlyProxies(_ReadOnlyElementProxy sourceProxy):
cdef _ReadOnlyElementProxy el
@@ -228,3 +232,71 @@
for el in sourceProxy._dependent_proxies:
el._c_node = NULL
del sourceProxy._dependent_proxies[:]
+
+
+cdef class _ReadOnlyRootElementProxy(_ReadOnlyElementProxy):
+ """A read-only element that frees the subtree on deallocation.
+ """
+ def __dealloc__(self):
+ if self._c_node is not NULL:
+ tree.xmlFreeNode(self._c_node)
+
+cdef class _AppendOnlyElementProxy(_ReadOnlyElementProxy):
+ """A read-only element that allows adding children and changing the
+ text content (i.e. everything that adds to the subtree).
+ """
+ cpdef append(self, other_element):
+ """Append a copy of an Element to the list of children.
+ """
+ cdef xmlNode* c_next
+ cdef xmlNode* c_node
+ self._assertNode()
+ c_node = _roNodeOf(other_element)
+ c_node = _copyNodeToDoc(c_node, self._c_node.doc)
+ c_next = c_node.next
+ tree.xmlAddChild(self._c_node, c_node)
+ _moveTail(c_next, c_node)
+
+ def extend(self, elements):
+ """Append a copy of all Elements from a sequence to the list of
+ children.
+ """
+ self._assertNode()
+ for element in elements:
+ self.append(element)
+
+ property text:
+ """Text before the first subelement. This is either a string or the
+ value None, if there was no text.
+ """
+ def __get__(self):
+ self._assertNode()
+ return _collectText(self._c_node.children)
+
+ def __set__(self, value):
+ self._assertNode()
+ if isinstance(value, QName):
+ value = python.PyUnicode_FromEncodedObject(
+ _resolveQNameText(self, value), 'UTF-8', 'strict')
+ _setNodeText(self._c_node, value)
+
+cdef _AppendOnlyElementProxy _newAppendOnlyProxy(
+ _ReadOnlyElementProxy source_proxy, xmlNode* c_node):
+ cdef _AppendOnlyElementProxy el
+ el = <_AppendOnlyElementProxy>NEW_RO_PROXY(_AppendOnlyElementProxy)
+ el._c_node = c_node
+ _initReadOnlyProxy(el, source_proxy)
+ return el
+
+cdef xmlNode* _roNodeOf(element) except NULL:
+ cdef xmlNode* c_node
+ if isinstance(element, _Element):
+ c_node = (<_Element>element)._c_node
+ elif isinstance(element, _ReadOnlyElementProxy):
+ c_node = (<_ReadOnlyElementProxy>element)._c_node
+ else:
+ raise TypeError("invalid value to append()")
+
+ if c_node is NULL:
+ raise TypeError("invalid element")
+ return c_node
Modified: lxml/trunk/src/lxml/tests/test_xslt.py
==============================================================================
--- lxml/trunk/src/lxml/tests/test_xslt.py (original)
+++ lxml/trunk/src/lxml/tests/test_xslt.py Sun Mar 2 09:31:20 2008
@@ -604,6 +604,26 @@
self.assertEquals(self._rootstring(result),
'X')
+ def test_extension_element(self):
+ tree = self.parse('B')
+ style = self.parse('''\
+
+
+ b
+
+''')
+
+ class mytext(etree.XSLTExtension):
+ pass
+
+ result = tree.xslt(style, extensions={})
+ self.assertEquals(self._rootstring(result),
+ 'X')
+
def test_xslt_document_XML(self):
# make sure document('') works from parsed strings
xslt = etree.XSLT(etree.XML("""\
Modified: lxml/trunk/src/lxml/xslt.pxd
==============================================================================
--- lxml/trunk/src/lxml/xslt.pxd (original)
+++ lxml/trunk/src/lxml/xslt.pxd Sun Mar 2 09:31:20 2008
@@ -1,4 +1,4 @@
-from tree cimport xmlDoc, xmlDict
+from tree cimport xmlDoc, xmlNode, xmlDict
from xpath cimport xmlXPathContext, xmlXPathFunction
cdef extern from "libxslt/xslt.h":
@@ -22,6 +22,11 @@
void* _private
xmlDict* dict
int profile
+ xmlNode* node
+ xmlDoc* output
+ xmlNode* insert
+
+ ctypedef struct xsltStackElem
cdef xsltStylesheet* xsltParseStylesheetDoc(xmlDoc* doc) nogil
cdef void xsltFreeStylesheet(xsltStylesheet* sheet) nogil
@@ -59,6 +64,9 @@
char** params, char* output,
void* profile,
xsltTransformContext* context) nogil
+ cdef void xsltProcessOneNode(xsltTransformContext* ctxt,
+ xmlNode* contextNode,
+ xsltStackElem* params)
cdef xsltTransformContext* xsltNewTransformContext(xsltStylesheet* style,
xmlDoc* doc) nogil
cdef void xsltFreeTransformContext(xsltTransformContext* context) nogil
From scoder at codespeak.net Sun Mar 2 09:31:28 2008
From: scoder at codespeak.net (scoder at codespeak.net)
Date: Sun, 2 Mar 2008 09:31:28 +0100 (CET)
Subject: [Lxml-checkins] r52026 - in lxml/trunk: . doc src/lxml
src/lxml/tests
Message-ID: <20080302083128.96BF3168539@codespeak.net>
Author: scoder
Date: Sun Mar 2 09:31:28 2008
New Revision: 52026
Added:
lxml/trunk/src/lxml/xsltext.pxi
Modified:
lxml/trunk/ (props changed)
lxml/trunk/CHANGES.txt
lxml/trunk/doc/extensions.txt
lxml/trunk/doc/xpathxslt.txt
lxml/trunk/src/lxml/lxml.etree.pyx
lxml/trunk/src/lxml/readonlytree.pxi
lxml/trunk/src/lxml/tests/test_xslt.py
lxml/trunk/src/lxml/xslt.pxd
lxml/trunk/src/lxml/xslt.pxi
Log:
r3665 at delle: sbehnel | 2008-03-02 08:56:20 +0100
r3655 at delle: sbehnel | 2008-03-01 22:27:17 +0100
partial reimplementation of the extension element mechanism, works now
Modified: lxml/trunk/CHANGES.txt
==============================================================================
--- lxml/trunk/CHANGES.txt (original)
+++ lxml/trunk/CHANGES.txt Sun Mar 2 09:31:28 2008
@@ -8,6 +8,8 @@
Features added
--------------
+* Extension elements for XSLT
+
* ``Element.base`` property returns the xml:base or HTML base URL of
an Element.
Modified: lxml/trunk/doc/extensions.txt
==============================================================================
--- lxml/trunk/doc/extensions.txt (original)
+++ lxml/trunk/doc/extensions.txt Sun Mar 2 09:31:28 2008
@@ -1,15 +1,24 @@
-Extension functions for XPath and XSLT
-======================================
+Python extensions for XPath and XSLT
+====================================
-This document describes how to use Python extension functions in XPath and
-XSLT. They allow you to do things like this::
+This document describes how to use Python extension functions in XPath
+and XSLT like this::
-Here is how such a function looks like. As the first argument, it always
-receives a context object (see below). The other arguments are provided by
-the respective call in the XPath expression, one in the following examples.
-Any number of arguments is allowed::
+It also describes how to use Python extension elements in XSLT like
+this::
+
+
+
+
+
+
+
+Here is how an extension function looks like. As the first argument,
+it always receives a context object (see below). The other arguments
+are provided by the respective call in the XPath expression, one in
+the following examples. Any number of arguments is allowed::
>>> def hello(dummy, a):
... return "Hello %s" % a
@@ -18,14 +27,23 @@
>>> def loadsofargs(dummy, *args):
... return "Got %d arguments." % len(args)
+And here is how an extension element looks like::
+
+ >>> from lxml import etree
+ >>> class MyExtElement(etree.XSLTExtension):
+ ... def execute(self, context, self_node, input_node, output_parent):
+ ... # just copy own content input to output
+ ... output_parent.extend( list(self_node) )
+
.. contents::
..
1 The FunctionNamespace
2 Global prefix assignment
- 3 Evaluators and XSLT
- 4 Evaluator-local extensions
- 5 What to return from a function
+ 3 The XPath context
+ 4 Evaluators and XSLT
+ 5 Evaluator-local extensions
+ 6 What to return from a function
The FunctionNamespace
@@ -36,7 +54,6 @@
FunctionNamespace class. For simplicity, we choose the empty namespace
(None)::
- >>> from lxml import etree
>>> ns = etree.FunctionNamespace(None)
>>> ns['hello'] = hello
>>> ns['countargs'] = loadsofargs
Modified: lxml/trunk/doc/xpathxslt.txt
==============================================================================
--- lxml/trunk/doc/xpathxslt.txt (original)
+++ lxml/trunk/doc/xpathxslt.txt Sun Mar 2 09:31:28 2008
@@ -454,6 +454,14 @@
'\nText\n'
+Extension elements
+------------------
+
+Just like `custom extension functions`_, lxml supports custom
+extension *elements*.
+
+
+
The ``xslt()`` tree method
--------------------------
Modified: lxml/trunk/src/lxml/lxml.etree.pyx
==============================================================================
--- lxml/trunk/src/lxml/lxml.etree.pyx (original)
+++ lxml/trunk/src/lxml/lxml.etree.pyx Sun Mar 2 09:31:28 2008
@@ -2578,9 +2578,15 @@
include "iterparse.pxi" # incremental XML parsing
include "xmlid.pxi" # XMLID and IDDict
include "xinclude.pxi" # XInclude
+
+
+################################################################################
+# Include submodules for XPath and XSLT
+
include "extensions.pxi" # XPath/XSLT extension functions
include "xpath.pxi" # XPath evaluation
include "xslt.pxi" # XSL transformations
+include "xsltext.pxi" # XSL extension elements
################################################################################
Modified: lxml/trunk/src/lxml/readonlytree.pxi
==============================================================================
--- lxml/trunk/src/lxml/readonlytree.pxi (original)
+++ lxml/trunk/src/lxml/readonlytree.pxi Sun Mar 2 09:31:28 2008
@@ -2,6 +2,7 @@
cdef class _ReadOnlyElementProxy:
"The main read-only Element proxy class (for internal use only!)."
+ cdef bint _free_after_use
cdef xmlNode* _c_node
cdef object _source_proxy
cdef object _dependent_proxies
@@ -12,6 +13,11 @@
assert self._c_node is not NULL, "Proxy invalidated!"
return 0
+ cdef void free_after_use(self):
+ """Should the xmlNode* be freed when releasing the proxy?
+ """
+ self._free_after_use = 1
+
property tag:
"""Element tag
"""
@@ -216,6 +222,7 @@
cdef inline _initReadOnlyProxy(_ReadOnlyElementProxy el,
_ReadOnlyElementProxy source_proxy):
+ el._free_after_use = 0
if source_proxy is None:
el._source_proxy = el
el._dependent_proxies = [el]
@@ -224,23 +231,19 @@
python.PyList_Append(source_proxy._dependent_proxies, el)
cdef _freeReadOnlyProxies(_ReadOnlyElementProxy sourceProxy):
+ cdef xmlNode* c_node
cdef _ReadOnlyElementProxy el
if sourceProxy is None:
return
if sourceProxy._dependent_proxies is None:
return
for el in sourceProxy._dependent_proxies:
+ c_node = el._c_node
el._c_node = NULL
+ if el._free_after_use:
+ tree.xmlFreeNode(c_node)
del sourceProxy._dependent_proxies[:]
-
-cdef class _ReadOnlyRootElementProxy(_ReadOnlyElementProxy):
- """A read-only element that frees the subtree on deallocation.
- """
- def __dealloc__(self):
- if self._c_node is not NULL:
- tree.xmlFreeNode(self._c_node)
-
cdef class _AppendOnlyElementProxy(_ReadOnlyElementProxy):
"""A read-only element that allows adding children and changing the
text content (i.e. everything that adds to the subtree).
Modified: lxml/trunk/src/lxml/tests/test_xslt.py
==============================================================================
--- lxml/trunk/src/lxml/tests/test_xslt.py (original)
+++ lxml/trunk/src/lxml/tests/test_xslt.py Sun Mar 2 09:31:28 2008
@@ -613,17 +613,68 @@
extension-element-prefixes="myns"
exclude-result-prefixes="myns">
- b
+ b
''')
- class mytext(etree.XSLTExtension):
- pass
+ class MyExt(etree.XSLTExtension):
+ def execute(self, context, self_node, input_node, output_parent):
+ child = etree.Element(self_node.text)
+ child.text = 'X'
+ output_parent.append(child)
+
+ extensions = { ('testns', 'myext') : MyExt() }
- result = tree.xslt(style, extensions={})
+ result = tree.xslt(style, extensions=extensions)
self.assertEquals(self._rootstring(result),
'X')
+ def test_extension_element_content(self):
+ tree = self.parse('B')
+ style = self.parse('''\
+
+
+ XY
+
+''')
+
+ class MyExt(etree.XSLTExtension):
+ def execute(self, context, self_node, input_node, output_parent):
+ output_parent.extend(list(self_node)[1:])
+
+ extensions = { ('testns', 'myext') : MyExt() }
+
+ result = tree.xslt(style, extensions=extensions)
+ self.assertEquals(self._rootstring(result),
+ 'Y')
+
+ def test_extension_element_raise(self):
+ tree = self.parse('B')
+ style = self.parse('''\
+
+
+ b
+
+''')
+
+ class MyError(Exception):
+ pass
+
+ class MyExt(etree.XSLTExtension):
+ def execute(self, context, self_node, input_node, output_parent):
+ raise MyError("expected!")
+
+ extensions = { ('testns', 'myext') : MyExt() }
+ self.assertRaises(MyError, tree.xslt, style, extensions=extensions)
+
def test_xslt_document_XML(self):
# make sure document('') works from parsed strings
xslt = etree.XSLT(etree.XML("""\
Modified: lxml/trunk/src/lxml/xslt.pxd
==============================================================================
--- lxml/trunk/src/lxml/xslt.pxd (original)
+++ lxml/trunk/src/lxml/xslt.pxd Sun Mar 2 09:31:28 2008
@@ -8,6 +8,11 @@
cdef int LIBXSLT_VERSION
cdef extern from "libxslt/xsltInternals.h":
+ ctypedef enum xsltTransformState:
+ XSLT_STATE_OK # 0
+ XSLT_STATE_ERROR # 1
+ XSLT_STATE_STOPPED # 2
+
ctypedef struct xsltDocument:
xmlDoc* doc
@@ -25,6 +30,7 @@
xmlNode* node
xmlDoc* output
xmlNode* insert
+ xsltTransformState state
ctypedef struct xsltStackElem
@@ -32,6 +38,11 @@
cdef void xsltFreeStylesheet(xsltStylesheet* sheet) nogil
cdef extern from "libxslt/extensions.h":
+ ctypedef void (*xsltTransformFunction)(xsltTransformContext* ctxt,
+ xmlNode* context_node,
+ xmlNode* inst,
+ void* precomp_unused)
+
cdef int xsltRegisterExtFunction(xsltTransformContext* ctxt,
char* name,
char* URI,
@@ -43,6 +54,9 @@
char* name, char* URI) nogil
cdef int xsltRegisterExtPrefix(xsltStylesheet* style,
char* prefix, char* URI) nogil
+ cdef int xsltRegisterExtElement(xsltTransformContext* ctxt,
+ char* name, char* URI,
+ xsltTransformFunction function) nogil
cdef extern from "libxslt/documents.h":
ctypedef enum xsltLoadType:
@@ -82,7 +96,9 @@
cdef void xsltSetTransformErrorFunc(
xsltTransformContext*, void* ctxt,
void (*handler)(void* ctxt, char* msg, ...)) nogil
-
+ cdef void xsltTransformError(xsltTransformContext* ctxt,
+ xsltStylesheet* style,
+ xmlNode* node, char* msg, ...)
cdef extern from "libxslt/security.h":
ctypedef struct xsltSecurityPrefs
ctypedef enum xsltSecurityOption:
Modified: lxml/trunk/src/lxml/xslt.pxi
==============================================================================
--- lxml/trunk/src/lxml/xslt.pxi (original)
+++ lxml/trunk/src/lxml/xslt.pxi Sun Mar 2 09:31:28 2008
@@ -229,15 +229,34 @@
cdef class _XSLTContext(_BaseContext):
cdef xslt.xsltTransformContext* _xsltCtxt
+ cdef object _extension_elements
+ cdef _ReadOnlyElementProxy _extension_element_proxy
def __init__(self, namespaces, extensions, enable_regexp):
self._xsltCtxt = NULL
- if extensions is not None:
- for ns, prefix in extensions:
- if ns is None:
+ self._extension_elements = EMPTY_READ_ONLY_DICT
+ if extensions is not None and extensions:
+ for ns_name_tuple, extension in extensions.items():
+ if ns_name_tuple[0] is None:
raise XSLTExtensionError(
"extensions must not have empty namespaces")
+ if isinstance(extension, XSLTExtension):
+ if self._extension_elements is EMPTY_READ_ONLY_DICT:
+ self._extension_elements = {}
+ extensions = python.PyDict_Copy(extensions)
+ ns_utf = _utf8(ns_name_tuple[0])
+ name_utf = _utf8(ns_name_tuple[1])
+ python.PyDict_SetItem(
+ self._extension_elements, (ns_utf, name_utf),
+ extension)
+ python.PyDict_DelItem(extensions, ns_name_tuple)
_BaseContext.__init__(self, namespaces, extensions, enable_regexp)
+ cdef _BaseContext _copy(self):
+ cdef _XSLTContext context
+ context = <_XSLTContext>_BaseContext._copy(self)
+ context._extension_elements = self._extension_elements
+ return context
+
cdef register_context(self, xslt.xsltTransformContext* xsltCtxt,
_Document doc):
self._xsltCtxt = xsltCtxt
@@ -245,6 +264,7 @@
self._register_context(doc)
self.registerLocalFunctions(xsltCtxt, _register_xslt_function)
self.registerGlobalFunctions(xsltCtxt, _register_xslt_function)
+ _registerXSLTExtensions(xsltCtxt, self._extension_elements)
cdef free_context(self):
self._cleanup_context()
@@ -437,6 +457,11 @@
tree.xmlFreeDoc(c_result)
resolver_context._raise_if_stored()
+ if context._exc._has_raised():
+ if c_result is not NULL:
+ tree.xmlFreeDoc(c_result)
+ context._exc._raise_if_stored()
+
if c_result is NULL:
# last error seems to be the most accurate here
error = self._error_log.last_error
Added: lxml/trunk/src/lxml/xsltext.pxi
==============================================================================
--- (empty file)
+++ lxml/trunk/src/lxml/xsltext.pxi Sun Mar 2 09:31:28 2008
@@ -0,0 +1,111 @@
+# 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 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.
+ """
+ pass
+
+ def apply_templates(self, _XSLTContext context not None, node):
+ """apply_templates(self, context, node)
+
+ Call this method to continue applying templates to the input
+ document. Starts at the
+
+ The return value is a list of elements that were generated.
+ """
+ cdef xmlNode* c_parent
+ cdef xmlNode* c_node
+ cdef xmlNode* c_next
+ cdef xmlNode* c_context_node
+ cdef _ReadOnlyElementProxy proxy
+ c_context_node = _roNodeOf(node)
+ #assert c_context_node.doc is context._xsltContext.node.doc, \
+ # "switching input documents during transformation is not currently supported"
+
+ c_parent = tree.xmlNewDocNode(
+ context._xsltCtxt.output, NULL, "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
+
+ results = []
+ c_node = c_parent.children
+ try:
+ while c_node is not NULL:
+ c_next = c_node.next
+ tree.xmlUnlinkNode(c_node)
+ proxy = _newReadOnlyProxy(
+ context._extension_element_proxy, c_node)
+ proxy.free_after_use()
+ python.PyList_Append(results, proxy)
+ c_node = c_next
+ finally:
+ tree.xmlFreeNode(c_parent)
+ return results
+
+
+cdef _registerXSLTExtensions(xslt.xsltTransformContext* c_ctxt,
+ extension_dict):
+ for ns, name in extension_dict:
+ xslt.xsltRegisterExtElement(
+ c_ctxt, _cstr(name), _cstr(ns), _callExtensionElement)
+
+cdef void _callExtensionElement(xslt.xsltTransformContext* c_ctxt,
+ xmlNode* c_context_node,
+ xmlNode* c_inst_node,
+ void* dummy) with gil:
+ cdef _XSLTContext context
+ cdef XSLTExtension extension
+ cdef python.PyObject* dict_result
+ cdef char* c_uri
+ cdef _ReadOnlyElementProxy context_node, self_node, output_parent
+ 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:
+ dict_result = python.PyDict_GetItem(
+ context._extension_elements, (c_uri, c_inst_node.name))
+ if dict_result is NULL:
+ raise KeyError("extension element %s not found",
+ c_inst_node.name)
+ extension =