Skip to content

Commit 1caca21

Browse files
lewisjbalalek
authored andcommitted
Merge pull request opencv#8934 from lewisjb:python-classes
* Refactor Python Classes
1 parent 42fbbfe commit 1caca21

File tree

2 files changed

+80
-30
lines changed

2 files changed

+80
-30
lines changed

modules/python/src2/cv2.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,14 +1609,20 @@ void initcv2()
16091609
return;
16101610
#endif
16111611

1612+
16121613
#if PY_MAJOR_VERSION >= 3
1613-
Py_INCREF(&cv2_UMatWrapperType);
1614+
#define PUBLISH_OBJECT(name, type) Py_INCREF(&type);\
1615+
PyModule_AddObject(m, name, (PyObject *)&type);
16141616
#else
1615-
// Unrolled Py_INCREF(&cv2_UMatWrapperType) without (PyObject*) cast
1616-
// due to "warning: dereferencing type-punned pointer will break strict-aliasing rules"
1617-
_Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA (&cv2_UMatWrapperType)->ob_refcnt++;
1617+
// Unrolled Py_INCREF(&type) without (PyObject*) cast
1618+
// due to "warning: dereferencing type-punned pointer will break strict-aliasing rules"
1619+
#define PUBLISH_OBJECT(name, type) _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA (&type)->ob_refcnt++;\
1620+
PyModule_AddObject(m, name, (PyObject *)&type);
16181621
#endif
1619-
PyModule_AddObject(m, "UMat", (PyObject *)&cv2_UMatWrapperType);
1622+
1623+
PUBLISH_OBJECT("UMat", cv2_UMatWrapperType);
1624+
1625+
#include "pyopencv_generated_type_publish.h"
16201626

16211627
#define PUBLISH(I) PyDict_SetItemString(d, #I, PyInt_FromLong(I))
16221628
//#define PUBLISHU(I) PyDict_SetItemString(d, #I, PyLong_FromUnsignedLong(I))

modules/python/src2/gen2.py

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,12 @@
2525
return failmsgp("Incorrect type of self (must be '${name}' or its derivative)");
2626
""")
2727

28-
gen_template_call_constructor_prelude = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type);
29-
new (&(self->v)) Ptr<$cname>(); // init Ptr with placement new
28+
gen_template_call_constructor_prelude = Template("""new (&(self->v)) Ptr<$cname>(); // init Ptr with placement new
3029
if(self) """)
3130

3231
gen_template_call_constructor = Template("""self->v.reset(new ${cname}${args})""")
3332

34-
gen_template_simple_call_constructor_prelude = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type);
35-
if(self) """)
33+
gen_template_simple_call_constructor_prelude = Template("""if(self) """)
3634

3735
gen_template_simple_call_constructor = Template("""new (&(self->v)) ${cname}${args}""")
3836

