diff options
Diffstat (limited to '')
-rw-r--r-- | src/cython/wrapper.py | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/src/cython/wrapper.py b/src/cython/wrapper.py new file mode 100644 index 0000000..241782a --- /dev/null +++ b/src/cython/wrapper.py @@ -0,0 +1,360 @@ +import os
+import sys
+import argparse
+
+from pygccxml import parser, declarations
+
+class wType:
+ def __init__(self, c_str):
+ self.c_str = c_str
+ self.wrapped_types = ["bint", "double", "int", "unsigned int",
+ "Coord", "IntCoord", "Dim2", "size_t"]
+
+ def get_C(self):
+ """returns plain c string"""
+ return self.c_str
+
+ def get_Cython_type(self):
+ """Returns corresponding Cython type string"""
+ typestr= self.get_C()
+ typestr= typestr.replace(" const ", " ")
+ if typestr.find("::Geom::")==0:
+ typestr = typestr[len("::Geom::"):]
+ if typestr == "bool":
+ typestr = "bint"
+ return typestr
+
+ def get_Python_function_argument(self):
+ """Returns argument type to Python function call"""
+
+ typestr = self.get_Cython_type()
+ if typestr[-1]=="&":
+ typestr = typestr[:-2]
+ if typestr in self.wrapped_types:
+ return typestr+" "
+ return "cy_"+typestr+" "
+ def get_Python_return(self):
+ """ Returns a string used to wrap a function returning this type
+ returns a string to be called with format( 'function call to c function ')
+ """
+ typestr = self.get_Cython_type()
+
+ if typestr[-1] == "&":
+ typestr = typestr[:-2]
+ if typestr in self.wrapped_types:
+ return "return {}"
+ if typestr == "void":
+ return "{}"
+ if typestr[-1] == "*":
+ typestr = typestr[:-2]+"_p"
+ return "return wrap_"+typestr+"({})"
+
+ def get_wrap_method(self, delim):
+ """Returns wrap method in a form of a list of lines"""
+ ret = []
+ typestr = self.get_Cython_type()
+
+ if typestr[-1] == "&":
+ typestr = typestr[:-2]
+ if typestr in self.wrapped_types:
+ return []
+ if typestr == "void":
+ return []
+ if typestr[-1] == "*":
+ typestr = typestr[:-2]+"_p"
+ ret.append("cdef cy_{} wrap_{}({} p):".format(typestr, typestr, typestr))
+ ret.append(delim+"cdef {} * retp = new {}()".format(typestr, typestr))
+ ret.append(delim+"retp[0] = p")
+ ret.append(delim+"cdef cy_{} r = cy_{}.__new__(cy_{})".format(typestr, typestr, typestr))
+ ret.append(delim+"r.thisptr = retp")
+ ret.append(delim+"return r")
+ return ret
+
+ def get_Python_pass_argument(self):
+ """ Returns argument to be passed to C function, to be called with .format(arg_name)"""
+ typestr = self.get_Cython_type()
+ if typestr[-1]=="&":
+ typestr = typestr[:-2]
+ if typestr in self.wrapped_types:
+ return "{}"
+ if typestr[-1] == "*":
+ return "{}.thisptr"
+ return "deref( {}.thisptr )"
+
+class CythonWrapper:
+ def __init__(self, gn):
+ self.global_namespace = gn
+ self.delim = ' '
+
+ def wrap_constructor(self, constructor, types_dict):
+ pxd_lines = []
+ pyx_lines = []
+
+ pxd_arguments = []
+ for argument_type in constructor.argument_types:
+ pxd_arguments.append( types_dict[argument_type.decl_string].get_Cython_type() )
+ pxd_argument_str = "({})".format(", ".join(pxd_arguments))
+
+ pxd_function_str = constructor.name+pxd_argument_str
+ pxd_lines.append(self.delim*2+pxd_function_str)
+
+ #python function
+ arguments_python = ["self"]
+ for argument in constructor.arguments:
+ arguments_python.append(types_dict[argument.type.decl_string].get_Python_function_argument()+argument.name)
+ declaration = "def {}".format("__init__")+"({}):".format(", ".join(arguments_python))
+
+ arguments_cython = []
+ for argument in constructor.arguments:
+ arguments_cython.append(types_dict[argument.type.decl_string].get_Python_pass_argument().format(argument.name))
+ function_call = "new "+constructor.name+"({})".format(", ".join(arguments_cython))
+
+ return_statement = "self.thisptr = {}".format(function_call)
+ pyx_lines.append(self.delim+declaration)
+ pyx_lines.append(self.delim*2 + return_statement)
+
+ return (pxd_lines, pyx_lines)
+
+ def wrap_operator(self, operator, types_dict):
+ pxd_lines = []
+ pyx_lines = []
+
+ pxd_arguments = []
+ for argument_type in operator.argument_types:
+ pxd_arguments.append( types_dict[argument_type.decl_string].get_Cython_type() )
+ pxd_argument_str = "({})".format(", ".join(pxd_arguments))
+ boost_wrapped = ["operator{}=".format(i) for i in ["+", "-", "*", "/", "|", "&"]]
+ pxd_function_str = types_dict[operator.return_type.decl_string].get_Cython_type()+\
+ " "+operator.name+pxd_argument_str
+
+ if not (operator.name in boost_wrapped):
+ pxd_lines.append(self.delim*2+pxd_function_str)
+
+ else:
+ pxd_lines.append(self.delim*2+"#"+pxd_function_str)
+
+ pxd_function_str = types_dict[operator.return_type.decl_string].get_Cython_type()+\
+ " "+operator.name[:-1]+pxd_argument_str
+ pxd_lines.append(self.delim*2+pxd_function_str)
+
+
+ special_methods = {
+ "+" : "__add__",
+ "-" : "__sub__",
+ "*" : "__mul__",
+ "/" : "__div__",
+ "|" : "__or__",
+ "&" : "__and__",
+ "[]": "__getitem__",
+ "()": "__call__",
+ "==": "__richcmp__",
+ "!=": "__richcmp__"
+ }
+
+ if operator.name in boost_wrapped:
+ operation = operator.name[-2]
+ else:
+ operation = operator.name[ len("operator"): ]
+
+ if operation in special_methods:
+ python_name = special_methods[operation]
+ else:
+ python_name = operator.name
+
+ arguments_python = ["self"]
+ for argument in operator.arguments:
+ arguments_python.append(types_dict[argument.type.decl_string].get_Python_function_argument()+argument.name)
+ declaration = "def {}".format(python_name)+"({}):".format(", ".join(arguments_python))
+
+ arguments_cython = []
+ for argument in operator.arguments:
+ arguments_cython.append(types_dict[argument.type.decl_string].get_Python_pass_argument().format(argument.name))
+ function_call = "deref( self.thisptr ) "+operation+" {}".format(", ".join(arguments_cython))
+
+ return_statement = types_dict[operator.return_type.decl_string].get_Python_return().format(function_call)
+
+ if (operator.arguments == []) and (operation == '-'):
+ declaration = "def __neg__(self):"
+ pyx_lines.append(self.delim+declaration)
+ pyx_lines.append(self.delim*2 + return_statement)
+
+ return (pxd_lines, pyx_lines)
+
+ def wrap_method(self, method, types_dict):
+ pxd_lines = []
+ pyx_lines = []
+
+ pxd_arguments = []
+ for argument_type in method.argument_types:
+ pxd_arguments.append( types_dict[argument_type.decl_string].get_Cython_type() )
+ pxd_argument_str = "({})".format(", ".join(pxd_arguments))
+
+
+ pxd_function_str = types_dict[method.return_type.decl_string].get_Cython_type()+\
+ " "+method.name+pxd_argument_str
+ pxd_lines.append(self.delim*2+pxd_function_str)
+
+ #python function
+ arguments_python = ["self"]
+ for argument in method.arguments:
+ arguments_python.append(types_dict[argument.type.decl_string].get_Python_function_argument()+argument.name)
+ declaration = "def {}".format(method.name)+"({}):".format(", ".join(arguments_python))
+
+ arguments_cython = []
+ for argument in method.arguments:
+ arguments_cython.append(types_dict[argument.type.decl_string].get_Python_pass_argument().format(argument.name))
+ function_call = "self.thisptr."+method.name+"({})".format(", ".join(arguments_cython))
+
+ return_statement = types_dict[method.return_type.decl_string].get_Python_return().format(function_call)
+ pyx_lines.append(self.delim+declaration)
+ pyx_lines.append(self.delim*2 + return_statement)
+
+ return (pxd_lines, pyx_lines)
+
+ def wrap_free_function(self, function, types_dict):
+ pxd_lines = []
+ pyx_lines = []
+ decl_lines = []
+
+ pxd_arguments = []
+ for argument_type in function.argument_types:
+ pxd_arguments.append( types_dict[argument_type.decl_string].get_Cython_type() )
+ pxd_argument_str = "({})".format(", ".join(pxd_arguments))
+
+
+ pxd_function_str = types_dict[function.return_type.decl_string].get_Cython_type()+\
+ " "+function.name+pxd_argument_str
+ pxd_lines.append(self.delim+pxd_function_str)
+
+ #python function
+ arguments_python = []
+ for argument in function.arguments:
+ arguments_python.append(types_dict[argument.type.decl_string].get_Python_function_argument()+argument.name)
+ declaration = "def cy_{}".format(function.name)+"({}):".format(", ".join(arguments_python))
+
+ decl_lines.append("from _cy_??? import cy_{0} as {0}".format(function.name))
+
+ arguments_cython = []
+ for argument in function.arguments:
+
+ arguments_cython.append(types_dict[argument.type.decl_string].get_Python_pass_argument().format(argument.name))
+ function_call = function.name+"({})".format(", ".join(arguments_cython))
+
+ return_statement = types_dict[function.return_type.decl_string].get_Python_return().format(function_call)
+ pyx_lines.append(declaration)
+ pyx_lines.append(self.delim + return_statement)
+
+ return (pxd_lines, pyx_lines, decl_lines)
+
+
+ def wrap_class(self, class_name, class_file):
+ pxd_lines = []
+ pyx_lines = []
+ decl_lines = []
+ other_lines = []
+
+ pxd_lines.append("cdef extern from \"2geom/{}\" namespace \"Geom\":".format(class_file))
+ pxd_lines.append(self.delim+"cdef cppclass {}:".format(class_name))
+ pyx_lines.append("cdef class cy_{}:".format(class_name))
+ pyx_lines.append(self.delim+"cdef {}* thisptr".format(class_name))
+ #get_types
+ types_dict = self.collect_types(class_name)
+ #parse
+ c_class = self.global_namespace.namespace(name = "Geom").class_(name=class_name)
+
+ for member in c_class.get_members():
+ if isinstance(member, declarations.calldef.constructor_t):
+ pxd, pyx = self.wrap_constructor(member, types_dict)
+ pxd_lines += pxd
+ pyx_lines += pyx
+ elif isinstance(member, declarations.calldef.member_operator_t):
+ pxd, pyx = self.wrap_operator(member, types_dict)
+ pxd_lines += pxd
+ pyx_lines += pyx
+ elif isinstance(member, declarations.calldef.member_function_t):
+ pxd, pyx = self.wrap_method(member, types_dict)
+ pxd_lines += pxd
+ pyx_lines += pyx
+
+ pyx_lines.append("#free functions:")
+ for ff in self.global_namespace.namespace(name = "Geom").free_funs():
+ if os.path.basename(ff.location.file_name) == class_file:
+ pxd, pyx, decl = self.wrap_free_function(ff, types_dict)
+ pxd_lines += map(lambda x: x[4:], pxd)
+ pyx_lines += pyx
+ decl_lines += decl
+
+ for ff in self.global_namespace.namespace(name = "Geom").free_operators():
+ if os.path.basename(ff.location.file_name) == class_file:
+ pxd, pyx = self.wrap_operator(ff, types_dict)
+ pxd_lines += map(lambda x: x[4:], pxd)
+ pyx_lines += pyx
+
+ pyx_lines.append("")
+ for ctype in types_dict:
+ wtype = types_dict[ctype]
+ for line in wtype.get_wrap_method(self.delim):
+ pyx_lines.append(line)
+ print "------"
+ for i in pxd_lines:
+ print i
+ print "------"
+ for i in pyx_lines:
+ print i
+ print "------"
+ for i in decl_lines:
+ print i
+
+
+ def collect_types(self, class_name):
+ types_set = set()
+ c_class = self.global_namespace.namespace(name = "Geom").class_(name=class_name)
+ for member in c_class.get_members():
+ if any([isinstance(member, declarations.calldef.constructor_t),
+ isinstance(member, declarations.calldef.member_operator_t),
+ isinstance(member, declarations.calldef.member_function_t)]):
+ for a_type in member.argument_types:
+ types_set.add(a_type.decl_string)
+ if isinstance(member, declarations.calldef.member_operator_t) or isinstance(member, declarations.calldef.member_function_t):
+ types_set.add(member.return_type.decl_string)
+ for ff in list(self.global_namespace.namespace(name = "Geom").free_funs()) + list(self.global_namespace.namespace(name = "Geom").free_operators()):
+ for a_type in ff.argument_types:
+ types_set.add(a_type.decl_string)
+ types_set.add(ff.return_type.decl_string)
+ types_dict = dict()
+
+ for f_type in types_set:
+
+ types_dict[f_type] = wType(f_type)
+ return types_dict
+
+def main():
+
+ #set up argument parser
+ cmd_parser = argparse.ArgumentParser()
+ cmd_parser.add_argument('--lib2geom_dir', action = 'store')
+ cmd_parser.add_argument('--file_name', action = 'store')
+ cmd_parser.add_argument('--class_name', action = 'store')
+ cmd_args = cmd_parser.parse_args()
+
+ #set up pygccxml
+ includes = [cmd_args.lib2geom_dir,
+ os.path.join(cmd_args.lib2geom_dir, 'src'),
+ "/usr/include/boost"]
+
+ config = parser.config_t(compiler='gcc',
+ include_paths=includes,
+ cflags="")
+
+ file_to_parse = [os.path.join(cmd_args.lib2geom_dir, "src", "2geom", cmd_args.file_name)]
+
+ decls = parser.parse( file_to_parse, config )
+ global_namespace = declarations.get_global_namespace( decls )
+ wrapper = CythonWrapper(global_namespace)
+ wrapper.wrap_class(cmd_args.class_name, cmd_args.file_name)
+
+main()
+
+#run with
+#python2 wrapper.py --lib2geom_dir ../../.. --file_name int-point.h --class_name IntPoint
+#from cython-bindings directory
|