From 9332a25a91e7a0584885d4e1715442de48a085ff Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Wed, 9 Jul 2003 10:22:14 +0000 Subject: [PATCH] patch from Sean Treadway, adding Python bindings for extension element and * 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 | 9 ++ python/generator.py | 3 + python/libxslt-python-api.xml | 8 + python/libxslt.c | 292 ++++++++++++++++++++++++++++++---- python/libxslt_wrap.h | 11 ++ python/libxsltclass.txt | 14 +- python/tests/Makefile.am | 1 + python/tests/extelem.py | 89 +++++++++++ 8 files changed, 387 insertions(+), 40 deletions(-) create mode 100644 python/tests/extelem.py diff --git a/ChangeLog b/ChangeLog index 8249a277..88289b6a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Wed Jul 9 12:19:34 CEST 2003 Daniel Veillard + + * 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 * python/libxml_wrap.h: applied patch from #116943 which should diff --git a/python/generator.py b/python/generator.py index ac587a34..87216016 100755 --- a/python/generator.py +++ b/python/generator.py @@ -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_"), diff --git a/python/libxslt-python-api.xml b/python/libxslt-python-api.xml index ecdc5266..648f2614 100644 --- a/python/libxslt-python-api.xml +++ b/python/libxslt-python-api.xml @@ -24,6 +24,14 @@ + + Register a Python written element to the XSLT engine + + + + + + Register a Python written function to the XSLT engine diff --git a/python/libxslt.c b/python/libxslt.c index a6310a51..84978414 100644 --- a/python/libxslt.c +++ b/python/libxslt.c @@ -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); } diff --git a/python/libxslt_wrap.h b/python/libxslt_wrap.h index 8e07e686..d1c79077 100644 --- a/python/libxslt_wrap.h +++ b/python/libxslt_wrap.h @@ -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); diff --git a/python/libxsltclass.txt b/python/libxsltclass.txt index 0c7b2ebc..71bcddc6 100644 --- a/python/libxsltclass.txt +++ b/python/libxsltclass.txt @@ -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() diff --git a/python/tests/Makefile.am b/python/tests/Makefile.am index cb7a2f18..936cf6e5 100644 --- a/python/tests/Makefile.am +++ b/python/tests/Makefile.am @@ -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 index 00000000..b01f9c3a --- /dev/null +++ b/python/tests/extelem.py @@ -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(""" + + + +
FAILURE
+
somethingnestedeven
+
+
+""" % EXT_URL) + +style = libxslt.parseStylesheetDoc(styledoc) +libxslt.registerExtModuleElement("test", EXT_URL, compile_test, transform_test) +doc = libxml2.parseDoc("") +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()