@@ -189,6 +187,7 @@
189187
pyopencv_${name}_Type.tp_dealloc = pyopencv_${name}_dealloc;
190188
pyopencv_${name}_Type.tp_repr = pyopencv_${name}_repr;
191189
pyopencv_${name}_Type.tp_getset = pyopencv_${name}_getseters;
190+
pyopencv_${name}_Type.tp_init = (initproc)${constructor};
192191
pyopencv_${name}_Type.tp_methods = pyopencv_${name}_methods;${extra_specials}
193192
}
194193
""")
@@ -280,6 +279,7 @@ def __init__(self, name, decl=None):
280279
self.props = []
281280
self.consts = {}
282281
self.base = None
282+
self.constructor = None
283283
customname = False
284284

285285
if decl:
@@ -353,6 +353,9 @@ def gen_code(self, all_classes):
353353
sorted_methods = list(self.methods.items())
354354
sorted_methods.sort()
355355

356+
if self.constructor is not None:
357+
methods_code.write(self.constructor.gen_code(all_classes))
358+
356359
for mname, m in sorted_methods:
357360
methods_code.write(m.gen_code(all_classes))
358361
methods_inits.write(m.get_tab_entry())
@@ -361,10 +364,14 @@ def gen_code(self, all_classes):
361364
if self.base and self.base in all_classes:
362365
baseptr = "&pyopencv_" + all_classes[self.base].name + "_Type"
363366

367+
constructor_name = "0"
368+
if self.constructor is not None:
369+
constructor_name = self.constructor.get_wrapper_name()
370+
364371
code = gen_template_type_impl.substitute(name=self.name, wname=self.wname, cname=self.cname,
365372
getset_code=getset_code.getvalue(), getset_inits=getset_inits.getvalue(),
366373
methods_code=methods_code.getvalue(), methods_inits=methods_inits.getvalue(),
367-
baseptr=baseptr, extra_specials="")
374+
baseptr=baseptr, constructor=constructor_name, extra_specials="")
368375

369376
return code
370377

@@ -521,12 +528,13 @@ def init_pyproto(self):
521528

522529

523530
class FuncInfo(object):
524-
def __init__(self, classname, name, cname, isconstructor, namespace):
531+
def __init__(self, classname, name, cname, isconstructor, namespace, isclassmethod):
525532
self.classname = classname
526533
self.name = name
527534
self.cname = cname
528535
self.isconstructor = isconstructor
529536
self.namespace = namespace
537+
self.isclassmethod = isclassmethod
530538
self.variants = []
531539

532540
def add_variant(self, decl):
@@ -540,11 +548,19 @@ def get_wrapper_name(self):
540548
name = "getelem"
541549
else:
542550
classname = ""
551+
552+
if self.isclassmethod:
553+
name += "_cls"
554+
543555
return "pyopencv_" + self.namespace.replace('.','_') + '_' + classname + name
544556

545-
def get_wrapper_prototype(self):
557+
def get_wrapper_prototype(self, all_classes):
546558
full_fname = self.get_wrapper_name()
547-
if self.classname and not self.isconstructor:
559+
if self.isconstructor:
560+
return "static int {fn_name}(pyopencv_{type_name}_t* self, PyObject* args, PyObject* kw)".format(
561+
fn_name=full_fname, type_name=all_classes[self.classname].name)
562+
563+
if self.classname:
548564
self_arg = "self"
549565
else:
550566
self_arg = ""
@@ -591,12 +607,16 @@ def get_tab_entry(self):
591607
# Convert unicode chars to xml representation, but keep as string instead of bytes
592608
full_docstring = full_docstring.encode('ascii', errors='xmlcharrefreplace').decode()
593609

594-
return Template(' {"$py_funcname", (PyCFunction)$wrap_funcname, METH_VARARGS | METH_KEYWORDS, "$py_docstring"},\n'
610+
flags = ["METH_VARARGS", "METH_KEYWORDS"]
611+
if self.isclassmethod:
612+
flags.append("METH_CLASS")
613+
614+
return Template(' {"$py_funcname", (PyCFunction)$wrap_funcname, $flags, "$py_docstring"},\n'
595615
).substitute(py_funcname = self.variants[0].wname, wrap_funcname=self.get_wrapper_name(),
596-
py_docstring = full_docstring)
616+
flags = " | ".join(flags), py_docstring = full_docstring)
597617

598618
def gen_code(self, all_classes):
599-
proto = self.get_wrapper_prototype()
619+
proto = self.get_wrapper_prototype(all_classes)
600620
code = "%s\n{\n" % (proto,)
601621
code += " using namespace %s;\n\n" % self.namespace.replace('.', '::')
602622

@@ -609,7 +629,9 @@ def gen_code(self, all_classes):
609629
selfinfo = all_classes[self.classname]
610630
if not self.isconstructor:
611631
amp = "&" if selfinfo.issimple else ""
612-
if selfinfo.isalgorithm:
632+
if self.isclassmethod:
633+
pass
634+
elif selfinfo.isalgorithm:
613635
code += gen_template_check_self_algo.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp)
614636
else:
615637
get = "" if selfinfo.issimple else ".get()"
@@ -692,7 +714,6 @@ def gen_code(self, all_classes):
692714
code_args += ")"
693715

694716
if self.isconstructor:
695-
code_decl += " pyopencv_%s_t* self = 0;\n" % selfinfo.name
696717
if selfinfo.issimple:
697718
templ_prelude = gen_template_simple_call_constructor_prelude
698719
templ = gen_template_simple_call_constructor
@@ -708,7 +729,7 @@ def gen_code(self, all_classes):
708729
if v.rettype:
709730
code_decl += " " + v.rettype + " retval;\n"
710731
code_fcall += "retval = "
711-
if ismethod:
732+
if ismethod and not self.isclassmethod:
712733
code_fcall += "_self_->" + self.cname
713734
else:
714735
code_fcall += self.cname
@@ -750,7 +771,7 @@ def gen_code(self, all_classes):
750771
code_ret = "Py_RETURN_NONE"
751772
elif len(v.py_outlist) == 1:
752773
if self.isconstructor:
753-
code_ret = "return (PyObject*)self"
774+
code_ret = "return 0"
754775
else:
755776
aname, argno = v.py_outlist[0]
756777
code_ret = "return pyopencv_from(%s)" % (aname,)
@@ -773,7 +794,11 @@ def gen_code(self, all_classes):
773794
else:
774795
# try to execute each signature
775796
code += " PyErr_Clear();\n\n".join([" {\n" + v + " }\n" for v in all_code_variants])
776-
code += "\n return NULL;\n}\n\n"
797+
798+
def_ret = "NULL"
799+
if self.isconstructor:
800+
def_ret = "-1"
801+
code += "\n return %s;\n}\n\n" % def_ret
777802
return code
778803

779804

@@ -796,6 +821,7 @@ def clear(self):
796821
self.code_funcs = StringIO()
797822
self.code_type_reg = StringIO()
798823
self.code_ns_reg = StringIO()
824+
self.code_type_publish = StringIO()
799825
self.class_idx = 0
800826

801827
def add_class(self, stype, name, decl):
@@ -848,20 +874,32 @@ def add_func(self, decl):
848874
isclassmethod = True
849875
elif m.startswith("="):
850876
name = m[1:]
851-
if isclassmethod:
852-
name = "_".join(classes+[name])
853-
classname = ''
854-
elif isconstructor:
877+
if isconstructor:
855878
name = "_".join(classes[:-1]+[name])
856879

857-
if classname and not isconstructor:
858-
cname = barename
880+
if isclassmethod:
881+
# Add it as a method to the class
859882
func_map = self.classes[classname].methods
860-
else:
883+
func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, isclassmethod))
884+
func.add_variant(decl)
885+
886+
# Add it as global function
887+
g_name = "_".join(classes+[name])
861888
func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
889+
func = func_map.setdefault(g_name, FuncInfo("", g_name, cname, isconstructor, namespace, False))
890+
func.add_variant(decl)
891+
else:
892+
if classname and not isconstructor:
893+
cname = barename
894+
func_map = self.classes[classname].methods
895+
else:
896+
func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
897+
898+
func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, isclassmethod))
899+
func.add_variant(decl)
862900

863-
func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace))
864-
func.add_variant(decl)
901+
if classname and isconstructor:
902+
self.classes[classname].constructor = func
865903

866904

867905
def gen_namespace(self, ns_name):
@@ -870,6 +908,8 @@ def gen_namespace(self, ns_name):
870908

871909
self.code_ns_reg.write('static PyMethodDef methods_%s[] = {\n'%wname)
872910
for name, func in sorted(ns.funcs.items()):
911+
if func.isconstructor:
912+
continue
873913
self.code_ns_reg.write(func.get_tab_entry())
874914
self.code_ns_reg.write(' {NULL, NULL}\n};\n\n')
875915

@@ -960,12 +1000,15 @@ def gen(self, srcfiles, output_path):
9601000
self.code_types.write(code)
9611001
if not classinfo.ismap:
9621002
self.code_type_reg.write("MKTYPE2(%s);\n" % (classinfo.name,) )
1003+
self.code_type_publish.write("PUBLISH_OBJECT(\"{name}\", pyopencv_{name}_Type);\n".format(name=classinfo.name))
9631004

9641005
# step 3: generate the code for all the global functions
9651006
for ns_name, ns in sorted(self.namespaces.items()):
9661007
if ns_name.split('.')[0] != 'cv':
9671008
continue
9681009
for name, func in sorted(ns.funcs.items()):
1010+
if func.isconstructor:
1011+
continue
9691012
code = func.gen_code(self.classes)
9701013
self.code_funcs.write(code)
9711014
self.gen_namespace(ns_name)
@@ -983,6 +1026,7 @@ def gen(self, srcfiles, output_path):
9831026
self.save(output_path, "pyopencv_generated_types.h", self.code_types)
9841027
self.save(output_path, "pyopencv_generated_type_reg.h", self.code_type_reg)
9851028
self.save(output_path, "pyopencv_generated_ns_reg.h", self.code_ns_reg)
1029+
self.save(output_path, "pyopencv_generated_type_publish.h", self.code_type_publish)
9861030

9871031
if __name__ == "__main__":
9881032
srcfiles = hdr_parser.opencv_hdr_list

0 commit comments

Comments
 (0)