From 0809ae562ed5765a119665bf90e19934f406aa5a Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Thu, 31 May 2012 11:04:11 +0000 Subject: [PATCH] Fixed regression in python wrappers generator --- modules/python/src2/gen2.py | 127 +++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 62 deletions(-) diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index 830c378..a14cb1d 100644 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -211,15 +211,18 @@ class ClassInfo(object): self.props = [] self.consts = {} customname = False - + if decl: self.bases = decl[1].split()[1:] if len(self.bases) > 1: print "Warning: class %s has more than 1 base class (not supported by Python C extensions)" % (self.name,) - print "Bases: ", self.bases + print "Bases: ", " ".join(self.bases) print "Only the first base class will be used" - self.bases = self.bases[:1] + self.bases = [self.bases[0].strip(",")] #return sys.exit(-1) + if self.bases and self.bases[0].startswith("cv::"): + self.bases[0] = self.bases[0][4:] + print self.bases, decl[1] for m in decl[2]: if m.startswith("="): self.wname = m[1:] @@ -229,10 +232,10 @@ class ClassInfo(object): elif m == "/Simple": self.issimple = True self.props = [ClassProp(p) for p in decl[3]] - + if not customname and self.wname.startswith("Cv"): self.wname = self.wname[2:] - + def gen_map_code(self, all_classes): code = "static bool pyopencv_to(PyObject* src, %s& dst, const char* name)\n{\n PyObject* tmp;\n bool ok;\n" % (self.cname) code += "".join([gen_template_set_prop_from_map.substitute(propname=p.name,proptype=p.tp) for p in self.props]) @@ -241,21 +244,21 @@ class ClassInfo(object): else: code += "\n return true;\n}\n" return code - + def gen_code(self, all_classes): if self.ismap: return self.gen_map_code(all_classes) - + getset_code = cStringIO.StringIO() getset_inits = cStringIO.StringIO() - + sorted_props = [(p.name, p) for p in self.props] sorted_props.sort() - + access_op = "->" if self.issimple: access_op = "." - + for pname, p in sorted_props: getset_code.write(gen_template_get_prop.substitute(name=self.name, member=pname, membertype=p.tp, access=access_op)) if p.readonly: @@ -263,29 +266,29 @@ class ClassInfo(object): else: getset_code.write(gen_template_set_prop.substitute(name=self.name, member=pname, membertype=p.tp, access=access_op)) getset_inits.write(gen_template_rw_prop_init.substitute(name=self.name, member=pname)) - + methods_code = cStringIO.StringIO() methods_inits = cStringIO.StringIO() - + sorted_methods = self.methods.items() sorted_methods.sort() - + for mname, m in sorted_methods: methods_code.write(m.gen_code(all_classes)) methods_inits.write(m.get_tab_entry()) - + baseptr = "NULL" if self.bases and all_classes.has_key(self.bases[0]): baseptr = "&pyopencv_" + all_classes[self.bases[0]].name + "_Type" - + code = gen_template_type_impl.substitute(name=self.name, wname=self.wname, cname=self.cname, getset_code=getset_code.getvalue(), getset_inits=getset_inits.getvalue(), methods_code=methods_code.getvalue(), methods_inits=methods_inits.getvalue(), baseptr=baseptr, extra_specials="") - + return code - - + + class ConstInfo(object): def __init__(self, name, val): self.cname = name.replace(".", "::") @@ -295,7 +298,7 @@ class ConstInfo(object): self.name = re.sub(r"([a-z])([A-Z])", r"\1_\2", self.name) self.name = self.name.upper() self.value = val - + class ArgInfo(object): def __init__(self, arg_tuple): self.tp = arg_tuple[0] @@ -323,7 +326,7 @@ class ArgInfo(object): self.arraycvt = m[2:].strip() self.py_inputarg = False self.py_outputarg = False - + def isbig(self): return self.tp == "Mat" or self.tp == "vector_Mat"# or self.tp.startswith("vector") @@ -338,7 +341,7 @@ class FuncVariant(object): self.wname = self.wname[2:] else: self.wname = self.classname - + self.rettype = decl[1] if self.rettype == "void": self.rettype = "" @@ -355,28 +358,28 @@ class FuncVariant(object): self.array_counters[c] = [ainfo.name] self.args.append(ainfo) self.init_pyproto() - + def init_pyproto(self): # string representation of argument list, with '[', ']' symbols denoting optional arguments, e.g. # "src1, src2[, dst[, mask]]" for cv.add argstr = "" - + # list of all input arguments of the Python function, with the argument numbers: # [("src1", 0), ("src2", 1), ("dst", 2), ("mask", 3)] # we keep an argument number to find the respective argument quickly, because # some of the arguments of C function may not present in the Python function (such as array counters) # or even go in a different order ("heavy" output parameters of the C function # become the first optional input parameters of the Python function, and thus they are placed right after - # non-optional input parameters) + # non-optional input parameters) arglist = [] - + # the list of "heavy" output parameters. Heavy parameters are the parameters # that can be expensive to allocate each time, such as vectors and matrices (see isbig). outarr_list = [] - + # the list of output parameters. Also includes input/output parameters. outlist = [] - + firstoptarg = 1000000 argno = -1 for a in self.args: @@ -400,12 +403,12 @@ class FuncVariant(object): arglist += outarr_list outarr_list = [] arglist.append((a.name, argno)) - + if outarr_list: firstoptarg = min(firstoptarg, len(arglist)) arglist += outarr_list firstoptarg = min(firstoptarg, len(arglist)) - + noptargs = len(arglist) - firstoptarg argnamelist = [aname for aname, argno in arglist] argstr = ", ".join(argnamelist[:firstoptarg]) @@ -425,7 +428,7 @@ class FuncVariant(object): outstr = ", ".join([o[0] for o in outlist]) else: outstr = "None" - + self.py_docstring = "%s(%s) -> %s" % (self.wname, argstr, outstr) self.py_noptargs = noptargs self.py_arglist = arglist @@ -444,10 +447,10 @@ class FuncInfo(object): self.cname = cname self.isconstructor = isconstructor self.variants = [] - + def add_variant(self, decl): self.variants.append(FuncVariant(self.classname, self.name, decl, self.isconstructor)) - + def get_wrapper_name(self): name = self.name if self.classname: @@ -457,7 +460,7 @@ class FuncInfo(object): else: classname = "" return "pyopencv_" + classname + name - + def get_wrapper_prototype(self): full_fname = self.get_wrapper_name() if self.classname and not self.isconstructor: @@ -465,7 +468,7 @@ class FuncInfo(object): else: self_arg = "" return "static PyObject* %s(PyObject* %s, PyObject* args, PyObject* kw)" % (full_fname, self_arg) - + def get_tab_entry(self): docstring_list = [] have_empty_constructor = False @@ -485,11 +488,11 @@ class FuncInfo(object): p1 = s.find("(") p2 = s.rfind(")") docstring_list = [s[:p1+1] + "[" + s[p1+1:p2] + "]" + s[p2:]] - + return Template(' {"$py_funcname", (PyCFunction)$wrap_funcname, METH_KEYWORDS, "$py_docstring"},\n' ).substitute(py_funcname = self.variants[0].wname, wrap_funcname=self.get_wrapper_name(), py_docstring = " or ".join(docstring_list)) - + def gen_code(self, all_classes): proto = self.get_wrapper_prototype() code = "%s\n{\n" % (proto,) @@ -560,9 +563,9 @@ class FuncInfo(object): code_decl += " PyObject* pyobj_%s = NULL;\n" % (a.name,) parse_name = "pyobj_" + a.name code_cvt_list.append("pyopencv_to(pyobj_%s, %s)" % (a.name, a.name)) - + all_cargs.append([amapping, parse_name]) - + defval = a.defval if not defval: defval = amapping[2] @@ -630,7 +633,7 @@ class FuncInfo(object): amapping = all_cargs[argno][0] backcvt_arg_list.append("%s(%s)" % (amapping[2], aname)) code_ret = "return Py_BuildValue(\"(%s)\", %s)" % \ - (fmtspec, ", ".join(["pyopencv_from(" + aname + ")" for aname, argno in v.py_outlist])) + (fmtspec, ", ".join(["pyopencv_from(" + aname + ")" for aname, argno in v.py_outlist])) all_code_variants.append(gen_template_func_body.substitute(code_decl=code_decl, code_parse=code_parse, code_fcall=code_fcall, code_ret=code_ret)) @@ -642,13 +645,13 @@ class FuncInfo(object): # try to execute each signature code += " PyErr_Clear();\n\n".join([" {\n" + v + " }\n" for v in all_code_variants]) code += "\n return NULL;\n}\n\n" - return code - - + return code + + class PythonWrapperGenerator(object): def __init__(self): self.clear() - + def clear(self): self.classes = {} self.funcs = {} @@ -664,20 +667,20 @@ class PythonWrapperGenerator(object): classinfo = ClassInfo(name, decl) classinfo.decl_idx = self.class_idx self.class_idx += 1 - + if self.classes.has_key(classinfo.name): print "Generator error: class %s (cname=%s) already exists" \ % (classinfo.name, classinfo.cname) - sys.exit(-1) + sys.exit(-1) self.classes[classinfo.name] = classinfo - + def add_const(self, name, decl): constinfo = ConstInfo(name, decl[1]) - + if self.consts.has_key(constinfo.name): print "Generator error: constant %s (cname=%s) already exists" \ % (constinfo.name, constinfo.cname) - sys.exit(-1) + sys.exit(-1) self.consts[constinfo.name] = constinfo def add_func(self, decl): @@ -704,7 +707,7 @@ class PythonWrapperGenerator(object): name = m[1:] customname = True func_map = self.funcs - + if not classname or isconstructor: pass elif isclassmethod: @@ -718,24 +721,24 @@ class PythonWrapperGenerator(object): print "Generator error: the class for method %s is missing" % (name,) sys.exit(-1) func_map = classinfo.methods - + func = func_map.get(name, FuncInfo(classname, name, cname, isconstructor)) func.add_variant(decl) if len(func.variants) == 1: func_map[name] = func - + def gen_const_reg(self, constinfo): self.code_const_reg.write("PUBLISH2(%s,%s);\n" % (constinfo.name, constinfo.cname)) - + def save(self, path, name, buf): f = open(path + "/" + name, "wt") f.write(buf.getvalue()) f.close() - + def gen(self, srcfiles, output_path): self.clear() parser = hdr_parser.CppHeaderParser() - + # step 1: scan the headers and build more descriptive maps of classes, consts, functions for hdr in srcfiles: decls = parser.parse(hdr) @@ -753,7 +756,7 @@ class PythonWrapperGenerator(object): else: # function self.add_func(decl) - + # step 2: generate code for the classes and their methods classlist = self.classes.items() classlist.sort() @@ -766,18 +769,18 @@ class PythonWrapperGenerator(object): else: templ = gen_template_type_decl self.code_types.write(templ.substitute(name=name, wname=classinfo.wname, cname=classinfo.cname)) - + # register classes in the same order as they have been declared. # this way, base classes will be registered in Python before their derivatives. classlist1 = [(classinfo.decl_idx, name, classinfo) for name, classinfo in classlist] classlist1.sort() - + for decl_idx, name, classinfo in classlist1: code = classinfo.gen_code(self.classes) self.code_types.write(code) if not classinfo.ismap: self.code_type_reg.write("MKTYPE2(%s);\n" % (classinfo.name,) ) - + # step 3: generate the code for all the global functions funclist = self.funcs.items() funclist.sort() @@ -785,13 +788,13 @@ class PythonWrapperGenerator(object): code = func.gen_code(self.classes) self.code_funcs.write(code) self.code_func_tab.write(func.get_tab_entry()) - + # step 4: generate the code for constants constlist = self.consts.items() constlist.sort() for name, constinfo in constlist: self.gen_const_reg(constinfo) - + # That's it. Now save all the files self.save(output_path, "pyopencv_generated_funcs.h", self.code_funcs) self.save(output_path, "pyopencv_generated_func_tab.h", self.code_func_tab) @@ -809,5 +812,5 @@ if __name__ == "__main__": generator = PythonWrapperGenerator() generator.gen(srcfiles, dstdir) - - + + -- 2.7.4