gen2.py 33 KB
Newer Older
1
#!/usr/bin/env python
2

3 4
from __future__ import print_function
import hdr_parser, sys, re, os
5 6
from string import Template

7 8 9 10 11
if sys.version_info[0] >= 3:
    from io import StringIO
else:
    from cStringIO import StringIO

Andrey Kamaev's avatar
Andrey Kamaev committed
12 13
ignored_arg_types = ["RNG*"]

14
gen_template_check_self = Template("""    if(!PyObject_TypeCheck(self, &pyopencv_${name}_Type))
15
        return failmsgp("Incorrect type of self (must be '${name}' or its derivative)");
16
    $cname* _self_ = ${amp}((pyopencv_${name}_t*)self)->v${get};
17 18
""")

19 20
gen_template_check_self_algo = Template("""    if(!PyObject_TypeCheck(self, &pyopencv_${name}_Type))
        return failmsgp("Incorrect type of self (must be '${name}' or its derivative)");
21
    $cname* _self_ = dynamic_cast<$cname*>(${amp}((pyopencv_${name}_t*)self)->v.get());
22 23
""")

24
gen_template_call_constructor_prelude = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type);
25
        new (&(self->v)) Ptr<$cname>(); // init Ptr with placement new
26
        if(self) """)
27

28 29 30 31 32 33
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 = Template("""self->v = ${cname}${args}""")
34 35 36 37 38 39 40

gen_template_parse_args = Template("""const char* keywords[] = { $kw_list, NULL };
    if( PyArg_ParseTupleAndKeywords(args, kw, "$fmtspec", (char**)keywords, $parse_arglist)$code_cvt )""")

gen_template_func_body = Template("""$code_decl
    $code_parse
    {
41
        ${code_prelude}ERRWRAP2($code_fcall);
42
        $code_ret;
43 44 45
    }
""")

46 47 48 49 50 51 52
py_major_version = sys.version_info[0]
if py_major_version >= 3:
    head_init_str = "PyVarObject_HEAD_INIT(&PyType_Type, 0)"
else:
    head_init_str = """PyObject_HEAD_INIT(&PyType_Type)
0,"""

53 54 55 56 57 58 59 60 61
gen_template_simple_type_decl = Template("""
struct pyopencv_${name}_t
{
    PyObject_HEAD
    ${cname} v;
};

static PyTypeObject pyopencv_${name}_Type =
{
62
    %s
63 64 65 66 67 68 69 70 71
    MODULESTR".$wname",
    sizeof(pyopencv_${name}_t),
};

static void pyopencv_${name}_dealloc(PyObject* self)
{
    PyObject_Del(self);
}

72
template<> PyObject* pyopencv_from(const ${cname}& r)
73 74 75 76 77 78
{
    pyopencv_${name}_t *m = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type);
    m->v = r;
    return (PyObject*)m;
}

79
template<> bool pyopencv_to(PyObject* src, ${cname}& dst, const char* name)
80
{
81
    if( src == NULL || src == Py_None )
82 83
        return true;
    if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type))
84
    {
85
        failmsg("Expected ${cname} for argument '%%s'", name);
86 87 88 89 90
        return false;
    }
    dst = ((pyopencv_${name}_t*)src)->v;
    return true;
}
91
""" % head_init_str)
92 93


94
gen_template_type_decl = Template("""
95 96 97
struct pyopencv_${name}_t
{
    PyObject_HEAD
98
    Ptr<${cname1}> v;
99 100
};

101 102
static PyTypeObject pyopencv_${name}_Type =
{
103
    %s
104 105 106 107
    MODULESTR".$wname",
    sizeof(pyopencv_${name}_t),
};

108 109
static void pyopencv_${name}_dealloc(PyObject* self)
{
110
    ((pyopencv_${name}_t*)self)->v.release();
111 112
    PyObject_Del(self);
}
113

114
template<> PyObject* pyopencv_from(const Ptr<${cname}>& r)
115 116
{
    pyopencv_${name}_t *m = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type);
117
    new (&(m->v)) Ptr<$cname1>(); // init Ptr with placement new
118 119 120 121
    m->v = r;
    return (PyObject*)m;
}

