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.
This commit is contained in:
Ghislain MARY 2014-12-16 15:43:43 +01:00
parent d317047869
commit a466a05a44

View file

@ -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}