Correctly handle passing of objects as method arguments in the Python wrapper.

This commit is contained in:
Ghislain MARY 2014-07-11 14:12:52 +02:00
parent affdb49e12
commit 0c37b9e59e
2 changed files with 59 additions and 32 deletions

View file

@ -40,26 +40,29 @@ class MethodDefinition:
def format_local_variables_definition(self):
self.return_type = self.xml_method_return.get('type')
if self.return_type != 'void':
self.body += "\t" + self.return_type + " cresult;\n"
self.build_value_format = self.__ctype_to_python_format(self.return_type)
self.return_complete_type = self.xml_method_return.get('completetype')
if self.return_complete_type != 'void':
self.body += "\t" + self.return_complete_type + " cresult;\n"
self.build_value_format = self.__ctype_to_python_format(self.return_type, self.return_complete_type)
if self.build_value_format == 'O':
self.body += "\tPyObject * pyresult;\n"
self.body += "\tPyObject * pyret;\n"
if self.self_arg is not None:
self.body += "\t" + self.self_arg.get('type') + "native_ptr;\n"
self.body += "\t" + self.self_arg.get('completetype') + "native_ptr;\n"
for xml_method_arg in self.xml_method_args:
method_type = xml_method_arg.get('type')
fmt = self.__ctype_to_python_format(method_type)
arg_name = xml_method_arg.get('name')
arg_type = xml_method_arg.get('type')
arg_complete_type = xml_method_arg.get('completetype')
fmt = self.__ctype_to_python_format(arg_type, arg_complete_type)
self.parse_tuple_format += fmt
if fmt == 'O':
# TODO
pass
elif strip_leading_linphone(method_type) in self.enum_names:
self.body += "\tint " + xml_method_arg.get('name') + ";\n"
self.body += "\tPyObject * " + arg_name + ";\n"
self.body += "\t" + arg_complete_type + " " + arg_name + "_native_ptr;\n"
elif strip_leading_linphone(arg_complete_type) in self.enum_names:
self.body += "\tint " + arg_name + ";\n"
else:
self.body += "\t" + xml_method_arg.get('type') + " " + xml_method_arg.get('name') + ";\n"
self.arg_names.append(xml_method_arg.get('name'))
self.body += "\t" + arg_complete_type + " " + arg_name + ";\n"
self.arg_names.append(arg_name)
def format_native_pointer_get(self):
self.body += "\tnative_ptr = pylinphone_" + self.class_['class_name'] + "_get_native_ptr(self);\n"
@ -74,6 +77,17 @@ class MethodDefinition:
self.body += "\t\treturn NULL;\n"
self.body += "\t}\n"
def format_object_args_native_pointers_checking(self):
for xml_method_arg in self.xml_method_args:
arg_name = xml_method_arg.get('name')
arg_type = xml_method_arg.get('type')
arg_complete_type = xml_method_arg.get('completetype')
fmt = self.__ctype_to_python_format(arg_type, arg_complete_type)
if fmt == 'O':
self.body += "\tif ((" + arg_name + "_native_ptr = pylinphone_" + strip_leading_linphone(arg_type) + "_get_native_ptr(" + arg_name + ")) == NULL) {\n"
self.body += "\t\treturn NULL;\n"
self.body += "\t}\n"
def format_arguments_parsing(self):
if self.self_arg is not None:
self.format_native_pointer_checking(False)
@ -81,9 +95,13 @@ class MethodDefinition:
self.body += "\tif (!PyArg_ParseTuple(args, \"" + self.parse_tuple_format + "\""
self.body += ', ' + ', '.join(map(lambda a: '&' + a, self.arg_names))
self.body += ")) {\n\t\treturn NULL;\n\t}\n"
self.format_object_args_native_pointers_checking()
def format_setter_value_checking_and_c_function_call(self):
attribute_name = self.method_node.get('property_name')
first_arg_type = self.xml_method_args[0].get('type')
first_arg_complete_type = self.xml_method_args[0].get('completetype')
first_arg_name = self.xml_method_args[0].get('name')
self.format_native_pointer_checking(True)
# Check that the value exists
self.body += "\tif (value == NULL) {\n"
@ -91,32 +109,45 @@ class MethodDefinition:
self.body += "\t\treturn -1;\n"
self.body += "\t}\n"
# Check the value
basic_type, checkfunc, convertfunc = self.__ctype_to_python_type(self.xml_method_args[0].get('type'))
type_str, checkfunc, convertfunc = self.__ctype_to_python_type(first_arg_type, first_arg_complete_type)
self.body += "\tif (!" + checkfunc + "(value)) {\n"
self.body += "\t\tPyErr_SetString(PyExc_TypeError, \"The " + attribute_name + " attribute value must be a " + basic_type + "\");\n"
self.body += "\t\tPyErr_SetString(PyExc_TypeError, \"The " + attribute_name + " attribute value must be a " + type_str + "\");\n"
self.body += "\t\treturn -1;\n"
self.body += "\t}\n"
# Call the C function
if convertfunc is None:
pass # TODO
else:
self.body += "\t" + self.arg_names[0] + " = (" + self.xml_method_args[0].get('type') + ")" + convertfunc + "(value);\n"
self.body += "\t" + self.method_node.get('name') + "(native_ptr, " + self.arg_names[0] + ");\n"
self.body += "\t" + first_arg_name + " = (" + first_arg_complete_type + ")" + convertfunc + "(value);\n"
if self.__ctype_to_python_format(first_arg_type, first_arg_complete_type) == 'O':
pass # TODO
else:
self.body += "\t" + self.method_node.get('name') + "(native_ptr, " + self.arg_names[0] + ");\n"
self.body += "\treturn 0;"
def format_tracing(self):
self.body += "\tpylinphone_trace(__FUNCTION__);\n"
def format_c_function_call(self):
arg_names = []
for xml_method_arg in self.xml_method_args:
arg_name = xml_method_arg.get('name')
arg_type = xml_method_arg.get('type')
arg_complete_type = xml_method_arg.get('completetype')
type_str, checkfunc, convertfunc = self.__ctype_to_python_type(arg_type, arg_complete_type)
if convertfunc is None:
arg_names.append(arg_name + "_native_ptr")
else:
arg_names.append(arg_name)
self.body += "\t"
if self.return_type != 'void':
self.body += "cresult = "
self.body += self.method_node.get('name') + "("
if self.self_arg is not None:
self.body += "native_ptr"
if len(self.arg_names) > 0:
if len(arg_names) > 0:
self.body += ', '
self.body += ', '.join(self.arg_names) + ");\n"
self.body += ', '.join(arg_names) + ");\n"
def format_method_result(self):
if self.return_type != 'void':
@ -141,18 +172,8 @@ class MethodDefinition:
self.body += "\t}\n"
self.body += "\tself->ob_type->tp_free(self);"
def __get_basic_type_from_c_type(self, ctype):
basic_type = 'int'
keywords = ['const', 'struct', 'enum', 'signed', 'unsigned', 'short', 'long', '*']
splitted_type = ctype.split(' ')
for s in splitted_type:
if s not in keywords:
basic_type = s
break
return (basic_type, splitted_type)
def __ctype_to_python_format(self, ctype):
basic_type, splitted_type = self.__get_basic_type_from_c_type(ctype)
def __ctype_to_python_format(self, basic_type, complete_type):
splitted_type = complete_type.split(' ')
if basic_type == 'char':
if '*' in splitted_type:
return 'z'
@ -191,8 +212,8 @@ class MethodDefinition:
else:
return 'O'
def __ctype_to_python_type(self, ctype):
basic_type, splitted_type = self.__get_basic_type_from_c_type(ctype)
def __ctype_to_python_type(self, basic_type, complete_type):
splitted_type = complete_type.split(' ')
if basic_type == 'char':
if '*' in splitted_type:
return ('string', 'PyString_Check', 'PyString_AsString')
@ -351,6 +372,7 @@ class LinphoneModule(object):
# Force return value type of dealloc function to prevent declaring useless local variables
# TODO: Investigate. Maybe we should decide that setters must always return an int value.
method.xml_method_return.set('type', 'void')
method.xml_method_return.set('completetype', 'void')
method.format_local_variables_definition()
method.format_tracing()
method.format_setter_value_checking_and_c_function_call()
@ -360,6 +382,7 @@ class LinphoneModule(object):
method = MethodDefinition(method_node, class_, self.enum_names)
# Force return value type of dealloc function to prevent declaring useless local variables
method.xml_method_return.set('type', 'void')
method.xml_method_return.set('completetype', 'void')
method.format_local_variables_definition()
method.format_native_pointer_get()
method.format_tracing()

View file

@ -67,6 +67,10 @@ typedef struct {
{{class_cname}} *native_ptr;
} pylinphone_{{class_name}}Object;
{{/classes}}
{{#classes}}
static {{class_cname}} * pylinphone_{{class_name}}_get_native_ptr(PyObject *self) {
return ((pylinphone_{{class_name}}Object *)self)->native_ptr;
}