122
template<> bool pyopencv_to(PyObject* src, Ptr<${cname}>& dst, const char* name)
123 124 125 126 127
{
    if( src == NULL || src == Py_None )
        return true;
    if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type))
    {
128
        failmsg("Expected ${cname} for argument '%%s'", name);
129 130
        return false;
    }
131
    dst = ((pyopencv_${name}_t*)src)->v.dynamicCast<${cname}>();
132 133 134
    return true;
}

135
""" % head_init_str)
136 137

gen_template_map_type_cvt = Template("""
138
template<> bool pyopencv_to(PyObject* src, ${cname}& dst, const char* name);
139
""")
140

141 142 143 144 145 146 147 148 149 150
gen_template_set_prop_from_map = Template("""
    if( PyMapping_HasKeyString(src, (char*)"$propname") )
    {
        tmp = PyMapping_GetItemString(src, (char*)"$propname");
        ok = tmp && pyopencv_to(tmp, dst.$propname);
        Py_DECREF(tmp);
        if(!ok) return false;
    }""")

gen_template_type_impl = Template("""
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
static PyObject* pyopencv_${name}_repr(PyObject* self)
{
    char str[1000];
    sprintf(str, "<$wname %p>", self);
    return PyString_FromString(str);
}

${getset_code}

static PyGetSetDef pyopencv_${name}_getseters[] =
{${getset_inits}
    {NULL}  /* Sentinel */
};

${methods_code}

static PyMethodDef pyopencv_${name}_methods[] =
{
${methods_inits}
    {NULL,          NULL}
};

