Merge pull request #15915 from VadimLevin:dev/norm_fix
Fix implicit conversion from array to scalar in python bindings
* Fix wrong conversion behavior for primitive types
- Introduce ArgTypeInfo namedtuple instead of plain tuple.
If strict conversion parameter for type is set to true, it is
handled like object argument in PyArg_ParseTupleAndKeywords and
converted to concrete type with the appropriate pyopencv_to function
call.
- Remove deadcode and unused variables.
- Fix implicit conversion from numpy array with 1 element to scalar
- Fix narrowing conversion to size_t type.
* Fix wrong conversion behavior for primitive types
- Introduce ArgTypeInfo namedtuple instead of plain tuple.
If strict conversion parameter for type is set to true, it is
handled like object argument in PyArg_ParseTupleAndKeywords and
converted to concrete type with the appropriate pyopencv_to function
call.
- Remove deadcode and unused variables.
- Fix implicit conversion from numpy array with 1 element to scalar
- Fix narrowing conversion to size_t type.·
- Enable tests with wrong conversion behavior
- Restrict passing None as value
- Restrict bool to integer/floating types conversion
* Add PyIntType support for Python 2
* Remove possible narrowing conversion of size_t
* Bindings conversion update
- Remove unused macro
- Add better conversion for types to numpy types descriptors
- Add argument name to fail messages
- NoneType treated as a valid argument. Better handling will be added
as a standalone patch
* Add descriptor specialization for size_t
* Add check for signed to unsigned integer conversion safety
- If signed integer is positive it can be safely converted
to unsigned
- Add check for plain python 2 objects
- Add check for numpy scalars
- Add simple type_traits implementation for better code style
* Resolve type "overflow" false negative in safe casting check
- Move type_traits to separate header
* Add copyright message to type_traits.hpp
* Limit conversion scope for integral numpy types
- Made canBeSafelyCasted specialized only for size_t, so
type_traits header became unused and was removed.
- Added clarification about descriptor pointer
This commit is contained in:
committed by
Alexander Alekhin
parent
4cc458eb10
commit
31289d2f32
+54
-23
@@ -4,12 +4,14 @@ from __future__ import print_function
|
||||
import hdr_parser, sys, re, os
|
||||
from string import Template
|
||||
from pprint import pprint
|
||||
from collections import namedtuple
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
from io import StringIO
|
||||
else:
|
||||
from cStringIO import StringIO
|
||||
|
||||
|
||||
forbidden_arg_types = ["void*"]
|
||||
|
||||
ignored_arg_types = ["RNG*"]
|
||||
@@ -172,18 +174,48 @@ gen_template_prop_init = Template("""
|
||||
gen_template_rw_prop_init = Template("""
|
||||
{(char*)"${member}", (getter)pyopencv_${name}_get_${member}, (setter)pyopencv_${name}_set_${member}, (char*)"${member}", NULL},""")
|
||||
|
||||
class FormatStrings:
|
||||
string = 's'
|
||||
unsigned_char = 'b'
|
||||
short_int = 'h'
|
||||
int = 'i'
|
||||
unsigned_int = 'I'
|
||||
long = 'l'
|
||||
unsigned_long = 'k'
|
||||
long_long = 'L'
|
||||
unsigned_long_long = 'K'
|
||||
size_t = 'n'
|
||||
float = 'f'
|
||||
double = 'd'
|
||||
object = 'O'
|
||||
|
||||
ArgTypeInfo = namedtuple('ArgTypeInfo',
|
||||
['atype', 'format_str', 'default_value',
|
||||
'strict_conversion'])
|
||||
# strict_conversion is False by default
|
||||
ArgTypeInfo.__new__.__defaults__ = (False,)
|
||||
|
||||
simple_argtype_mapping = {
|
||||
"bool": ("bool", "b", "0"),
|
||||
"size_t": ("size_t", "I", "0"),
|
||||
"int": ("int", "i", "0"),
|
||||
"float": ("float", "f", "0.f"),
|
||||
"double": ("double", "d", "0"),
|
||||
"c_string": ("char*", "s", '(char*)""')
|
||||
"bool": ArgTypeInfo("bool", FormatStrings.unsigned_char, "0", True),
|
||||
"size_t": ArgTypeInfo("size_t", FormatStrings.unsigned_long_long, "0", True),
|
||||
"int": ArgTypeInfo("int", FormatStrings.int, "0", True),
|
||||
"float": ArgTypeInfo("float", FormatStrings.float, "0.f", True),
|
||||
"double": ArgTypeInfo("double", FormatStrings.double, "0", True),
|
||||
"c_string": ArgTypeInfo("char*", FormatStrings.string, '(char*)""')
|
||||
}
|
||||
|
||||
|
||||
def normalize_class_name(name):
|
||||
return re.sub(r"^cv\.", "", name).replace(".", "_")
|
||||
|
||||
|
||||
def get_type_format_string(arg_type_info):
|
||||
if arg_type_info.strict_conversion:
|
||||
return FormatStrings.object
|
||||
else:
|
||||
return arg_type_info.format_str
|
||||
|
||||
|
||||
class ClassProp(object):
|
||||
def __init__(self, decl):
|
||||
self.tp = decl[0].replace("*", "_ptr")
|
||||
@@ -576,7 +608,7 @@ class FuncInfo(object):
|
||||
fullname = selfinfo.wname + "." + fullname
|
||||
|
||||
all_code_variants = []
|
||||
declno = -1
|
||||
|
||||
for v in self.variants:
|
||||
code_decl = ""
|
||||
code_ret = ""
|
||||
@@ -584,7 +616,6 @@ class FuncInfo(object):
|
||||
|
||||
code_args = "("
|
||||
all_cargs = []
|
||||
parse_arglist = []
|
||||
|
||||
if v.isphantom and ismethod and not self.is_static:
|
||||
code_args += "_self_"
|
||||
@@ -617,22 +648,22 @@ class FuncInfo(object):
|
||||
if any(tp in codegen.enums.keys() for tp in tp_candidates):
|
||||
defval0 = "static_cast<%s>(%d)" % (a.tp, 0)
|
||||
|
||||
amapping = simple_argtype_mapping.get(tp, (tp, "O", defval0))
|
||||
arg_type_info = simple_argtype_mapping.get(tp, ArgTypeInfo(tp, FormatStrings.object, defval0, True))
|
||||
parse_name = a.name
|
||||
if a.py_inputarg:
|
||||
if amapping[1] == "O":
|
||||
if arg_type_info.strict_conversion:
|
||||
code_decl += " PyObject* pyobj_%s = NULL;\n" % (a.name,)
|
||||
parse_name = "pyobj_" + a.name
|
||||
if a.tp == 'char':
|
||||
code_cvt_list.append("convert_to_char(pyobj_%s, &%s, %s)"% (a.name, a.name, a.crepr()))
|
||||
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()))
|
||||
|
||||
all_cargs.append([amapping, parse_name])
|
||||
all_cargs.append([arg_type_info, parse_name])
|
||||
|
||||
defval = a.defval
|
||||
if not defval:
|
||||
defval = amapping[2]
|
||||
defval = arg_type_info.default_value
|
||||
else:
|
||||
if "UMat" in tp:
|
||||
if "Mat" in defval and "UMat" not in defval:
|
||||
@@ -641,14 +672,14 @@ class FuncInfo(object):
|
||||
if "Mat" in defval and "GpuMat" not in defval:
|
||||
defval = defval.replace("Mat", "cuda::GpuMat")
|
||||
# "tp arg = tp();" is equivalent to "tp arg;" in the case of complex types
|
||||
if defval == tp + "()" and amapping[1] == "O":
|
||||
if defval == tp + "()" and arg_type_info.format_str == FormatStrings.object:
|
||||
defval = ""
|
||||
if a.outputarg and not a.inputarg:
|
||||
defval = ""
|
||||
if defval:
|
||||
code_decl += " %s %s=%s;\n" % (amapping[0], a.name, defval)
|
||||
code_decl += " %s %s=%s;\n" % (arg_type_info.atype, a.name, defval)
|
||||
else:
|
||||
code_decl += " %s %s;\n" % (amapping[0], a.name)
|
||||
code_decl += " %s %s;\n" % (arg_type_info.atype, a.name)
|
||||
|
||||
if not code_args.endswith("("):
|
||||
code_args += ", "
|
||||
@@ -690,12 +721,16 @@ class FuncInfo(object):
|
||||
if v.rettype:
|
||||
tp = v.rettype
|
||||
tp1 = tp.replace("*", "_ptr")
|
||||
amapping = simple_argtype_mapping.get(tp, (tp, "O", "0"))
|
||||
all_cargs.append(amapping)
|
||||
default_info = ArgTypeInfo(tp, FormatStrings.object, "0")
|
||||
arg_type_info = simple_argtype_mapping.get(tp, default_info)
|
||||
all_cargs.append(arg_type_info)
|
||||
|
||||
if v.args and v.py_arglist:
|
||||
# form the format spec for PyArg_ParseTupleAndKeywords
|
||||
fmtspec = "".join([all_cargs[argno][0][1] for aname, argno in v.py_arglist])
|
||||
fmtspec = "".join([
|
||||
get_type_format_string(all_cargs[argno][0])
|
||||
for aname, argno in v.py_arglist
|
||||
])
|
||||
if v.py_noptargs > 0:
|
||||
fmtspec = fmtspec[:-v.py_noptargs] + "|" + fmtspec[-v.py_noptargs:]
|
||||
fmtspec += ":" + fullname
|
||||
@@ -723,10 +758,6 @@ class FuncInfo(object):
|
||||
else:
|
||||
# there 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:
|
||||
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]))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user