3 # generate python wrappers from the XML API description
7 enums
= {} # { enumType: { enumConstant: enumValue } }
13 if __name__
== "__main__":
14 # launched as a script
15 srcPref
= os
.path
.dirname(sys
.argv
[0])
18 srcPref
= os
.path
.dirname(__file__
)
20 #######################################################################
22 # That part if purely the API acquisition phase from the
23 # libvirt API description
25 #######################################################################
31 sgmlop
= None # accelerator not available
37 """sgmlop based XML parser. this is typically 15x faster
40 def __init__(self
, target
):
43 self
.finish_starttag
= target
.start
44 self
.finish_endtag
= target
.end
45 self
.handle_data
= target
.data
48 self
.parser
= sgmlop
.XMLParser()
49 self
.parser
.register(self
)
50 self
.feed
= self
.parser
.feed
52 "amp": "&", "gt": ">", "lt": "<",
53 "apos": "'", "quot": '"'
60 self
.parser
= self
.feed
= None # nuke circular reference
62 def handle_entityref(self
, entity
):
65 self
.handle_data(self
.entity
[entity
])
67 self
.handle_data("&%s;" % entity
)
73 class SlowParser(xmllib
.XMLParser
):
74 """slow but safe standard parser, based on the XML parser in
75 Python's standard library."""
77 def __init__(self
, target
):
78 self
.unknown_starttag
= target
.start
79 self
.handle_data
= target
.data
80 self
.unknown_endtag
= target
.end
81 xmllib
.XMLParser
.__init
__(self
)
83 def getparser(target
= None):
84 # get the fastest available parser, and attach it to an
85 # unmarshalling object. return both objects.
89 return FastParser(target
), target
90 return SlowParser(target
), target
94 self
._methodname
= None
102 def getmethodname(self
):
103 return self
._methodname
105 def data(self
, text
):
107 print "data %s" % text
108 self
._data
.append(text
)
110 def start(self
, tag
, attrs
):
112 print "start %s, %s" % (tag
, attrs
)
113 if tag
== 'function':
117 self
.function_cond
= None
118 self
.function_args
= []
119 self
.function_descr
= None
120 self
.function_return
= None
121 self
.function_file
= None
122 if attrs
.has_key('name'):
123 self
.function
= attrs
['name']
124 if attrs
.has_key('file'):
125 self
.function_file
= attrs
['file']
131 if self
.in_function
== 1:
132 self
.function_arg_name
= None
133 self
.function_arg_type
= None
134 self
.function_arg_info
= None
135 if attrs
.has_key('name'):
136 self
.function_arg_name
= attrs
['name']
137 if self
.function_arg_name
== 'from':
138 self
.function_arg_name
= 'frm'
139 if attrs
.has_key('type'):
140 self
.function_arg_type
= attrs
['type']
141 if attrs
.has_key('info'):
142 self
.function_arg_info
= attrs
['info']
143 elif tag
== 'return':
144 if self
.in_function
== 1:
145 self
.function_return_type
= None
146 self
.function_return_info
= None
147 self
.function_return_field
= None
148 if attrs
.has_key('type'):
149 self
.function_return_type
= attrs
['type']
150 if attrs
.has_key('info'):
151 self
.function_return_info
= attrs
['info']
152 if attrs
.has_key('field'):
153 self
.function_return_field
= attrs
['field']
155 enum(attrs
['type'],attrs
['name'],attrs
['value'])
160 if tag
== 'function':
161 if self
.function
!= None:
162 function(self
.function
, self
.function_descr
,
163 self
.function_return
, self
.function_args
,
164 self
.function_file
, self
.function_cond
)
167 if self
.in_function
== 1:
168 self
.function_args
.append([self
.function_arg_name
,
169 self
.function_arg_type
,
170 self
.function_arg_info
])
171 elif tag
== 'return':
172 if self
.in_function
== 1:
173 self
.function_return
= [self
.function_return_type
,
174 self
.function_return_info
,
175 self
.function_return_field
]
180 if self
.in_function
== 1:
181 self
.function_descr
= str
186 if self
.in_function
== 1:
187 self
.function_cond
= str
190 def function(name
, desc
, ret
, args
, file, cond
):
191 functions
[name
] = (desc
, ret
, args
, file, cond
)
193 def enum(type, name
, value
):
194 if not enums
.has_key(type):
196 enums
[type][name
] = value
198 #######################################################################
200 # Some filtering rukes to drop functions/types which should not
201 # be exposed as-is on the Python interface
203 #######################################################################
205 functions_failed
= []
206 functions_skipped
= [
207 "virConnectListDomains" , "virDomainGetUUID"
214 'int *': "usually a return type",
217 #######################################################################
219 # Table of remapping to/from the python type or class to the C
222 #######################################################################
225 'void': (None, None, None, None),
226 'int': ('i', None, "int", "int"),
227 'long': ('i', None, "int", "int"),
228 'double': ('d', None, "double", "double"),
229 'unsigned int': ('i', None, "int", "int"),
230 'unsigned long': ('i', None, "int", "int"),
231 'unsigned char *': ('z', None, "charPtr", "char *"),
232 'char *': ('z', None, "charPtr", "char *"),
233 'const char *': ('z', None, "charPtrConst", "const char *"),
234 'virDomainPtr': ('O', "virDomain", "virDomainPtr", "virDomainPtr"),
235 'const virDomainPtr': ('O', "virDomain", "virDomainPtr", "virDomainPtr"),
236 'virDomain *': ('O', "virDomain", "virDomainPtr", "virDomainPtr"),
237 'const virDomain *': ('O', "virDomain", "virDomainPtr", "virDomainPtr"),
238 'virConnectPtr': ('O', "virConnect", "virConnectPtr", "virConnectPtr"),
239 'const virConnectPtr': ('O', "virConnect", "virConnectPtr", "virConnectPtr"),
240 'virConnect *': ('O', "virConnect", "virConnectPtr", "virConnectPtr"),
241 'const virConnect *': ('O', "virConnect", "virConnectPtr", "virConnectPtr"),
249 foreign_encoding_args
= (
252 #######################################################################
254 # This part writes the C <-> Python stubs libxml2-py.[ch] and
255 # the table libxml2-export.c to add when registrering the Python module
257 #######################################################################
259 # Class methods which are written by hand in libvir.c but the Python-level
260 # code is still automatically generated (so they are not in skip_function()).
262 'virConnectListDomainsID',
267 def skip_function(name
):
268 if name
== "virConnectClose":
270 if name
== "virDomainFree":
275 def print_function_wrapper(name
, output
, export
, include
):
279 global skipped_modules
282 (desc
, ret
, args
, file, cond
) = functions
[name
]
284 print "failed to get function %s infos"
287 if skipped_modules
.has_key(file):
289 if skip_function(name
) == 1:
291 if name
in skip_impl
:
292 # Don't delete the function entry in the caller.
303 # This should be correct
304 if arg
[1][0:6] == "const ":
306 c_args
= c_args
+ " %s %s;\n" % (arg
[1], arg
[0])
307 if py_types
.has_key(arg
[1]):
308 (f
, t
, n
, c
) = py_types
[arg
[1]]
309 if (f
== 'z') and (name
in foreign_encoding_args
) and (num_bufs
== 0):
314 format_args
= format_args
+ ", &pyobj_%s" % (arg
[0])
315 c_args
= c_args
+ " PyObject *pyobj_%s;\n" % (arg
[0])
316 c_convert
= c_convert
+ \
317 " %s = (%s) Py%s_Get(pyobj_%s);\n" % (arg
[0],
320 format_args
= format_args
+ ", &%s" % (arg
[0])
322 format_args
= format_args
+ ", &py_buffsize%d" % num_bufs
323 c_args
= c_args
+ " int py_buffsize%d;\n" % num_bufs
324 num_bufs
= num_bufs
+ 1
326 c_call
= c_call
+ ", ";
327 c_call
= c_call
+ "%s" % (arg
[0])
329 if skipped_types
.has_key(arg
[1]):
331 if unknown_types
.has_key(arg
[1]):
332 lst
= unknown_types
[arg
[1]]
335 unknown_types
[arg
[1]] = [name
]
338 format
= format
+ ":%s" % (name
)
341 if file == "python_accessor":
342 if args
[1][1] == "char *":
343 c_call
= "\n if (%s->%s != NULL) free(%s->%s);\n" % (
344 args
[0][0], args
[1][0], args
[0][0], args
[1][0])
345 c_call
= c_call
+ " %s->%s = (%s)strdup((const xmlChar *)%s);\n" % (args
[0][0],
346 args
[1][0], args
[1][1], args
[1][0])
348 c_call
= "\n %s->%s = %s;\n" % (args
[0][0], args
[1][0],
351 c_call
= "\n %s(%s);\n" % (name
, c_call
);
352 ret_convert
= " Py_INCREF(Py_None);\n return(Py_None);\n"
353 elif py_types
.has_key(ret
[0]):
354 (f
, t
, n
, c
) = py_types
[ret
[0]]
355 c_return
= " %s c_retval;\n" % (ret
[0])
356 if file == "python_accessor" and ret
[2] != None:
357 c_call
= "\n c_retval = %s->%s;\n" % (args
[0][0], ret
[2])
359 c_call
= "\n c_retval = %s(%s);\n" % (name
, c_call
);
360 ret_convert
= " py_retval = libvirt_%sWrap((%s) c_retval);\n" % (n
,c
)
361 ret_convert
= ret_convert
+ " return(py_retval);\n"
362 elif py_return_types
.has_key(ret
[0]):
363 (f
, t
, n
, c
) = py_return_types
[ret
[0]]
364 c_return
= " %s c_retval;\n" % (ret
[0])
365 c_call
= "\n c_retval = %s(%s);\n" % (name
, c_call
);
366 ret_convert
= " py_retval = libvirt_%sWrap((%s) c_retval);\n" % (n
,c
)
367 ret_convert
= ret_convert
+ " return(py_retval);\n"
369 if skipped_types
.has_key(ret
[0]):
371 if unknown_types
.has_key(ret
[0]):
372 lst
= unknown_types
[ret
[0]]
375 unknown_types
[ret
[0]] = [name
]
378 if cond
!= None and cond
!= "":
379 include
.write("#if %s\n" % cond
)
380 export
.write("#if %s\n" % cond
)
381 output
.write("#if %s\n" % cond
)
383 include
.write("PyObject * ")
384 include
.write("libvirt_%s(PyObject *self, PyObject *args);\n" % (name
));
386 export
.write(" { (char *)\"%s\", libvirt_%s, METH_VARARGS, NULL },\n" %
390 # Those have been manually generated
391 if cond
!= None and cond
!= "":
392 include
.write("#endif\n");
393 export
.write("#endif\n");
394 output
.write("#endif\n");
396 if file == "python_accessor" and ret
[0] != "void" and ret
[2] is None:
397 # Those have been manually generated
398 if cond
!= None and cond
!= "":
399 include
.write("#endif\n");
400 export
.write("#endif\n");
401 output
.write("#endif\n");
404 output
.write("PyObject *\n")
405 output
.write("libvirt_%s(PyObject *self ATTRIBUTE_UNUSED," % (name
))
406 output
.write(" PyObject *args")
408 output
.write(" ATTRIBUTE_UNUSED")
409 output
.write(") {\n")
411 output
.write(" PyObject *py_retval;\n")
413 output
.write(c_return
)
417 output
.write("\n if (!PyArg_ParseTuple(args, (char *)\"%s\"%s))\n" %
418 (format
, format_args
))
419 output
.write(" return(NULL);\n")
421 output
.write(c_convert
)
424 output
.write(ret_convert
)
425 output
.write("}\n\n")
426 if cond
!= None and cond
!= "":
427 include
.write("#endif /* %s */\n" % cond
)
428 export
.write("#endif /* %s */\n" % cond
)
429 output
.write("#endif /* %s */\n" % cond
)
434 global py_return_types
438 f
= open(os
.path
.join(srcPref
,"libvirt-api.xml"))
440 (parser
, target
) = getparser()
445 f
= open(os
.path
.join(srcPref
,"..","docs","libvirt-api.xml"))
447 (parser
, target
) = getparser()
454 n
= len(functions
.keys())
455 print "Found %d functions in libvirt-api.xml" % (n
)
457 py_types
['pythonObject'] = ('O', "pythonObject", "pythonObject", "pythonObject")
459 f
= open(os
.path
.join(srcPref
,"libvirt-python-api.xml"))
461 (parser
, target
) = getparser()
468 print "Found %d functions in libvirt-python-api.xml" % (
469 len(functions
.keys()) - n
)
474 include
= open("libvirt-py.h", "w")
475 include
.write("/* Generated */\n\n")
476 export
= open("libvirt-export.c", "w")
477 export
.write("/* Generated */\n\n")
478 wrapper
= open("libvirt-py.c", "w")
479 wrapper
.write("/* Generated */\n\n")
480 wrapper
.write("#include <Python.h>\n")
481 wrapper
.write("#include <libvirt.h>\n")
482 wrapper
.write("#include \"libvirt_wrap.h\"\n")
483 wrapper
.write("#include \"libvirt-py.h\"\n\n")
484 for function
in functions
.keys():
485 ret
= print_function_wrapper(function
, wrapper
, export
, include
)
488 functions_failed
.append(function
)
489 del functions
[function
]
491 skipped
= skipped
+ 1
492 functions_skipped
.append(function
)
493 del functions
[function
]
495 nb_wrap
= nb_wrap
+ 1
500 print "Generated %d wrapper functions, %d failed, %d skipped\n" % (nb_wrap
,
502 print "Missing type converters: "
503 for type in unknown_types
.keys():
504 print "%s:%d " % (type, len(unknown_types
[type])),
507 #######################################################################
509 # This part writes part of the Python front-end classes based on
510 # mapping rules between types and classes and also based on function
511 # renaming to get consistent function names at the Python level
513 #######################################################################
516 # The type automatically remapped to generated classes
519 "virDomainPtr": ("._o", "virDomain(_obj=%s)", "virDomain"),
520 "virDomain *": ("._o", "virDomain(_obj=%s)", "virDomain"),
521 "virConnectPtr": ("._o", "virConnect(_obj=%s)", "virConnect"),
522 "virConnect *": ("._o", "virConnect(_obj=%s)", "virConnect"),
528 primary_classes
= ["virDomain", "virConnect"]
532 classes_destructors
= {
533 "virDomain": "virDomainFree",
534 "virConnect": "virConnectClose",
537 functions_noexcept
= {
540 reference_keepers
= {
543 function_classes
= {}
545 function_classes
["None"] = []
548 'virDomainDestroy': "self._o = None",
551 def nameFixup(name
, classe
, type, file):
552 listname
= classe
+ "List"
555 if name
[0:l
] == listname
:
557 func
= string
.lower(func
[0:1]) + func
[1:]
558 elif name
[0:12] == "virDomainGet":
560 func
= string
.lower(func
[0:1]) + func
[1:]
561 elif name
[0:9] == "virDomain":
563 func
= string
.lower(func
[0:1]) + func
[1:]
564 elif name
[0:10] == "virConnect":
566 func
= string
.lower(func
[0:1]) + func
[1:]
567 elif name
[0:3] == "xml":
569 func
= string
.lower(func
[0:1]) + func
[1:]
578 if func
== "xMLDesc":
583 def functionCompare(info1
, info2
):
584 (index1
, func1
, name1
, ret1
, args1
, file1
) = info1
585 (index2
, func2
, name2
, ret2
, args2
, file2
) = info2
591 if file1
== "python_accessor":
593 if file2
== "python_accessor":
601 def writeDoc(name
, args
, indent
, output
):
602 if functions
[name
][0] is None or functions
[name
][0] == "":
604 val
= functions
[name
][0]
605 val
= string
.replace(val
, "NULL", "None");
610 i
= string
.rfind(str, " ");
619 output
.write(' """\n')
624 global py_return_types
627 global function_classes
630 global converter_type
631 global primary_classes
632 global converter_type
633 global classes_ancestor
634 global converter_type
635 global primary_classes
636 global classes_ancestor
637 global classes_destructors
638 global functions_noexcept
640 for type in classes_type
.keys():
641 function_classes
[classes_type
[type][2]] = []
644 # Build the list of C types to look for ordered to start
645 # with primary classes
649 ctypes_processed
= {}
650 classes_processed
= {}
651 for classe
in primary_classes
:
652 classes_list
.append(classe
)
653 classes_processed
[classe
] = ()
654 for type in classes_type
.keys():
655 tinfo
= classes_type
[type]
656 if tinfo
[2] == classe
:
658 ctypes_processed
[type] = ()
659 for type in classes_type
.keys():
660 if ctypes_processed
.has_key(type):
662 tinfo
= classes_type
[type]
663 if not classes_processed
.has_key(tinfo
[2]):
664 classes_list
.append(tinfo
[2])
665 classes_processed
[tinfo
[2]] = ()
668 ctypes_processed
[type] = ()
670 for name
in functions
.keys():
672 (desc
, ret
, args
, file, cond
) = functions
[name
]
674 classe
= classes_type
[type][2]
676 if name
[0:3] == "vir" and len(args
) >= 1 and args
[0][1] == type:
678 func
= nameFixup(name
, classe
, type, file)
679 info
= (0, func
, name
, ret
, args
, file)
680 function_classes
[classe
].append(info
)
681 elif name
[0:3] == "vir" and len(args
) >= 2 and args
[1][1] == type \
682 and file != "python_accessor":
684 func
= nameFixup(name
, classe
, type, file)
685 info
= (1, func
, name
, ret
, args
, file)
686 function_classes
[classe
].append(info
)
689 func
= nameFixup(name
, "None", file, file)
690 info
= (0, func
, name
, ret
, args
, file)
691 function_classes
['None'].append(info
)
693 classes
= open("libvirt.py", "w")
694 classes
.write("""#!/usr/bin/python -u
696 # Those are the autogenerated Python bindings for libvirt.
697 # Check python/generator.py in the source distribution of libvirt
698 # to find out more about the generation process
702 classes
.write("import libvirtmod\n")
703 classes
.write("import types\n\n")
705 txt
= open("libvirtclass.txt", "w")
706 txt
.write(" Generated Classes for libvir-python\n\n")
708 txt
.write("#\n# Global functions of the module\n#\n\n")
709 if function_classes
.has_key("None"):
710 flist
= function_classes
["None"]
711 flist
.sort(functionCompare
)
714 (index
, func
, name
, ret
, args
, file) = info
716 classes
.write("#\n# Functions from module %s\n#\n\n" % file)
717 txt
.write("\n# functions from module %s\n" % file)
719 classes
.write("def %s(" % func
)
720 txt
.write("%s()\n" % func
);
725 classes
.write("%s" % arg
[0])
727 classes
.write("):\n")
728 writeDoc(name
, args
, ' ', classes
);
731 if classes_type
.has_key(arg
[1]):
732 classes
.write(" if %s is None: %s__o = None\n" %
734 classes
.write(" else: %s__o = %s%s\n" %
735 (arg
[0], arg
[0], classes_type
[arg
[1]][0]))
737 classes
.write(" ret = ");
740 classes
.write("libvirtmod.%s(" % name
)
745 classes
.write("%s" % arg
[0])
746 if classes_type
.has_key(arg
[1]):
747 classes
.write("__o");
749 classes
.write(")\n");
751 if classes_type
.has_key(ret
[0]):
755 if functions_noexcept
.has_key(name
):
756 classes
.write(" if ret is None:return None\n");
757 classes
.write(" return ");
758 classes
.write(classes_type
[ret
[0]][1] % ("ret"));
761 classes
.write(" return ret\n");
764 txt
.write("\n\n#\n# Set of classes of the module\n#\n\n")
765 for classname
in classes_list
:
766 if classname
== "None":
769 if classes_ancestor
.has_key(classname
):
770 txt
.write("\n\nClass %s(%s)\n" % (classname
,
771 classes_ancestor
[classname
]))
772 classes
.write("class %s(%s):\n" % (classname
,
773 classes_ancestor
[classname
]))
774 classes
.write(" def __init__(self, _obj=None):\n")
775 if reference_keepers
.has_key(classname
):
776 rlist
= reference_keepers
[classname
]
778 classes
.write(" self.%s = None\n" % ref
[1])
779 classes
.write(" self._o = _obj\n")
780 classes
.write(" %s.__init__(self, _obj=_obj)\n\n" % (
781 classes_ancestor
[classname
]))
783 txt
.write("Class %s()\n" % (classname
))
784 classes
.write("class %s:\n" % (classname
))
785 classes
.write(" def __init__(self, _obj=None):\n")
786 if reference_keepers
.has_key(classname
):
787 list = reference_keepers
[classname
]
789 classes
.write(" self.%s = None\n" % ref
[1])
790 classes
.write(" if _obj != None:self._o = _obj;return\n")
791 classes
.write(" self._o = None\n\n");
793 if classes_destructors
.has_key(classname
):
794 classes
.write(" def __del__(self):\n")
795 classes
.write(" if self._o != None:\n")
796 classes
.write(" libvirtmod.%s(self._o)\n" %
797 classes_destructors
[classname
]);
798 classes
.write(" self._o = None\n\n");
799 destruct
=classes_destructors
[classname
]
800 flist
= function_classes
[classname
]
801 flist
.sort(functionCompare
)
804 (index
, func
, name
, ret
, args
, file) = info
806 # Do not provide as method the destructors for the class
807 # to avoid double free
812 if file == "python_accessor":
813 classes
.write(" # accessors for %s\n" % (classname
))
814 txt
.write(" # accessors\n")
816 classes
.write(" #\n")
817 classes
.write(" # %s functions from module %s\n" % (
819 txt
.write("\n # functions from module %s\n" % file)
820 classes
.write(" #\n\n")
822 classes
.write(" def %s(self" % func
)
823 txt
.write(" %s()\n" % func
);
827 classes
.write(", %s" % arg
[0])
829 classes
.write("):\n")
830 writeDoc(name
, args
, ' ', classes
);
833 if classes_type
.has_key(arg
[1]):
835 classes
.write(" if %s is None: %s__o = None\n" %
837 classes
.write(" else: %s__o = %s%s\n" %
838 (arg
[0], arg
[0], classes_type
[arg
[1]][0]))
841 classes
.write(" ret = ");
844 classes
.write("libvirtmod.%s(" % name
)
850 classes
.write("%s" % arg
[0])
851 if classes_type
.has_key(arg
[1]):
852 classes
.write("__o");
854 classes
.write("self");
855 if classes_type
.has_key(arg
[1]):
856 classes
.write(classes_type
[arg
[1]][0])
858 classes
.write(")\n");
859 if function_post
.has_key(name
):
860 classes
.write(" %s\n" % (function_post
[name
]));
862 if classes_type
.has_key(ret
[0]):
866 if functions_noexcept
.has_key(name
):
868 " if ret is None:return None\n");
871 # generate the returned class wrapper for the object
873 classes
.write(" __tmp = ");
874 classes
.write(classes_type
[ret
[0]][1] % ("ret"));
878 # Sometime one need to keep references of the source
879 # class in the returned class object.
880 # See reference_keepers for the list
882 tclass
= classes_type
[ret
[0]][2]
883 if reference_keepers
.has_key(tclass
):
884 list = reference_keepers
[tclass
]
886 if pref
[0] == classname
:
887 classes
.write(" __tmp.%s = self\n" %
892 classes
.write(" return __tmp\n");
893 elif converter_type
.has_key(ret
[0]):
897 if functions_noexcept
.has_key(name
):
899 " if ret is None:return None");
900 classes
.write(" return ");
901 classes
.write(converter_type
[ret
[0]] % ("ret"));
904 classes
.write(" return ret\n");
908 # Generate enum constants
910 for type,enum
in enums
.items():
911 classes
.write("# %s\n" % type)
913 items
.sort(lambda i1
,i2
: cmp(long(i1
[1]),long(i2
[1])))
914 for name
,value
in items
:
915 classes
.write("%s = %s\n" % (name
,value
))
918 if len(functions_skipped
) != 0:
919 txt
.write("\nFunctions skipped:\n")
920 for function
in functions_skipped
:
921 txt
.write(" %s\n" % function
)
922 if len(functions_failed
) != 0:
923 txt
.write("\nFunctions failed:\n")
924 for function
in functions_failed
:
925 txt
.write(" %s\n" % function
)