static void pyopencv_${name}_specials(void)
{
    pyopencv_${name}_Type.tp_base = ${baseptr};
    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_methods = pyopencv_${name}_methods;${extra_specials}
}
""")

183

184 185 186
gen_template_get_prop = Template("""
static PyObject* pyopencv_${name}_get_${member}(pyopencv_${name}_t* p, void *closure)
{
187
    return pyopencv_from(p->v${access}${member});
188 189 190
}
""")

191 192 193
gen_template_get_prop_algo = Template("""
static PyObject* pyopencv_${name}_get_${member}(pyopencv_${name}_t* p, void *closure)
{
194
    return pyopencv_from(dynamic_cast<$cname*>(p->v.get())${access}${member});
195 196 197
}
""")

198 199 200 201 202 203 204 205
gen_template_set_prop = Template("""
static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value, void *closure)
{
    if (value == NULL)
    {
        PyErr_SetString(PyExc_TypeError, "Cannot delete the ${member} attribute");
        return -1;
    }
206
    return pyopencv_to(value, p->v${access}${member}) ? 0 : -1;
207 208 209
}
""")

210 211 212 213 214 215 216 217
gen_template_set_prop_algo = Template("""
static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value, void *closure)
{
    if (value == NULL)
    {
        PyErr_SetString(PyExc_TypeError, "Cannot delete the ${member} attribute");
        return -1;
    }
218
    return pyopencv_to(value, dynamic_cast<$cname*>(p->v.get())${access}${member}) ? 0 : -1;
219 220 221 222
}
""")


223 224 225 226 227 228 229
gen_template_prop_init = Template("""
    {(char*)"${member}", (getter)pyopencv_${name}_get_${member}, NULL, (char*)"${member}", NULL},""")

gen_template_rw_prop_init = Template("""
    {(char*)"${member}", (getter)pyopencv_${name}_get_${member}, (setter)pyopencv_${name}_set_${member}, (char*)"${member}", NULL},""")

simple_argtype_mapping = {
230 231 232 233
    "bool": ("bool", "b", "0"),
    "int": ("int", "i", "0"),
    "float": ("float", "f", "0.f"),
    "double": ("double", "d", "0"),
234
    "c_string": ("char*", "s", '(char*)""')
235 236
}

237 238 239
def normalize_class_name(name):
    return re.sub(r"^cv\.", "", name).replace(".", "_")

240 241
class ClassProp(object):
    def __init__(self, decl):
242
        self.tp = decl[0].replace("*", "_ptr")
243 244 245 246 247 248 249 250
        self.name = decl[1]
        self.readonly = True
        if "/RW" in decl[3]:
            self.readonly = False

class ClassInfo(object):
    def __init__(self, name, decl=None):
        self.cname = name.replace(".", "::")
251
        self.name = self.wname = normalize_class_name(name)
252
        self.ismap = False
253
        self.issimple = False
254
        self.isalgorithm = False
255 256 257 258
        self.methods = {}
        self.props = []
        self.consts = {}
        customname = False
259

260 261 262
        if decl:
            self.bases = decl[1].split()[1:]
            if len(self.bases) > 1:
263 264 265
                print("Note: Class %s has more than 1 base class (not supported by Python C extensions)" % (self.name,))
                print("      Bases: ", " ".join(self.bases))
                print("      Only the first base class will be used")
266
                self.bases = [self.bases[0].strip(",")]
267
                #return sys.exit(-1)
268 269
            if self.bases and self.bases[0].startswith("cv::"):
                self.bases[0] = self.bases[0][4:]
270 271
            if self.bases and self.bases[0] == "Algorithm":
                self.isalgorithm = True
272 273 274 275 276 277
            for m in decl[2]:
                if m.startswith("="):
                    self.wname = m[1:]
                    customname = True
                elif m == "/Map":
                    self.ismap = True
278 279
                elif m == "/Simple":
                    self.issimple = True
280
            self.props = [ClassProp(p) for p in decl[3]]
281

282 283
        if not customname and self.wname.startswith("Cv"):
            self.wname = self.wname[2:]
284

285 286
    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)
287
        code += "".join([gen_template_set_prop_from_map.substitute(propname=p.name,proptype=p.tp) for p in self.props])
288 289 290 291
        if self.bases:
            code += "\n    return pyopencv_to(src, (%s&)dst, name);\n}\n" % all_classes[self.bases[0]].cname
        else:
            code += "\n    return true;\n}\n"
292
        return code
293

294 295
    def gen_code(self, all_classes):
        if self.ismap:
296
            return self.gen_map_code(all_classes)
297

298 299
        getset_code = StringIO()
        getset_inits = StringIO()
300

301 302
        sorted_props = [(p.name, p) for p in self.props]
        sorted_props.sort()
303

304 305 306
        access_op = "->"
        if self.issimple:
            access_op = "."
307

308
        for pname, p in sorted_props:
309 310 311 312
            if self.isalgorithm:
                getset_code.write(gen_template_get_prop_algo.substitute(name=self.name, cname=self.cname, member=pname, membertype=p.tp, access=access_op))
            else:
                getset_code.write(gen_template_get_prop.substitute(name=self.name, member=pname, membertype=p.tp, access=access_op))
313
            if p.readonly:
314
                getset_inits.write(gen_template_prop_init.substitute(name=self.name, member=pname))
315
            else:
316 317 318 319
                if self.isalgorithm:
                    getset_code.write(gen_template_set_prop_algo.substitute(name=self.name, cname=self.cname, member=pname, membertype=p.tp, access=access_op))
                else:
                    getset_code.write(gen_template_set_prop.substitute(name=self.name, member=pname, membertype=p.tp, access=access_op))
320
                getset_inits.write(gen_template_rw_prop_init.substitute(name=self.name, member=pname))
321

322 323
        methods_code = StringIO()
        methods_inits = StringIO()
324

325
        sorted_methods = list(self.methods.items())
326
        sorted_methods.sort()
327

328
        for mname, m in sorted_methods:
329 330
            methods_code.write(m.gen_code(all_classes))
            methods_inits.write(m.get_tab_entry())
331

332
        baseptr = "NULL"
333
        if self.bases and self.bases[0] in all_classes:
334
            baseptr = "&pyopencv_" + all_classes[self.bases[0]].name + "_Type"
335

336 337 338
        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(),
339
            baseptr=baseptr, extra_specials="")
340

341
        return code
342 343


344 345 346 347 348 349 350 351 352
class ConstInfo(object):
    def __init__(self, name, val):
        self.cname = name.replace(".", "::")
        self.name = re.sub(r"^cv\.", "", name).replace(".", "_")
        if self.name.startswith("Cv"):
            self.name = self.name[2:]
        self.name = re.sub(r"([a-z])([A-Z])", r"\1_\2", self.name)
        self.name = self.name.upper()
        self.value = val
353

354 355 356 357 358 359 360 361 362 363
class ArgInfo(object):
    def __init__(self, arg_tuple):
        self.tp = arg_tuple[0]
        self.name = arg_tuple[1]
        self.defval = arg_tuple[2]
        self.isarray = False
        self.arraylen = 0
        self.arraycvt = None
        self.inputarg = True
        self.outputarg = False
364
        self.returnarg = False
365 366 367 368
        for m in arg_tuple[3]:
            if m == "/O":
                self.inputarg = False
                self.outputarg = True
369
                self.returnarg = True
370 371 372
            elif m == "/IO":
                self.inputarg = True
                self.outputarg = True
373
                self.returnarg = True
374 375 376 377 378 379 380 381
            elif m.startswith("/A"):
                self.isarray = True
                self.arraylen = m[2:].strip()
            elif m.startswith("/CA"):
                self.isarray = True
                self.arraycvt = m[2:].strip()
        self.py_inputarg = False
        self.py_outputarg = False
382

383
    def isbig(self):
384
        return self.tp == "Mat" or self.tp == "vector_Mat"# or self.tp.startswith("vector")
385

386 387 388
    def crepr(self):
        return "ArgInfo(\"%s\", %d)" % (self.name, self.outputarg)

389 390

class FuncVariant(object):
391 392 393
    def __init__(self, classname, name, decl, isconstructor):
        self.classname = classname
        self.name = self.wname = name
394
        self.isconstructor = isconstructor
395 396 397 398 399
        if self.isconstructor:
            if self.wname.startswith("Cv"):
                self.wname = self.wname[2:]
            else:
                self.wname = self.classname
400

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
        self.rettype = decl[1]
        if self.rettype == "void":
            self.rettype = ""
        self.args = []
        self.array_counters = {}
        for a in decl[3]:
            ainfo = ArgInfo(a)
            if ainfo.isarray and not ainfo.arraycvt:
                c = ainfo.arraylen
                c_arrlist = self.array_counters.get(c, [])
                if c_arrlist:
                    c_arrlist.append(ainfo.name)
                else:
                    self.array_counters[c] = [ainfo.name]
            self.args.append(ainfo)
        self.init_pyproto()
417

418 419 420 421
    def init_pyproto(self):
        # string representation of argument list, with '[', ']' symbols denoting optional arguments, e.g.
        # "src1, src2[, dst[, mask]]" for cv.add
        argstr = ""
422

423 424 425 426 427 428
        # 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
429
        # non-optional input parameters)
430
        arglist = []
431

432 433 434
        # 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 = []
435

436 437
        # the list of output parameters. Also includes input/output parameters.
        outlist = []
438

439
        firstoptarg = 1000000
440 441 442 443 444
        argno = -1
        for a in self.args:
            argno += 1
            if a.name in self.array_counters:
                continue
Andrey Kamaev's avatar
Andrey Kamaev committed
445 446
            if a.tp in ignored_arg_types:
                continue
447
            if a.returnarg:
448
                outlist.append((a.name, argno))
449
            if (not a.inputarg) and a.isbig():
450
                outarr_list.append((a.name, argno))
451
                continue
452
            if not a.inputarg:
453 454 455 456
                continue
            if not a.defval:
                arglist.append((a.name, argno))
            else:
457
                firstoptarg = min(firstoptarg, len(arglist))
458 459 460 461 462 463
                # if there are some array output parameters before the first default parameter, they
                # are added as optional parameters before the first optional parameter
                if outarr_list:
                    arglist += outarr_list
                    outarr_list = []
                arglist.append((a.name, argno))
464

465
        if outarr_list:
466
            firstoptarg = min(firstoptarg, len(arglist))
467
            arglist += outarr_list
468
        firstoptarg = min(firstoptarg, len(arglist))
469

470 471 472 473 474 475 476 477 478 479
        noptargs = len(arglist) - firstoptarg
        argnamelist = [aname for aname, argno in arglist]
        argstr = ", ".join(argnamelist[:firstoptarg])
        argstr = "[, ".join([argstr] + argnamelist[firstoptarg:])
        argstr += "]" * noptargs
        if self.rettype:
            outlist = [("retval", -1)] + outlist
        elif self.isconstructor:
            assert outlist == []
            outlist = [("self", -1)]
480 481 482 483 484 485
        if self.isconstructor:
            classname = self.classname
            if classname.startswith("Cv"):
                classname=classname[2:]
            outstr = "<%s object>" % (classname,)
        elif outlist:
486 487 488
            outstr = ", ".join([o[0] for o in outlist])
        else:
            outstr = "None"
489

490
        self.py_docstring = "%s(%s) -> %s" % (self.wname, argstr, outstr)
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
        self.py_noptargs = noptargs
        self.py_arglist = arglist
        for aname, argno in arglist:
            self.args[argno].py_inputarg = True
        for aname, argno in outlist:
            if argno >= 0:
                self.args[argno].py_outputarg = True
        self.py_outlist = outlist


class FuncInfo(object):
    def __init__(self, classname, name, cname, isconstructor):
        self.classname = classname
        self.name = name
        self.cname = cname
        self.isconstructor = isconstructor
        self.variants = []
508

509
    def add_variant(self, decl):
510
        self.variants.append(FuncVariant(self.classname, self.name, decl, self.isconstructor))
511

512
    def get_wrapper_name(self):
513
        name = self.name
514
        if self.classname:
515 516 517
            classname = self.classname + "_"
            if "[" in name:
                name = "getelem"
518
        else:
519 520
            classname = ""
        return "pyopencv_" + classname + name
521

522 523 524 525 526 527 528
    def get_wrapper_prototype(self):
        full_fname = self.get_wrapper_name()
        if self.classname and not self.isconstructor:
            self_arg = "self"
        else:
            self_arg = ""
        return "static PyObject* %s(PyObject* %s, PyObject* args, PyObject* kw)" % (full_fname, self_arg)
529

530 531 532 533 534 535 536 537 538 539 540 541 542 543
    def get_tab_entry(self):
        docstring_list = []
        have_empty_constructor = False
        for v in self.variants:
            s = v.py_docstring
            if (not v.py_arglist) and self.isconstructor:
                have_empty_constructor = True
            if s not in docstring_list:
                docstring_list.append(s)
        # if there are just 2 constructors: default one and some other,
        # we simplify the notation.
        # Instead of ClassName(args ...) -> object or ClassName() -> object
        # we write ClassName([args ...]) -> object
        if have_empty_constructor and len(self.variants) == 2:
544
            idx = self.variants[1].py_arglist != []
545 546 547
            s = self.variants[idx].py_docstring
            p1 = s.find("(")
            p2 = s.rfind(")")
548
            docstring_list = [s[:p1+1] + "[" + s[p1+1:p2] + "]" + s[p2:]]
549

550
        return Template('    {"$py_funcname", (PyCFunction)$wrap_funcname, METH_VARARGS | METH_KEYWORDS, "$py_docstring"},\n'
551
                        ).substitute(py_funcname = self.variants[0].wname, wrap_funcname=self.get_wrapper_name(),
552
                                     py_docstring = "  or  ".join(docstring_list))
553

554 555 556 557 558 559 560 561 562 563 564 565
    def gen_code(self, all_classes):
        proto = self.get_wrapper_prototype()
        code = "%s\n{\n" % (proto,)

        selfinfo = ClassInfo("")
        ismethod = self.classname != "" and not self.isconstructor
        # full name is needed for error diagnostic in PyArg_ParseTupleAndKeywords
        fullname = self.name

        if self.classname:
            selfinfo = all_classes[self.classname]
            if not self.isconstructor:
566
                amp = "&" if selfinfo.issimple else ""
567 568 569
                if selfinfo.isalgorithm:
                    code += gen_template_check_self_algo.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp)
                else:
570 571
                    get = "" if selfinfo.issimple else ".get()"
                    code += gen_template_check_self.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp, get=get)
572 573 574 575 576 577 578 579 580
                fullname = selfinfo.wname + "." + fullname

        all_code_variants = []
        declno = -1
        for v in self.variants:
            code_decl = ""
            code_ret = ""
            code_cvt_list = []

581
            code_args = "("
582 583 584 585 586 587 588 589
            all_cargs = []
            parse_arglist = []

            # declare all the C function arguments,
            # add necessary conversions from Python objects to code_cvt_list,
            # form the function/method call,
            # for the list of type mappings
            for a in v.args:
Andrey Kamaev's avatar
Andrey Kamaev committed
590 591 592 593 594
                if a.tp in ignored_arg_types:
                    defval = a.defval
                    if not defval and a.tp.endswith("*"):
                        defval = 0
                    assert defval
595 596 597
                    if not code_args.endswith("("):
                        code_args += ", "
                    code_args += defval
Andrey Kamaev's avatar
Andrey Kamaev committed
598 599
                    all_cargs.append([[None, ""], ""])
                    continue
600 601 602 603 604 605 606 607 608 609
                tp1 = tp = a.tp
                amp = ""
                defval0 = ""
                if tp.endswith("*"):
                    tp = tp1 = tp[:-1]
                    amp = "&"
                    if tp.endswith("*"):
                        defval0 = "0"
                        tp1 = tp.replace("*", "_ptr")
                if tp1.endswith("*"):
610
                    print("Error: type with star: a.tp=%s, tp=%s, tp1=%s" % (a.tp, tp, tp1))
611 612
                    sys.exit(-1)

613
                amapping = simple_argtype_mapping.get(tp, (tp, "O", defval0))
614
                parse_name = a.name
615 616 617
                if a.py_inputarg:
                    if amapping[1] == "O":
                        code_decl += "    PyObject* pyobj_%s = NULL;\n" % (a.name,)
618
                        parse_name = "pyobj_" + a.name
619 620 621 622
                        if a.tp == 'char':
                            code_cvt_list.append("convert_to_char(pyobj_%s, &%s, %s)"% (a.name, a.name, a.crepr()))
                        else:
                            code_cvt_list.append("pyopencv_to(pyobj_%s, %s, %s)" % (a.name, a.name, a.crepr()))
623

624
                all_cargs.append([amapping, parse_name])
625

626 627
                defval = a.defval
                if not defval:
628
                    defval = amapping[2]
629 630 631
                # "tp arg = tp();" is equivalent to "tp arg;" in the case of complex types
                if defval == tp + "()" and amapping[1] == "O":
                    defval = ""
632 633
                if a.outputarg and not a.inputarg:
                    defval = ""
634 635 636 637 638
                if defval:
                    code_decl += "    %s %s=%s;\n" % (amapping[0], a.name, defval)
                else:
                    code_decl += "    %s %s;\n" % (amapping[0], a.name)

639 640 641 642 643 644 645 646 647 648 649 650 651 652
                if not code_args.endswith("("):
                    code_args += ", "
                code_args += amp + a.name

            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
                else:
                    templ_prelude = gen_template_call_constructor_prelude
                    templ = gen_template_call_constructor
653

654 655 656 657 658 659 660 661 662 663 664 665 666
                code_prelude = templ_prelude.substitute(name=selfinfo.name, cname=selfinfo.cname)
                code_fcall = templ.substitute(name=selfinfo.name, cname=selfinfo.cname, args=code_args)
            else:
                code_prelude = ""
                code_fcall = ""
                if v.rettype:
                    code_decl += "    " + v.rettype + " retval;\n"
                    code_fcall += "retval = "
                if ismethod:
                    code_fcall += "_self_->" + self.cname
                else:
                    code_fcall += self.cname
                code_fcall += code_args
667 668 669 670 671 672 673 674 675 676 677

            if code_cvt_list:
                code_cvt_list = [""] + code_cvt_list

            # add info about return value, if any, to all_cargs. if there non-void return value,
            # it is encoded in v.py_outlist as ("retval", -1) pair.
            # As [-1] in Python accesses the last element of a list, we automatically handle the return value by
            # adding the necessary info to the end of all_cargs list.
            if v.rettype:
                tp = v.rettype
                tp1 = tp.replace("*", "_ptr")
678
                amapping = simple_argtype_mapping.get(tp, (tp, "O", "0"))
679 680
                all_cargs.append(amapping)

681
            if v.args and v.py_arglist:
682
                # form the format spec for PyArg_ParseTupleAndKeywords
683
                fmtspec = "".join([all_cargs[argno][0][1] for aname, argno in v.py_arglist])
684 685 686 687 688 689 690 691 692 693 694
                if v.py_noptargs > 0:
                    fmtspec = fmtspec[:-v.py_noptargs] + "|" + fmtspec[-v.py_noptargs:]
                fmtspec += ":" + fullname

                # form the argument parse code that:
                #   - declares the list of keyword parameters
                #   - calls PyArg_ParseTupleAndKeywords
                #   - converts complex arguments from PyObject's to native OpenCV types
                code_parse = gen_template_parse_args.substitute(
                    kw_list = ", ".join(['"' + aname + '"' for aname, argno in v.py_arglist]),
                    fmtspec = fmtspec,
695
                    parse_arglist = ", ".join(["&" + all_cargs[argno][1] for aname, argno in v.py_arglist]),
696 697
                    code_cvt = " &&\n        ".join(code_cvt_list))
            else:
698
                code_parse = "if(PyObject_Size(args) == 0 && (kw == NULL || PyObject_Size(kw) == 0))"
699 700

            if len(v.py_outlist) == 0:
701
                code_ret = "Py_RETURN_NONE"
702 703
            elif len(v.py_outlist) == 1:
                if self.isconstructor:
704
                    code_ret = "return (PyObject*)self"
705 706
                else:
                    aname, argno = v.py_outlist[0]
707
                    code_ret = "return pyopencv_from(%s)" % (aname,)
708 709 710 711 712
            else:
                # ther is more than 1 return parameter; form the tuple out of them
                fmtspec = "N"*len(v.py_outlist)
                backcvt_arg_list = []
                for aname, argno in v.py_outlist:
713
                    amapping = all_cargs[argno][0]
714
                    backcvt_arg_list.append("%s(%s)" % (amapping[2], aname))
715
                code_ret = "return Py_BuildValue(\"(%s)\", %s)" % \
716
                    (fmtspec, ", ".join(["pyopencv_from(" + aname + ")" for aname, argno in v.py_outlist]))
717 718

            all_code_variants.append(gen_template_func_body.substitute(code_decl=code_decl,
719
                code_parse=code_parse, code_prelude=code_prelude, code_fcall=code_fcall, code_ret=code_ret))
720 721 722 723 724 725 726 727

        if len(all_code_variants)==1:
            # if the function/method has only 1 signature, then just put it
            code += all_code_variants[0]
        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"
728 729 730
        return code


731 732 733
class PythonWrapperGenerator(object):
    def __init__(self):
        self.clear()
734

735 736 737 738
    def clear(self):
        self.classes = {}
        self.funcs = {}
        self.consts = {}
739 740 741 742 743
        self.code_types = StringIO()
        self.code_funcs = StringIO()
        self.code_func_tab = StringIO()
        self.code_type_reg = StringIO()
        self.code_const_reg = StringIO()
744
        self.class_idx = 0
745 746 747

    def add_class(self, stype, name, decl):
        classinfo = ClassInfo(name, decl)
748 749
        classinfo.decl_idx = self.class_idx
        self.class_idx += 1
750

751 752 753
        if classinfo.name in self.classes:
            print("Generator error: class %s (cname=%s) already exists" \
                % (classinfo.name, classinfo.cname))
754
            sys.exit(-1)
755
        self.classes[classinfo.name] = classinfo
756 757
        if classinfo.bases and not classinfo.isalgorithm:
            classinfo.isalgorithm = self.classes[classinfo.bases[0]].isalgorithm
758

759 760
    def add_const(self, name, decl):
        constinfo = ConstInfo(name, decl[1])
761

762 763 764
        if constinfo.name in self.consts:
            print("Generator error: constant %s (cname=%s) already exists" \
                % (constinfo.name, constinfo.cname))
765
            sys.exit(-1)
766 767 768
        self.consts[constinfo.name] = constinfo

    def add_func(self, decl):
769
        classname = bareclassname = ""
770 771 772
        name = decl[0]
        dpos = name.rfind(".")
        if dpos >= 0 and name[:dpos] != "cv":
773
            classname = bareclassname = re.sub(r"^cv\.", "", name[:dpos])
774
            name = name[dpos+1:]
775 776 777 778
            dpos = classname.rfind(".")
            if dpos >= 0:
                bareclassname = classname[dpos+1:]
                classname = classname.replace(".", "_")
779 780
        cname = name
        name = re.sub(r"^cv\.", "", name)
781
        isconstructor = cname == bareclassname
782 783 784 785 786 787 788 789 790 791
        cname = cname.replace(".", "::")
        isclassmethod = False
        customname = False
        for m in decl[2]:
            if m == "/S":
                isclassmethod = True
            elif m.startswith("="):
                name = m[1:]
                customname = True
        func_map = self.funcs
792

793 794 795 796 797 798 799 800 801 802
        if not classname or isconstructor:
            pass
        elif isclassmethod:
            if not customname:
                name = classname + "_" + name
            cname = classname + "::" + cname
            classname = ""
        else:
            classinfo = self.classes.get(classname, ClassInfo(""))
            if not classinfo.name:
803
                print("Generator error: the class for method %s is missing" % (name,))
804 805
                sys.exit(-1)
            func_map = classinfo.methods
806

807 808 809 810
        func = func_map.get(name, FuncInfo(classname, name, cname, isconstructor))
        func.add_variant(decl)
        if len(func.variants) == 1:
            func_map[name] = func
811

812 813
    def gen_const_reg(self, constinfo):
        self.code_const_reg.write("PUBLISH2(%s,%s);\n" % (constinfo.name, constinfo.cname))
814

815 816 817 818
    def save(self, path, name, buf):
        f = open(path + "/" + name, "wt")
        f.write(buf.getvalue())
        f.close()
819

820
    def gen(self, srcfiles, output_path):
821
        self.clear()
822
        parser = hdr_parser.CppHeaderParser()
823

824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840
        # step 1: scan the headers and build more descriptive maps of classes, consts, functions
        for hdr in srcfiles:
            decls = parser.parse(hdr)
            for decl in decls:
                name = decl[0]
                if name.startswith("struct") or name.startswith("class"):
                    # class/struct
                    p = name.find(" ")
                    stype = name[:p]
                    name = name[p+1:].strip()
                    self.add_class(stype, name, decl)
                elif name.startswith("const"):
                    # constant
                    self.add_const(name.replace("const ", "").strip(), decl)
                else:
                    # function
                    self.add_func(decl)
841

842
        # step 2: generate code for the classes and their methods
843
        classlist = list(self.classes.items())
844
        classlist.sort()
845 846 847 848 849 850 851 852
        for name, classinfo in classlist:
            if classinfo.ismap:
                self.code_types.write(gen_template_map_type_cvt.substitute(name=name, cname=classinfo.cname))
            else:
                if classinfo.issimple:
                    templ = gen_template_simple_type_decl
                else:
                    templ = gen_template_type_decl
853 854
                self.code_types.write(templ.substitute(name=name, wname=classinfo.wname, cname=classinfo.cname,
                                      cname1=("cv::Algorithm" if classinfo.isalgorithm else classinfo.cname)))
855

856 857 858 859
        # 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()
860

861
        for decl_idx, name, classinfo in classlist1:
862 863 864 865
            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,) )
866

867
        # step 3: generate the code for all the global functions
868
        funclist = list(self.funcs.items())
869 870 871 872
        funclist.sort()
        for name, func in funclist:
            code = func.gen_code(self.classes)
            self.code_funcs.write(code)
873
            self.code_func_tab.write(func.get_tab_entry())
874

875
        # step 4: generate the code for constants
876
        constlist = list(self.consts.items())
877 878 879
        constlist.sort()
        for name, constinfo in constlist:
            self.gen_const_reg(constinfo)
880

881 882
        # That's it. Now save all the files
        self.save(output_path, "pyopencv_generated_funcs.h", self.code_funcs)
883
        self.save(output_path, "pyopencv_generated_func_tab.h", self.code_func_tab)
884 885 886 887 888
        self.save(output_path, "pyopencv_generated_const_reg.h", self.code_const_reg)
        self.save(output_path, "pyopencv_generated_types.h", self.code_types)
        self.save(output_path, "pyopencv_generated_type_reg.h", self.code_type_reg)

if __name__ == "__main__":
889 890
    srcfiles = hdr_parser.opencv_hdr_list
    dstdir = "/Users/vp/tmp"
891
    if len(sys.argv) > 1:
892
        dstdir = sys.argv[1]
893
    if len(sys.argv) > 2:
894 895 896
        srcfiles = sys.argv[2:]
    generator = PythonWrapperGenerator()
    generator.gen(srcfiles, dstdir)