Initial revision
[TestXSLT.git] / libxml2 / python / generator.py
1 #!/usr/bin/python -u
2 #
3 # generate python wrappers from the XML API description
4 #
5
6 functions = {}
7
8 import sys
9 import string
10
11 #######################################################################
12 #
13 #  That part if purely the API acquisition phase from the
14 #  XML API description
15 #
16 #######################################################################
17 import os
18 import xmllib
19 try:
20     import sgmlop
21 except ImportError:
22     sgmlop = None # accelerator not available
23
24 debug = 0
25
26 if sgmlop:
27     class FastParser:
28         """sgmlop based XML parser.  this is typically 15x faster
29            than SlowParser..."""
30
31         def __init__(self, target):
32
33             # setup callbacks
34             self.finish_starttag = target.start
35             self.finish_endtag = target.end
36             self.handle_data = target.data
37
38             # activate parser
39             self.parser = sgmlop.XMLParser()
40             self.parser.register(self)
41             self.feed = self.parser.feed
42             self.entity = {
43                 "amp": "&", "gt": ">", "lt": "<",
44                 "apos": "'", "quot": '"'
45                 }
46
47         def close(self):
48             try:
49                 self.parser.close()
50             finally:
51                 self.parser = self.feed = None # nuke circular reference
52
53         def handle_entityref(self, entity):
54             # <string> entity
55             try:
56                 self.handle_data(self.entity[entity])
57             except KeyError:
58                 self.handle_data("&%s;" % entity)
59
60 else:
61     FastParser = None
62
63
64 class SlowParser(xmllib.XMLParser):
65     """slow but safe standard parser, based on the XML parser in
66        Python's standard library."""
67
68     def __init__(self, target):
69         self.unknown_starttag = target.start
70         self.handle_data = target.data
71         self.unknown_endtag = target.end
72         xmllib.XMLParser.__init__(self)
73
74 def getparser(target = None):
75     # get the fastest available parser, and attach it to an
76     # unmarshalling object.  return both objects.
77     if target is None:
78         target = docParser()
79     if FastParser:
80         return FastParser(target), target
81     return SlowParser(target), target
82
83 class docParser:
84     def __init__(self):
85         self._methodname = None
86         self._data = []
87         self.in_function = 0
88
89     def close(self):
90         if debug:
91             print "close"
92
93     def getmethodname(self):
94         return self._methodname
95
96     def data(self, text):
97         if debug:
98             print "data %s" % text
99         self._data.append(text)
100
101     def start(self, tag, attrs):
102         if debug:
103             print "start %s, %s" % (tag, attrs)
104         if tag == 'function':
105             self._data = []
106             self.in_function = 1
107             self.function = None
108             self.function_args = []
109             self.function_descr = None
110             self.function_return = None
111             self.function_file = None
112             if attrs.has_key('name'):
113                 self.function = attrs['name']
114             if attrs.has_key('file'):
115                 self.function_file = attrs['file']
116         elif tag == 'info':
117             self._data = []
118         elif tag == 'arg':
119             if self.in_function == 1:
120                 self.function_arg_name = None
121                 self.function_arg_type = None
122                 self.function_arg_info = None
123                 if attrs.has_key('name'):
124                     self.function_arg_name = attrs['name']
125                 if attrs.has_key('type'):
126                     self.function_arg_type = attrs['type']
127                 if attrs.has_key('info'):
128                     self.function_arg_info = attrs['info']
129         elif tag == 'return':
130             if self.in_function == 1:
131                 self.function_return_type = None
132                 self.function_return_info = None
133                 self.function_return_field = None
134                 if attrs.has_key('type'):
135                     self.function_return_type = attrs['type']
136                 if attrs.has_key('info'):
137                     self.function_return_info = attrs['info']
138                 if attrs.has_key('field'):
139                     self.function_return_field = attrs['field']
140
141
142     def end(self, tag):
143         if debug:
144             print "end %s" % tag
145         if tag == 'function':
146             if self.function != None:
147                 function(self.function, self.function_descr,
148                          self.function_return, self.function_args,
149                          self.function_file)
150                 self.in_function = 0
151         elif tag == 'arg':
152             if self.in_function == 1:
153                 self.function_args.append([self.function_arg_name,
154                                            self.function_arg_type,
155                                            self.function_arg_info])
156         elif tag == 'return':
157             if self.in_function == 1:
158                 self.function_return = [self.function_return_type,
159                                         self.function_return_info,
160                                         self.function_return_field]
161         elif tag == 'info':
162             str = ''
163             for c in self._data:
164                 str = str + c
165             if self.in_function == 1:
166                 self.function_descr = str
167                 
168                 
169 def function(name, desc, ret, args, file):
170     global functions
171
172     functions[name] = (desc, ret, args, file)
173
174 #######################################################################
175 #
176 #  Some filtering rukes to drop functions/types which should not
177 #  be exposed as-is on the Python interface
178 #
179 #######################################################################
180
181 skipped_modules = {
182     'xmlmemory': None,
183     'DOCBparser': None,
184     'SAX': None,
185     'hash': None,
186     'list': None,
187     'threads': None,
188 #    'xpointer': None,
189 }
190 skipped_types = {
191     'int *': "usually a return type",
192     'xmlSAXHandlerPtr': "not the proper interface for SAX",
193     'htmlSAXHandlerPtr': "not the proper interface for SAX",
194     'xmlRMutexPtr': "thread specific, skipped",
195     'xmlMutexPtr': "thread specific, skipped",
196     'xmlGlobalStatePtr': "thread specific, skipped",
197     'xmlListPtr': "internal representation not suitable for python",
198     'xmlBufferPtr': "internal representation not suitable for python",
199     'FILE *': None,
200 }
201
202 #######################################################################
203 #
204 #  Table of remapping to/from the python type or class to the C
205 #  counterpart.
206 #
207 #######################################################################
208
209 py_types = {
210     'void': (None, None, None, None),
211     'int':  ('i', None, "int", "int"),
212     'long':  ('i', None, "int", "int"),
213     'double':  ('d', None, "double", "double"),
214     'unsigned int':  ('i', None, "int", "int"),
215     'xmlChar':  ('c', None, "int", "int"),
216     'unsigned char *':  ('z', None, "charPtr", "char *"),
217     'char *':  ('z', None, "charPtr", "char *"),
218     'const char *':  ('z', None, "charPtrConst", "const char *"),
219     'xmlChar *':  ('z', None, "xmlCharPtr", "xmlChar *"),
220     'const xmlChar *':  ('z', None, "xmlCharPtrConst", "const xmlChar *"),
221     'xmlNodePtr':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
222     'const xmlNodePtr':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
223     'xmlNode *':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
224     'const xmlNode *':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
225     'xmlDtdPtr':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
226     'const xmlDtdPtr':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
227     'xmlDtd *':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
228     'const xmlDtd *':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
229     'xmlAttrPtr':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
230     'const xmlAttrPtr':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
231     'xmlAttr *':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
232     'const xmlAttr *':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
233     'xmlEntityPtr':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
234     'const xmlEntityPtr':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
235     'xmlEntity *':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
236     'const xmlEntity *':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
237     'xmlElementPtr':  ('O', "xmlElement", "xmlElementPtr", "xmlElementPtr"),
238     'const xmlElementPtr':  ('O', "xmlElement", "xmlElementPtr", "xmlElementPtr"),
239     'xmlElement *':  ('O', "xmlElement", "xmlElementPtr", "xmlElementPtr"),
240     'const xmlElement *':  ('O', "xmlElement", "xmlElementPtr", "xmlElementPtr"),
241     'xmlAttributePtr':  ('O', "xmlAttribute", "xmlAttributePtr", "xmlAttributePtr"),
242     'const xmlAttributePtr':  ('O', "xmlAttribute", "xmlAttributePtr", "xmlAttributePtr"),
243     'xmlAttribute *':  ('O', "xmlAttribute", "xmlAttributePtr", "xmlAttributePtr"),
244     'const xmlAttribute *':  ('O', "xmlAttribute", "xmlAttributePtr", "xmlAttributePtr"),
245     'xmlNsPtr':  ('O', "xmlNode", "xmlNsPtr", "xmlNsPtr"),
246     'const xmlNsPtr':  ('O', "xmlNode", "xmlNsPtr", "xmlNsPtr"),
247     'xmlNs *':  ('O', "xmlNode", "xmlNsPtr", "xmlNsPtr"),
248     'const xmlNs *':  ('O', "xmlNode", "xmlNsPtr", "xmlNsPtr"),
249     'xmlDocPtr':  ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"),
250     'const xmlDocPtr':  ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"),
251     'xmlDoc *':  ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"),
252     'const xmlDoc *':  ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"),
253     'htmlDocPtr':  ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"),
254     'const htmlDocPtr':  ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"),
255     'htmlDoc *':  ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"),
256     'const htmlDoc *':  ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"),
257     'htmlNodePtr':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
258     'const htmlNodePtr':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
259     'htmlNode *':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
260     'const htmlNode *':  ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"),
261     'xmlXPathContextPtr':  ('O', "xmlXPathContext", "xmlXPathContextPtr", "xmlXPathContextPtr"),
262     'xmlXPathContext *':  ('O', "xpathContext", "xmlXPathContextPtr", "xmlXPathContextPtr"),
263     'xmlXPathParserContextPtr':  ('O', "xmlXPathParserContext", "xmlXPathParserContextPtr", "xmlXPathParserContextPtr"),
264     'xmlParserCtxtPtr': ('O', "parserCtxt", "xmlParserCtxtPtr", "xmlParserCtxtPtr"),
265     'xmlParserCtxt *': ('O', "parserCtxt", "xmlParserCtxtPtr", "xmlParserCtxtPtr"),
266     'htmlParserCtxtPtr': ('O', "parserCtxt", "xmlParserCtxtPtr", "xmlParserCtxtPtr"),
267     'htmlParserCtxt *': ('O', "parserCtxt", "xmlParserCtxtPtr", "xmlParserCtxtPtr"),
268     'xmlCatalogPtr': ('O', "catalog", "xmlCatalogPtr", "xmlCatalogPtr"),
269     'FILE *': ('O', "File", "FILEPtr", "FILE *"),
270     'xmlURIPtr': ('O', "URI", "xmlURIPtr", "xmlURIPtr"),
271     'xmlOutputBufferPtr': ('O', "outputBuffer", "xmlOutputBufferPtr", "xmlOutputBufferPtr"),
272     'xmlParserInputBufferPtr': ('O', "inputBuffer", "xmlParserInputBufferPtr", "xmlParserInputBufferPtr"),
273     'xmlRegexpPtr': ('O', "xmlReg", "xmlRegexpPtr", "xmlRegexpPtr"),
274     'xmlTextReaderLocatorPtr': ('O', "xmlTextReaderLocator", "xmlTextReaderLocatorPtr", "xmlTextReaderLocatorPtr"),
275     'xmlTextReaderPtr': ('O', "xmlTextReader", "xmlTextReaderPtr", "xmlTextReaderPtr"),
276     'xmlRelaxNGPtr': ('O', "relaxNgSchema", "xmlRelaxNGPtr", "xmlRelaxNGPtr"),
277     'xmlRelaxNGParserCtxtPtr': ('O', "relaxNgParserCtxt", "xmlRelaxNGParserCtxtPtr", "xmlRelaxNGParserCtxtPtr"),
278     'xmlRelaxNGValidCtxtPtr': ('O', "relaxNgValidCtxt", "xmlRelaxNGValidCtxtPtr", "xmlRelaxNGValidCtxtPtr"),
279 }
280
281 py_return_types = {
282     'xmlXPathObjectPtr':  ('O', "foo", "xmlXPathObjectPtr", "xmlXPathObjectPtr"),
283 }
284
285 unknown_types = {}
286
287 #######################################################################
288 #
289 #  This part writes the C <-> Python stubs libxml2-py.[ch] and
290 #  the table libxml2-export.c to add when registrering the Python module
291 #
292 #######################################################################
293
294 def skip_function(name):
295     if name[0:12] == "xmlXPathWrap":
296         return 1
297     if name == "xmlFreeParserCtxt":
298         return 1
299     if name == "xmlFreeTextReader":
300         return 1
301 #    if name[0:11] == "xmlXPathNew":
302 #        return 1
303     return 0
304
305 def print_function_wrapper(name, output, export, include):
306     global py_types
307     global unknown_types
308     global functions
309     global skipped_modules
310
311     try:
312         (desc, ret, args, file) = functions[name]
313     except:
314         print "failed to get function %s infos"
315         return
316
317     if skipped_modules.has_key(file):
318         return 0
319     if skip_function(name) == 1:
320         return 0
321
322     c_call = "";
323     format=""
324     format_args=""
325     c_args=""
326     c_return=""
327     c_convert=""
328     for arg in args:
329         # This should be correct
330         if arg[1][0:6] == "const ":
331             arg[1] = arg[1][6:]
332         c_args = c_args + "    %s %s;\n" % (arg[1], arg[0])
333         if py_types.has_key(arg[1]):
334             (f, t, n, c) = py_types[arg[1]]
335             if f != None:
336                 format = format + f
337             if t != None:
338                 format_args = format_args + ", &pyobj_%s" % (arg[0])
339                 c_args = c_args + "    PyObject *pyobj_%s;\n" % (arg[0])
340                 c_convert = c_convert + \
341                    "    %s = (%s) Py%s_Get(pyobj_%s);\n" % (arg[0],
342                    arg[1], t, arg[0]);
343             else:
344                 format_args = format_args + ", &%s" % (arg[0])
345             if c_call != "":
346                 c_call = c_call + ", ";
347             c_call = c_call + "%s" % (arg[0])
348         else:
349             if skipped_types.has_key(arg[1]):
350                 return 0
351             if unknown_types.has_key(arg[1]):
352                 lst = unknown_types[arg[1]]
353                 lst.append(name)
354             else:
355                 unknown_types[arg[1]] = [name]
356             return -1
357     if format != "":
358         format = format + ":%s" % (name)
359
360     if ret[0] == 'void':
361         if file == "python_accessor":
362             if args[1][1] == "char *" or args[1][1] == "xmlChar *":
363                 c_call = "\n    if (%s->%s != NULL) xmlFree(%s->%s);\n" % (
364                                  args[0][0], args[1][0], args[0][0], args[1][0])
365                 c_call = c_call + "    %s->%s = xmlStrdup((const xmlChar *)%s);\n" % (args[0][0],
366                                  args[1][0], args[1][0])
367             else:
368                 c_call = "\n    %s->%s = %s;\n" % (args[0][0], args[1][0],
369                                                    args[1][0])
370         else:
371             c_call = "\n    %s(%s);\n" % (name, c_call);
372         ret_convert = "    Py_INCREF(Py_None);\n    return(Py_None);\n"
373     elif py_types.has_key(ret[0]):
374         (f, t, n, c) = py_types[ret[0]]
375         c_return = "    %s c_retval;\n" % (ret[0])
376         if file == "python_accessor" and ret[2] != None:
377             c_call = "\n    c_retval = %s->%s;\n" % (args[0][0], ret[2])
378         else:
379             c_call = "\n    c_retval = %s(%s);\n" % (name, c_call);
380         ret_convert = "    py_retval = libxml_%sWrap((%s) c_retval);\n" % (n,c)
381         ret_convert = ret_convert + "    return(py_retval);\n"
382     elif py_return_types.has_key(ret[0]):
383         (f, t, n, c) = py_return_types[ret[0]]
384         c_return = "    %s c_retval;\n" % (ret[0])
385         c_call = "\n    c_retval = %s(%s);\n" % (name, c_call);
386         ret_convert = "    py_retval = libxml_%sWrap((%s) c_retval);\n" % (n,c)
387         ret_convert = ret_convert + "    return(py_retval);\n"
388     else:
389         if skipped_types.has_key(ret[0]):
390             return 0
391         if unknown_types.has_key(ret[0]):
392             lst = unknown_types[ret[0]]
393             lst.append(name)
394         else:
395             unknown_types[ret[0]] = [name]
396         return -1
397
398     if file == "debugXML":
399         include.write("#ifdef LIBXML_DEBUG_ENABLED\n");
400         export.write("#ifdef LIBXML_DEBUG_ENABLED\n");
401         output.write("#ifdef LIBXML_DEBUG_ENABLED\n");
402     elif file == "HTMLtree" or file == "HTMLparser":
403         include.write("#ifdef LIBXML_HTML_ENABLED\n");
404         export.write("#ifdef LIBXML_HTML_ENABLED\n");
405         output.write("#ifdef LIBXML_HTML_ENABLED\n");
406     elif file == "c14n":
407         include.write("#ifdef LIBXML_C14N_ENABLED\n");
408         export.write("#ifdef LIBXML_C14N_ENABLED\n");
409         output.write("#ifdef LIBXML_C14N_ENABLED\n");
410     elif file == "xpathInternals" or file == "xpath":
411         include.write("#ifdef LIBXML_XPATH_ENABLED\n");
412         export.write("#ifdef LIBXML_XPATH_ENABLED\n");
413         output.write("#ifdef LIBXML_XPATH_ENABLED\n");
414     elif file == "xpointer":
415         include.write("#ifdef LIBXML_XPTR_ENABLED\n");
416         export.write("#ifdef LIBXML_XPTR_ENABLED\n");
417         output.write("#ifdef LIBXML_XPTR_ENABLED\n");
418     elif file == "xinclude":
419         include.write("#ifdef LIBXML_XINCLUDE_ENABLED\n");
420         export.write("#ifdef LIBXML_XINCLUDE_ENABLED\n");
421         output.write("#ifdef LIBXML_XINCLUDE_ENABLED\n");
422     elif file == "xmlregexp":
423         include.write("#ifdef LIBXML_REGEXP_ENABLED\n");
424         export.write("#ifdef LIBXML_REGEXP_ENABLED\n");
425         output.write("#ifdef LIBXML_REGEXP_ENABLED\n");
426     elif file == "xmlschemas" or file == "xmlschemastypes" or \
427          file == "relaxng":
428         include.write("#ifdef LIBXML_SCHEMAS_ENABLED\n");
429         export.write("#ifdef LIBXML_SCHEMAS_ENABLED\n");
430         output.write("#ifdef LIBXML_SCHEMAS_ENABLED\n");
431
432     include.write("PyObject * ")
433     include.write("libxml_%s(PyObject *self, PyObject *args);\n" % (name));
434
435     export.write("    { (char *)\"%s\", libxml_%s, METH_VARARGS, NULL },\n" %
436                  (name, name))
437
438     if file == "python":
439         # Those have been manually generated
440         return 1
441     if file == "python_accessor" and ret[0] != "void" and ret[2] is None:
442         # Those have been manually generated
443         return 1
444
445     output.write("PyObject *\n")
446     output.write("libxml_%s(ATTRIBUTE_UNUSED PyObject *self," % (name))
447     if format == "":
448         output.write("ATTRIBUTE_UNUSED ")
449     output.write(" PyObject *args) {\n")
450     if ret[0] != 'void':
451         output.write("    PyObject *py_retval;\n")
452     if c_return != "":
453         output.write(c_return)
454     if c_args != "":
455         output.write(c_args)
456     if format != "":
457         output.write("\n    if (!PyArg_ParseTuple(args, (char *)\"%s\"%s))\n" %
458                      (format, format_args))
459         output.write("        return(NULL);\n")
460     if c_convert != "":
461         output.write(c_convert)
462                                                               
463     output.write(c_call)
464     output.write(ret_convert)
465     output.write("}\n\n")
466     if file == "debugXML":
467         include.write("#endif /* LIBXML_DEBUG_ENABLED */\n");
468         export.write("#endif /* LIBXML_DEBUG_ENABLED */\n");
469         output.write("#endif /* LIBXML_DEBUG_ENABLED */\n");
470     elif file == "HTMLtree" or file == "HTMLparser":
471         include.write("#endif /* LIBXML_HTML_ENABLED */\n");
472         export.write("#endif /* LIBXML_HTML_ENABLED */\n");
473         output.write("#endif /* LIBXML_HTML_ENABLED */\n");
474     elif file == "c14n":
475         include.write("#endif /* LIBXML_C14N_ENABLED */\n");
476         export.write("#endif /* LIBXML_C14N_ENABLED */\n");
477         output.write("#endif /* LIBXML_C14N_ENABLED */\n");
478     elif file == "xpathInternals" or file == "xpath":
479         include.write("#endif /* LIBXML_XPATH_ENABLED */\n");
480         export.write("#endif /* LIBXML_XPATH_ENABLED */\n");
481         output.write("#endif /* LIBXML_XPATH_ENABLED */\n");
482     elif file == "xpointer":
483         include.write("#endif /* LIBXML_XPTR_ENABLED */\n");
484         export.write("#endif /* LIBXML_XPTR_ENABLED */\n");
485         output.write("#endif /* LIBXML_XPTR_ENABLED */\n");
486     elif file == "xinclude":
487         include.write("#endif /* LIBXML_XINCLUDE_ENABLED */\n");
488         export.write("#endif /* LIBXML_XINCLUDE_ENABLED */\n");
489         output.write("#endif /* LIBXML_XINCLUDE_ENABLED */\n");
490     elif file == "xmlregexp":
491         include.write("#endif /* LIBXML_REGEXP_ENABLED */\n");
492         export.write("#endif /* LIBXML_REGEXP_ENABLED */\n");
493         output.write("#endif /* LIBXML_REGEXP_ENABLED */\n");
494     elif file == "xmlschemas" or file == "xmlschemastypes" or \
495          file == "relaxng":
496         include.write("#endif /* LIBXML_SCHEMAS_ENABLED */\n");
497         export.write("#endif /* LIBXML_SCHEMAS_ENABLED */\n");
498         output.write("#endif /* LIBXML_SCHEMAS_ENABLED */\n");
499     return 1
500
501 def buildStubs():
502     global py_types
503     global py_return_types
504     global unknown_types
505
506     try:
507         f = open("libxml2-api.xml")
508         data = f.read()
509         (parser, target)  = getparser()
510         parser.feed(data)
511         parser.close()
512     except IOError, msg:
513         try:
514             f = open("../doc/libxml2-api.xml")
515             data = f.read()
516             (parser, target)  = getparser()
517             parser.feed(data)
518             parser.close()
519         except IOError, msg:
520             print file, ":", msg
521             sys.exit(1)
522
523     n = len(functions.keys())
524     print "Found %d functions in libxml2-api.xml" % (n)
525
526     py_types['pythonObject'] = ('O', "pythonObject", "pythonObject", "pythonObject")
527     try:
528         f = open("libxml2-python-api.xml")
529         data = f.read()
530         (parser, target)  = getparser()
531         parser.feed(data)
532         parser.close()
533     except IOError, msg:
534         print file, ":", msg
535
536
537     print "Found %d functions in libxml2-python-api.xml" % (
538           len(functions.keys()) - n)
539     nb_wrap = 0
540     failed = 0
541     skipped = 0
542
543     include = open("libxml2-py.h", "w")
544     include.write("/* Generated */\n\n")
545     export = open("libxml2-export.c", "w")
546     export.write("/* Generated */\n\n")
547     wrapper = open("libxml2-py.c", "w")
548     wrapper.write("/* Generated */\n\n")
549     wrapper.write("#include <Python.h>\n")
550 #    wrapper.write("#include \"config.h\"\n")
551     wrapper.write("#include <libxml/xmlversion.h>\n")
552     wrapper.write("#include <libxml/tree.h>\n")
553     wrapper.write("#include \"libxml_wrap.h\"\n")
554     wrapper.write("#include \"libxml2-py.h\"\n\n")
555     for function in functions.keys():
556         ret = print_function_wrapper(function, wrapper, export, include)
557         if ret < 0:
558             failed = failed + 1
559             del functions[function]
560         if ret == 0:
561             skipped = skipped + 1
562             del functions[function]
563         if ret == 1:
564             nb_wrap = nb_wrap + 1
565     include.close()
566     export.close()
567     wrapper.close()
568
569     print "Generated %d wrapper functions, %d failed, %d skipped\n" % (nb_wrap,
570                                                               failed, skipped);
571     print "Missing type converters: "
572     for type in unknown_types.keys():
573         print "%s:%d " % (type, len(unknown_types[type])),
574     print
575
576 #######################################################################
577 #
578 #  This part writes part of the Python front-end classes based on
579 #  mapping rules between types and classes and also based on function
580 #  renaming to get consistent function names at the Python level
581 #
582 #######################################################################
583
584 #
585 # The type automatically remapped to generated classes
586 #
587 classes_type = {
588     "xmlNodePtr": ("._o", "xmlNode(_obj=%s)", "xmlNode"),
589     "xmlNode *": ("._o", "xmlNode(_obj=%s)", "xmlNode"),
590     "xmlDocPtr": ("._o", "xmlDoc(_obj=%s)", "xmlDoc"),
591     "xmlDocPtr *": ("._o", "xmlDoc(_obj=%s)", "xmlDoc"),
592     "htmlDocPtr": ("._o", "xmlDoc(_obj=%s)", "xmlDoc"),
593     "htmlxmlDocPtr *": ("._o", "xmlDoc(_obj=%s)", "xmlDoc"),
594     "xmlAttrPtr": ("._o", "xmlAttr(_obj=%s)", "xmlAttr"),
595     "xmlAttr *": ("._o", "xmlAttr(_obj=%s)", "xmlAttr"),
596     "xmlNsPtr": ("._o", "xmlNs(_obj=%s)", "xmlNs"),
597     "xmlNs *": ("._o", "xmlNs(_obj=%s)", "xmlNs"),
598     "xmlDtdPtr": ("._o", "xmlDtd(_obj=%s)", "xmlDtd"),
599     "xmlDtd *": ("._o", "xmlDtd(_obj=%s)", "xmlDtd"),
600     "xmlEntityPtr": ("._o", "xmlEntity(_obj=%s)", "xmlEntity"),
601     "xmlEntity *": ("._o", "xmlEntity(_obj=%s)", "xmlEntity"),
602     "xmlElementPtr": ("._o", "xmlElement(_obj=%s)", "xmlElement"),
603     "xmlElement *": ("._o", "xmlElement(_obj=%s)", "xmlElement"),
604     "xmlAttributePtr": ("._o", "xmlAttribute(_obj=%s)", "xmlAttribute"),
605     "xmlAttribute *": ("._o", "xmlAttribute(_obj=%s)", "xmlAttribute"),
606     "xmlXPathContextPtr": ("._o", "xpathContext(_obj=%s)", "xpathContext"),
607     "xmlXPathContext *": ("._o", "xpathContext(_obj=%s)", "xpathContext"),
608     "xmlXPathParserContext *": ("._o", "xpathParserContext(_obj=%s)", "xpathParserContext"),
609     "xmlXPathParserContextPtr": ("._o", "xpathParserContext(_obj=%s)", "xpathParserContext"),
610     "xmlParserCtxtPtr": ("._o", "parserCtxt(_obj=%s)", "parserCtxt"),
611     "xmlParserCtxt *": ("._o", "parserCtxt(_obj=%s)", "parserCtxt"),
612     "htmlParserCtxtPtr": ("._o", "parserCtxt(_obj=%s)", "parserCtxt"),
613     "htmlParserCtxt *": ("._o", "parserCtxt(_obj=%s)", "parserCtxt"),
614     "xmlCatalogPtr": ("._o", "catalog(_obj=%s)", "catalog"),
615     "xmlURIPtr": ("._o", "URI(_obj=%s)", "URI"),
616     "xmlOutputBufferPtr": ("._o", "outputBuffer(_obj=%s)", "outputBuffer"),
617     "xmlParserInputBufferPtr": ("._o", "inputBuffer(_obj=%s)", "inputBuffer"),
618     "xmlRegexpPtr": ("._o", "xmlReg(_obj=%s)", "xmlReg"),
619     "xmlTextReaderLocatorPtr": ("._o", "xmlTextReaderLocator(_obj=%s)", "xmlTextReaderLocator"),
620     "xmlTextReaderPtr": ("._o", "xmlTextReader(_obj=%s)", "xmlTextReader"),
621     'xmlRelaxNGPtr': ('._o', "relaxNgSchema(_obj=%s)", "relaxNgSchema"),
622     'xmlRelaxNGParserCtxtPtr': ('._o', "relaxNgParserCtxt(_obj=%s)", "relaxNgParserCtxt"),
623     'xmlRelaxNGValidCtxtPtr': ('._o', "relaxNgValidCtxt(_obj=%s)", "relaxNgValidCtxt"),
624 }
625
626 converter_type = {
627     "xmlXPathObjectPtr": "xpathObjectRet(%s)",
628 }
629
630 primary_classes = ["xmlNode", "xmlDoc"]
631
632 classes_ancestor = {
633     "xmlNode" : "xmlCore",
634     "xmlDtd" : "xmlNode",
635     "xmlDoc" : "xmlNode",
636     "xmlAttr" : "xmlNode",
637     "xmlNs" : "xmlNode",
638     "xmlEntity" : "xmlNode",
639     "xmlElement" : "xmlNode",
640     "xmlAttribute" : "xmlNode",
641     "outputBuffer": "ioWriteWrapper",
642     "inputBuffer": "ioReadWrapper",
643     "parserCtxt": "parserCtxtCore",
644     "xmlTextReader": "xmlTextReaderCore",
645 }
646 classes_destructors = {
647     "parserCtxt": "xmlFreeParserCtxt",
648     "catalog": "xmlFreeCatalog",
649     "URI": "xmlFreeURI",
650 #    "outputBuffer": "xmlOutputBufferClose",
651     "inputBuffer": "xmlFreeParserInputBuffer",
652     "xmlReg": "xmlRegFreeRegexp",
653     "xmlTextReader": "xmlFreeTextReader",
654     "relaxNgSchema": "xmlRelaxNGFree",
655     "relaxNgParserCtxt": "xmlRelaxNGFreeParserCtxt",
656     "relaxNgValidCtxt": "xmlRelaxNGFreeValidCtxt",
657 }
658
659 functions_noexcept = {
660     "xmlHasProp": 1,
661     "xmlHasNsProp": 1,
662     "xmlDocSetRootElement": 1,
663 }
664
665 reference_keepers = {
666     "xmlTextReader": [('inputBuffer', 'input')],
667     "relaxNgValidCtxt": [('relaxNgSchema', 'schema')],
668 }
669
670 function_classes = {}
671
672 function_classes["None"] = []
673
674 def nameFixup(name, classe, type, file):
675     listname = classe + "List"
676     ll = len(listname)
677     l = len(classe)
678     if name[0:l] == listname:
679         func = name[l:]
680         func = string.lower(func[0:1]) + func[1:]
681     elif name[0:12] == "xmlParserGet" and file == "python_accessor":
682         func = name[12:]
683         func = string.lower(func[0:1]) + func[1:]
684     elif name[0:12] == "xmlParserSet" and file == "python_accessor":
685         func = name[12:]
686         func = string.lower(func[0:1]) + func[1:]
687     elif name[0:10] == "xmlNodeGet" and file == "python_accessor":
688         func = name[10:]
689         func = string.lower(func[0:1]) + func[1:]
690     elif name[0:9] == "xmlURIGet" and file == "python_accessor":
691         func = name[9:]
692         func = string.lower(func[0:1]) + func[1:]
693     elif name[0:9] == "xmlURISet" and file == "python_accessor":
694         func = name[6:]
695         func = string.lower(func[0:1]) + func[1:]
696     elif name[0:17] == "xmlXPathParserGet" and file == "python_accessor":
697         func = name[17:]
698         func = string.lower(func[0:1]) + func[1:]
699     elif name[0:11] == "xmlXPathGet" and file == "python_accessor":
700         func = name[11:]
701         func = string.lower(func[0:1]) + func[1:]
702     elif name[0:11] == "xmlXPathSet" and file == "python_accessor":
703         func = name[8:]
704         func = string.lower(func[0:1]) + func[1:]
705     elif name[0:15] == "xmlOutputBuffer" and file != "python":
706         func = name[15:]
707         func = string.lower(func[0:1]) + func[1:]
708     elif name[0:20] == "xmlParserInputBuffer" and file != "python":
709         func = name[20:]
710         func = string.lower(func[0:1]) + func[1:]
711     elif name[0:9] == "xmlRegexp" and file == "xmlregexp":
712         func = "regexp" + name[9:]
713     elif name[0:6] == "xmlReg" and file == "xmlregexp":
714         func = "regexp" + name[6:]
715     elif name[0:20] == "xmlTextReaderLocator" and file == "xmlreader":
716         func = name[20:]
717     elif name[0:13] == "xmlTextReader" and file == "xmlreader":
718         func = name[13:]
719     elif name[0:11] == "xmlACatalog":
720         func = name[11:]
721         func = string.lower(func[0:1]) + func[1:]
722     elif name[0:l] == classe:
723         func = name[l:]
724         func = string.lower(func[0:1]) + func[1:]
725     elif name[0:7] == "libxml_":
726         func = name[7:]
727         func = string.lower(func[0:1]) + func[1:]
728     elif name[0:6] == "xmlGet":
729         func = name[6:]
730         func = string.lower(func[0:1]) + func[1:]
731     elif name[0:3] == "xml":
732         func = name[3:]
733         func = string.lower(func[0:1]) + func[1:]
734     else:
735         func = name
736     if func[0:5] == "xPath":
737         func = "xpath" + func[5:]
738     elif func[0:4] == "xPtr":
739         func = "xpointer" + func[4:]
740     elif func[0:8] == "xInclude":
741         func = "xinclude" + func[8:]
742     elif func[0:2] == "iD":
743         func = "ID" + func[2:]
744     elif func[0:3] == "uRI":
745         func = "URI" + func[3:]
746     elif func[0:4] == "uTF8":
747         func = "UTF8" + func[4:]
748     elif func[0:3] == 'sAX':
749         func = "SAX" + func[3:]
750     return func
751
752
753 def functionCompare(info1, info2):
754     (index1, func1, name1, ret1, args1, file1) = info1
755     (index2, func2, name2, ret2, args2, file2) = info2
756     if file1 == file2:
757         if func1 < func2:
758             return -1
759         if func1 > func2:
760             return 1
761     if file1 == "python_accessor":
762         return -1
763     if file2 == "python_accessor":
764         return 1
765     if file1 < file2:
766         return -1
767     if file1 > file2:
768         return 1
769     return 0
770
771 def writeDoc(name, args, indent, output):
772      if functions[name][0] is None or functions[name][0] == "":
773          return
774      val = functions[name][0]
775      val = string.replace(val, "NULL", "None");
776      output.write(indent)
777      output.write('"""')
778      while len(val) > 60:
779          str = val[0:60]
780          i = string.rfind(str, " ");
781          if i < 0:
782              i = 60
783          str = val[0:i]
784          val = val[i:]
785          output.write(str)
786          output.write('\n  ');
787          output.write(indent)
788      output.write(val);
789      output.write(' """\n')
790
791 def buildWrappers():
792     global ctypes
793     global py_types
794     global py_return_types
795     global unknown_types
796     global functions
797     global function_classes
798     global classes_type
799     global classes_list
800     global converter_type
801     global primary_classes
802     global converter_type
803     global classes_ancestor
804     global converter_type
805     global primary_classes
806     global classes_ancestor
807     global classes_destructors
808     global functions_noexcept
809
810     for type in classes_type.keys():
811         function_classes[classes_type[type][2]] = []
812
813     #
814     # Build the list of C types to look for ordered to start
815     # with primary classes
816     #
817     ctypes = []
818     classes_list = []
819     ctypes_processed = {}
820     classes_processed = {}
821     for classe in primary_classes:
822         classes_list.append(classe)
823         classes_processed[classe] = ()
824         for type in classes_type.keys():
825             tinfo = classes_type[type]
826             if tinfo[2] == classe:
827                 ctypes.append(type)
828                 ctypes_processed[type] = ()
829     for type in classes_type.keys():
830         if ctypes_processed.has_key(type):
831             continue
832         tinfo = classes_type[type]
833         if not classes_processed.has_key(tinfo[2]):
834             classes_list.append(tinfo[2])
835             classes_processed[tinfo[2]] = ()
836             
837         ctypes.append(type)
838         ctypes_processed[type] = ()
839
840     for name in functions.keys():
841         found = 0;
842         (desc, ret, args, file) = functions[name]
843         for type in ctypes:
844             classe = classes_type[type][2]
845
846             if name[0:3] == "xml" and len(args) >= 1 and args[0][1] == type:
847                 found = 1
848                 func = nameFixup(name, classe, type, file)
849                 info = (0, func, name, ret, args, file)
850                 function_classes[classe].append(info)
851             elif name[0:3] == "xml" and len(args) >= 2 and args[1][1] == type \
852                 and file != "python_accessor":
853                 found = 1
854                 func = nameFixup(name, classe, type, file)
855                 info = (1, func, name, ret, args, file)
856                 function_classes[classe].append(info)
857             elif name[0:4] == "html" and len(args) >= 1 and args[0][1] == type:
858                 found = 1
859                 func = nameFixup(name, classe, type, file)
860                 info = (0, func, name, ret, args, file)
861                 function_classes[classe].append(info)
862             elif name[0:4] == "html" and len(args) >= 2 and args[1][1] == type \
863                 and file != "python_accessor":
864                 found = 1
865                 func = nameFixup(name, classe, type, file)
866                 info = (1, func, name, ret, args, file)
867                 function_classes[classe].append(info)
868             if found == 1:
869                 break
870         if found == 1:
871             continue
872         if name[0:8] == "xmlXPath":
873             continue
874         if name[0:6] == "xmlStr":
875             continue
876         if name[0:10] == "xmlCharStr":
877             continue
878         func = nameFixup(name, "None", file, file)
879         info = (0, func, name, ret, args, file)
880         function_classes['None'].append(info)
881    
882     classes = open("libxml2class.py", "w")
883     txt = open("libxml2class.txt", "w")
884     txt.write("          Generated Classes for libxml2-python\n\n")
885
886     txt.write("#\n# Global functions of the module\n#\n\n")
887     if function_classes.has_key("None"):
888         flist = function_classes["None"]
889         flist.sort(functionCompare)
890         oldfile = ""
891         for info in flist:
892             (index, func, name, ret, args, file) = info
893             if file != oldfile:
894                 classes.write("#\n# Functions from module %s\n#\n\n" % file)
895                 txt.write("\n# functions from module %s\n" % file)
896                 oldfile = file
897             classes.write("def %s(" % func)
898             txt.write("%s()\n" % func);
899             n = 0
900             for arg in args:
901                 if n != 0:
902                     classes.write(", ")
903                 classes.write("%s" % arg[0])
904                 n = n + 1
905             classes.write("):\n")
906             writeDoc(name, args, '    ', classes);
907
908             for arg in args:
909                 if classes_type.has_key(arg[1]):
910                     classes.write("    if %s is None: %s__o = None\n" %
911                                   (arg[0], arg[0]))
912                     classes.write("    else: %s__o = %s%s\n" %
913                                   (arg[0], arg[0], classes_type[arg[1]][0]))
914             if ret[0] != "void":
915                 classes.write("    ret = ");
916             else:
917                 classes.write("    ");
918             classes.write("libxml2mod.%s(" % name)
919             n = 0
920             for arg in args:
921                 if n != 0:
922                     classes.write(", ");
923                 classes.write("%s" % arg[0])
924                 if classes_type.has_key(arg[1]):
925                     classes.write("__o");
926                 n = n + 1
927             classes.write(")\n");
928             if ret[0] != "void":
929                 if classes_type.has_key(ret[0]):
930                     #
931                     # Raise an exception
932                     #
933                     if functions_noexcept.has_key(name):
934                         classes.write("    if ret is None:return None\n");
935                     elif string.find(name, "URI") >= 0:
936                         classes.write(
937                         "    if ret is None:raise uriError('%s() failed')\n"
938                                       % (name))
939                     elif string.find(name, "XPath") >= 0:
940                         classes.write(
941                         "    if ret is None:raise xpathError('%s() failed')\n"
942                                       % (name))
943                     elif string.find(name, "Parse") >= 0:
944                         classes.write(
945                         "    if ret is None:raise parserError('%s() failed')\n"
946                                       % (name))
947                     else:
948                         classes.write(
949                         "    if ret is None:raise treeError('%s() failed')\n"
950                                       % (name))
951                     classes.write("    return ");
952                     classes.write(classes_type[ret[0]][1] % ("ret"));
953                     classes.write("\n");
954                 else:
955                     classes.write("    return ret\n");
956             classes.write("\n");
957
958     txt.write("\n\n#\n# Set of classes of the module\n#\n\n")
959     for classname in classes_list:
960         if classname == "None":
961             pass
962         else:
963             if classes_ancestor.has_key(classname):
964                 txt.write("\n\nClass %s(%s)\n" % (classname,
965                           classes_ancestor[classname]))
966                 classes.write("class %s(%s):\n" % (classname,
967                               classes_ancestor[classname]))
968                 classes.write("    def __init__(self, _obj=None):\n")
969                 if reference_keepers.has_key(classname):
970                     rlist = reference_keepers[classname]
971                     for ref in rlist:
972                         classes.write("        self.%s = None\n" % ref[1])
973                 classes.write("        self._o = None\n")
974                 classes.write("        %s.__init__(self, _obj=_obj)\n\n" % (
975                               classes_ancestor[classname]))
976                 if classes_ancestor[classname] == "xmlCore" or \
977                    classes_ancestor[classname] == "xmlNode":
978                     classes.write("    def __repr__(self):\n")
979                     format = "<%s (%%s) object at 0x%%x>" % (classname)
980                     classes.write("        return \"%s\" %% (self.name, id (self))\n\n" % (
981                                   format))
982             else:
983                 txt.write("Class %s()\n" % (classname))
984                 classes.write("class %s:\n" % (classname))
985                 classes.write("    def __init__(self, _obj=None):\n")
986                 if reference_keepers.has_key(classname):
987                     list = reference_keepers[classname]
988                     for ref in list:
989                         classes.write("        self.%s = None\n" % ref[1])
990                 classes.write("        if _obj != None:self._o = _obj;return\n")
991                 classes.write("        self._o = None\n\n");
992             if classes_destructors.has_key(classname):
993                 classes.write("    def __del__(self):\n")
994                 classes.write("        if self._o != None:\n")
995                 classes.write("            libxml2mod.%s(self._o)\n" %
996                               classes_destructors[classname]);
997                 classes.write("        self._o = None\n\n");
998             flist = function_classes[classname]
999             flist.sort(functionCompare)
1000             oldfile = ""
1001             for info in flist:
1002                 (index, func, name, ret, args, file) = info
1003                 if file != oldfile:
1004                     if file == "python_accessor":
1005                         classes.write("    # accessors for %s\n" % (classname))
1006                         txt.write("    # accessors\n")
1007                     else:
1008                         classes.write("    #\n")
1009                         classes.write("    # %s functions from module %s\n" % (
1010                                       classname, file))
1011                         txt.write("\n    # functions from module %s\n" % file)
1012                         classes.write("    #\n\n")
1013                 oldfile = file
1014                 classes.write("    def %s(self" % func)
1015                 txt.write("    %s()\n" % func);
1016                 n = 0
1017                 for arg in args:
1018                     if n != index:
1019                         classes.write(", %s" % arg[0])
1020                     n = n + 1
1021                 classes.write("):\n")
1022                 writeDoc(name, args, '        ', classes);
1023                 n = 0
1024                 for arg in args:
1025                     if classes_type.has_key(arg[1]):
1026                         if n != index:
1027                             classes.write("        if %s is None: %s__o = None\n" %
1028                                           (arg[0], arg[0]))
1029                             classes.write("        else: %s__o = %s%s\n" %
1030                                           (arg[0], arg[0], classes_type[arg[1]][0]))
1031                     n = n + 1
1032                 if ret[0] != "void":
1033                     classes.write("        ret = ");
1034                 else:
1035                     classes.write("        ");
1036                 classes.write("libxml2mod.%s(" % name)
1037                 n = 0
1038                 for arg in args:
1039                     if n != 0:
1040                         classes.write(", ");
1041                     if n != index:
1042                         classes.write("%s" % arg[0])
1043                         if classes_type.has_key(arg[1]):
1044                             classes.write("__o");
1045                     else:
1046                         classes.write("self");
1047                         if classes_type.has_key(arg[1]):
1048                             classes.write(classes_type[arg[1]][0])
1049                     n = n + 1
1050                 classes.write(")\n");
1051                 if ret[0] != "void":
1052                     if classes_type.has_key(ret[0]):
1053                         #
1054                         # Raise an exception
1055                         #
1056                         if functions_noexcept.has_key(name):
1057                             classes.write(
1058                                 "        if ret is None:return None\n");
1059                         elif string.find(name, "URI") >= 0:
1060                             classes.write(
1061                     "        if ret is None:raise uriError('%s() failed')\n"
1062                                           % (name))
1063                         elif string.find(name, "XPath") >= 0:
1064                             classes.write(
1065                     "        if ret is None:raise xpathError('%s() failed')\n"
1066                                           % (name))
1067                         elif string.find(name, "Parse") >= 0:
1068                             classes.write(
1069                     "        if ret is None:raise parserError('%s() failed')\n"
1070                                           % (name))
1071                         else:
1072                             classes.write(
1073                     "        if ret is None:raise treeError('%s() failed')\n"
1074                                           % (name))
1075
1076                         #
1077                         # generate the returned class wrapper for the object
1078                         #
1079                         classes.write("        __tmp = ");
1080                         classes.write(classes_type[ret[0]][1] % ("ret"));
1081                         classes.write("\n");
1082
1083                         #
1084                         # Sometime one need to keep references of the source
1085                         # class in the returned class object.
1086                         # See reference_keepers for the list
1087                         #
1088                         tclass = classes_type[ret[0]][2]
1089                         if reference_keepers.has_key(tclass):
1090                             list = reference_keepers[tclass]
1091                             for pref in list:
1092                                 if pref[0] == classname:
1093                                     classes.write("        __tmp.%s = self\n" %
1094                                                   pref[1])
1095                         #
1096                         # return the class
1097                         #
1098                         classes.write("        return __tmp\n");
1099                     elif converter_type.has_key(ret[0]):
1100                         #
1101                         # Raise an exception
1102                         #
1103                         if functions_noexcept.has_key(name):
1104                             classes.write(
1105                                 "        if ret is None:return None");
1106                         elif string.find(name, "URI") >= 0:
1107                             classes.write(
1108                     "        if ret is None:raise uriError('%s() failed')\n"
1109                                           % (name))
1110                         elif string.find(name, "XPath") >= 0:
1111                             classes.write(
1112                     "        if ret is None:raise xpathError('%s() failed')\n"
1113                                           % (name))
1114                         elif string.find(name, "Parse") >= 0:
1115                             classes.write(
1116                     "        if ret is None:raise parserError('%s() failed')\n"
1117                                           % (name))
1118                         else:
1119                             classes.write(
1120                     "        if ret is None:raise treeError('%s() failed')\n"
1121                                           % (name))
1122                         classes.write("        return ");
1123                         classes.write(converter_type[ret[0]] % ("ret"));
1124                         classes.write("\n");
1125                     else:
1126                         classes.write("        return ret\n");
1127                 classes.write("\n");
1128
1129     txt.close()
1130     classes.close()
1131
1132
1133 buildStubs()
1134 buildWrappers()