mirror of
https://github.com/opencv/opencv.git
synced 2026-07-29 07:13:02 +04:00
Merge branch 4.x
This commit is contained in:
+101
-32
@@ -1,16 +1,34 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from __future__ import print_function
|
||||
import hdr_parser, sys, re, os
|
||||
import hdr_parser, sys, re
|
||||
from string import Template
|
||||
from pprint import pprint
|
||||
from collections import namedtuple
|
||||
from itertools import chain
|
||||
|
||||
from typing_stubs_generator import TypingStubsGenerator
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
from io import StringIO
|
||||
else:
|
||||
from cStringIO import StringIO
|
||||
|
||||
if sys.version_info >= (3, 6):
|
||||
from typing_stubs_generation import SymbolName
|
||||
else:
|
||||
SymbolName = namedtuple('SymbolName', ('namespaces', 'classes', 'name'))
|
||||
|
||||
def parse_symbol_name(cls, full_symbol_name, known_namespaces):
|
||||
chunks = full_symbol_name.split('.')
|
||||
namespaces, name = chunks[:-1], chunks[-1]
|
||||
classes = []
|
||||
while len(namespaces) > 0 and '.'.join(namespaces) not in known_namespaces:
|
||||
classes.insert(0, namespaces.pop())
|
||||
return cls(tuple(namespaces), tuple(classes), name)
|
||||
|
||||
setattr(SymbolName, "parse", classmethod(parse_symbol_name))
|
||||
|
||||
|
||||
forbidden_arg_types = ["void*"]
|
||||
|
||||
@@ -182,6 +200,7 @@ ${variant}
|
||||
}
|
||||
""")
|
||||
|
||||
|
||||
class FormatStrings:
|
||||
string = 's'
|
||||
unsigned_char = 'b'
|
||||
@@ -197,9 +216,9 @@ class FormatStrings:
|
||||
double = 'd'
|
||||
object = 'O'
|
||||
|
||||
|
||||
ArgTypeInfo = namedtuple('ArgTypeInfo',
|
||||
['atype', 'format_str', 'default_value',
|
||||
'strict_conversion'])
|
||||
['atype', 'format_str', 'default_value', 'strict_conversion'])
|
||||
# strict_conversion is False by default
|
||||
ArgTypeInfo.__new__.__defaults__ = (False,)
|
||||
|
||||
@@ -256,12 +275,16 @@ class ClassProp(object):
|
||||
class ClassInfo(object):
|
||||
def __init__(self, name, decl=None, codegen=None):
|
||||
# Scope name can be a module or other class e.g. cv::SimpleBlobDetector::Params
|
||||
scope_name, self.original_name = name.rsplit(".", 1)
|
||||
self.original_scope_name, self.original_name = name.rsplit(".", 1)
|
||||
|
||||
# In case scope refer the outer class exported with different name
|
||||
if codegen:
|
||||
scope_name = codegen.get_export_scope_name(scope_name)
|
||||
self.scope_name = re.sub(r"^cv\.?", "", scope_name)
|
||||
self.export_scope_name = codegen.get_export_scope_name(
|
||||
self.original_scope_name
|
||||
)
|
||||
else:
|
||||
self.export_scope_name = self.original_scope_name
|
||||
self.export_scope_name = re.sub(r"^cv\.?", "", self.export_scope_name)
|
||||
|
||||
self.export_name = self.original_name
|
||||
|
||||
@@ -312,8 +335,8 @@ class ClassInfo(object):
|
||||
|
||||
@property
|
||||
def wname(self):
|
||||
if len(self.scope_name) > 0:
|
||||
return self.scope_name.replace(".", "_") + "_" + self.export_name
|
||||
if len(self.export_scope_name) > 0:
|
||||
return self.export_scope_name.replace(".", "_") + "_" + self.export_name
|
||||
|
||||
return self.export_name
|
||||
|
||||
@@ -322,16 +345,16 @@ class ClassInfo(object):
|
||||
return self.class_id
|
||||
|
||||
@property
|
||||
def full_scope_name(self):
|
||||
return "cv." + self.scope_name if len(self.scope_name) else "cv"
|
||||
def full_export_scope_name(self):
|
||||
return "cv." + self.export_scope_name if len(self.export_scope_name) else "cv"
|
||||
|
||||
@property
|
||||
def full_export_name(self):
|
||||
return self.full_scope_name + "." + self.export_name
|
||||
return self.full_export_scope_name + "." + self.export_name
|
||||
|
||||
@property
|
||||
def full_original_name(self):
|
||||
return self.full_scope_name + "." + self.original_name
|
||||
return self.original_scope_name + "." + self.original_name
|
||||
|
||||
@property
|
||||
def has_export_alias(self):
|
||||
@@ -415,7 +438,7 @@ class ClassInfo(object):
|
||||
baseptr,
|
||||
constructor_name,
|
||||
# Leading dot is required to provide correct class naming
|
||||
"." + self.scope_name if len(self.scope_name) > 0 else self.scope_name
|
||||
"." + self.export_scope_name if len(self.export_scope_name) > 0 else self.export_scope_name
|
||||
)
|
||||
|
||||
|
||||
@@ -448,6 +471,15 @@ class ArgInfo(object):
|
||||
self.py_outputarg = False
|
||||
self.enclosing_arg = enclosing_arg
|
||||
|
||||
def __str__(self):
|
||||
return 'ArgInfo("{}", tp="{}", default="{}", in={}, out={})'.format(
|
||||
self.name, self.tp, self.defval, self.inputarg,
|
||||
self.outputarg
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
@property
|
||||
def export_name(self):
|
||||
if self.name in python_reserved_keywords:
|
||||
@@ -568,6 +600,10 @@ class FuncVariant(object):
|
||||
self.args.append(ainfo)
|
||||
self.init_pyproto(namespace, classname, known_classes)
|
||||
|
||||
def is_arg_optional(self, py_arg_index):
|
||||
# type: (FuncVariant, int) -> bool
|
||||
return py_arg_index >= len(self.py_arglist) - self.py_noptargs
|
||||
|
||||
def init_pyproto(self, namespace, classname, known_classes):
|
||||
# string representation of argument list, with '[', ']' symbols denoting optional arguments, e.g.
|
||||
# "src1, src2[, dst[, mask]]" for cv.add
|
||||
@@ -768,7 +804,7 @@ 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", CV_PY_FN_WITH_KW_($wrap_funcname, $flags),\n "$py_docstring"},\n'
|
||||
return Template(' {"$py_funcname", CV_PY_FN_WITH_KW_($wrap_funcname, $flags), "$py_docstring"},\n'
|
||||
).substitute(py_funcname = self.variants[0].wname, wrap_funcname=self.get_wrapper_name(),
|
||||
flags = 'METH_STATIC' if self.is_static else '0', py_docstring = full_docstring)
|
||||
|
||||
@@ -1045,6 +1081,7 @@ class PythonWrapperGenerator(object):
|
||||
self.namespaces = {}
|
||||
self.consts = {}
|
||||
self.enums = {}
|
||||
self.typing_stubs_generator = TypingStubsGenerator()
|
||||
self.code_include = StringIO()
|
||||
self.code_enums = StringIO()
|
||||
self.code_types = StringIO()
|
||||
@@ -1091,19 +1128,13 @@ class PythonWrapperGenerator(object):
|
||||
return original_scope_name
|
||||
|
||||
def split_decl_name(self, name):
|
||||
chunks = name.split('.')
|
||||
namespace = chunks[:-1]
|
||||
classes = []
|
||||
while namespace and '.'.join(namespace) not in self.parser.namespaces:
|
||||
classes.insert(0, namespace.pop())
|
||||
return namespace, classes, chunks[-1]
|
||||
|
||||
return SymbolName.parse(name, self.parser.namespaces)
|
||||
|
||||
def add_const(self, name, decl):
|
||||
cname = name.replace('.','::')
|
||||
namespace, classes, name = self.split_decl_name(name)
|
||||
namespace = '.'.join(namespace)
|
||||
name = '_'.join(classes+[name])
|
||||
name = '_'.join(chain(classes, (name, )))
|
||||
ns = self.namespaces.setdefault(namespace, Namespace())
|
||||
if name in ns.consts:
|
||||
print("Generator error: constant %s (cname=%s) already exists" \
|
||||
@@ -1118,20 +1149,31 @@ class PythonWrapperGenerator(object):
|
||||
#print(cname + ' => ' + str(py_name) + ' (value=' + value + ')')
|
||||
|
||||
def add_enum(self, name, decl):
|
||||
enumeration_name = SymbolName.parse(name, self.parser.namespaces)
|
||||
is_scoped_enum = decl[0].startswith("enum class") \
|
||||
or decl[0].startswith("enum struct")
|
||||
|
||||
wname = normalize_class_name(name)
|
||||
if wname.endswith("<unnamed>"):
|
||||
wname = None
|
||||
else:
|
||||
self.enums[wname] = name
|
||||
const_decls = decl[3]
|
||||
|
||||
enum_entries = {}
|
||||
for decl in const_decls:
|
||||
name = decl[0]
|
||||
self.add_const(name.replace("const ", "").strip(), decl)
|
||||
enum_entries[decl[0].split(".")[-1]] = decl[1]
|
||||
|
||||
self.add_const(decl[0].replace("const ", "").strip(), decl)
|
||||
|
||||
# Extra enumerations tracking is required to generate stubs for
|
||||
# all enumerations, including <unnamed> once, otherwise they
|
||||
# will be forgiven
|
||||
self.typing_stubs_generator.add_enum(enumeration_name, is_scoped_enum,
|
||||
enum_entries)
|
||||
|
||||
def add_func(self, decl):
|
||||
namespace, classes, barename = self.split_decl_name(decl[0])
|
||||
cname = "::".join(namespace+classes+[barename])
|
||||
cname = "::".join(chain(namespace, classes, (barename, )))
|
||||
name = barename
|
||||
classname = ''
|
||||
bareclassname = ''
|
||||
@@ -1158,7 +1200,7 @@ class PythonWrapperGenerator(object):
|
||||
return
|
||||
|
||||
if isconstructor:
|
||||
name = "_".join(classes[:-1]+[name])
|
||||
name = "_".join(chain(classes[:-1], (name, )))
|
||||
|
||||
if is_static:
|
||||
# Add it as a method to the class
|
||||
@@ -1167,7 +1209,7 @@ class PythonWrapperGenerator(object):
|
||||
func.add_variant(decl, self.classes, isphantom)
|
||||
|
||||
# Add it as global function
|
||||
g_name = "_".join(classes+[name])
|
||||
g_name = "_".join(chain(classes, (name, )))
|
||||
w_classes = []
|
||||
for i in range(0, len(classes)):
|
||||
classes_i = classes[:i+1]
|
||||
@@ -1179,10 +1221,15 @@ class PythonWrapperGenerator(object):
|
||||
w_classes.append(w_classname)
|
||||
g_wname = "_".join(w_classes+[name])
|
||||
func_map = self.namespaces.setdefault(namespace_str, Namespace()).funcs
|
||||
# Static functions should be called using class names, not like
|
||||
# module-level functions, so first step is to remove them from
|
||||
# type hints.
|
||||
self.typing_stubs_generator.add_ignored_function_name(g_name)
|
||||
# Exports static function with internal name (backward compatibility)
|
||||
func = func_map.setdefault(g_name, FuncInfo("", g_name, cname, isconstructor, namespace_str, False))
|
||||
func.add_variant(decl, self.classes, isphantom)
|
||||
if g_wname != g_name: # TODO OpenCV 5.0
|
||||
self.typing_stubs_generator.add_ignored_function_name(g_wname)
|
||||
wfunc = func_map.setdefault(g_wname, FuncInfo("", g_wname, cname, isconstructor, namespace_str, False))
|
||||
wfunc.add_variant(decl, self.classes, isphantom)
|
||||
else:
|
||||
@@ -1347,19 +1394,33 @@ class PythonWrapperGenerator(object):
|
||||
for decl_idx, name, classinfo in classlist1:
|
||||
if classinfo.ismap:
|
||||
continue
|
||||
|
||||
def _registerType(classinfo):
|
||||
if classinfo.decl_idx in published_types:
|
||||
#print(classinfo.decl_idx, classinfo.name, ' - already published')
|
||||
return
|
||||
# If class already registered it means that there is
|
||||
# a correponding node in the AST. This check is partically
|
||||
# useful for base classes.
|
||||
return self.typing_stubs_generator.find_class_node(
|
||||
classinfo, self.parser.namespaces
|
||||
)
|
||||
published_types.add(classinfo.decl_idx)
|
||||
|
||||
# Registering a class means creation of the AST node from the
|
||||
# given class information
|
||||
class_node = self.typing_stubs_generator.create_class_node(
|
||||
classinfo, self.parser.namespaces
|
||||
)
|
||||
|
||||
if classinfo.base and classinfo.base in self.classes:
|
||||
base_classinfo = self.classes[classinfo.base]
|
||||
#print(classinfo.decl_idx, classinfo.name, ' - request publishing of base type ', base_classinfo.decl_idx, base_classinfo.name)
|
||||
_registerType(base_classinfo)
|
||||
# print(classinfo.decl_idx, classinfo.name, ' - request publishing of base type ', base_classinfo.decl_idx, base_classinfo.name)
|
||||
base_node = _registerType(base_classinfo)
|
||||
class_node.add_base(base_node)
|
||||
|
||||
#print(classinfo.decl_idx, classinfo.name, ' - published!')
|
||||
# print(classinfo.decl_idx, classinfo.name, ' - published!')
|
||||
self.code_type_publish.write(classinfo.gen_def(self))
|
||||
return class_node
|
||||
|
||||
_registerType(classinfo)
|
||||
|
||||
@@ -1373,6 +1434,10 @@ class PythonWrapperGenerator(object):
|
||||
continue
|
||||
code = func.gen_code(self)
|
||||
self.code_funcs.write(code)
|
||||
# If function is not ignored - create an AST node for it
|
||||
if name not in self.typing_stubs_generator.type_hints_ignored_functions:
|
||||
self.typing_stubs_generator.create_function_node(func)
|
||||
|
||||
self.gen_namespace(ns_name)
|
||||
self.code_ns_init.write('CVPY_MODULE("{}", {});\n'.format(ns_name[2:], normalize_class_name(ns_name)))
|
||||
|
||||
@@ -1388,6 +1453,10 @@ class PythonWrapperGenerator(object):
|
||||
for name, constinfo in constlist:
|
||||
self.gen_const_reg(constinfo)
|
||||
|
||||
# All symbols are collected and AST is reconstructed, generating
|
||||
# typing stubs...
|
||||
self.typing_stubs_generator.generate(output_path)
|
||||
|
||||
# That's it. Now save all the files
|
||||
self.save(output_path, "pyopencv_generated_include.h", self.code_include)
|
||||
self.save(output_path, "pyopencv_generated_funcs.h", self.code_funcs)
|
||||
|
||||
Reference in New Issue
Block a user