From 76c467a163532bc11782b28e332c504922a8dde8 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Wed, 2 Jul 2014 16:49:05 +0200 Subject: [PATCH] Begin generation of python module code. --- tools/python/apixml2python.py | 30 ++++ tools/python/apixml2python/__init__.py | 0 tools/python/apixml2python/linphone.py | 88 ++++++++++ .../apixml2python/linphone_module.mustache | 161 ++++++++++++++++++ tools/python/setup.py | 9 + 5 files changed, 288 insertions(+) create mode 100755 tools/python/apixml2python.py create mode 100644 tools/python/apixml2python/__init__.py create mode 100644 tools/python/apixml2python/linphone.py create mode 100644 tools/python/apixml2python/linphone_module.mustache create mode 100644 tools/python/setup.py diff --git a/tools/python/apixml2python.py b/tools/python/apixml2python.py new file mode 100755 index 000000000..63f7ed8db --- /dev/null +++ b/tools/python/apixml2python.py @@ -0,0 +1,30 @@ +#!/usr/bin/python + +import argparse +import os +import pystache +import sys +import xml.etree.ElementTree as ET + +sys.path.append(os.path.realpath(__file__)) +from apixml2python.linphone import LinphoneModule + + +def generate(apixmlfile): + tree = ET.parse(apixmlfile) + renderer = pystache.Renderer() + m = LinphoneModule(tree) + f = open("linphone.c", "w") + f.write(renderer.render(m)) + + +def main(argv = None): + if argv is None: + argv = sys.argv + argparser = argparse.ArgumentParser(description="Generate a Python wrapper of the Linphone API.") + argparser.add_argument('apixmlfile', help="XML file of the Linphone API generated by genapixml.py.") + args = argparser.parse_args() + generate(args.apixmlfile) + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/python/apixml2python/__init__.py b/tools/python/apixml2python/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tools/python/apixml2python/linphone.py b/tools/python/apixml2python/linphone.py new file mode 100644 index 000000000..f58d923f8 --- /dev/null +++ b/tools/python/apixml2python/linphone.py @@ -0,0 +1,88 @@ +class LinphoneModule(object): + def __init__(self, tree): + self.enums = [] + xml_enums = tree.findall("./enums/enum") + for xml_enum in xml_enums: + e = {} + e['enum_name'] = xml_enum.get('name') + e['enum_doc'] = self.__format_doc(xml_enum.find('briefdescription'), xml_enum.find('detaileddescription')) + e['enum_values'] = [] + xml_enum_values = xml_enum.findall("./values/value") + for xml_enum_value in xml_enum_values: + v = {} + v['enum_value_name'] = xml_enum_value.get('name') + e['enum_values'].append(v) + self.enums.append(e) + self.classes = [] + xml_classes = tree.findall("./classes/class") + for xml_class in xml_classes: + c = {} + c['class_name'] = xml_class.get('name') + c['class_c_function_prefix'] = xml_class.get('cfunctionprefix') + c['class_doc'] = self.__format_doc(xml_class.find('briefdescription'), xml_class.find('detaileddescription')) + c['class_type_methods'] = [] + xml_type_methods = xml_class.findall("./classmethods/classmethod") + for xml_type_method in xml_type_methods: + m = {} + m['method_name'] = xml_type_method.get('name').replace(c['class_c_function_prefix'], '') + c['class_type_methods'].append(m) + c['class_instance_methods'] = [] + xml_instance_methods = xml_class.findall("./instancemethods/instancemethod") + for xml_instance_method in xml_instance_methods: + m = {} + m['method_name'] = xml_instance_method.get('name').replace(c['class_c_function_prefix'], '') + c['class_instance_methods'].append(m) + c['class_properties'] = [] + xml_properties = xml_class.findall("./properties/property") + for xml_property in xml_properties: + p = {} + p['property_name'] = xml_property.get('name') + xml_property_getter = xml_property.find("./getter") + xml_property_setter = xml_property.find("./setter") + if xml_property_getter is not None: + p['getter_name'] = xml_property_getter.get('name').replace(c['class_c_function_prefix'], '') + if xml_property_setter is not None: + p['setter_name'] = xml_property_setter.get('name').replace(c['class_c_function_prefix'], '') + c['class_properties'].append(p) + self.classes.append(c) + + def __format_node(self, node): + desc = '' + if node.tag == 'para': + desc += node.text.strip() + for n in list(node): + desc += self.__format_node(n) + elif node.tag == 'note': + desc += node.text.strip() + for n in list(node): + desc += self.__format_node(n) + elif node.tag == 'ref': + desc += ' ' + node.text.strip() + ' ' + tail = node.tail.strip() + if tail != '': + if node.tag != 'ref': + desc += '\n' + desc += tail + if node.tag == 'para': + desc += '\n' + return desc + + def __format_doc(self, brief_description, detailed_description): + doc = '' + if brief_description is None: + brief_description = '' + if detailed_description is None: + detailed_description = '' + else: + desc = '' + for node in list(detailed_description): + desc += self.__format_node(node) + '\n' + detailed_description = desc.strip().replace('\n', '\\n') + brief_description = brief_description.strip() + doc += brief_description + if detailed_description != '': + if doc != '': + doc += '\\n\\n' + doc+= detailed_description + doc = '\"' + doc + '\"' + return doc diff --git a/tools/python/apixml2python/linphone_module.mustache b/tools/python/apixml2python/linphone_module.mustache new file mode 100644 index 000000000..52721287a --- /dev/null +++ b/tools/python/apixml2python/linphone_module.mustache @@ -0,0 +1,161 @@ +#include +#include + +{{#classes}} + +typedef struct { + PyObject_HEAD + {{class_name}} *native_ptr; +} pylinphone_{{class_name}}Object; + +static PyObject * pylinphone_{{class_name}}_new(PyTypeObject *type, PyObject *args, PyObject *kw) { + pylinphone_{{class_name}}Object *self = (pylinphone_{{class_name}}Object *)type->tp_alloc(type, 0); + return (PyObject *)self; +} + +static void pylinphone_{{class_name}}_dealloc(PyObject *self) { + self->ob_type->tp_free(self); +} + +{{#class_type_methods}} + +static PyObject * pylinphone_{{class_name}}_type_method_{{method_name}}(PyObject *self, PyObject *args) { + // TODO: Fill implementation + Py_RETURN_NONE; +} + +{{/class_type_methods}} + +static PyMethodDef pylinphone_{{class_name}}_type_methods[] = { + // TODO: Handle doc +{{#class_type_methods}} + { "{{method_name}}", pylinphone_{{class_name}}_type_method_{{method_name}}, METH_VARARGS, "" }, +{{/class_type_methods}} + { NULL, NULL, 0, NULL } /* Sentinel */ +}; + +{{#class_instance_methods}} + +static PyObject * pylinphone_{{class_name}}_instance_method_{{method_name}}(PyObject *self, PyObject *args) { + // TODO: Fill implementation + Py_RETURN_NONE; +} + +{{/class_instance_methods}} + +static PyMethodDef pylinphone_{{class_name}}_instance_methods[] = { + // TODO: Handle doc +{{#class_instance_methods}} + { "{{method_name}}", pylinphone_{{class_name}}_instance_method_{{method_name}}, METH_VARARGS, "" }, +{{/class_instance_methods}} + { NULL, NULL, 0, NULL } /* Sentinel */ +}; + +{{#class_properties}} + +static PyObject * pylinphone_{{class_name}}_{{getter_name}}(pylinphone_{{class_name}}Object *self, void *closure) { + // TODO: Fill implementation + Py_RETURN_NONE; +} + +static int pylinphone_{{class_name}}_{{setter_name}}(pylinphone_{{class_name}}Object *self, PyObject *value, void *closure) { + // TODO: Fill implementation + return 0; +} + +{{/class_properties}} + +static PyGetSetDef pylinphone_{{class_name}}_getseters[] = { + // TODO: Handle doc +{{#class_properties}} + { "{{property_name}}", (getter)pylinphone_{{class_name}}_{{getter_name}}, (setter)pylinphone_{{class_name}}_{{setter_name}}, "" }, +{{/class_properties}} + { NULL, NULL, NULL, NULL, NULL } /* Sentinel */ +}; + +static PyTypeObject pylinphone_{{class_name}}Type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "linphone.{{name}}", /* tp_name */ + sizeof(pylinphone_{{class_name}}Object), /* tp_basicsize */ + 0, /* tp_itemsize */ + pylinphone_{{class_name}}_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + {{{class_doc}}}, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + pylinphone_{{class_name}}_instance_methods, /* tp_methods */ + 0, /* tp_members */ + pylinphone_{{class_name}}_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + pylinphone_{{class_name}}_new, /* tp_new */ + 0, /* tp_free */ +}; + +{{/classes}} + +static PyMethodDef pylinphone_ModuleMethods[] = { + { NULL, NULL, 0, NULL } /* Sentinel */ +}; + +static PyMethodDef pylinphone_NoMethods[] = { + { NULL, NULL, 0, NULL } /* Sentinel */ +}; + +PyMODINIT_FUNC initlinphone(void) { + PyObject *m; + PyObject *menum; + PyMethodDef *def; + +{{#classes}} + if (PyType_Ready(&pylinphone_{{class_name}}Type) < 0) return; +{{/classes}} + + m = Py_InitModule3("linphone", pylinphone_ModuleMethods, "Python module giving access to the Linphone library."); + if (m == NULL) return; + +{{#enums}} + menum = Py_InitModule3("{{enum_name}}", pylinphone_NoMethods, {{{enum_doc}}}); + if (menum == NULL) return; + if (PyModule_AddObject(m, "{{enum_name}}", menum) < 0) return; +{{#enum_values}} + if (PyModule_AddIntConstant(menum, "{{enum_value_name}}", {{enum_value_name}}) < 0) return; +{{/enum_values}} +{{/enums}} + +{{#classes}} + Py_INCREF(&pylinphone_{{class_name}}Type); + PyModule_AddObject(m, "{{class_name}}", (PyObject *)&pylinphone_{{class_name}}Type); + for (def = pylinphone_{{class_name}}_type_methods; def->ml_name != NULL; def++) { + PyObject *func = PyCFunction_New(def, NULL); + PyObject *method = PyMethod_New(func, NULL, (PyObject *)&pylinphone_{{class_name}}Type); + PyDict_SetItemString(pylinphone_{{class_name}}Type.tp_dict, def->ml_name, method); + Py_DECREF(method); + Py_DECREF(func); + } +{{/classes}} +} diff --git a/tools/python/setup.py b/tools/python/setup.py new file mode 100644 index 000000000..cf88c1b28 --- /dev/null +++ b/tools/python/setup.py @@ -0,0 +1,9 @@ +#!/usr/bin/python + +from distutils.core import setup, Extension + +m = Extension('linphone', + include_dirs = ['/home/ghislain/linphone-install/include'], + sources = ['linphone.c'] +) +setup(name = 'Linphone', version = '1.0', description = 'Linphone package', ext_modules = [m])