mirror of
https://github.com/opencv/opencv.git
synced 2025-06-10 19:24:07 +08:00
Merge pull request #21553 from VadimLevin:dev/vlevin/scope-for-classes-4x-port
4.x: submodule or a class scope for exported classes * feature: submodule or a class scope for exported classes All classes are registered in the scope that corresponds to C++ namespace or exported class. Example: `cv::ml::Boost` is exported as `cv.ml.Boost` `cv::SimpleBlobDetector::Params` is exported as `cv.SimpleBlobDetector.Params` For backward compatibility all classes are registered in the global module with their mangling name containing scope information. Example: `cv::ml::Boost` has `cv.ml_Boost` alias to `cv.ml.Boost` type * refactor: remove redundant GAPI aliases * fix: use explicit string literals in CVPY_TYPE macro * fix: add handling for class aliases
This commit is contained in:
parent
cd9edba26f
commit
119d8b3aca
@ -223,6 +223,53 @@ namespace nested {
|
||||
CV_WRAP static inline bool testEchoBooleanFunction(bool flag) {
|
||||
return flag;
|
||||
}
|
||||
|
||||
class CV_EXPORTS_W CV_WRAP_AS(ExportClassName) OriginalClassName
|
||||
{
|
||||
public:
|
||||
struct CV_EXPORTS_W_SIMPLE Params
|
||||
{
|
||||
CV_PROP_RW int int_value;
|
||||
CV_PROP_RW float float_value;
|
||||
|
||||
CV_WRAP explicit Params(int int_param = 123, float float_param = 3.5f)
|
||||
{
|
||||
int_value = int_param;
|
||||
float_value = float_param;
|
||||
}
|
||||
};
|
||||
|
||||
explicit OriginalClassName(const OriginalClassName::Params& params = OriginalClassName::Params())
|
||||
{
|
||||
params_ = params;
|
||||
}
|
||||
|
||||
CV_WRAP int getIntParam() const
|
||||
{
|
||||
return params_.int_value;
|
||||
}
|
||||
|
||||
CV_WRAP float getFloatParam() const
|
||||
{
|
||||
return params_.float_value;
|
||||
}
|
||||
|
||||
CV_WRAP static std::string originalName()
|
||||
{
|
||||
return "OriginalClassName";
|
||||
}
|
||||
|
||||
CV_WRAP static Ptr<OriginalClassName>
|
||||
create(const OriginalClassName::Params& params = OriginalClassName::Params())
|
||||
{
|
||||
return makePtr<OriginalClassName>(params);
|
||||
}
|
||||
|
||||
private:
|
||||
OriginalClassName::Params params_;
|
||||
};
|
||||
|
||||
typedef OriginalClassName::Params OriginalClassName_Params;
|
||||
} // namespace nested
|
||||
|
||||
namespace fs {
|
||||
|
@ -287,15 +287,4 @@ def kernel(op_cls):
|
||||
return kernel_with_params
|
||||
|
||||
|
||||
# FIXME: On the c++ side every class is placed in cv2 module.
|
||||
cv.gapi.wip.draw.Rect = cv.gapi_wip_draw_Rect
|
||||
cv.gapi.wip.draw.Text = cv.gapi_wip_draw_Text
|
||||
cv.gapi.wip.draw.Circle = cv.gapi_wip_draw_Circle
|
||||
cv.gapi.wip.draw.Line = cv.gapi_wip_draw_Line
|
||||
cv.gapi.wip.draw.Mosaic = cv.gapi_wip_draw_Mosaic
|
||||
cv.gapi.wip.draw.Image = cv.gapi_wip_draw_Image
|
||||
cv.gapi.wip.draw.Poly = cv.gapi_wip_draw_Poly
|
||||
|
||||
cv.gapi.streaming.queue_capacity = cv.gapi_streaming_queue_capacity
|
||||
|
||||
cv.gapi.wip.GStreamerPipeline = cv.gapi_wip_gst_GStreamerPipeline
|
||||
|
@ -79,9 +79,9 @@ static int convert_to_char(PyObject *o, char *dst, const ArgInfo& info)
|
||||
#include "pyopencv_generated_enums.h"
|
||||
|
||||
#ifdef CVPY_DYNAMIC_INIT
|
||||
#define CVPY_TYPE(WNAME, NAME, STORAGE, SNAME, _1, _2) CVPY_TYPE_DECLARE_DYNAMIC(WNAME, NAME, STORAGE, SNAME)
|
||||
#define CVPY_TYPE(EXPORT_NAME, CLASS_ID, STORAGE, SNAME, _1, _2, SCOPE) CVPY_TYPE_DECLARE_DYNAMIC(EXPORT_NAME, CLASS_ID, STORAGE, SNAME, SCOPE)
|
||||
#else
|
||||
#define CVPY_TYPE(WNAME, NAME, STORAGE, SNAME, _1, _2) CVPY_TYPE_DECLARE(WNAME, NAME, STORAGE, SNAME)
|
||||
#define CVPY_TYPE(EXPORT_NAME, CLASS_ID, STORAGE, SNAME, _1, _2, SCOPE) CVPY_TYPE_DECLARE(EXPORT_NAME, CLASS_ID, STORAGE, SNAME, SCOPE)
|
||||
#endif
|
||||
#include "pyopencv_generated_types.h"
|
||||
#undef CVPY_TYPE
|
||||
@ -281,6 +281,189 @@ static bool init_submodule(PyObject * root, const char * name, PyMethodDef * met
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline
|
||||
bool registerTypeInModuleScope(PyObject* module, const char* type_name, PyObject* type_obj)
|
||||
{
|
||||
if (PyModule_AddObject(module, type_name, type_obj) < 0)
|
||||
{
|
||||
PyErr_Format(PyExc_ImportError,
|
||||
"Failed to register type '%s' in module scope '%s'",
|
||||
type_name, PyModule_GetName(module)
|
||||
);
|
||||
Py_DECREF(type_obj);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline
|
||||
bool registerTypeInClassScope(PyObject* cls, const char* type_name, PyObject* type_obj)
|
||||
{
|
||||
if (!PyType_CheckExact(cls)) {
|
||||
PyErr_Format(PyExc_ImportError,
|
||||
"Failed to register type '%s' in class scope. "
|
||||
"Scope class object has a wrong type", type_name
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (PyObject_SetAttrString(cls, type_name, type_obj) < 0)
|
||||
{
|
||||
#ifndef Py_LIMITED_API
|
||||
PyObject* cls_dict = reinterpret_cast<PyTypeObject*>(cls)->tp_dict;
|
||||
if (PyDict_SetItemString(cls_dict, type_name, type_obj) >= 0) {
|
||||
/// Clearing the error set by PyObject_SetAttrString:
|
||||
/// TypeError: can't set attributes of built-in/extension type NAME
|
||||
PyErr_Clear();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
const std::string cls_name = getPyObjectNameAttr(cls);
|
||||
PyErr_Format(PyExc_ImportError,
|
||||
"Failed to register type '%s' in '%s' class scope. Can't update scope dictionary",
|
||||
type_name, cls_name.c_str()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline
|
||||
PyObject* getScopeFromTypeObject(PyObject* obj, const std::string& scope_name)
|
||||
{
|
||||
if (!PyType_CheckExact(obj)) {
|
||||
const std::string type_name = getPyObjectNameAttr(obj);
|
||||
return PyErr_Format(PyExc_ImportError,
|
||||
"Failed to get scope from type '%s' "
|
||||
"Scope class object has a wrong type", type_name.c_str()
|
||||
);
|
||||
}
|
||||
/// When using LIMITED API all classes are registered in the heap
|
||||
#if defined(Py_LIMITED_API)
|
||||
return PyObject_GetAttrString(obj, scope_name.c_str());
|
||||
#else
|
||||
/// Otherwise classes may be registed on the stack or heap
|
||||
PyObject* type_dict = reinterpret_cast<PyTypeObject*>(obj)->tp_dict;
|
||||
if (!type_dict) {
|
||||
const std::string type_name = getPyObjectNameAttr(obj);
|
||||
return PyErr_Format(PyExc_ImportError,
|
||||
"Failed to get scope from type '%s' "
|
||||
"Type dictionary is not available", type_name.c_str()
|
||||
);
|
||||
}
|
||||
return PyDict_GetItemString(type_dict, scope_name.c_str());
|
||||
#endif // Py_LIMITED_API
|
||||
}
|
||||
|
||||
static inline
|
||||
PyObject* findTypeScope(PyObject* root_module, const std::string& scope_name)
|
||||
{
|
||||
PyObject* scope = root_module;
|
||||
if (scope_name.empty())
|
||||
{
|
||||
return scope;
|
||||
}
|
||||
/// Starting with 1 to omit leading dot in the scope name
|
||||
size_t name_end = scope_name.find('.', 1);
|
||||
if (name_end == std::string::npos)
|
||||
{
|
||||
name_end = scope_name.size();
|
||||
}
|
||||
for (size_t name_start = 1; name_start < scope_name.size() && scope; )
|
||||
{
|
||||
const std::string current_scope_name = scope_name.substr(name_start,
|
||||
name_end - name_start);
|
||||
|
||||
if (PyModule_CheckExact(scope))
|
||||
{
|
||||
PyObject* scope_dict = PyModule_GetDict(scope);
|
||||
if (!scope_dict)
|
||||
{
|
||||
return PyErr_Format(PyExc_ImportError,
|
||||
"Scope '%s' dictionary is not available during the search for "
|
||||
" the '%s' scope object", current_scope_name.c_str(),
|
||||
scope_name.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
scope = PyDict_GetItemString(scope_dict, current_scope_name.c_str());
|
||||
}
|
||||
else if (PyType_CheckExact(scope))
|
||||
{
|
||||
scope = getScopeFromTypeObject(scope, current_scope_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return PyErr_Format(PyExc_ImportError,
|
||||
"Can't find scope '%s'. '%s' doesn't reference a module or a class",
|
||||
scope_name.c_str(), current_scope_name.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
name_start = name_end + 1;
|
||||
name_end = scope_name.find('.', name_start);
|
||||
if (name_end == std::string::npos)
|
||||
{
|
||||
name_end = scope_name.size();
|
||||
}
|
||||
}
|
||||
if (!scope)
|
||||
{
|
||||
return PyErr_Format(PyExc_ImportError,
|
||||
"Module or class with name '%s' can't be found in '%s' module",
|
||||
scope_name.c_str(), PyModule_GetName(root_module)
|
||||
);
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
static bool registerNewType(PyObject* root_module, const char* type_name,
|
||||
PyObject* type_obj, const std::string& scope_name)
|
||||
{
|
||||
PyObject* scope = findTypeScope(root_module, scope_name);
|
||||
|
||||
/// If scope can't be found it means that there is an error during
|
||||
/// bindings generation
|
||||
if (!scope) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PyModule_CheckExact(scope))
|
||||
{
|
||||
if (!registerTypeInModuleScope(scope, type_name, type_obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/// In Python 2 it is disallowed to register an inner classes
|
||||
/// via modifing dictionary of the built-in type.
|
||||
if (!registerTypeInClassScope(scope, type_name, type_obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Expose all classes that are defined in the submodules as aliases in the
|
||||
/// root module for backward compatibility
|
||||
/// If submodule and root module are same than no aliases registration are
|
||||
/// required
|
||||
if (scope != root_module)
|
||||
{
|
||||
std::string type_name_str(type_name);
|
||||
|
||||
std::string alias_name;
|
||||
alias_name.reserve(scope_name.size() + type_name_str.size());
|
||||
std::replace_copy(scope_name.begin() + 1, scope_name.end(), std::back_inserter(alias_name), '.', '_');
|
||||
alias_name += '_';
|
||||
alias_name += type_name_str;
|
||||
|
||||
return registerTypeInModuleScope(root_module, alias_name.c_str(), type_obj);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#include "pyopencv_generated_modules_content.h"
|
||||
|
||||
static bool init_body(PyObject * m)
|
||||
@ -294,10 +477,10 @@ static bool init_body(PyObject * m)
|
||||
#undef CVPY_MODULE
|
||||
|
||||
#ifdef CVPY_DYNAMIC_INIT
|
||||
#define CVPY_TYPE(WNAME, NAME, _1, _2, BASE, CONSTRUCTOR) CVPY_TYPE_INIT_DYNAMIC(WNAME, NAME, return false, BASE, CONSTRUCTOR)
|
||||
#define CVPY_TYPE(EXPORT_NAME, CLASS_ID, _1, _2, BASE, CONSTRUCTOR, SCOPE) CVPY_TYPE_INIT_DYNAMIC(EXPORT_NAME, CLASS_ID, return false, BASE, CONSTRUCTOR, SCOPE)
|
||||
PyObject * pyopencv_NoBase_TypePtr = NULL;
|
||||
#else
|
||||
#define CVPY_TYPE(WNAME, NAME, _1, _2, BASE, CONSTRUCTOR) CVPY_TYPE_INIT_STATIC(WNAME, NAME, return false, BASE, CONSTRUCTOR)
|
||||
#define CVPY_TYPE(EXPORT_NAME, CLASS_ID, _1, _2, BASE, CONSTRUCTOR, SCOPE) CVPY_TYPE_INIT_STATIC(EXPORT_NAME, CLASS_ID, return false, BASE, CONSTRUCTOR, SCOPE)
|
||||
PyTypeObject * pyopencv_NoBase_TypePtr = NULL;
|
||||
#endif
|
||||
#include "pyopencv_generated_types.h"
|
||||
|
@ -245,10 +245,20 @@ class ClassProp(object):
|
||||
self.readonly = False
|
||||
|
||||
class ClassInfo(object):
|
||||
def __init__(self, name, decl=None):
|
||||
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)
|
||||
|
||||
# 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_name = self.original_name
|
||||
|
||||
self.class_id = normalize_class_name(name)
|
||||
|
||||
self.cname = name.replace(".", "::")
|
||||
self.name = self.wname = normalize_class_name(name)
|
||||
self.sname = name[name.rfind('.') + 1:]
|
||||
self.ismap = False
|
||||
self.issimple = False
|
||||
self.isalgorithm = False
|
||||
@ -258,12 +268,11 @@ class ClassInfo(object):
|
||||
self.consts = {}
|
||||
self.base = None
|
||||
self.constructor = None
|
||||
customname = False
|
||||
|
||||
if decl:
|
||||
bases = decl[1].split()[1:]
|
||||
if len(bases) > 1:
|
||||
print("Note: Class %s has more than 1 base class (not supported by Python C extensions)" % (self.name,))
|
||||
print("Note: Class %s has more than 1 base class (not supported by Python C extensions)" % (self.cname,))
|
||||
print(" Bases: ", " ".join(bases))
|
||||
print(" Only the first base class will be used")
|
||||
#return sys.exit(-1)
|
||||
@ -277,21 +286,43 @@ class ClassInfo(object):
|
||||
|
||||
for m in decl[2]:
|
||||
if m.startswith("="):
|
||||
wname = m[1:]
|
||||
npos = name.rfind('.')
|
||||
if npos >= 0:
|
||||
self.wname = normalize_class_name(name[:npos] + '.' + wname)
|
||||
else:
|
||||
self.wname = wname
|
||||
customname = True
|
||||
# Aliasing only affects the exported class name, not class identifier
|
||||
self.export_name = m[1:]
|
||||
elif m == "/Map":
|
||||
self.ismap = True
|
||||
elif m == "/Simple":
|
||||
self.issimple = True
|
||||
self.props = [ClassProp(p) for p in decl[3]]
|
||||
|
||||
if not customname and self.wname.startswith("Cv"):
|
||||
self.wname = self.wname[2:]
|
||||
if not self.has_export_alias and self.original_name.startswith("Cv"):
|
||||
self.export_name = self.export_name[2:]
|
||||
|
||||
@property
|
||||
def wname(self):
|
||||
if len(self.scope_name) > 0:
|
||||
return self.scope_name.replace(".", "_") + "_" + self.export_name
|
||||
|
||||
return self.export_name
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.class_id
|
||||
|
||||
@property
|
||||
def full_scope_name(self):
|
||||
return "cv." + self.scope_name if len(self.scope_name) else "cv"
|
||||
|
||||
@property
|
||||
def full_export_name(self):
|
||||
return self.full_scope_name + "." + self.export_name
|
||||
|
||||
@property
|
||||
def full_original_name(self):
|
||||
return self.full_scope_name + "." + self.original_name
|
||||
|
||||
@property
|
||||
def has_export_alias(self):
|
||||
return self.export_name != self.original_name
|
||||
|
||||
def gen_map_code(self, codegen):
|
||||
all_classes = codegen.classes
|
||||
@ -345,9 +376,11 @@ class ClassInfo(object):
|
||||
methods_code.write(m.gen_code(codegen))
|
||||
methods_inits.write(m.get_tab_entry())
|
||||
|
||||
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())
|
||||
code = gen_template_type_impl.substitute(name=self.name,
|
||||
getset_code=getset_code.getvalue(),
|
||||
getset_inits=getset_inits.getvalue(),
|
||||
methods_code=methods_code.getvalue(),
|
||||
methods_inits=methods_inits.getvalue())
|
||||
|
||||
return code
|
||||
|
||||
@ -361,13 +394,15 @@ class ClassInfo(object):
|
||||
if self.constructor is not None:
|
||||
constructor_name = self.constructor.get_wrapper_name()
|
||||
|
||||
return "CVPY_TYPE({}, {}, {}, {}, {}, {});\n".format(
|
||||
self.wname,
|
||||
self.name,
|
||||
return 'CVPY_TYPE({}, {}, {}, {}, {}, {}, "{}");\n'.format(
|
||||
self.export_name,
|
||||
self.class_id,
|
||||
self.cname if self.issimple else "Ptr<{}>".format(self.cname),
|
||||
self.sname if self.issimple else "Ptr",
|
||||
self.original_name if self.issimple else "Ptr",
|
||||
baseptr,
|
||||
constructor_name
|
||||
constructor_name,
|
||||
# Leading dot is required to provide correct class naming
|
||||
"." + self.scope_name if len(self.scope_name) > 0 else self.scope_name
|
||||
)
|
||||
|
||||
|
||||
@ -823,12 +858,12 @@ class FuncInfo(object):
|
||||
classinfo = all_classes[self.classname]
|
||||
#if dump: pprint(vars(classinfo))
|
||||
if self.isconstructor:
|
||||
py_name = 'cv.' + classinfo.wname
|
||||
elif self.is_static:
|
||||
py_name = '.'.join([self.namespace, classinfo.sname + '_' + self.variants[0].wname])
|
||||
py_name = classinfo.full_export_name
|
||||
else:
|
||||
py_name = classinfo.full_export_name + "." + self.variants[0].wname
|
||||
|
||||
if not self.is_static:
|
||||
cname = classinfo.cname + '::' + cname
|
||||
py_name = 'cv.' + classinfo.wname + '.' + self.variants[0].wname
|
||||
else:
|
||||
py_name = '.'.join([self.namespace, self.variants[0].wname])
|
||||
#if dump: print(cname + " => " + py_name)
|
||||
@ -870,7 +905,7 @@ class PythonWrapperGenerator(object):
|
||||
self.class_idx = 0
|
||||
|
||||
def add_class(self, stype, name, decl):
|
||||
classinfo = ClassInfo(name, decl)
|
||||
classinfo = ClassInfo(name, decl, self)
|
||||
classinfo.decl_idx = self.class_idx
|
||||
self.class_idx += 1
|
||||
|
||||
@ -880,16 +915,30 @@ class PythonWrapperGenerator(object):
|
||||
sys.exit(-1)
|
||||
self.classes[classinfo.name] = classinfo
|
||||
|
||||
# Add Class to json file.
|
||||
namespace, classes, name = self.split_decl_name(name)
|
||||
namespace, _, _ = self.split_decl_name(name)
|
||||
namespace = '.'.join(namespace)
|
||||
name = '_'.join(classes+[name])
|
||||
# Registering a namespace if it is not already handled or
|
||||
# doesn't have anything except classes defined in it
|
||||
self.namespaces.setdefault(namespace, Namespace())
|
||||
|
||||
py_name = 'cv.' + classinfo.wname # use wrapper name
|
||||
# Add Class to json file.
|
||||
py_name = classinfo.full_export_name # use wrapper name
|
||||
py_signatures = self.py_signatures.setdefault(classinfo.cname, [])
|
||||
py_signatures.append(dict(name=py_name))
|
||||
#print('class: ' + classinfo.cname + " => " + py_name)
|
||||
|
||||
def get_export_scope_name(self, original_scope_name):
|
||||
# Outer classes should be registered before their content - inner classes in this case
|
||||
class_scope = self.classes.get(normalize_class_name(original_scope_name), None)
|
||||
|
||||
if class_scope:
|
||||
return class_scope.full_export_name
|
||||
|
||||
# Otherwise it is a namespace.
|
||||
# If something is messed up at this point - it will be revelead during
|
||||
# library import
|
||||
return original_scope_name
|
||||
|
||||
def split_decl_name(self, name):
|
||||
chunks = name.split('.')
|
||||
namespace = chunks[:-1]
|
||||
@ -979,6 +1028,7 @@ class PythonWrapperGenerator(object):
|
||||
w_classes.append(w_classname)
|
||||
g_wname = "_".join(w_classes+[name])
|
||||
func_map = self.namespaces.setdefault(namespace_str, Namespace()).funcs
|
||||
# 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, isphantom)
|
||||
if g_wname != g_name: # TODO OpenCV 5.0
|
||||
|
@ -62,6 +62,10 @@
|
||||
|
||||
#endif // PY_MAJOR >=3
|
||||
|
||||
#ifndef PyType_CheckExact
|
||||
#define PyType_CheckExact(obj) (Py_TYPE(op) == &PyType_Type)
|
||||
#endif // !PyType_CheckExact
|
||||
|
||||
static inline bool getUnicodeString(PyObject * obj, std::string &str)
|
||||
{
|
||||
bool res = false;
|
||||
@ -93,6 +97,26 @@ static inline bool getUnicodeString(PyObject * obj, std::string &str)
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline
|
||||
std::string getPyObjectNameAttr(PyObject* obj)
|
||||
{
|
||||
std::string obj_name;
|
||||
PyObject* cls_name_obj = PyObject_GetAttrString(obj, "__name__");
|
||||
if (cls_name_obj && !getUnicodeString(cls_name_obj, obj_name)) {
|
||||
obj_name.clear();
|
||||
}
|
||||
#ifndef Py_LIMITED_API
|
||||
if (PyType_CheckExact(obj) && obj_name.empty())
|
||||
{
|
||||
obj_name = reinterpret_cast<PyTypeObject*>(obj)->tp_name;
|
||||
}
|
||||
#endif
|
||||
if (obj_name.empty()) {
|
||||
obj_name = "<UNAVAILABLE>";
|
||||
}
|
||||
return obj_name;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
#define CV_PY_FN_WITH_KW_(fn, flags) (PyCFunction)(void*)(PyCFunctionWithKeywords)(fn), (flags) | METH_VARARGS | METH_KEYWORDS
|
||||
@ -174,107 +198,106 @@ PyObject* pyopencv_from(const TYPE& src)
|
||||
#endif
|
||||
|
||||
|
||||
#define CVPY_TYPE_DECLARE(WNAME, NAME, STORAGE, SNAME) \
|
||||
struct pyopencv_##NAME##_t \
|
||||
#define CVPY_TYPE_DECLARE(EXPORT_NAME, CLASS_ID, STORAGE, SNAME, SCOPE) \
|
||||
struct pyopencv_##CLASS_ID##_t \
|
||||
{ \
|
||||
PyObject_HEAD \
|
||||
STORAGE v; \
|
||||
}; \
|
||||
static PyTypeObject pyopencv_##NAME##_TypeXXX = \
|
||||
static PyTypeObject pyopencv_##CLASS_ID##_TypeXXX = \
|
||||
{ \
|
||||
CVPY_TYPE_HEAD \
|
||||
MODULESTR"."#WNAME, \
|
||||
sizeof(pyopencv_##NAME##_t), \
|
||||
MODULESTR SCOPE"."#EXPORT_NAME, \
|
||||
sizeof(pyopencv_##CLASS_ID##_t), \
|
||||
}; \
|
||||
static PyTypeObject * pyopencv_##NAME##_TypePtr = &pyopencv_##NAME##_TypeXXX; \
|
||||
static bool pyopencv_##NAME##_getp(PyObject * self, STORAGE * & dst) \
|
||||
static PyTypeObject * pyopencv_##CLASS_ID##_TypePtr = &pyopencv_##CLASS_ID##_TypeXXX; \
|
||||
static bool pyopencv_##CLASS_ID##_getp(PyObject * self, STORAGE * & dst) \
|
||||
{ \
|
||||
if (PyObject_TypeCheck(self, pyopencv_##NAME##_TypePtr)) \
|
||||
if (PyObject_TypeCheck(self, pyopencv_##CLASS_ID##_TypePtr)) \
|
||||
{ \
|
||||
dst = &(((pyopencv_##NAME##_t*)self)->v); \
|
||||
dst = &(((pyopencv_##CLASS_ID##_t*)self)->v); \
|
||||
return true; \
|
||||
} \
|
||||
return false; \
|
||||
} \
|
||||
static PyObject * pyopencv_##NAME##_Instance(const STORAGE &r) \
|
||||
static PyObject * pyopencv_##CLASS_ID##_Instance(const STORAGE &r) \
|
||||
{ \
|
||||
pyopencv_##NAME##_t *m = PyObject_NEW(pyopencv_##NAME##_t, pyopencv_##NAME##_TypePtr); \
|
||||
pyopencv_##CLASS_ID##_t *m = PyObject_NEW(pyopencv_##CLASS_ID##_t, pyopencv_##CLASS_ID##_TypePtr); \
|
||||
new (&(m->v)) STORAGE(r); \
|
||||
return (PyObject*)m; \
|
||||
} \
|
||||
static void pyopencv_##NAME##_dealloc(PyObject* self) \
|
||||
static void pyopencv_##CLASS_ID##_dealloc(PyObject* self) \
|
||||
{ \
|
||||
((pyopencv_##NAME##_t*)self)->v.STORAGE::~SNAME(); \
|
||||
((pyopencv_##CLASS_ID##_t*)self)->v.STORAGE::~SNAME(); \
|
||||
PyObject_Del(self); \
|
||||
} \
|
||||
static PyObject* pyopencv_##NAME##_repr(PyObject* self) \
|
||||
static PyObject* pyopencv_##CLASS_ID##_repr(PyObject* self) \
|
||||
{ \
|
||||
char str[1000]; \
|
||||
sprintf(str, "<"#WNAME" %p>", self); \
|
||||
sprintf(str, "< " MODULESTR SCOPE"."#EXPORT_NAME" %p>", self); \
|
||||
return PyString_FromString(str); \
|
||||
}
|
||||
|
||||
|
||||
#define CVPY_TYPE_INIT_STATIC(WNAME, NAME, ERROR_HANDLER, BASE, CONSTRUCTOR) \
|
||||
#define CVPY_TYPE_INIT_STATIC(EXPORT_NAME, CLASS_ID, ERROR_HANDLER, BASE, CONSTRUCTOR, SCOPE) \
|
||||
{ \
|
||||
pyopencv_##NAME##_TypePtr->tp_base = pyopencv_##BASE##_TypePtr; \
|
||||
pyopencv_##NAME##_TypePtr->tp_dealloc = pyopencv_##NAME##_dealloc; \
|
||||
pyopencv_##NAME##_TypePtr->tp_repr = pyopencv_##NAME##_repr; \
|
||||
pyopencv_##NAME##_TypePtr->tp_getset = pyopencv_##NAME##_getseters; \
|
||||
pyopencv_##NAME##_TypePtr->tp_init = (initproc) CONSTRUCTOR; \
|
||||
pyopencv_##NAME##_TypePtr->tp_methods = pyopencv_##NAME##_methods; \
|
||||
pyopencv_##NAME##_TypePtr->tp_alloc = PyType_GenericAlloc; \
|
||||
pyopencv_##NAME##_TypePtr->tp_new = PyType_GenericNew; \
|
||||
pyopencv_##NAME##_TypePtr->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; \
|
||||
if (PyType_Ready(pyopencv_##NAME##_TypePtr) != 0) \
|
||||
pyopencv_##CLASS_ID##_TypePtr->tp_base = pyopencv_##BASE##_TypePtr; \
|
||||
pyopencv_##CLASS_ID##_TypePtr->tp_dealloc = pyopencv_##CLASS_ID##_dealloc; \
|
||||
pyopencv_##CLASS_ID##_TypePtr->tp_repr = pyopencv_##CLASS_ID##_repr; \
|
||||
pyopencv_##CLASS_ID##_TypePtr->tp_getset = pyopencv_##CLASS_ID##_getseters; \
|
||||
pyopencv_##CLASS_ID##_TypePtr->tp_init = (initproc) CONSTRUCTOR; \
|
||||
pyopencv_##CLASS_ID##_TypePtr->tp_methods = pyopencv_##CLASS_ID##_methods; \
|
||||
pyopencv_##CLASS_ID##_TypePtr->tp_alloc = PyType_GenericAlloc; \
|
||||
pyopencv_##CLASS_ID##_TypePtr->tp_new = PyType_GenericNew; \
|
||||
pyopencv_##CLASS_ID##_TypePtr->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; \
|
||||
if (PyType_Ready(pyopencv_##CLASS_ID##_TypePtr) != 0) \
|
||||
{ \
|
||||
ERROR_HANDLER; \
|
||||
} \
|
||||
CVPY_TYPE_INCREF(pyopencv_##NAME##_TypePtr); \
|
||||
if (PyModule_AddObject(m, #WNAME, (PyObject *)pyopencv_##NAME##_TypePtr) < 0) \
|
||||
CVPY_TYPE_INCREF(pyopencv_##CLASS_ID##_TypePtr); \
|
||||
if (!registerNewType(m, #EXPORT_NAME, (PyObject*)pyopencv_##CLASS_ID##_TypePtr, SCOPE)) \
|
||||
{ \
|
||||
printf("Failed to register a new type: " #WNAME ", base (" #BASE ")\n"); \
|
||||
Py_DECREF(pyopencv_##NAME##_TypePtr); \
|
||||
printf("Failed to register a new type: " #EXPORT_NAME ", base (" #BASE ") in " SCOPE " \n"); \
|
||||
ERROR_HANDLER; \
|
||||
} \
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
#define CVPY_TYPE_DECLARE_DYNAMIC(WNAME, NAME, STORAGE, SNAME) \
|
||||
struct pyopencv_##NAME##_t \
|
||||
#define CVPY_TYPE_DECLARE_DYNAMIC(EXPORT_NAME, CLASS_ID, STORAGE, SNAME, SCOPE) \
|
||||
struct pyopencv_##CLASS_ID##_t \
|
||||
{ \
|
||||
PyObject_HEAD \
|
||||
STORAGE v; \
|
||||
}; \
|
||||
static PyObject * pyopencv_##NAME##_TypePtr = 0; \
|
||||
static bool pyopencv_##NAME##_getp(PyObject * self, STORAGE * & dst) \
|
||||
static PyObject * pyopencv_##CLASS_ID##_TypePtr = 0; \
|
||||
static bool pyopencv_##CLASS_ID##_getp(PyObject * self, STORAGE * & dst) \
|
||||
{ \
|
||||
if (PyObject_TypeCheck(self, (PyTypeObject*)pyopencv_##NAME##_TypePtr)) \
|
||||
if (PyObject_TypeCheck(self, (PyTypeObject*)pyopencv_##CLASS_ID##_TypePtr)) \
|
||||
{ \
|
||||
dst = &(((pyopencv_##NAME##_t*)self)->v); \
|
||||
dst = &(((pyopencv_##CLASS_ID##_t*)self)->v); \
|
||||
return true; \
|
||||
} \
|
||||
return false; \
|
||||
} \
|
||||
static PyObject * pyopencv_##NAME##_Instance(const STORAGE &r) \
|
||||
static PyObject * pyopencv_##CLASS_ID##_Instance(const STORAGE &r) \
|
||||
{ \
|
||||
pyopencv_##NAME##_t *m = PyObject_New(pyopencv_##NAME##_t, (PyTypeObject*)pyopencv_##NAME##_TypePtr); \
|
||||
pyopencv_##CLASS_ID##_t *m = PyObject_New(pyopencv_##CLASS_ID##_t, (PyTypeObject*)pyopencv_##CLASS_ID##_TypePtr); \
|
||||
new (&(m->v)) STORAGE(r); \
|
||||
return (PyObject*)m; \
|
||||
} \
|
||||
static void pyopencv_##NAME##_dealloc(PyObject* self) \
|
||||
static void pyopencv_##CLASS_ID##_dealloc(PyObject* self) \
|
||||
{ \
|
||||
((pyopencv_##NAME##_t*)self)->v.STORAGE::~SNAME(); \
|
||||
((pyopencv_##CLASS_ID##_t*)self)->v.STORAGE::~SNAME(); \
|
||||
PyObject_Del(self); \
|
||||
} \
|
||||
static PyObject* pyopencv_##NAME##_repr(PyObject* self) \
|
||||
static PyObject* pyopencv_##CLASS_ID##_repr(PyObject* self) \
|
||||
{ \
|
||||
char str[1000]; \
|
||||
sprintf(str, "<"#WNAME" %p>", self); \
|
||||
sprintf(str, "< " MODULESTR SCOPE"."#EXPORT_NAME" %p>", self); \
|
||||
return PyString_FromString(str); \
|
||||
} \
|
||||
static PyType_Slot pyopencv_##NAME##_Slots[] = \
|
||||
static PyType_Slot pyopencv_##CLASS_ID##_Slots[] = \
|
||||
{ \
|
||||
{Py_tp_dealloc, 0}, \
|
||||
{Py_tp_repr, 0}, \
|
||||
@ -285,37 +308,36 @@ PyObject* pyopencv_from(const TYPE& src)
|
||||
{Py_tp_new, 0}, \
|
||||
{0, 0} \
|
||||
}; \
|
||||
static PyType_Spec pyopencv_##NAME##_Spec = \
|
||||
static PyType_Spec pyopencv_##CLASS_ID##_Spec = \
|
||||
{ \
|
||||
MODULESTR"."#WNAME, \
|
||||
sizeof(pyopencv_##NAME##_t), \
|
||||
MODULESTR SCOPE"."#EXPORT_NAME, \
|
||||
sizeof(pyopencv_##CLASS_ID##_t), \
|
||||
0, \
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, \
|
||||
pyopencv_##NAME##_Slots \
|
||||
pyopencv_##CLASS_ID##_Slots \
|
||||
};
|
||||
|
||||
#define CVPY_TYPE_INIT_DYNAMIC(WNAME, NAME, ERROR_HANDLER, BASE, CONSTRUCTOR) \
|
||||
#define CVPY_TYPE_INIT_DYNAMIC(EXPORT_NAME, CLASS_ID, ERROR_HANDLER, BASE, CONSTRUCTOR, SCOPE) \
|
||||
{ \
|
||||
pyopencv_##NAME##_Slots[0].pfunc /*tp_dealloc*/ = (void*)pyopencv_##NAME##_dealloc; \
|
||||
pyopencv_##NAME##_Slots[1].pfunc /*tp_repr*/ = (void*)pyopencv_##NAME##_repr; \
|
||||
pyopencv_##NAME##_Slots[2].pfunc /*tp_getset*/ = (void*)pyopencv_##NAME##_getseters; \
|
||||
pyopencv_##NAME##_Slots[3].pfunc /*tp_init*/ = (void*) CONSTRUCTOR; \
|
||||
pyopencv_##NAME##_Slots[4].pfunc /*tp_methods*/ = pyopencv_##NAME##_methods; \
|
||||
pyopencv_##NAME##_Slots[5].pfunc /*tp_alloc*/ = (void*)PyType_GenericAlloc; \
|
||||
pyopencv_##NAME##_Slots[6].pfunc /*tp_new*/ = (void*)PyType_GenericNew; \
|
||||
pyopencv_##CLASS_ID##_Slots[0].pfunc /*tp_dealloc*/ = (void*)pyopencv_##CLASS_ID##_dealloc; \
|
||||
pyopencv_##CLASS_ID##_Slots[1].pfunc /*tp_repr*/ = (void*)pyopencv_##CLASS_ID##_repr; \
|
||||
pyopencv_##CLASS_ID##_Slots[2].pfunc /*tp_getset*/ = (void*)pyopencv_##CLASS_ID##_getseters; \
|
||||
pyopencv_##CLASS_ID##_Slots[3].pfunc /*tp_init*/ = (void*) CONSTRUCTOR; \
|
||||
pyopencv_##CLASS_ID##_Slots[4].pfunc /*tp_methods*/ = pyopencv_##CLASS_ID##_methods; \
|
||||
pyopencv_##CLASS_ID##_Slots[5].pfunc /*tp_alloc*/ = (void*)PyType_GenericAlloc; \
|
||||
pyopencv_##CLASS_ID##_Slots[6].pfunc /*tp_new*/ = (void*)PyType_GenericNew; \
|
||||
PyObject * bases = 0; \
|
||||
if (pyopencv_##BASE##_TypePtr) \
|
||||
bases = PyTuple_Pack(1, pyopencv_##BASE##_TypePtr); \
|
||||
pyopencv_##NAME##_TypePtr = PyType_FromSpecWithBases(&pyopencv_##NAME##_Spec, bases); \
|
||||
if (!pyopencv_##NAME##_TypePtr) \
|
||||
pyopencv_##CLASS_ID##_TypePtr = PyType_FromSpecWithBases(&pyopencv_##CLASS_ID##_Spec, bases); \
|
||||
if (!pyopencv_##CLASS_ID##_TypePtr) \
|
||||
{ \
|
||||
printf("Failed to create type from spec: " #WNAME ", base (" #BASE ")\n"); \
|
||||
printf("Failed to create type from spec: " #CLASS_ID ", base (" #BASE ")\n"); \
|
||||
ERROR_HANDLER; \
|
||||
} \
|
||||
if (PyModule_AddObject(m, #WNAME, (PyObject *)pyopencv_##NAME##_TypePtr) < 0) \
|
||||
if (!registerNewType(m, #EXPORT_NAME, (PyObject*)pyopencv_##CLASS_ID##_TypePtr, SCOPE)) \
|
||||
{ \
|
||||
printf("Failed to register a new type: " #WNAME ", base (" #BASE ")\n"); \
|
||||
Py_DECREF(pyopencv_##NAME##_TypePtr); \
|
||||
printf("Failed to register a new type: " #EXPORT_NAME ", base (" #BASE ") in " SCOPE " \n"); \
|
||||
ERROR_HANDLER; \
|
||||
} \
|
||||
}
|
||||
|
@ -633,6 +633,74 @@ class Arguments(NewOpenCVTests):
|
||||
self.assertEqual(flag, cv.utils.nested.testEchoBooleanFunction(flag),
|
||||
msg="Function in nested module returns wrong result")
|
||||
|
||||
def test_class_from_submodule_has_global_alias(self):
|
||||
self.assertTrue(hasattr(cv.ml, "Boost"),
|
||||
msg="Class is not registered in the submodule")
|
||||
self.assertTrue(hasattr(cv, "ml_Boost"),
|
||||
msg="Class from submodule doesn't have alias in the "
|
||||
"global module")
|
||||
self.assertEqual(cv.ml.Boost, cv.ml_Boost,
|
||||
msg="Classes from submodules and global module don't refer "
|
||||
"to the same type")
|
||||
|
||||
def test_inner_class_has_global_alias(self):
|
||||
self.assertTrue(hasattr(cv.SimpleBlobDetector, "Params"),
|
||||
msg="Class is not registered as inner class")
|
||||
self.assertTrue(hasattr(cv, "SimpleBlobDetector_Params"),
|
||||
msg="Inner class doesn't have alias in the global module")
|
||||
self.assertEqual(cv.SimpleBlobDetector.Params, cv.SimpleBlobDetector_Params,
|
||||
msg="Inner class and class in global module don't refer "
|
||||
"to the same type")
|
||||
self.assertTrue(hasattr(cv, "SimpleBlobDetector_Params"),
|
||||
msg="Inner class doesn't have alias in the global module")
|
||||
|
||||
def test_export_class_with_different_name(self):
|
||||
self.assertTrue(hasattr(cv.utils.nested, "ExportClassName"),
|
||||
msg="Class with export alias is not registered in the submodule")
|
||||
self.assertTrue(hasattr(cv, "utils_nested_ExportClassName"),
|
||||
msg="Class with export alias doesn't have alias in the "
|
||||
"global module")
|
||||
self.assertEqual(cv.utils.nested.ExportClassName.originalName(), "OriginalClassName")
|
||||
|
||||
instance = cv.utils.nested.ExportClassName.create()
|
||||
self.assertTrue(isinstance(instance, cv.utils.nested.ExportClassName),
|
||||
msg="Factory function returns wrong class instance: {}".format(type(instance)))
|
||||
self.assertTrue(hasattr(cv.utils.nested, "ExportClassName_create"),
|
||||
msg="Factory function should have alias in the same module as the class")
|
||||
# self.assertFalse(hasattr(cv.utils.nested, "OriginalClassName_create"),
|
||||
# msg="Factory function should not be registered with original class name, "\
|
||||
# "when class has different export name")
|
||||
|
||||
def test_export_inner_class_of_class_exported_with_different_name(self):
|
||||
if not hasattr(cv.utils.nested, "ExportClassName"):
|
||||
raise unittest.SkipTest("Outer class with export alias is not registered in the submodule")
|
||||
|
||||
self.assertTrue(hasattr(cv.utils.nested.ExportClassName, "Params"),
|
||||
msg="Inner class with export alias is not registered in "
|
||||
"the outer class")
|
||||
self.assertTrue(hasattr(cv, "utils_nested_ExportClassName_Params"),
|
||||
msg="Inner class with export alias is not registered in "
|
||||
"global module")
|
||||
params = cv.utils.nested.ExportClassName.Params()
|
||||
params.int_value = 45
|
||||
params.float_value = 4.5
|
||||
|
||||
instance = cv.utils.nested.ExportClassName.create(params)
|
||||
self.assertTrue(isinstance(instance, cv.utils.nested.ExportClassName),
|
||||
msg="Factory function returns wrong class instance: {}".format(type(instance)))
|
||||
self.assertEqual(
|
||||
params.int_value, instance.getIntParam(),
|
||||
msg="Class initialized with wrong integer parameter. Expected: {}. Actual: {}".format(
|
||||
params.int_value, instance.getIntParam()
|
||||
))
|
||||
self.assertEqual(
|
||||
params.float_value, instance.getFloatParam(),
|
||||
msg="Class initialized with wrong integer parameter. Expected: {}. Actual: {}".format(
|
||||
params.float_value, instance.getFloatParam()
|
||||
))
|
||||
|
||||
|
||||
|
||||
|
||||
class CanUsePurePythonModuleFunction(NewOpenCVTests):
|
||||
def test_can_get_ocv_version(self):
|
||||
|
Loading…
Reference in New Issue
Block a user