diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index be8965c6e0..8949f356c1 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -1609,14 +1609,20 @@ void initcv2() return; #endif + #if PY_MAJOR_VERSION >= 3 - Py_INCREF(&cv2_UMatWrapperType); +#define PUBLISH_OBJECT(name, type) Py_INCREF(&type);\ + PyModule_AddObject(m, name, (PyObject *)&type); #else - // Unrolled Py_INCREF(&cv2_UMatWrapperType) without (PyObject*) cast - // due to "warning: dereferencing type-punned pointer will break strict-aliasing rules" - _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA (&cv2_UMatWrapperType)->ob_refcnt++; +// Unrolled Py_INCREF(&type) without (PyObject*) cast +// due to "warning: dereferencing type-punned pointer will break strict-aliasing rules" +#define PUBLISH_OBJECT(name, type) _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA (&type)->ob_refcnt++;\ + PyModule_AddObject(m, name, (PyObject *)&type); #endif - PyModule_AddObject(m, "UMat", (PyObject *)&cv2_UMatWrapperType); + + PUBLISH_OBJECT("UMat", cv2_UMatWrapperType); + +#include "pyopencv_generated_type_publish.h" #define PUBLISH(I) PyDict_SetItemString(d, #I, PyInt_FromLong(I)) //#define PUBLISHU(I) PyDict_SetItemString(d, #I, PyLong_FromUnsignedLong(I)) diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index da7a92333a..5a754ed825 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -25,14 +25,12 @@ gen_template_check_self_algo = Template(""" $cname* _self_ = NULL; return failmsgp("Incorrect type of self (must be '${name}' or its derivative)"); """) -gen_template_call_constructor_prelude = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); - new (&(self->v)) Ptr<$cname>(); // init Ptr with placement new +gen_template_call_constructor_prelude = Template("""new (&(self->v)) Ptr<$cname>(); // init Ptr with placement new if(self) """) gen_template_call_constructor = Template("""self->v.reset(new ${cname}${args})""") -gen_template_simple_call_constructor_prelude = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); - if(self) """) +gen_template_simple_call_constructor_prelude = Template("""if(self) """) gen_template_simple_call_constructor = Template("""new (&(self->v)) ${cname}${args}""") @@ -189,6 +187,7 @@ static void pyopencv_${name}_specials(void) pyopencv_${name}_Type.tp_dealloc = pyopencv_${name}_dealloc; pyopencv_${name}_Type.tp_repr = pyopencv_${name}_repr; pyopencv_${name}_Type.tp_getset = pyopencv_${name}_getseters; + pyopencv_${name}_Type.tp_init = (initproc)${constructor}; pyopencv_${name}_Type.tp_methods = pyopencv_${name}_methods;${extra_specials} } """) @@ -280,6 +279,7 @@ class ClassInfo(object): self.props = [] self.consts = {} self.base = None + self.constructor = None customname = False if decl: @@ -353,6 +353,9 @@ class ClassInfo(object): sorted_methods = list(self.methods.items()) sorted_methods.sort() + if self.constructor is not None: + methods_code.write(self.constructor.gen_code(all_classes)) + for mname, m in sorted_methods: methods_code.write(m.gen_code(all_classes)) methods_inits.write(m.get_tab_entry()) @@ -361,10 +364,14 @@ class ClassInfo(object): if self.base and self.base in all_classes: baseptr = "&pyopencv_" + all_classes[self.base].name + "_Type" + constructor_name = "0" + if self.constructor is not None: + constructor_name = self.constructor.get_wrapper_name() + 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="") + baseptr=baseptr, constructor=constructor_name, extra_specials="") return code @@ -521,12 +528,13 @@ class FuncVariant(object): class FuncInfo(object): - def __init__(self, classname, name, cname, isconstructor, namespace): + def __init__(self, classname, name, cname, isconstructor, namespace, isclassmethod): self.classname = classname self.name = name self.cname = cname self.isconstructor = isconstructor self.namespace = namespace + self.isclassmethod = isclassmethod self.variants = [] def add_variant(self, decl): @@ -540,11 +548,19 @@ class FuncInfo(object): name = "getelem" else: classname = "" + + if self.isclassmethod: + name += "_cls" + return "pyopencv_" + self.namespace.replace('.','_') + '_' + classname + name - def get_wrapper_prototype(self): + def get_wrapper_prototype(self, all_classes): full_fname = self.get_wrapper_name() - if self.classname and not self.isconstructor: + if self.isconstructor: + return "static int {fn_name}(pyopencv_{type_name}_t* self, PyObject* args, PyObject* kw)".format( + fn_name=full_fname, type_name=all_classes[self.classname].name) + + if self.classname: self_arg = "self" else: self_arg = "" @@ -591,12 +607,16 @@ class FuncInfo(object): # Convert unicode chars to xml representation, but keep as string instead of bytes full_docstring = full_docstring.encode('ascii', errors='xmlcharrefreplace').decode() - return Template(' {"$py_funcname", (PyCFunction)$wrap_funcname, METH_VARARGS | METH_KEYWORDS, "$py_docstring"},\n' + flags = ["METH_VARARGS", "METH_KEYWORDS"] + if self.isclassmethod: + flags.append("METH_CLASS") + + return Template(' {"$py_funcname", (PyCFunction)$wrap_funcname, $flags, "$py_docstring"},\n' ).substitute(py_funcname = self.variants[0].wname, wrap_funcname=self.get_wrapper_name(), - py_docstring = full_docstring) + flags = " | ".join(flags), py_docstring = full_docstring) def gen_code(self, all_classes): - proto = self.get_wrapper_prototype() + proto = self.get_wrapper_prototype(all_classes) code = "%s\n{\n" % (proto,) code += " using namespace %s;\n\n" % self.namespace.replace('.', '::') @@ -609,7 +629,9 @@ class FuncInfo(object): selfinfo = all_classes[self.classname] if not self.isconstructor: amp = "&" if selfinfo.issimple else "" - if selfinfo.isalgorithm: + if self.isclassmethod: + pass + elif selfinfo.isalgorithm: code += gen_template_check_self_algo.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp) else: get = "" if selfinfo.issimple else ".get()" @@ -692,7 +714,6 @@ class FuncInfo(object): code_args += ")" if self.isconstructor: - code_decl += " pyopencv_%s_t* self = 0;\n" % selfinfo.name if selfinfo.issimple: templ_prelude = gen_template_simple_call_constructor_prelude templ = gen_template_simple_call_constructor @@ -708,7 +729,7 @@ class FuncInfo(object): if v.rettype: code_decl += " " + v.rettype + " retval;\n" code_fcall += "retval = " - if ismethod: + if ismethod and not self.isclassmethod: code_fcall += "_self_->" + self.cname else: code_fcall += self.cname @@ -750,7 +771,7 @@ class FuncInfo(object): code_ret = "Py_RETURN_NONE" elif len(v.py_outlist) == 1: if self.isconstructor: - code_ret = "return (PyObject*)self" + code_ret = "return 0" else: aname, argno = v.py_outlist[0] code_ret = "return pyopencv_from(%s)" % (aname,) @@ -773,7 +794,11 @@ class FuncInfo(object): else: # 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" + + def_ret = "NULL" + if self.isconstructor: + def_ret = "-1" + code += "\n return %s;\n}\n\n" % def_ret return code @@ -796,6 +821,7 @@ class PythonWrapperGenerator(object): self.code_funcs = StringIO() self.code_type_reg = StringIO() self.code_ns_reg = StringIO() + self.code_type_publish = StringIO() self.class_idx = 0 def add_class(self, stype, name, decl): @@ -848,20 +874,32 @@ class PythonWrapperGenerator(object): isclassmethod = True elif m.startswith("="): name = m[1:] - if isclassmethod: - name = "_".join(classes+[name]) - classname = '' - elif isconstructor: + if isconstructor: name = "_".join(classes[:-1]+[name]) - if classname and not isconstructor: - cname = barename + if isclassmethod: + # Add it as a method to the class func_map = self.classes[classname].methods - else: + func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, isclassmethod)) + func.add_variant(decl) + + # Add it as global function + g_name = "_".join(classes+[name]) func_map = self.namespaces.setdefault(namespace, Namespace()).funcs + func = func_map.setdefault(g_name, FuncInfo("", g_name, cname, isconstructor, namespace, False)) + func.add_variant(decl) + else: + if classname and not isconstructor: + cname = barename + func_map = self.classes[classname].methods + else: + func_map = self.namespaces.setdefault(namespace, Namespace()).funcs + + func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, isclassmethod)) + func.add_variant(decl) - func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace)) - func.add_variant(decl) + if classname and isconstructor: + self.classes[classname].constructor = func def gen_namespace(self, ns_name): @@ -870,6 +908,8 @@ class PythonWrapperGenerator(object): self.code_ns_reg.write('static PyMethodDef methods_%s[] = {\n'%wname) for name, func in sorted(ns.funcs.items()): + if func.isconstructor: + continue self.code_ns_reg.write(func.get_tab_entry()) self.code_ns_reg.write(' {NULL, NULL}\n};\n\n') @@ -960,12 +1000,15 @@ class PythonWrapperGenerator(object): self.code_types.write(code) if not classinfo.ismap: self.code_type_reg.write("MKTYPE2(%s);\n" % (classinfo.name,) ) + self.code_type_publish.write("PUBLISH_OBJECT(\"{name}\", pyopencv_{name}_Type);\n".format(name=classinfo.name)) # step 3: generate the code for all the global functions for ns_name, ns in sorted(self.namespaces.items()): if ns_name.split('.')[0] != 'cv': continue for name, func in sorted(ns.funcs.items()): + if func.isconstructor: + continue code = func.gen_code(self.classes) self.code_funcs.write(code) self.gen_namespace(ns_name) @@ -983,6 +1026,7 @@ class PythonWrapperGenerator(object): self.save(output_path, "pyopencv_generated_types.h", self.code_types) self.save(output_path, "pyopencv_generated_type_reg.h", self.code_type_reg) self.save(output_path, "pyopencv_generated_ns_reg.h", self.code_ns_reg) + self.save(output_path, "pyopencv_generated_type_publish.h", self.code_type_publish) if __name__ == "__main__": srcfiles = hdr_parser.opencv_hdr_list