From a466a05a4401fa0488f0841ec826e0ec7cec3fa6 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 16 Dec 2014 15:43:43 +0100 Subject: [PATCH] More permissive type checking in the Python module. - int values are accepted where a long is expected. - int or long values are accepted where a float is expected. --- tools/python/apixml2python/linphone.py | 137 ++++++++++++++----------- 1 file changed, 75 insertions(+), 62 deletions(-) diff --git a/tools/python/apixml2python/linphone.py b/tools/python/apixml2python/linphone.py index 6412b2bb7..ce1488769 100644 --- a/tools/python/apixml2python/linphone.py +++ b/tools/python/apixml2python/linphone.py @@ -88,8 +88,8 @@ class ArgumentType: self.contained_type = contained_type self.linphone_module = linphone_module self.type_str = None - self.check_func = None - self.convert_func = None + self.check_condition = None + self.convert_code = None self.convert_from_func = None self.fmt_str = 'O' self.cfmt_str = '%p' @@ -105,33 +105,33 @@ class ArgumentType: if self.basic_type == 'char': if '*' in splitted_type: self.type_str = 'string' - self.check_func = 'PyString_Check' - self.convert_func = 'PyString_AsString' + self.check_condition = "!PyString_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyString_AsString({arg_name});\n" self.fmt_str = 'z' self.cfmt_str = '\\"%s\\"' else: self.type_str = 'int' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AsLong' + self.check_condition = "!PyInt_Check({arg_name}) && !PyLong_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyInt_AsLong({arg_name});\n" self.fmt_str = 'b' self.cfmt_str = '%08x' elif self.basic_type == 'int': if 'unsigned' in splitted_type: self.type_str = 'unsigned int' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AsUnsignedLongMask' + self.check_condition = "!PyInt_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyInt_AsUnsignedLongMask({arg_name});\n" self.fmt_str = 'I' self.cfmt_str = '%u' else: self.type_str = 'int' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AS_LONG' + self.check_condition = "!PyInt_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyInt_AS_LONG({arg_name});\n" self.fmt_str = 'i' self.cfmt_str = '%d' elif self.basic_type in ['int8_t', 'int16_t' 'int32_t']: self.type_str = 'int' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AS_LONG' + self.check_condition = "!PyInt_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyInt_AS_LONG({arg_name});\n" if self.basic_type == 'int8_t': self.fmt_str = 'c' elif self.basic_type == 'int16_t': @@ -141,8 +141,8 @@ class ArgumentType: self.cfmt_str = '%d' elif self.basic_type in ['uint8_t', 'uint16_t', 'uint32_t']: self.type_str = 'unsigned int' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AsUnsignedLongMask' + self.check_condition = "!PyInt_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyInt_AsUnsignedLongMask({arg_name});\n" if self.basic_type == 'uint8_t': self.fmt_str = 'b' elif self.basic_type == 'uint16_t': @@ -152,66 +152,83 @@ class ArgumentType: self.cfmt_str = '%u' elif self.basic_type == 'int64_t': self.type_str = '64bits int' - self.check_func = 'PyLong_Check' - self.convert_func = 'PyLong_AsLongLong' + self.check_condition = "!PyInt_Check({arg_name}) && !PyLong_Check({arg_name})" + self.convert_code = \ +"""if (PyInt_Check({arg_name})) {result_name}{result_suffix} = {cast}(PY_LONG_LONG)PyInt_AsLong({arg_name}); + else if (PyLong_Check({arg_name})) {result_name}{result_suffix} = {cast}PyLong_AsLongLong({arg_name}); +""" self.fmt_str = 'L' self.cfmt_str = '%ld' elif self.basic_type == 'uint64_t': self.type_str = '64bits unsigned int' - self.check_func = 'PyLong_Check' - self.convert_func = 'PyLong_AsUnsignedLongLong' + self.check_condition = "!PyLong_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyLong_AsUnsignedLongLong({arg_name});\n" self.fmt_str = 'K' self.cfmt_str = '%lu' elif self.basic_type == 'size_t': - self.type_str = 'size_t' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AsSsize_t' - self.fmt_str = 'n' + self.type_str = 'int' + self.check_condition = "!PyInt_Check({arg_name}) && !PyLong_Check({arg_name})" + self.convert_code = \ +"""if (PyInt_Check({arg_name})) {result_name}{result_suffix} = {cast}(size_t)PyInt_AsSsize_t({arg_name}); + else if (PyLong_Check({arg_name})) {result_name}{result_suffix} = {cast}(size_t)PyLong_AsSsize_t({arg_name}); +""" + self.fmt_str = 'L' self.cfmt_str = '%lu' - elif self.basic_type in ['float', 'double']: + elif self.basic_type == 'float': self.type_str = 'float' - self.check_func = 'PyFloat_Check' - self.convert_func = 'PyFloat_AsDouble' - if self.basic_type == 'float': - self.fmt_str = 'f' - elif self.basic_type == 'double': - self.fmt_str = 'd' + self.check_condition = "!PyFloat_Check({arg_name})" + self.convert_code = \ +"""if (PyInt_Check({arg_name})) {result_name}{result_suffix} = {cast}(float)PyInt_AsLong({arg_name}); + else if (PyLong_Check({arg_name})) {result_name}{result_suffix} = {cast}(float)PyLong_AsLong({arg_name}); + else if (PyFloat_Check({arg_name})) {result_name}{result_suffix} = {cast}(float)PyFloat_AsDouble({arg_name}); +""" + self.fmt_str = 'f' + self.cfmt_str = '%f' + elif self.basic_type == 'double': + self.type_str = 'float' + self.check_condition = "!PyFloat_Check({arg_name})" + self.convert_code = \ +"""if (PyInt_Check({arg_name})) {result_name}{result_suffix} = {cast}(double)PyInt_AsLong({arg_name}); + else if (PyLong_Check({arg_name})) {result_name}{result_suffix} = {cast}(double)PyLong_AsLong({arg_name}); + else if (PyFloat_Check({arg_name})) {result_name}{result_suffix} = {cast}(double)PyFloat_AsDouble({arg_name}); +""" + self.fmt_str = 'd' self.cfmt_str = '%f' elif self.basic_type == 'bool_t': self.type_str = 'bool' - self.check_func = 'PyBool_Check' - self.convert_func = 'PyObject_IsTrue' + self.check_condition = "!PyBool_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyObject_IsTrue({arg_name});\n" self.convert_from_func = 'PyBool_FromLong' self.fmt_str = 'O' self.cfmt_str = '%p' self.cnativefmt_str = '%u' elif self.basic_type == 'time_t': self.type_str = 'DateTime' - self.check_func = 'PyDateTime_Check' - self.convert_func = 'PyDateTime_As_time_t' + self.check_condition = "!PyDateTime_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyDateTime_As_time_t({arg_name});\n" self.convert_from_func = 'PyDateTime_From_time_t' self.fmt_str = 'O' self.cfmt_str = '%p' self.cnativefmt_str = '%ld' elif self.basic_type == 'MSList': self.type_str = 'list of linphone.' + self.contained_type - self.check_func = 'PyList_Check' - self.convert_func = 'PyList_AsMSListOf' + self.contained_type + self.check_condition = "!PyList_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyList_AsMSListOf" + self.contained_type + "({arg_name});\n" self.convert_from_func = 'PyList_FromMSListOf' + self.contained_type self.fmt_str = 'O' self.cfmt_str = '%p' elif self.basic_type == 'MSVideoSize': self.type_str = 'linphone.VideoSize' - self.check_func = 'PyLinphoneVideoSize_Check' - self.convert_func = 'PyLinphoneVideoSize_AsMSVideoSize' + self.check_condition = "!PyLinphoneVideoSize_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyLinphoneVideoSize_AsMSVideoSize({arg_name});\n" self.convert_from_func = 'PyLinphoneVideoSize_FromMSVideoSize' self.fmt_str = 'O' self.cfmt_str = '%p' self.cast_convert_func_result = False elif self.basic_type == 'LCSipTransports': self.type_str = 'linphone.SipTransports' - self.check_func = 'PyLinphoneSipTransports_Check' - self.convert_func = 'PyLinphoneSipTransports_AsLCSipTransports' + self.check_condition = "!PyLinphoneSipTransports_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyLinphoneSipTransports_AsLCSipTransports({arg_name});\n" self.convert_from_func = 'PyLinphoneSipTransports_FromLCSipTransports' self.fmt_str = 'O' self.cfmt_str = '%p' @@ -219,13 +236,13 @@ class ArgumentType: else: if strip_leading_linphone(self.basic_type) in self.linphone_module.enum_names: self.type_str = 'int' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AsLong' + self.check_condition = "!PyInt_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyInt_AsLong({arg_name});\n" self.fmt_str = 'i' self.cfmt_str = '%d' elif is_callback(self.complete_type): self.type_str = 'callable' - self.check_func = 'PyCallable_Check' + self.check_condition = "!PyCallable_Check({arg_name})" self.cnativefmt_str = None elif '*' in splitted_type: self.type_str = 'linphone.' + strip_leading_linphone(self.basic_type) @@ -267,7 +284,7 @@ class MethodDefinition: elif argument_type.fmt_str == 'O' and argument_type.use_native_pointer: body += "\tPyObject * " + arg_name + ";\n" body += "\t" + arg_complete_type + " " + arg_name + "_native_ptr = NULL;\n" - elif argument_type.fmt_str == 'O' and argument_type.convert_func is not None: + elif argument_type.fmt_str == 'O' and argument_type.convert_code is not None: body += "\tPyObject * " + arg_name + ";\n" body += "\t" + arg_complete_type + " " + arg_name + "_native_obj;\n" elif strip_leading_linphone(arg_complete_type) in self.linphone_module.enum_names: @@ -295,10 +312,8 @@ class MethodDefinition: arg_complete_type = xml_method_arg.get('completetype') arg_contained_type = xml_method_arg.get('containedtype') argument_type = ArgumentType(arg_type, arg_complete_type, arg_contained_type, self.linphone_module) - if argument_type.fmt_str == 'O' and argument_type.convert_func is not None: - args_conversion_code += \ -""" {arg_name}_native_obj = {convert_func}({arg_name}); -""".format(arg_name=arg_name, convert_func=argument_type.convert_func) + if argument_type.fmt_str == 'O' and argument_type.convert_code is not None: + args_conversion_code += argument_type.convert_code.format(result_name=arg_name, result_suffix='_native_obj', cast='', arg_name=arg_name) return \ """ {class_native_ptr_check_code} {parse_tuple_code} @@ -349,7 +364,7 @@ class MethodDefinition: argument_type = ArgumentType(arg_type, arg_complete_type, arg_contained_type, self.linphone_module) if argument_type.fmt_str == 'O' and argument_type.use_native_pointer: arg_names.append(arg_name + "_native_ptr") - elif argument_type.fmt_str == 'O' and argument_type.convert_func is not None: + elif argument_type.fmt_str == 'O' and argument_type.convert_code is not None: arg_names.append(arg_name + "_native_obj") else: arg_names.append(arg_name) @@ -441,11 +456,11 @@ class MethodDefinition: """.format(arg_name=arg_name, arg_type=strip_leading_linphone(arg_type), type_str=argument_type.type_str) else: body += \ -""" if (!{check_func}({arg_name})) {{ +""" if ({check_condition}) {{ PyErr_SetString(PyExc_TypeError, "The '{arg_name}' argument must be a {type_str} instance."); return NULL; }} -""".format(arg_name=arg_name, check_func=argument_type.check_func, type_str=argument_type.type_str) +""".format(arg_name=arg_name, check_condition=argument_type.check_condition.format(arg_name=arg_name), type_str=argument_type.type_str) if body != '': body = body[1:] # Remove leading '\t' return body @@ -488,7 +503,6 @@ class MethodDefinition: if self.build_value_format == 'O': body += "\tPyObject * pyresult;\n" body += "\tPyObject * pyret;\n" - body += "\tconst char *pyret_fmt;\n" return body def parse_method_node(self): @@ -688,7 +702,7 @@ class SetterMethodDefinition(MethodDefinition): MethodDefinition.__init__(self, linphone_module, class_, method_node) def format_arguments_parsing(self): - if self.first_argument_type.check_func is None: + if self.first_argument_type.check_condition is None: attribute_type_check_code = \ """if ((value != Py_None) && !PyObject_IsInstance(value, (PyObject *)&pylinphone_{class_name}Type)) {{ PyErr_SetString(PyExc_TypeError, "The '{attribute_name}' attribute value must be a linphone.{class_name} instance."); @@ -700,11 +714,11 @@ class SetterMethodDefinition(MethodDefinition): if self.first_argument_type.type_str == 'string': checknotnone = "(value != Py_None) && " attribute_type_check_code = \ -"""if ({checknotnone}!{checkfunc}(value)) {{ +"""if ({checknotnone}{check_condition}) {{ PyErr_SetString(PyExc_TypeError, "The '{attribute_name}' attribute value must be a {type_str}."); return -1; }} -""".format(checknotnone=checknotnone, checkfunc=self.first_argument_type.check_func, attribute_name=self.attribute_name, type_str=self.first_argument_type.type_str) +""".format(checknotnone=checknotnone, check_condition=self.first_argument_type.check_condition.format(arg_name='value'), attribute_name=self.attribute_name, type_str=self.first_argument_type.type_str) attribute_conversion_code = '' callback_setting_code = '' if is_callback(self.first_argument_type.complete_type): @@ -713,18 +727,17 @@ class SetterMethodDefinition(MethodDefinition): Py_INCREF(value); ((pylinphone_{class_name}Object *)self)->{callback_name} = value; """.format(class_name=self.class_['class_name'], callback_name=compute_event_name(self.first_arg_complete_type, self.class_['class_name'])) - if (self.first_argument_type.convert_func is None) or \ - (self.first_argument_type.fmt_str == 'O' and self.first_argument_type.convert_func is not None): + if (self.first_argument_type.convert_code is None) or \ + (self.first_argument_type.fmt_str == 'O' and self.first_argument_type.convert_code is not None): attribute_conversion_code += "{arg_name} = value;\n".format(arg_name="_" + self.first_arg_name) - if self.first_argument_type.convert_func is not None: + if self.first_argument_type.convert_code is not None: cast_code = '' suffix = '' if self.first_argument_type.cast_convert_func_result: cast_code = "({arg_type})".format(arg_type=self.first_arg_complete_type) - if self.first_argument_type.fmt_str == 'O' and self.first_argument_type.convert_func is not None: + if self.first_argument_type.fmt_str == 'O' and self.first_argument_type.convert_code is not None: suffix = '_native_obj' - attribute_conversion_code += "\t{arg_name}{suffix} = {cast_code}{convertfunc}(value);\n".format( - arg_name="_" + self.first_arg_name, suffix=suffix, cast_code=cast_code, convertfunc=self.first_argument_type.convert_func) + attribute_conversion_code += self.first_argument_type.convert_code.format(result_name="_" + self.first_arg_name, result_suffix=suffix, cast=cast_code, arg_name='value') attribute_native_ptr_check_code = '' if self.first_argument_type.use_native_pointer: attribute_native_ptr_check_code = \ @@ -761,7 +774,7 @@ class SetterMethodDefinition(MethodDefinition): suffix = '' if self.first_argument_type.fmt_str == 'O' and self.first_argument_type.use_native_pointer: suffix = '_native_ptr' - elif self.first_argument_type.fmt_str == 'O' and self.first_argument_type.convert_func is not None: + elif self.first_argument_type.fmt_str == 'O' and self.first_argument_type.convert_code is not None: suffix = '_native_obj' return \ """ {method_name}(native_ptr, {arg_name}{suffix}); @@ -891,7 +904,7 @@ class EventCallbackMethodDefinition(MethodDefinition): """.format(class_name=strip_leading_linphone(self.return_type)) else: - convert_python_result_code = "\t\tcresult = {convertfunc}(pyresult);\n".format(convertfunc=argument_type.convert_func) + convert_python_result_code = '\t\t' + argument_type.convert_code.format(result_name='cresult', result_suffix='', cast='', arg_name='pyresult') return \ """ if ((func != NULL) && PyCallable_Check(func)) {{ {create_python_objects_code}