Attached is a patch of the libxslt-1.0.30 source which contains the modifications needed to callback into python for extension element binding. (Should patch against 1.0.31 too) Thanks a ton for completing the function binding. I based most of the code off of that. Issues and oddities: * Type wrapping I followed the example in libxslt.c for wrapping the xsltElemPreCompPtr. Given that I am not entirely clear on the role of this object, I didn't touch the semantics behind it, only wrapped the pointer to act as a placeholder. ` Shouldn't these type wrappers go in types.c? What is the role of xsltElemPreCompPtr? * Returning from callbacks What should the behavior be when returning failure from the callback methods? Right now results are ignored. * Recommended method of packing python tuples PyTuple_SetItem steals object references, which bit me in the butt. Use Py_BuildValue instead. http://www.python.org/doc/current/api/refcountDetails.html#l2h-13 I haven't touched the functionality of libxslt_xsltRegisterExtModuleFunction but I saw somewhere you mentioned there was still a bug. If you're seeing segfaults then refcount bugs could be coming from your argument creation. * Super minor cleanup I removed some unreferenced variables and initialized some pointers. Fixed the bug found also in the libxml2 python wrapper regarding PyFile checking. * New python api declaration + generate.py I get some strange behavior when running generate.py. I haven't debugged the generation process, but these methods appear in my class list during generation which don't show up in the 1.0.30 libxsltclass.txt distribution. Is it related to my modification of libxslt-python-api.xml? I get around this by deleting the following lines from my libxsltclass.txt file before making the python module. # functions from module transform applyImports() applyStripSpaces() applyTemplates() attribute() callTemplate() choose() comment() copy() copyOf() documentElem() element() forEach() freeTransformContext() if() number() processingInstruction() registerAllElement() sort() text() valueOf() The work around I mention is not included in the diff, but thought I'd bring the behavior to your attention. I'm not depending on the naming scheme I chose. Please edit it to follow your naming scheme. Also, I think it's a bit ugly that one has to "cast" the incoming PyCObjects to usable ones (ctxt to transformCtxt). This should be done in the wrapper. Now to putting this into use! It's fairly straightforward. The following python code will show the order of extension class and element compile/transform: #################### #!/usr/bin/env python import sys import libxml2 import libxslt BOUND_URI = 'http://example.com/bound' UNBOUND_URI = 'http://example.com/unbound' ELEM = 'test' def test_compile(stylesheet, instruction, transform_method): print "### UNBOUND COMPILE" def test_transform(context, node, instruction, styleCompile): print "### UNBOUND TRANSFORM" class ext_class(libxslt.extensionModule): def styleInit(self, *args): print "styleInit" libxslt.registerExtModuleElement(ELEM, BOUND_URI, self.ext_compile, self.ext_transform) def ctxtInit(self, *args): print 'ctxtInit' def ext_compile(self, style, inst, func): print "### BOUND COMPILE", func == self.ext_transform def ext_transform(self, ctx, node, instruction, comp): print "### BOUND TRANSFORM" tctx = libxslt.transformCtxt(_obj=ctx) tctx.insertNode().addContent('-- SUCCESS --') styledoc = libxml2.parseDoc(""" <xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:bound='http://example.com/bound' xmlns:unbound='http://example.com/unbound' xsl:extension-element-prefixes='bound unbound'> <xsl:template match='/'> <article><bound:test/></article> <unbound:test/> </xsl:template> </xsl:stylesheet> """) libxslt.registerExtensionClass(BOUND_URI, ext_class()) libxslt.registerExtModuleElement(ELEM, UNBOUND_URI, test_compile, test_transform) print "parse SS" style = libxslt.parseStylesheetDoc(styledoc) xmldoc = libxml2.parseDoc("""<doc/>""") print "apply SS" res = style.applyStylesheet(xmldoc, {}) res.dump(sys.stdout) style.freeStylesheet() xmldoc.freeDoc() #################### Which outputs the following: parse SS styleInit ### BOUND COMPILE 1 ### UNBOUND COMPILE apply SS ctxtInit ### BOUND TRANSFORM ### UNBOUND TRANSFORM <?xml version="1.0"?> <article>-- SUCCESS --</article> #################### However, I've made a little test case with nested extension elements. Obviously, only my outer element gets transformed first because it doesn't continue the processing of its elements. What do I need to call to process the contents of the extension element before actually doing the custom processing? -Sean
extelem-libxslt-1.0.30.diff.gz