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
pull/21488/head
Vadim Levin 3 years ago
parent 5cc27fd3b5
commit ccebbbc0ac
  1. 47
      modules/core/include/opencv2/core/bindings_utils.hpp
  2. 191
      modules/python/src2/cv2.cpp
  3. 110
      modules/python/src2/gen2.py
  4. 144
      modules/python/src2/pycompat.hpp
  5. 85
      modules/python/test/test_misc.py

@ -217,6 +217,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
//! @} // core_utils

@ -2066,9 +2066,9 @@ static int convert_to_char(PyObject *o, char *dst, const ArgInfo& info)
#include "pyopencv_custom_headers.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
@ -2251,6 +2251,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)
@ -2264,10 +2447,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"

@ -243,10 +243,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
@ -261,7 +271,7 @@ class ClassInfo(object):
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)
@ -275,22 +285,47 @@ 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 self.has_export_alias and self.original_name.startswith("Cv"):
self.export_name = self.export_name[2:]
if not customname and self.wname.startswith("Cv"):
self.wname = self.wname[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
code = "static bool pyopencv_to(PyObject* src, %s& dst, const ArgInfo& info)\n{\n PyObject* tmp;\n bool ok;\n" % (self.cname)
@ -343,9 +378,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
@ -359,13 +396,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
)
@ -815,12 +854,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)
@ -862,7 +901,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
@ -872,16 +911,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]
@ -971,6 +1024,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

@ -60,6 +60,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;
@ -91,6 +95,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
@ -172,107 +196,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}, \
@ -283,37 +306,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; \
} \
}

@ -618,6 +618,91 @@ 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.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_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 SamplesFindFile(NewOpenCVTests):

Loading…
Cancel
Save