patch from Sean Treadway, adding Python bindings for extension element and
authorDaniel Veillard <veillard@src.gnome.org>
Wed, 9 Jul 2003 10:22:14 +0000 (10:22 +0000)
committerDaniel Veillard <veillard@src.gnome.org>
Wed, 9 Jul 2003 10:22:14 +0000 (10:22 +0000)
* python/generator.py python/libxslt-python-api.xml python/libxslt.c
  python/libxslt_wrap.h python/libxsltclass.txt: patch from
  Sean Treadway, adding Python bindings for extension element and
  some bindings cleanups.
* python/tests/Makefile.am python/tests/extelem.py: also add an
  example/test.
Daniel

ChangeLog
python/generator.py
python/libxslt-python-api.xml
python/libxslt.c
python/libxslt_wrap.h
python/libxsltclass.txt
python/tests/Makefile.am
python/tests/extelem.py [new file with mode: 0644]

index 8249a27..88289b6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+Wed Jul  9 12:19:34 CEST 2003 Daniel Veillard <daniel@veillard.com>
+
+       * python/generator.py python/libxslt-python-api.xml python/libxslt.c
+         python/libxslt_wrap.h python/libxsltclass.txt: patch from
+         Sean Treadway, adding Python bindings for extension element and
+         some bindings cleanups.
+       * python/tests/Makefile.am python/tests/extelem.py: also add an
+         example/test.
+
 Tue Jul  8 12:20:11 CEST 2003 Daniel Veillard <daniel@veillard.com>
 
        * python/libxml_wrap.h: applied patch from #116943 which should
