From 1971ee2698cb84c7699c96d3302f00e20d42c0d3 Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Thu, 31 Jan 2002 20:29:19 +0000 Subject: [PATCH] minor optimization more work on the python bindings, they now support * xpath.c: minor optimization * python/generator.py python/libxml.c python/libxml.py python/libxml_wrap.h: more work on the python bindings, they now support XPath and there is no evident leak Daniel --- ChangeLog | 7 + python/generator.py | 436 +++++++++++++++++++++++++++---------------- python/libxml.c | 271 +++++++++++++++++++++------ python/libxml.py | 53 +++++- python/libxml_wrap.h | 18 +- xpath.c | 6 +- 6 files changed, 569 insertions(+), 222 deletions(-) diff --git a/ChangeLog b/ChangeLog index c929bfed..40d43f11 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Thu Jan 31 21:27:37 CET 2002 Daniel Veillard + + * xpath.c: minor optimization + * python/generator.py python/libxml.c python/libxml.py + python/libxml_wrap.h: more work on the python bindings, + they now support XPath and there is no evident leak + Thu Jan 31 00:48:06 CET 2002 Daniel Veillard * python/generator.py python/libxml.c python/libxml.py: diff --git a/python/generator.py b/python/generator.py index 0eac3447..decd4202 100755 --- a/python/generator.py +++ b/python/generator.py @@ -5,8 +5,15 @@ functions = {} -import os import string + +####################################################################### +# +# That part if purely the API acquisition phase from the +# XML API description +# +####################################################################### +import os import xmllib try: import sgmlop @@ -159,6 +166,12 @@ def function(name, desc, ret, args, file): functions[name] = (desc, ret, args, file) +####################################################################### +# +# Some filtering rukes to drop functions/types which should not +# be exposed as-is on the Python interface +# +####################################################################### skipped_modules = { 'xmlmemory': None, @@ -167,6 +180,7 @@ skipped_modules = { 'hash': None, 'list': None, 'threads': None, + 'xpointer': None, } skipped_types = { 'int *': "usually a return type", @@ -181,6 +195,14 @@ skipped_types = { 'xmlBufferPtr': "internal representation not suitable for python", 'FILE *': None, } + +####################################################################### +# +# Table of remapping to/from the python type or class to the C +# counterpart. +# +####################################################################### + py_types = { 'void': (None, None, None, None), 'int': ('i', None, "int", "int"), @@ -209,18 +231,18 @@ py_types = { 'const xmlEntityPtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), 'xmlEntity *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), 'const xmlEntity *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), - 'xmlElementPtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), - 'const xmlElementPtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), - 'xmlElement *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), - 'const xmlElement *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), - 'xmlAttributePtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), - 'const xmlAttributePtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), - 'xmlAttribute *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), - 'const xmlAttribute *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), - 'xmlNsPtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), - 'const xmlNsPtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), - 'xmlNs *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), - 'const xmlNs *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'xmlElementPtr': ('O', "xmlElement", "xmlElementPtr", "xmlElementPtr"), + 'const xmlElementPtr': ('O', "xmlElement", "xmlElementPtr", "xmlElementPtr"), + 'xmlElement *': ('O', "xmlElement", "xmlElementPtr", "xmlElementPtr"), + 'const xmlElement *': ('O', "xmlElement", "xmlElementPtr", "xmlElementPtr"), + 'xmlAttributePtr': ('O', "xmlAttribute", "xmlAttributePtr", "xmlAttributePtr"), + 'const xmlAttributePtr': ('O', "xmlAttribute", "xmlAttributePtr", "xmlAttributePtr"), + 'xmlAttribute *': ('O', "xmlAttribute", "xmlAttributePtr", "xmlAttributePtr"), + 'const xmlAttribute *': ('O', "xmlAttribute", "xmlAttributePtr", "xmlAttributePtr"), + 'xmlNsPtr': ('O', "xmlNode", "xmlNsPtr", "xmlNsPtr"), + 'const xmlNsPtr': ('O', "xmlNode", "xmlNsPtr", "xmlNsPtr"), + 'xmlNs *': ('O', "xmlNode", "xmlNsPtr", "xmlNsPtr"), + 'const xmlNs *': ('O', "xmlNode", "xmlNsPtr", "xmlNsPtr"), 'xmlDocPtr': ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"), 'const xmlDocPtr': ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"), 'xmlDoc *': ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"), @@ -233,10 +255,30 @@ py_types = { 'const htmlNodePtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), 'htmlNode *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), 'const htmlNode *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'xmlXPathContextPtr': ('O', "xmlXPathContext", "xmlXPathContextPtr", "xmlXPathContextPtr"), + 'xmlXPathContext *': ('O', "xpathContext", "xmlXPathContextPtr", "xmlXPathContextPtr"), +} + +py_return_types = { + 'xmlXPathObjectPtr': ('O', "foo", "xmlXPathObjectPtr", "xmlXPathObjectPtr"), } unknown_types = {} +####################################################################### +# +# This part writes the C <-> Python stubs libxml2-py.[ch] and +# the table libxml2-export.c to add when registrering the Python module +# +####################################################################### + +def skip_function(name): + if name[0:12] == "xmlXPathWrap": + return 1 +# if name[0:11] == "xmlXPathNew": +# return 1 + return 0 + def print_function_wrapper(name, output, export, include): global py_types global unknown_types @@ -251,6 +293,8 @@ def print_function_wrapper(name, output, export, include): if skipped_modules.has_key(file): return 0 + if skip_function(name) == 1: + return 0 c_call = ""; format="" @@ -299,6 +343,12 @@ def print_function_wrapper(name, output, export, include): c_call = "\n c_retval = %s(%s);\n" % (name, c_call); ret_convert = " py_retval = libxml_%sWrap((%s) c_retval);\n" % (n,c) ret_convert = ret_convert + " return(py_retval);\n" + elif py_return_types.has_key(ret[0]): + (f, t, n, c) = py_return_types[ret[0]] + c_return = " %s c_retval;\n" % (ret[0]) + c_call = "\n c_retval = %s(%s);\n" % (name, c_call); + ret_convert = " py_retval = libxml_%sWrap((%s) c_retval);\n" % (n,c) + ret_convert = ret_convert + " return(py_retval);\n" else: if skipped_types.has_key(ret[0]): return 0 @@ -374,156 +424,199 @@ print "Generated %d wrapper functions, %d failed, %d skipped\n" % (nb_wrap, failed, skipped); print "Missing type converters: %s" % (unknown_types.keys()) +####################################################################### +# +# This part writes part of the Python front-end classes based on +# mapping rules between types and classes and also based on function +# renaming to get consistent function names at the Python level +# +####################################################################### + +# +# The type automatically remapped to generated classes +# +classes_type = { + "xmlNodePtr": ("._o", "xmlNode(_obj=%s)", "xmlNode"), + "xmlNode *": ("._o", "xmlNode(_obj=%s)", "xmlNode"), + "xmlDocPtr": ("._o", "xmlDoc(_obj=%s)", "xmlDoc"), + "xmlDocPtr *": ("._o", "xmlDoc(_obj=%s)", "xmlDoc"), + "htmlDocPtr": ("._o", "xmlDoc(_obj=%s)", "xmlDoc"), + "htmlxmlDocPtr *": ("._o", "xmlDoc(_obj=%s)", "xmlDoc"), + "xmlAttrPtr": ("._o", "xmlAttr(_obj=%s)", "xmlAttr"), + "xmlAttr *": ("._o", "xmlAttr(_obj=%s)", "xmlAttr"), + "xmlNsPtr": ("._o", "xmlNs(_obj=%s)", "xmlNs"), + "xmlNs *": ("._o", "xmlNs(_obj=%s)", "xmlNs"), + "xmlDtdPtr": ("._o", "xmlDtd(_obj=%s)", "xmlDtd"), + "xmlDtd *": ("._o", "xmlDtd(_obj=%s)", "xmlDtd"), + "xmlEntityPtr": ("._o", "xmlEntity(_obj=%s)", "xmlEntity"), + "xmlEntity *": ("._o", "xmlEntity(_obj=%s)", "xmlEntity"), + "xmlElementPtr": ("._o", "xmlElement(_obj=%s)", "xmlElement"), + "xmlElement *": ("._o", "xmlElement(_obj=%s)", "xmlElement"), + "xmlAttributePtr": ("._o", "xmlAttribute(_obj=%s)", "xmlAttribute"), + "xmlAttribute *": ("._o", "xmlAttribute(_obj=%s)", "xmlAttribute"), + "xmlXPathContextPtr": ("._o", "xpathContext(_obj=%s)", "xpathContext"), +} + +converter_type = { + "xmlXPathObjectPtr": "xpathObjectRet(%s)", +} + +primary_classes = ["xmlNode", "xmlDoc"] + +classes_ancestor = { + "xmlNode" : "xmlCore", + "xmlDoc" : "xmlCore", + "xmlAttr" : "xmlCore", + "xmlNs" : "xmlCore", + "xmlDtd" : "xmlCore", + "xmlEntity" : "xmlCore", + "xmlElement" : "xmlCore", + "xmlAttribute" : "xmlCore", +} +classes_destructors = { + "xpathContext": "xmlXPathFreeContext", +} + function_classes = {} -for name in functions.keys(): - (desc, ret, args, file) = functions[name] - if name[0:3] == "xml" and len(args) >= 1 and args[0][1] == "xmlNodePtr": - if name[0:11] == "xmlNodeList": - func = name[11:] - func = string.lower(func[0:1]) + func[1:] - info = (0, func, name, ret, args, file) - if function_classes.has_key('xmlNode'): - function_classes['xmlNode'].append(info) - else: - function_classes['xmlNode'] = [info] - elif name[0:7] == "xmlNode": - func = name[7:] - func = string.lower(func[0:1]) + func[1:] - info = (0, func, name, ret, args, file) - if function_classes.has_key('xmlNode'): - function_classes['xmlNode'].append(info) - else: - function_classes['xmlNode'] = [info] - elif name[0:6] == "xmlGet": - func = name[6:] - func = string.lower(func[0:1]) + func[1:] - info = (0, func, name, ret, args, file) - if function_classes.has_key('xmlNode'): - function_classes['xmlNode'].append(info) - else: - function_classes['xmlNode'] = [info] - else: - func = name[3:] - func = string.lower(func[0:1]) + func[1:] - info = (0, func, name, ret, args, file) - if function_classes.has_key('xmlNode'): - function_classes['xmlNode'].append(info) - else: - function_classes['xmlNode'] = [info] - elif name[0:3] == "xml" and len(args) >= 2 and args[1][1] == "xmlNodePtr": - if name[0:11] == "xmlNodeList": - func = name[11:] - func = string.lower(func[0:1]) + func[1:] - info = (1, func, name, ret, args, file) - if function_classes.has_key('xmlNode'): - function_classes['xmlNode'].append(info) - else: - function_classes['xmlNode'] = [info] - elif name[0:7] == "xmlNode": - func = name[7:] - func = string.lower(func[0:1]) + func[1:] - info = (1, func, name, ret, args, file) - if function_classes.has_key('xmlNode'): - function_classes['xmlNode'].append(info) - else: - function_classes['xmlNode'] = [info] - elif name[0:6] == "xmlGet": - func = name[6:] - func = string.lower(func[0:1]) + func[1:] - info = (1, func, name, ret, args, file) - if function_classes.has_key('xmlNode'): - function_classes['xmlNode'].append(info) - else: - function_classes['xmlNode'] = [info] - else: - func = name[3:] - func = string.lower(func[0:1]) + func[1:] - info = (1, func, name, ret, args, file) - if function_classes.has_key('xmlNode'): - function_classes['xmlNode'].append(info) - else: - function_classes['xmlNode'] = [info] - elif name[0:3] == "xml" and len(args) >= 1 and args[0][1] == "xmlDocPtr": - if name[0:6] == "xmlDoc": - func = name[6:] - func = string.lower(func[0:1]) + func[1:] - info = (0, func, name, ret, args, file) - if function_classes.has_key('xmlDoc'): - function_classes['xmlDoc'].append(info) - else: - function_classes['xmlDoc'] = [info] - elif name[0:6] == "xmlGet": - func = name[6:] - func = string.lower(func[0:1]) + func[1:] - info = (0, func, name, ret, args, file) - if function_classes.has_key('xmlDoc'): - function_classes['xmlDoc'].append(info) - else: - function_classes['xmlDoc'] = [info] - else: - func = name[3:] - func = string.lower(func[0:1]) + func[1:] - info = (0, func, name, ret, args, file) - if function_classes.has_key('xmlDoc'): - function_classes['xmlDoc'].append(info) - else: - function_classes['xmlDoc'] = [info] - elif name[0:3] == "xml" and len(args) >= 2 and args[1][1] == "xmlDocPtr": - if name[0:6] == "xmlDoc": - func = name[6:] - func = string.lower(func[0:1]) + func[1:] - info = (1, func, name, ret, args, file) - if function_classes.has_key('xmlDoc'): - function_classes['xmlDoc'].append(info) - else: - function_classes['xmlDoc'] = [info] - elif name[0:6] == "xmlGet": - func = name[6:] - func = string.lower(func[0:1]) + func[1:] - info = (1, func, name, ret, args, file) - if function_classes.has_key('xmlDoc'): - function_classes['xmlDoc'].append(info) - else: - function_classes['xmlDoc'] = [info] - else: - func = name[3:] - func = string.lower(func[0:1]) + func[1:] - info = (1, func, name, ret, args, file) - if function_classes.has_key('xmlDoc'): - function_classes['xmlDoc'].append(info) - else: - function_classes['xmlDoc'] = [info] - elif ret[0] == "xmlDocPtr" or ret[0] == "xmlDtdPtr": + +function_classes["None"] = [] +for type in classes_type.keys(): + function_classes[classes_type[type][2]] = [] + +# +# Build the list of C types to look for ordered to start with primary classes +# +ctypes = [] +ctypes_processed = {} +for classe in primary_classes: + for type in classes_type.keys(): + tinfo = classes_type[type] + if tinfo[2] == classe: + ctypes.append(type) + ctypes_processed[type] = () +for type in classes_type.keys(): + if ctypes_processed.has_key(type): + continue + tinfo = classes_type[type] + ctypes.append(type) + ctypes_processed[type] = () + +def nameFixup(function, classe, type): + listname = classe + "List" + ll = len(listname) + l = len(classe) + if name[0:l] == listname: + func = name[l:] + func = string.lower(func[0:1]) + func[1:] + elif name[0:l] == classe: + func = name[l:] + func = string.lower(func[0:1]) + func[1:] + elif name[0:6] == "xmlGet": + func = name[6:] + func = string.lower(func[0:1]) + func[1:] + elif name[0:3] == "xml": func = name[3:] func = string.lower(func[0:1]) + func[1:] - info = (0, func, name, ret, args, file) - if function_classes.has_key('None'): - function_classes['None'].append(info) - else: - function_classes['None'] = [info] else: - print "unable to guess class for %s:%s" % (file,name) + func = name + if func[0:5] == "xPath": + func = "xpath" + func[5:] + elif func[0:4] == "xPtr": + func = "xpointer" + func[4:] + elif func[0:8] == "xInclude": + func = "xinclude" + func[8:] + elif func[0:2] == "iD": + func = "ID" + func[2:] + elif func[0:3] == "uRI": + func = "URI" + func[3:] + elif func[0:4] == "uTF8": + func = "UTF8" + func[4:] + return func -classes_type = { - "xmlNodePtr": ("._o", "xmlNode(_obj=%s)"), - "xmlNode *": ("._o", "xmlNode(_obj=%s)"), - "xmlDocPtr": ("._o", "xmlDoc(_obj=%s)"), - "xmlDocPtr *": ("._o", "xmlDoc(_obj=%s)"), - "xmlAttrPtr": ("._o", "xmlNode(_obj=%s)"), - "xmlAttr *": ("._o", "xmlNode(_obj=%s)"), - "xmlNsPtr": ("._o", "xmlNode(_obj=%s)"), - "xmlNs *": ("._o", "xmlNode(_obj=%s)"), - "xmlDtdPtr": ("._o", "xmlNode(_obj=%s)"), - "xmlDtd *": ("._o", "xmlNode(_obj=%s)"), - "xmlEntityPtr": ("._o", "xmlNode(_obj=%s)"), - "xmlEntity *": ("._o", "xmlNode(_obj=%s)"), -} +for name in functions.keys(): + found = 0; + (desc, ret, args, file) = functions[name] + for type in ctypes: + classe = classes_type[type][2] + + if name[0:3] == "xml" and len(args) >= 1 and args[0][1] == type: + found = 1 + func = nameFixup(name, classe, type) + info = (0, func, name, ret, args, file) + function_classes[classe].append(info) + elif name[0:3] == "xml" and len(args) >= 2 and args[1][1] == type: + found = 1 + func = nameFixup(name, classe, type) + info = (1, func, name, ret, args, file) + function_classes[classe].append(info) + elif name[0:4] == "html" and len(args) >= 1 and args[0][1] == type: + found = 1 + func = nameFixup(name, classe, type) + info = (0, func, name, ret, args, file) + function_classes[classe].append(info) + elif name[0:4] == "html" and len(args) >= 2 and args[1][1] == type: + found = 1 + func = nameFixup(name, classe, type) + info = (1, func, name, ret, args, file) + function_classes[classe].append(info) + if found == 1: + break + if found == 1: + continue + if name[0:8] == "xmlXPath": + continue + if name[0:6] == "xmlStr": + continue + if name[0:10] == "xmlCharStr": + continue + func = nameFixup(name, "None", file) + info = (0, func, name, ret, args, file) + function_classes['None'].append(info) classes = open("libxml2class.py", "w") +def functionCompare(info1, info2): + (index1, func1, name1, ret1, args1, file1) = info1 + (index2, func2, name2, ret2, args2, file2) = info2 + if file1 < file2: + return -1 + if file1 > file2: + return 1 + if func1 < func2: + return -1 + if func1 > func2: + return 1 + return 0 + +def writeDoc(name, args, indent, output): + if functions[name][0] == None or functions[name][0] == "": + return + val = functions[name][0] + val = string.replace(val, "NULL", "None"); + output.write(indent) + output.write('"""') + while len(val) > 60: + str = val[0:60] + i = string.rfind(str, " "); + if i < 0: + i = 60 + str = val[0:i] + val = val[i:] + output.write(str) + output.write('\n '); + output.write(indent) + output.write(val); + output.write('"""\n') + if function_classes.has_key("None"): - for info in function_classes["None"]: + flist = function_classes["None"] + flist.sort(functionCompare) + oldfile = "" + for info in flist: (index, func, name, ret, args, file) = info + if file != oldfile: + classes.write("#\n# Functions from module %s\n#\n\n" % file) + oldfile = file classes.write("def %s(" % func) n = 0 for arg in args: @@ -532,6 +625,7 @@ if function_classes.has_key("None"): classes.write("%s" % arg[0]) n = n + 1 classes.write("):\n") + writeDoc(name, args, ' ', classes); if ret[0] != "void": classes.write(" ret = "); else: @@ -560,21 +654,35 @@ for classname in function_classes.keys(): if classname == "None": pass else: - classes.write("class %s(xmlCore):\n" % classname); - if classname == "xmlNode": + if classes_ancestor.has_key(classname): + classes.write("class %s(%s):\n" % (classname, + classes_ancestor[classname])) classes.write(" def __init__(self, _obj=None):\n") classes.write(" self._o = None\n") - classes.write(" xmlCore.__init__(self, _obj=_obj)\n\n") - elif classname == "xmlDoc": - classes.write(" def __init__(self, _obj=None):\n") - classes.write(" self._o = None\n") - classes.write(" xmlCore.__init__(self, _obj=_obj)\n\n") + classes.write(" %s.__init__(self, _obj=_obj)\n\n" % ( + classes_ancestor[classname])) else: + classes.write("class %s:\n" % (classname)) classes.write(" def __init__(self, _obj=None):\n") classes.write(" if _obj != None:self._o = _obj;return\n") classes.write(" self._o = None\n\n"); - for info in function_classes[classname]: + if classes_destructors.has_key(classname): + classes.write(" def __del__(self):\n") + classes.write(" if self._o != None:\n") + classes.write(" _libxml.%s(self._o)\n" % + classes_destructors[classname]); + classes.write(" self._o = None\n\n"); + flist = function_classes[classname] + flist.sort(functionCompare) + oldfile = "" + for info in flist: (index, func, name, ret, args, file) = info + if file != oldfile: + classes.write(" #\n") + classes.write(" # %s functions from module %s\n" % ( + classname, file)) + classes.write(" #\n\n") + oldfile = file classes.write(" def %s(self" % func) n = 0 for arg in args: @@ -582,6 +690,7 @@ for classname in function_classes.keys(): classes.write(", %s" % arg[0]) n = n + 1 classes.write("):\n") + writeDoc(name, args, ' ', classes); if ret[0] != "void": classes.write(" ret = "); else: @@ -605,6 +714,11 @@ for classname in function_classes.keys(): classes.write(" return "); classes.write(classes_type[ret[0]][1] % ("ret")); classes.write("\n"); + elif converter_type.has_key(ret[0]): + classes.write(" if ret == None: return None\n"); + classes.write(" return "); + classes.write(converter_type[ret[0]] % ("ret")); + classes.write("\n"); else: classes.write(" return ret\n"); classes.write("\n"); diff --git a/python/libxml.c b/python/libxml.c index 60bd3cb8..69649753 100644 --- a/python/libxml.c +++ b/python/libxml.c @@ -35,7 +35,7 @@ libxml_doubleWrap(double val) { } PyObject * -libxml_charPtrWrap(const char *str) { +libxml_charPtrWrap(char *str) { PyObject *ret; #ifdef DEBUG @@ -52,7 +52,40 @@ libxml_charPtrWrap(const char *str) { } PyObject * -libxml_xmlCharPtrWrap(const xmlChar *str) { +libxml_xmlCharPtrWrap(xmlChar *str) { + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlCharPtrWrap: str = %s\n", str); +#endif + if (str == NULL) { + Py_INCREF(Py_None); + return(Py_None); + } + /* TODO: look at deallocation */ + ret = PyString_FromString(str); + xmlFree(str); + return(ret); +} + +PyObject * +libxml_constcharPtrWrap(const char *str) { + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlcharPtrWrap: str = %s\n", str); +#endif + if (str == NULL) { + Py_INCREF(Py_None); + return(Py_None); + } + /* TODO: look at deallocation */ + ret = PyString_FromString(str); + return(ret); +} + +PyObject * +libxml_constxmlCharPtrWrap(const xmlChar *str) { PyObject *ret; #ifdef DEBUG @@ -98,12 +131,27 @@ libxml_xmlNodePtrWrap(xmlNodePtr node) { return(ret); } +PyObject * +libxml_xmlNsPtrWrap(xmlNsPtr ns) { + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlNsPtrWrap: node = %p\n", ns); +#endif + if (ns == NULL) { + Py_INCREF(Py_None); + return(Py_None); + } + ret = PyCObject_FromVoidPtrAndDesc((void *) ns, "xmlNsPtr", NULL); + return(ret); +} + PyObject * libxml_xmlAttrPtrWrap(xmlAttrPtr attr) { PyObject *ret; #ifdef DEBUG - printf("libxml_xmlNodePtrWrap: attr = %p\n", attr); + printf("libxml_xmlAttrNodePtrWrap: attr = %p\n", attr); #endif if (attr == NULL) { Py_INCREF(Py_None); @@ -113,6 +161,107 @@ libxml_xmlAttrPtrWrap(xmlAttrPtr attr) { return(ret); } +PyObject * +libxml_xmlAttributePtrWrap(xmlAttributePtr attr) { + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlAttributePtrWrap: attr = %p\n", attr); +#endif + if (attr == NULL) { + Py_INCREF(Py_None); + return(Py_None); + } + ret = PyCObject_FromVoidPtrAndDesc((void *) attr, "xmlAttributePtr", NULL); + return(ret); +} + +PyObject * +libxml_xmlElementPtrWrap(xmlElementPtr elem) { + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlElementNodePtrWrap: elem = %p\n", elem); +#endif + if (elem == NULL) { + Py_INCREF(Py_None); + return(Py_None); + } + ret = PyCObject_FromVoidPtrAndDesc((void *) elem, "xmlElementPtr", NULL); + return(ret); +} + +PyObject * +libxml_xmlXPathContextPtrWrap(xmlXPathContextPtr ctxt) { + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlXPathContextPtrWrap: ctxt = %p\n", ctxt); +#endif + if (ctxt == NULL) { + Py_INCREF(Py_None); + return(Py_None); + } + ret = PyCObject_FromVoidPtrAndDesc((void *) ctxt, "xmlXPathContextPtr", + NULL); + return(ret); +} + +PyObject * +libxml_xmlXPathObjectPtrWrap(xmlXPathObjectPtr obj) { + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlXPathObjectPtrWrap: ctxt = %p\n", obj); +#endif + if (obj == NULL) { + Py_INCREF(Py_None); + return(Py_None); + } + switch(obj->type) { + case XPATH_XSLT_TREE: + /* TODO !!!! Allocation problems */ + case XPATH_NODESET: + if ((obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) + ret = PyList_New(0); + else { + int i; + xmlNodePtr node; + + ret = PyList_New(obj->nodesetval->nodeNr); + for (i = 0;i < obj->nodesetval->nodeNr;i++) { + node = obj->nodesetval->nodeTab[i]; + /* TODO: try to cast directly to the proper node type */ + PyList_SetItem(ret, i, libxml_xmlNodePtrWrap(node)); + } + } + break; + case XPATH_BOOLEAN: + ret = PyInt_FromLong((long) obj->boolval); + break; + case XPATH_NUMBER: + ret = PyFloat_FromDouble(obj->floatval); + break; + case XPATH_STRING: + ret = PyString_FromString(obj->stringval); + break; + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + default: + printf("Unable to convert XPath object type %d\n", obj->type); + Py_INCREF(Py_None); + ret = Py_None; + } + xmlXPathFreeObject(obj); + return(ret); +} + +/************************************************************************ + * * + * The PyxmlNode type * + * * + ************************************************************************/ static void PyxmlNode_dealloc(PyxmlNode_Object * self) { @@ -171,6 +320,68 @@ static PyTypeObject PyxmlNode_Type = { PyxmlNode_Type__doc__ }; +/************************************************************************ + * * + * The PyxmlXPathContext type * + * * + ************************************************************************/ +static void +PyxmlXPathContext_dealloc(PyxmlXPathContext_Object * self) +{ + printf("TODO PyxmlXPathContext_dealloc\n"); + PyMem_DEL(self); +} + +static int +PyxmlXPathContext_compare(PyxmlXPathContext_Object * self, PyxmlXPathContext_Object * v) +{ + if (self->obj == v->obj) + return 0; + if (self->obj > v->obj) + return -1; + return 1; +} + +static long +PyxmlXPathContext_hash(PyxmlXPathContext_Object * self) +{ + return (long) self->obj; +} + +static PyObject * +PyxmlXPathContext_repr(PyxmlXPathContext_Object * self) +{ + char buf[100]; + + sprintf(buf, "", + (long) PyxmlXPathContext_Get(self)); + return PyString_FromString(buf); +} + +static char PyxmlXPathContext_Type__doc__[] = "This is the type of XPath evaluation contexts"; + +PyTypeObject PyxmlXPathContext_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size */ + "xmlXPathContext", /*tp_name */ + sizeof(PyxmlXPathContext_Object), /*tp_basicsize */ + 0, /*tp_itemsize */ + (destructor) PyxmlXPathContext_dealloc,/*tp_dealloc */ + (printfunc) 0, /*tp_print */ + (getattrfunc) 0, /*tp_getattr */ + (setattrfunc) 0, /*tp_setattr */ + (cmpfunc) PyxmlXPathContext_compare,/*tp_compare */ + (reprfunc) PyxmlXPathContext_repr, /*tp_repr */ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + (hashfunc) PyxmlXPathContext_hash, /*tp_hash */ + (ternaryfunc) 0, /*tp_call */ + (reprfunc) 0, /*tp_str */ + 0L, 0L, 0L, 0L, + PyxmlXPathContext_Type__doc__ +}; + /************************************************************************ * * * Global properties access * @@ -215,7 +426,7 @@ libxml_name(PyObject *self, PyObject *args) res = cur->name; break; } - resultobj = libxml_xmlCharPtrWrap(res); + resultobj = libxml_constxmlCharPtrWrap(res); return resultobj; } @@ -546,57 +757,10 @@ libxml_type(PyObject *self, PyObject *args) printf("libxml_type: cur = %p: %s\n", cur, res); #endif - resultobj = libxml_xmlCharPtrWrap(res); + resultobj = libxml_constxmlCharPtrWrap(res); return resultobj; } -/************************************************************************ - * * - * The interface raw code * - * * - ************************************************************************/ -static PyObject * -libxml_parseFile(PyObject *self, PyObject *args) -{ - PyObject *resultobj; - char *arg0; - xmlDocPtr result; - - if (!PyArg_ParseTuple(args, "s:parseFile", &arg0)) - return NULL; -#ifdef DEBUG - printf("libxml_parseFile: arg0 = %s\n", arg0); -#endif - result = (xmlDocPtr )xmlParseFile((char const *)arg0); - resultobj = libxml_xmlDocPtrWrap(result); -#ifdef DEBUG - printf("libxml_parseFile: resultobj = %p\n", resultobj); -#endif - return resultobj; -} - -static PyObject * -libxml_freeDoc(PyObject *self, PyObject *args) -{ - xmlDocPtr doc; - - if (!PyArg_ParseTuple(args, "O:freeDoc", &doc)) - return NULL; - switch(doc->type) { - case XML_DOCUMENT_NODE: - case XML_HTML_DOCUMENT_NODE: -#ifdef LIBXML_DOCB_ENABLED - case XML_DOCB_DOCUMENT_NODE: -#endif - xmlFreeDoc(doc); - break; - default: - break; - } - Py_INCREF(Py_None); - return(Py_None); -} - /************************************************************************ * * * The registration stuff * @@ -604,8 +768,6 @@ libxml_freeDoc(PyObject *self, PyObject *args) ************************************************************************/ static PyMethodDef libxmlMethods[] = { #include "libxml2-export.c" - { "parseFile", libxml_parseFile, METH_VARARGS }, - { "freeDoc", libxml_freeDoc, METH_VARARGS }, { "name", libxml_name, METH_VARARGS }, { "children", libxml_children, METH_VARARGS }, { "properties", libxml_properties, METH_VARARGS }, @@ -622,5 +784,6 @@ void init_libxml(void) { m = Py_InitModule("_libxml", libxmlMethods); d = PyModule_GetDict(m); PyDict_SetItemString(d, "xmlNodeType", (PyObject *)&PyxmlNode_Type); + PyDict_SetItemString(d, "xmlXPathContextType", (PyObject *)&PyxmlXPathContext_Type); } diff --git a/python/libxml.py b/python/libxml.py index 259a08b6..476bb279 100644 --- a/python/libxml.py +++ b/python/libxml.py @@ -1,5 +1,11 @@ import _libxml +# +# This class is the ancestor of all the Node classes. It provides +# the basic functionalities shared by all nodes (and handle +# gracefylly the exception), like name, navigation in the tree, +# doc reference and content access +# class xmlCore: def __init__(self, _obj=None): if _obj != None: @@ -17,7 +23,7 @@ class xmlCore: ret = _libxml.properties(self._o) if ret == None: return None - return xmlNode(_obj=ret) + return xmlAttr(_obj=ret) elif attr == "children": ret = _libxml.children(self._o) if ret == None: @@ -74,13 +80,25 @@ class xmlCore: if ret == None: return None return xmlNode(_obj=ret) + def get_properties(self): + ret = _libxml.properties(self._o) + if ret == None: + return None + return xmlAttr(_obj=ret) + def get_doc(self): + ret = _libxml.doc(self._o) + if ret == None: + return None + return xmlDoc(_obj=ret) def get_prev(self): ret = _libxml.prev(self._o) if ret == None: return None return xmlNode(_obj=ret) def get_content(self): - return self.content() + return _libxml.xmlNodeGetContent(self._o) + def getContent(self): + return _libxml.xmlNodeGetContent(self._o) def get_name(self): return _libxml.name(self._o) def get_type(self): @@ -93,6 +111,37 @@ class xmlCore: def free(self): _libxml.freeDoc(self._o) +# +# converters to present a nicer view of the XPath returns +# +def nodeWrap(o): + # TODO try to cast to the most appropriate node class + name = _libxml.name(o) + if name == "element" or name == "text": + return xmlNode(_obj=o) + if name == "attribute": + return xmlAttr(_obj=o) + if name[0:8] == "document": + return xmlDoc(_obj=o) + if name[0:8] == "namespace": + return xmlNs(_obj=o) + if name == "elem_decl": + return xmlElement(_obj=o) + if name == "attribute_decl": + return xmlAtribute(_obj=o) + if name == "entity_decl": + return xmlEntity(_obj=o) + if name == "dtd": + return xmlAttr(_obj=o) + return xmlNode(_obj=o) + +def xpathObjectRet(o): + if type(o) == type([]) or type(o) == type(()): + ret = map(lambda x: nodeWrap(x), o) + return ret + return o + # # Everything below this point is automatically generated # + diff --git a/python/libxml_wrap.h b/python/libxml_wrap.h index 00421f95..972e871e 100644 --- a/python/libxml_wrap.h +++ b/python/libxml_wrap.h @@ -12,6 +12,7 @@ #include #include #include +#include #define PyxmlNode_Get(v) (((PyxmlNode_Object *)(v))->obj) @@ -20,11 +21,24 @@ typedef struct { xmlNodePtr obj; } PyxmlNode_Object; +#define PyxmlXPathContext_Get(v) (((PyxmlXPathContext_Object *)(v))->obj) +typedef struct { + PyObject_HEAD + xmlXPathContextPtr obj; +} PyxmlXPathContext_Object; + PyObject * libxml_intWrap(int val); -PyObject * libxml_xmlCharPtrWrap(const xmlChar *str); -PyObject * libxml_charPtrWrap(const char *str); +PyObject * libxml_xmlCharPtrWrap(xmlChar *str); +PyObject * libxml_constxmlCharPtrWrap(const xmlChar *str); +PyObject * libxml_charPtrWrap(char *str); +PyObject * libxml_constcharPtrWrap(const char *str); PyObject * libxml_xmlDocPtrWrap(xmlDocPtr doc); PyObject * libxml_xmlNodePtrWrap(xmlNodePtr node); PyObject * libxml_xmlAttrPtrWrap(xmlAttrPtr attr); +PyObject * libxml_xmlNsPtrWrap(xmlNsPtr ns); +PyObject * libxml_xmlAttributePtrWrap(xmlAttributePtr ns); +PyObject * libxml_xmlElementPtrWrap(xmlElementPtr ns); PyObject * libxml_doubleWrap(double val); +PyObject * libxml_xmlXPathContextPtrWrap(xmlXPathContextPtr ctxt); +PyObject * libxml_xmlXPathObjectPtrWrap(xmlXPathObjectPtr obj); diff --git a/xpath.c b/xpath.c index f7120644..7861160f 100644 --- a/xpath.c +++ b/xpath.c @@ -6856,13 +6856,13 @@ xmlXPathIsNodeType(const xmlChar *name) { if (name == NULL) return(0); - if (xmlStrEqual(name, BAD_CAST "comment")) + if (xmlStrEqual(name, BAD_CAST "node")) return(1); if (xmlStrEqual(name, BAD_CAST "text")) return(1); - if (xmlStrEqual(name, BAD_CAST "processing-instruction")) + if (xmlStrEqual(name, BAD_CAST "comment")) return(1); - if (xmlStrEqual(name, BAD_CAST "node")) + if (xmlStrEqual(name, BAD_CAST "processing-instruction")) return(1); return(0); }