index ac587a3..8721601 100755 (executable)
@@ -185,6 +185,7 @@ skipped_modules = {
     'list': None,
     'threads': None,
     'xpointer': None,
+    'transform': None,
 }
 skipped_types = {
     'int *': "usually a return type",
@@ -267,6 +268,8 @@ py_types = {
     'FILE *': ('O', "File", "FILEPtr", "FILE *", "libxml_"),
     'xsltTransformContextPtr':  ('O', "transformCtxt", "xsltTransformContextPtr", "xsltTransformContextPtr", "libxslt_"),
     'xsltTransformContext *':  ('O', "transformCtxt", "xsltTransformContextPtr", "xsltTransformContextPtr", "libxslt_"),
+    'xsltStylePreCompPtr':  ('O', "compiledStyle", "xsltStylePreCompPtr", "xsltStylePreCompPtr", "libxslt_"),
+    'xsltStylePreComp *':  ('O', "compiledStyle", "xsltStylePreCompPtr", "xsltStylePreCompPtr", "libxslt_"),
     'xsltStylesheetPtr':  ('O', "stylesheet", "xsltStylesheetPtr", "xsltStylesheetPtr", "libxslt_"),
     'xsltStylesheet *':  ('O', "stylesheet", "xsltStylesheetPtr", "xsltStylesheetPtr", "libxslt_"),
     'xmlXPathContext *':  ('O', "xpathContext", "xmlXPathContextPtr", "xmlXPathContextPtr", "libxslt_"),
index ecdc526..648f261 100644 (file)
       <arg name='f' type='pythonObject' info='the python function'/>
       <arg name='ctx' type='pythonObject' info='a context for the callback'/>
     </function>
+    <function name='xsltRegisterExtModuleElement' file='python'>
+      <info>Register a Python written element to the XSLT engine</info>
+      <return type='int' info="0 in case of success, -1 in case of error"/>
+      <arg name='name' type='xmlChar *' info='the element name'/>
+      <arg name='URI' type='xmlChar *' info='the namespace or NULL'/>
+      <arg name='precompile' type='pythonObject' info='method called when stylesheet is compiled'/>
+      <arg name='transform' type='pythonObject' info='method called during transform, must not modify stylesheet'/>
+    </function>
     <function name='xsltRegisterExtModuleFunction' file='python'>
       <info>Register a Python written function to the XSLT engine</info>
       <return type='int' info="0 in case of success, -1 in case of error"/>
index a6310a5..8497841 100644 (file)
@@ -31,6 +31,7 @@
 /* #define DEBUG_ERROR */
 /* #define DEBUG_MEMORY */
 /* #define DEBUG_EXTENSIONS */
+/* #define DEBUG_EXTENSIONS */
 
 void initlibxsltmod(void);
 
@@ -72,6 +73,22 @@ libxslt_xsltTransformContextPtrWrap(xsltTransformContextPtr ctxt) {
     return(ret);
 }
 
+PyObject *
+libxslt_xsltElemPreCompPtrWrap(xsltElemPreCompPtr ctxt) {
+    PyObject *ret;
+
+#ifdef DEBUG
+    printf("libxslt_xsltElemPreCompPtrWrap: ctxt = %p\n", ctxt);
+#endif
+    if (ctxt == NULL) {
+       Py_INCREF(Py_None);
+       return(Py_None);
+    }
+    ret = PyCObject_FromVoidPtrAndDesc((void *) ctxt,
+                                      (char *)"xsltElemPreCompPtr", NULL);
+    return(ret);
+}
+
 /************************************************************************
  *                                                                     *
  *                     Extending the API                               *
@@ -79,7 +96,238 @@ libxslt_xsltTransformContextPtrWrap(xsltTransformContextPtr ctxt) {
  ************************************************************************/
 
 static xmlHashTablePtr libxslt_extModuleFunctions = NULL;
+static xmlHashTablePtr libxslt_extModuleElements = NULL;
+static xmlHashTablePtr libxslt_extModuleElementPreComp = NULL;
+
+static void
+deallocateCallback(void *payload, xmlChar *name ATTRIBUTE_UNUSED) {
+    PyObject *function = (PyObject *) payload;
+
+#ifdef DEBUG_EXTENSIONS
+    printf("deallocateCallback(%s) called\n", name);
+#endif
+
+    Py_XDECREF(function);
+}
+
+static void
+deallocateClasse(void *payload, xmlChar *name ATTRIBUTE_UNUSED) {
+    PyObject *class = (PyObject *) payload;
+
+#ifdef DEBUG_EXTENSIONS
+    printf("deallocateClasse(%s) called\n", name);
+#endif
+
+    Py_XDECREF(class);
+}
+
+
+/**
+ * libxslt_xsltElementPreCompCallback
+ * @style:  the stylesheet
+ * @inst:  the instruction in the stylesheet
+ *
+ * Callback for preprocessing of a custom element
+ */
+static xsltElemPreCompPtr
+libxslt_xsltElementPreCompCallback(xsltStylesheetPtr style, xmlNodePtr inst,
+              xsltTransformFunction function) {
+    xsltElemPreCompPtr ret;
+    const xmlChar *name;
+    PyObject *args;
+    PyObject *result;
+    PyObject *pyobj_element_f;
+    PyObject *pyobj_precomp_f;
+
+   const xmlChar *ns_uri;
+
+
+#ifdef DEBUG_EXTENSIONS
+    printf("libxslt_xsltElementPreCompCallback called\n");
+#endif
+
+    if (style == NULL) {
+       xsltTransformError(NULL, NULL, inst,
+            "libxslt_xsltElementPreCompCallback: no transformation context\n");
+           return (NULL);
+    }
+
+    if (inst == NULL) {
+       xsltTransformError(NULL, style, inst,
+            "libxslt_xsltElementPreCompCallback: no instruction\n");
+       if (style != NULL) style->errors++;
+       return (NULL);
+    }
+
+    if (style == NULL)
+       return (NULL);
+    
+    if (inst != NULL && inst->ns != NULL) {
+       name = inst->name;
+       ns_uri = inst->ns->href;
+    } else {
+       xsltTransformError(NULL, style, inst, 
+               "libxslt_xsltElementPreCompCallback: internal error bad parameter\n");
+               printf("libxslt_xsltElementPreCompCallback: internal error bad parameter\n");
+       if (style != NULL) style->errors++;
+       return (NULL);
+    }
+
+    /*
+     * Find the functions, they should be there it was there at lookup
+     */
+    pyobj_precomp_f = xmlHashLookup2(libxslt_extModuleElementPreComp,
+                                     name, ns_uri);
+    if (pyobj_precomp_f == NULL) {
+       xsltTransformError(NULL, style, inst, 
+               "libxslt_xsltElementPreCompCallback: internal error, could not find precompile python function!\n");
+       if (style != NULL) style->errors++;
+       return (NULL);
+    }
+
+    pyobj_element_f = xmlHashLookup2(libxslt_extModuleElements,
+                                     name, ns_uri);
+    if (pyobj_element_f == NULL) {
+       xsltTransformError(NULL, style, inst, 
+               "libxslt_xsltElementPreCompCallback: internal error, could not find element python function!\n");
+       if (style != NULL) style->errors++;
+       return (NULL);
+    }
+
+    args = Py_BuildValue("(OOO)", 
+           libxslt_xsltStylesheetPtrWrap(style),
+           libxml_xmlNodePtrWrap(inst),
+           pyobj_element_f);
+
+    Py_INCREF(pyobj_precomp_f); /* Protect refcount against reentrant manipulation of callback hash */
+    result = PyEval_CallObject(pyobj_precomp_f, args);
+    Py_DECREF(pyobj_precomp_f);
+    Py_DECREF(args);
+
+    /* FIXME allow callbacks to return meaningful information to modify compile process */
+    /* If error, do we need to check the result and throw exception? */
+
+    Py_XDECREF(result);
+
+    ret = xsltNewElemPreComp (style, inst, function);
+    return (ret);
+}
+
+
+static void
+libxslt_xsltElementTransformCallback(xsltTransformContextPtr ctxt,
+                                   xmlNodePtr node, 
+                                   xmlNodePtr inst,
+                                   xsltElemPreCompPtr comp) 
+{
+    PyObject *args, *result;
+    PyObject *func = NULL;
+    const xmlChar *name;
+    const xmlChar *ns_uri;
+
+    if (ctxt == NULL)
+       return;
+    
+    if (inst != NULL && inst->name != NULL && inst->ns != NULL && inst->ns->href != NULL) {
+       name = inst->name;
+       ns_uri = inst->ns->href;
+    } else {
+       printf("libxslt_xsltElementTransformCallback: internal error bad parameter\n");
+       return;
+    }
+
+#ifdef DEBUG_EXTENSIONS
+    printf("libxslt_xsltElementTransformCallback called name %s URI %s\n", name, ns_uri);
+#endif
+
+    /*
+     * Find the function, it should be there it was there at lookup
+     */
+    func = xmlHashLookup2(libxslt_extModuleElements,
+                                     name, ns_uri);
+    if (func == NULL) {
+       printf("libxslt_xsltElementTransformCallback: internal error %s not found !\n",
+              name);
+       return;
+    }
+
+    args = Py_BuildValue("OOOO",
+       libxslt_xsltTransformContextPtrWrap(ctxt),
+       libxml_xmlNodePtrWrap(node),
+       libxml_xmlNodePtrWrap(inst),
+       libxslt_xsltElemPreCompPtrWrap(comp));
+
+    Py_INCREF(func); /* Protect refcount against reentrant manipulation of callback hash */
+    result = PyEval_CallObject(func, args);
+    Py_DECREF(func);
+    Py_DECREF(args);
+
+    /* FIXME Check result of callobject and set exception if fail */
+
+    Py_XDECREF(result);
+}
+
+PyObject *
+libxslt_xsltRegisterExtModuleElement(PyObject *self ATTRIBUTE_UNUSED,
+                                     PyObject *args) {
+    PyObject *py_retval;
+    int ret = 0;
+    xmlChar *name;
+    xmlChar *ns_uri;
+    PyObject *pyobj_element_f;
+    PyObject *pyobj_precomp_f;
 
+#ifdef DEBUG_EXTENSIONS
+    printf("libxslt_xsltRegisterExtModuleElement called\n",
+          name, ns_uri);
+#endif
+
+    if (!PyArg_ParseTuple(args, (char *)"szOO:registerExtModuleElement",
+                         &name, &ns_uri, &pyobj_precomp_f, &pyobj_element_f))
+        return(NULL);
+
+    if ((name == NULL) || (pyobj_element_f == NULL) || (pyobj_precomp_f == NULL)) {
+       py_retval = libxml_intWrap(-1);
+       return(py_retval);
+    }
+
+#ifdef DEBUG_EXTENSIONS
+    printf("libxslt_xsltRegisterExtModuleElement(%s, %s) called\n",
+          name, ns_uri);
+#endif
+
+    if (libxslt_extModuleElements == NULL)
+       libxslt_extModuleElements = xmlHashCreate(10);
+
+    if (libxslt_extModuleElementPreComp == NULL)
+       libxslt_extModuleElementPreComp = xmlHashCreate(10);
+
+    if (libxslt_extModuleElements == NULL || libxslt_extModuleElementPreComp == NULL) {
+       py_retval = libxml_intWrap(-1);
+       return(py_retval);
+    }
+
+    ret = xmlHashAddEntry2(libxslt_extModuleElements, name, ns_uri, pyobj_element_f);
+    if (ret != 0) {
+       py_retval = libxml_intWrap(-1);
+       return(py_retval);
+    }
+    Py_XINCREF(pyobj_element_f);
+
+    ret = xmlHashAddEntry2(libxslt_extModuleElementPreComp, name, ns_uri, pyobj_precomp_f);
+    if (ret != 0) {
+       xmlHashRemoveEntry2(libxslt_extModuleElements, name, ns_uri, deallocateCallback);
+       py_retval = libxml_intWrap(-1);
+       return(py_retval);
+    }
+    Py_XINCREF(pyobj_precomp_f);
+
+    ret = xsltRegisterExtModuleElement(name, ns_uri, 
+                                       libxslt_xsltElementPreCompCallback,
+                                       libxslt_xsltElementTransformCallback);
+    py_retval = libxml_intWrap((int) ret);
+    return(py_retval);
+}
 static void
 libxslt_xmlXPathFuncCallback(xmlXPathParserContextPtr ctxt, int nargs) {
     PyObject *list, *cur, *result;
@@ -119,11 +367,17 @@ libxslt_xmlXPathFuncCallback(xmlXPathParserContextPtr ctxt, int nargs) {
        cur = libxml_xmlXPathObjectPtrWrap(obj);
        PyTuple_SetItem(list, i + 1, cur);
     }
+
+    Py_INCREF(current_function);
     result = PyEval_CallObject(current_function, list);
+    Py_DECREF(current_function);
     Py_DECREF(list);
 
-    obj = libxml_xmlXPathObjectPtrConvert(result);
-    valuePush(ctxt, obj);
+    /* Check for null in case of exception */
+    if (result != NULL) {
+       obj = libxml_xmlXPathObjectPtrConvert(result);
+       valuePush(ctxt, obj);
+    }
 }
 
 PyObject *
@@ -168,17 +422,6 @@ libxslt_xsltRegisterExtModuleFunction(PyObject *self ATTRIBUTE_UNUSED,
     return(py_retval);
 }
 
-static void
-deallocateCallback(void *payload, xmlChar *name ATTRIBUTE_UNUSED) {
-    PyObject *function = (PyObject *) payload;
-
-#ifdef DEBUG_XPATH
-    printf("deallocateCallback(%s) called\n", name);
-#endif
-
-    Py_XDECREF(function);
-}
-
 /************************************************************************
  *                                                                     *
  *                     Some customized front-ends                      *
@@ -262,7 +505,6 @@ PyObject *
 libxslt_xsltSaveResultToString(PyObject *self, PyObject *args) {
     PyObject *py_retval;        /* our final return value, a python string   */ 
     xmlChar  *buffer;
-    xmlChar  *tmp;
     int       size    = 0;
     int       emitted = 0;
     xmlDocPtr result;
@@ -424,7 +666,7 @@ static xmlHashTablePtr libxslt_extModuleClasses = NULL;
 static void *
 libxslt_xsltPythonExtModuleStyleInit(xsltStylesheetPtr style,
                                    const xmlChar * URI) {
-    PyObject *result;
+    PyObject *result = NULL;
     PyObject *class = NULL;
 
 #ifdef DEBUG_EXTENSIONS
@@ -485,7 +727,7 @@ libxslt_xsltPythonExtModuleStyleShutdown(xsltStylesheetPtr style,
 static void *
 libxslt_xsltPythonExtModuleCtxtInit(xsltTransformContextPtr ctxt,
                                    const xmlChar * URI) {
-    PyObject *result;
+    PyObject *result = NULL;
     PyObject *class = NULL;
 
 #ifdef DEBUG_EXTENSIONS
@@ -549,7 +791,6 @@ libxslt_xsltRegisterExtensionClass(PyObject *self ATTRIBUTE_UNUSED,
                                   PyObject *args) {
     PyObject *py_retval;
     int ret = 0;
-    xmlChar *name;
     xmlChar *ns_uri;
     PyObject *pyobj_c;
 
@@ -591,17 +832,6 @@ libxslt_xsltRegisterExtensionClass(PyObject *self ATTRIBUTE_UNUSED,
     return(py_retval);
 }
 
-static void
-deallocateClasse(void *payload, xmlChar *name ATTRIBUTE_UNUSED) {
-    PyObject *class = (PyObject *) payload;
-
-#ifdef DEBUG_EXTENSIONS
-    printf("deallocateClasse(%s) called\n", name);
-#endif
-
-    Py_XDECREF(class);
-}
-
 /************************************************************************
  *                                                                     *
  *                     Integrated cleanup                              *
@@ -615,6 +845,12 @@ libxslt_xsltCleanup(PyObject *self ATTRIBUTE_UNUSED,
     if (libxslt_extModuleFunctions != NULL) {
        xmlHashFree(libxslt_extModuleFunctions, deallocateCallback);
     }
+    if (libxslt_extModuleElements != NULL) {
+       xmlHashFree(libxslt_extModuleElements, deallocateCallback);
+    }
+    if (libxslt_extModuleElementPreComp != NULL) {
+       xmlHashFree(libxslt_extModuleElementPreComp, deallocateCallback);
+    }
     if (libxslt_extModuleClasses != NULL) {
        xmlHashFree(libxslt_extModuleClasses, deallocateClasse);
     }
index 8e07e68..d1c7907 100644 (file)
@@ -34,5 +34,16 @@ typedef struct {
     xsltTransformContextPtr obj;
 } PytransformCtxt_Object;
 
+#define PycompiledStyle_Get(v) (((v) == Py_None) ? NULL : \
+        (((PycompiledStyle_Object *)(v))->obj))
+
+typedef struct {
+    PyObject_HEAD
+    xsltTransformContextPtr obj;
+} PycompiledStyle_Object;
+
+
 PyObject * libxslt_xsltStylesheetPtrWrap(xsltStylesheetPtr ctxt);
 PyObject * libxslt_xsltTransformContextPtrWrap(xsltTransformContextPtr ctxt);
+PyObject * libxslt_xsltStylePreCompPtrWrap(xsltStylePreCompPtr comp);
+PyObject * libxslt_xsltElemPreCompPtrWrap(xsltElemPreCompPtr comp);
index 0c7b2eb..71bcddc 100644 (file)
@@ -19,13 +19,10 @@ registerAllExtras()
 # functions from module python
 cleanup()
 registerErrorHandler()
+registerExtModuleElement()
 registerExtModuleFunction()
 registerExtensionClass()
 
-# functions from module transform
-setXIncludeDefault()
-xincludeDefault()
-
 # functions from module xslt
 cleanupGlobals()
 
@@ -98,6 +95,7 @@ Class transformCtxt()
     shutdownCtxtExts()
 
     # functions from module extra
+    debug()
     registerExtras()
 
     # functions from module imports
@@ -118,11 +116,6 @@ Class transformCtxt()
     evalAttrValueTemplate()
     evalTemplateString()
 
-    # functions from module transform
-    applyStripSpaces()
-    freeTransformContext()
-    registerAllElement()
-
     # functions from module variables
     evalGlobalVariables()
     evalOneUserParam()
@@ -196,9 +189,6 @@ Class stylesheet()
     applyStylesheet()
     saveResultToString()
 
-    # functions from module transform
-    newTransformContext()
-
     # functions from module variables
     parseGlobalParam()
     parseGlobalVariable()
index cb7a2f1..936cf6e 100644 (file)
@@ -3,6 +3,7 @@ EXAMPLE_DIR = $(datadir)/doc/libxslt-python-$(LIBXSLT_VERSION)/examples
 TESTSPY=       \
     basic.py   \
     exslt.py   \
+    extelem.py \
     extfunc.py
 
 XMLS=          \
diff --git a/python/tests/extelem.py b/python/tests/extelem.py
new file mode 100644 (file)
index 0000000..b01f9c3
--- /dev/null
@@ -0,0 +1,89 @@
+#!/usr/bin/python -u
+import sys
+import string
+import StringIO
+import libxml2
+# Memory debug specific
+libxml2.debugMemory(1)
+import libxslt
+
+EXT_URL="http://example.com/foo"
+
+insertNodeName = None
+transformNodeName = None
+
+def compile_test(style, inst, func):
+    pass
+
+def transform_test(ctx, node, inst, comp):
+    global insertNodeName
+
+    #
+    # Small check to verify the context is correcly accessed
+    #
+    try:
+        #
+        # FIXME add some more sanity checks
+        #
+        tctxt = libxslt.transformCtxt(_obj=ctx)
+        insertNodeName = tctxt.insertNode().name
+
+        # FIXME find and confirm the note being replaced is called 'test'
+        # transformNodeName = libxml2.xmlNode(inst).name
+    except:
+        pass
+
+    tctxt.insertNode().addContent('SUCCESS')
+    
+
+
+styledoc = libxml2.parseDoc("""
+<xsl:stylesheet version='1.0'
+  xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
+  xmlns:foo='%s'
+  xsl:extension-element-prefixes='foo'>
+
+  <xsl:template match='/'>
+    <article><foo:test>FAILURE</foo:test></article>
+    <deeper><article><foo:test>something<foo:test>nested</foo:test>even</foo:test></article></deeper>
+  </xsl:template>
+</xsl:stylesheet>
+""" % EXT_URL)
+
+style = libxslt.parseStylesheetDoc(styledoc)
+libxslt.registerExtModuleElement("test", EXT_URL, compile_test, transform_test)
+doc = libxml2.parseDoc("<doc/>")
+result = style.applyStylesheet(doc, None)
+style.freeStylesheet()
+doc.freeDoc()
+
+
+extensions = StringIO.StringIO()
+libxslt.debugDumpExtensions(extensions)
+
+if 0 and extensions.buf.find(EXT_URL) < 0:
+    print "Element extension not registered (or dumping broken)"
+    sys.exit(1)
+
+root = result.children
+
+if root.name != "article":
+    print "Unexpected root node name"
+    sys.exit(1)
+if root.content != "SUCCESS":
+    print "Unexpected root node content, extension function failed"
+    sys.exit(1)
+if insertNodeName != 'article':
+    print "The function callback failed to access its context"
+    sys.exit(1)
+
+result.dump(sys.stdout)
+result.freeDoc()
+
+# Memory debug specific
+libxslt.cleanup()
+if libxml2.debugMemory(1) == 0:
+    print "OK"
+else:
+    print "Memory leak %d bytes" % (libxml2.debugMemory(1))
+    libxml2.dumpMemory()