aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken2/libshiboken
diff options
context:
space:
mode:
authorChristian Tismer <tismer@stackless.com>2018-10-14 11:13:40 +0200
committerChristian Tismer <tismer@stackless.com>2018-11-15 10:24:23 +0000
commit2533dab013455bf94da2d4766e54abaf4d735e1e (patch)
tree506f169c2f3a44fed3bfe623c1f626d40836a379 /sources/shiboken2/libshiboken
parent93b54f123771c5b7da9880e9a2f372562893707f (diff)
Signature: Implement Nested Classes and Functions for Shiboken
This patch again contains a complete overhaul of the signature module. The code was re-implemented to properly support nested classes. Also, the code was reduced by AutoDecRef and by adopting a concise C++ style. Note.. We will add a shiboken signature test and complete mapping.py after the split into three projects is done. The split changes a lot and is needed right now! Signatures were quite complete for PySide, but the support for Shiboken was under-developed. Since we are planning to generally enhance error messages by using the Signature module, we should be able to rely on them to always produce a signature. Therefore, a general overhaul was needed to resolve all cornes cases for Python 2 and 3. Nested classes are supported, as well as plain module functions. The usage of the typing module might improve over time, but the Signature implementation is now considered complete. The loader will respect now the path settings which might not be the package dir but the build dir. This is more consistens with COIN testing. Task-number: PYSIDE-795 Change-Id: I246449d4df895dadf2bcb4d997eaa13d78463d9b Reviewed-by: Simo Fält <simo.falt@qt.io>
Diffstat (limited to 'sources/shiboken2/libshiboken')
-rw-r--r--sources/shiboken2/libshiboken/signature.cpp575
1 files changed, 312 insertions, 263 deletions
diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp
index 24d60acc4..962e50d46 100644
--- a/sources/shiboken2/libshiboken/signature.cpp
+++ b/sources/shiboken2/libshiboken/signature.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "basewrapper.h"
+#include "autodecref.h"
extern "C"
{
@@ -77,7 +78,9 @@ typedef struct safe_globals_struc {
static safe_globals pyside_globals = 0;
-static PyObject *GetSignature_Function(PyCFunctionObject *, const char *);
+static PyObject *GetClassKey(PyObject *ob);
+
+static PyObject *GetSignature_Function(PyObject *, const char *);
static PyObject *GetSignature_TypeMod(PyObject *, const char *);
static PyObject *GetSignature_Wrapper(PyObject *, const char *);
static PyObject *get_signature(PyObject *self, PyObject *args);
@@ -108,24 +111,85 @@ CreateSignature(PyObject *props, PyObject *key)
static PyObject *
pyside_cf_get___signature__(PyObject *func, const char *modifier)
{
- return GetSignature_Function((PyCFunctionObject *)func, modifier);
+ return GetSignature_Function(func, modifier);
}
static PyObject *
pyside_sm_get___signature__(PyObject *sm, const char *modifier)
{
- PyObject *func, *ret;
+ Shiboken::AutoDecRef func(PyObject_GetAttrString(sm, "__func__"));
+ return GetSignature_Function(func, modifier);
+}
- func = PyObject_GetAttrString(sm, "__func__");
- ret = GetSignature_Function((PyCFunctionObject *)func, modifier);
- Py_XDECREF(func);
- return ret;
+static PyObject *
+_get_class_of_cf(PyObject *ob_cf)
+{
+ PyObject *selftype = PyCFunction_GET_SELF(ob_cf);
+ if (selftype == NULL)
+ selftype = PyDict_GetItem(pyside_globals->map_dict, (PyObject *)ob_cf);
+ if (selftype == NULL) {
+ if (!PyErr_Occurred())
+ Py_RETURN_NONE;
+ return NULL;
+ }
+ PyObject *typemod = (PyType_Check(selftype) || PyModule_Check(selftype))
+ ? selftype : (PyObject *)Py_TYPE(selftype);
+ // do we support module functions?
+ Py_INCREF(typemod);
+ return typemod;
+}
+
+static PyObject *
+_get_class_of_sm(PyObject *ob_sm)
+{
+ Shiboken::AutoDecRef func(PyObject_GetAttrString(ob_sm, "__func__"));
+ return _get_class_of_cf(func);
}
-#ifdef Py_LIMITED_API
+static PyObject *
+_get_class_of_descr(PyObject *ob)
+{
+ Shiboken::AutoDecRef func_name(PyObject_GetAttrString(ob, "__name__"));
+ return PyObject_GetAttrString(ob, "__objclass__");
+}
+
+static PyObject *
+GetClassOfFunc(PyObject *ob)
+{
+ if (PyType_Check(ob))
+ return ob;
+ if (Py_TYPE(ob) == &PyCFunction_Type)
+ return _get_class_of_cf(ob);
+ if (Py_TYPE(ob) == PepStaticMethod_TypePtr)
+ return _get_class_of_sm(ob);
+ if (Py_TYPE(ob) == PepMethodDescr_TypePtr)
+ return _get_class_of_descr(ob);
+ if (Py_TYPE(ob) == &PyWrapperDescr_Type)
+ return _get_class_of_descr(ob);
+ Py_FatalError("unexpected type in GetClassOfFunc");
+ return nullptr;
+}
+
+static PyObject *
+compute_name_key(PyObject *ob)
+{
+ if (PyType_Check(ob))
+ return GetClassKey(GetClassOfFunc(ob));
+ PyObject *func = ob;
+ if (Py_TYPE(ob) == PepStaticMethod_TypePtr)
+ func = PyObject_GetAttrString(ob, "__func__");
+ else
+ Py_INCREF(func);
+ Shiboken::AutoDecRef func_name(PyObject_GetAttrString(func, "__name__"));
+ Py_DECREF(func);
+ if (func_name.isNull())
+ Py_FatalError("unexpected name problem in compute_name_key");
+ Shiboken::AutoDecRef type_key(GetClassKey(GetClassOfFunc(ob)));
+ return Py_BuildValue("(OO)", type_key.object(), func_name.object());
+}
static int
-build_qualname_to_func(PyObject *obtype)
+build_name_key_to_func(PyObject *obtype)
{
PyTypeObject *type = (PyTypeObject *)obtype;
PyMethodDef *meth = type->tp_methods;
@@ -134,98 +198,53 @@ build_qualname_to_func(PyObject *obtype)
return 0;
for (; meth->ml_name != NULL; meth++) {
- PyObject *func = PyCFunction_NewEx(meth, obtype, NULL);
- PyObject *qualname = PyObject_GetAttrString(func, "__qualname__");
- if (func == NULL || qualname == NULL) {
+ Shiboken::AutoDecRef func(PyCFunction_NewEx(meth, obtype, NULL));
+ Shiboken::AutoDecRef name_key(compute_name_key(func));
+ if (func.isNull() || name_key.isNull()
+ || PyDict_SetItem(pyside_globals->map_dict, name_key, func) < 0)
return -1;
- }
- if (PyDict_SetItem(pyside_globals->map_dict, qualname, func) < 0) {
- return -1;
- }
- Py_DECREF(func);
- Py_DECREF(qualname);
}
return 0;
}
static PyObject *
-qualname_to_typename(PyObject *qualname)
-{
- PyObject *func = PyObject_GetAttrString(qualname, "split");
- PyObject *list = func ? PyObject_CallFunction(func, (char *)"(s)", ".")
- : NULL;
- PyObject *res = list ? PyList_GetItem(list, 0) : NULL;
- Py_XINCREF(res);
- Py_XDECREF(func);
- Py_XDECREF(list);
- return res;
-}
-
-static PyObject *
-qualname_to_func(PyObject *ob)
+name_key_to_func(PyObject *ob)
{
/*
- * If we have __qualname__, then we can easily build a mapping
- * from __qualname__ to PyCFunction. This is necessary when
- * the limited API does not let us go easily from descriptor
- * to PyMethodDef.
+ * We build a mapping from name_key to function.
+ * This could also be computed directly, but the Limited API
+ * makes this impossible. So we always build our own mapping.
*/
- PyObject *ret;
- PyObject *qualname = PyObject_GetAttrString((PyObject *)ob,
- "__qualname__");
- if (qualname != NULL) {
- ret = PyDict_GetItem(pyside_globals->map_dict, qualname);
- if (ret == NULL) {
- // do a lazy initialization
- PyObject *type_name = qualname_to_typename(qualname);
- PyObject *type = PyDict_GetItem(pyside_globals->map_dict,
- type_name);
- Py_XDECREF(type_name);
- if (type == NULL)
- Py_RETURN_NONE;
- if (build_qualname_to_func(type) < 0)
- return NULL;
- ret = PyDict_GetItem(pyside_globals->map_dict, qualname);
- }
- Py_XINCREF(ret);
- Py_DECREF(qualname);
- }
- else
+ Shiboken::AutoDecRef name_key(compute_name_key(ob));
+ if (name_key.isNull())
Py_RETURN_NONE;
+
+ PyObject *ret = PyDict_GetItem(pyside_globals->map_dict, name_key);
+ if (ret == NULL) {
+ // do a lazy initialization
+ Shiboken::AutoDecRef type_key(GetClassKey(GetClassOfFunc(ob)));
+ PyObject *type = PyDict_GetItem(pyside_globals->map_dict,
+ type_key);
+ if (type == nullptr)
+ Py_RETURN_NONE;
+ assert(PyType_Check(type));
+ if (build_name_key_to_func(type) < 0)
+ return NULL;
+ ret = PyDict_GetItem(pyside_globals->map_dict, name_key);
+ }
+ Py_XINCREF(ret);
return ret;
}
-#endif
static PyObject *
-pyside_md_get___signature__(PyObject *ob, const char *modifier)
-{
- PyObject *func;
- PyObject *result;
-#ifndef Py_LIMITED_API
- PyMethodDescrObject *descr = (PyMethodDescrObject *)ob;
-
-# if PYTHON_USES_D_COMMON
- func = PyCFunction_NewEx(descr->d_method,
- (PyObject *)descr->d_common.d_type, NULL);
-# else
- func = PyCFunction_NewEx(descr->d_method,
- (PyObject *)descr->d_type, NULL);
-# endif
-#else
- /*
- * With limited access, we cannot use the fields of a method descriptor,
- * but in Python 3 we have the __qualname__ field which allows us to
- * grab the method object from our registry.
- */
- func = qualname_to_func(ob);
-#endif
- if (func == Py_None)
+pyside_md_get___signature__(PyObject *ob_md, const char *modifier)
+{
+ Shiboken::AutoDecRef func(name_key_to_func(ob_md));
+ if (func.object() == Py_None)
return Py_None;
- if (func == NULL)
+ if (func.isNull())
Py_FatalError("missing mapping in MethodDescriptor");
- result = pyside_cf_get___signature__(func, modifier);
- Py_DECREF(func);
- return result;
+ return pyside_cf_get___signature__(func, modifier);
}
static PyObject *
@@ -245,30 +264,44 @@ static PyObject *
GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier);
static PyObject *
-GetSignature_Function(PyCFunctionObject *func, const char *modifier)
+GetClassKey(PyObject *ob)
{
- PyObject *typemod, *type_name, *dict, *props, *selftype;
- PyObject *func_name = PyObject_GetAttrString((PyObject *)func, "__name__");
- const char *sig_kind;
- int flags;
+ assert(PyType_Check(ob) || PyModule_Check(ob));
+ /*
+ * We obtain a unique key using the module name and the class name.
+ *
+ * The class name is a bit funny when modules are nested.
+ * Example:
+ *
+ * "sample.Photon.ValueIdentity" is a class.
+ * name: "ValueIdentity"
+ * module: "sample.Photon"
+ *
+ * This is the PyCFunction behavior, as opposed to Python functions.
+ */
+ Shiboken::AutoDecRef class_name(PyObject_GetAttrString(ob, "__name__"));
+ Shiboken::AutoDecRef module_name(PyObject_GetAttrString(ob, "__module__"));
- selftype = PyCFunction_GET_SELF((PyObject *)func);
- if (selftype == NULL)
- selftype = PyDict_GetItem(pyside_globals->map_dict, (PyObject *)func);
- if (selftype == NULL) {
- if (!PyErr_Occurred())
- Py_RETURN_NONE;
- return NULL;
- }
- if ((PyType_Check(selftype) || PyModule_Check(selftype)))
- typemod = selftype;
- else
- typemod = (PyObject *)Py_TYPE(selftype);
- type_name = PyObject_GetAttrString(typemod, "__name__");
- if (type_name == NULL)
+ if (module_name.isNull())
+ PyErr_Clear();
+
+ // Note: if we have a module, then __module__ is null, and we get
+ // the module name through __name__ .
+ if (class_name.isNull())
+ return nullptr;
+ if (module_name.object())
+ return Py_BuildValue("(OO)", module_name.object(), class_name.object());
+ return Py_BuildValue("O", class_name.object());
+}
+
+static PyObject *
+GetSignature_Function(PyObject *ob_func, const char *modifier)
+{
+ Shiboken::AutoDecRef typemod(GetClassOfFunc(ob_func));
+ Shiboken::AutoDecRef type_key(GetClassKey(typemod));
+ if (type_key.isNull())
Py_RETURN_NONE;
- dict = PyDict_GetItem(pyside_globals->arg_dict, type_name);
- Py_DECREF(type_name);
+ PyObject *dict = PyDict_GetItem(pyside_globals->arg_dict, type_key);
if (dict == NULL)
Py_RETURN_NONE;
if (PyTuple_Check(dict)) {
@@ -280,31 +313,35 @@ GetSignature_Function(PyCFunctionObject *func, const char *modifier)
if (dict == NULL)
Py_RETURN_NONE;
}
- props = PyDict_GetItem(dict, func_name);
+ Shiboken::AutoDecRef func_name(PyObject_GetAttrString(ob_func, "__name__"));
+ PyObject *props = !func_name.isNull() ? PyDict_GetItem(dict, func_name) : nullptr;
if (props == NULL)
Py_RETURN_NONE;
- flags = PyCFunction_GET_FLAGS((PyObject *)func);
- if (flags & METH_CLASS)
+
+ int flags = PyCFunction_GET_FLAGS(ob_func);
+ const char *sig_kind;
+ if (PyModule_Check(typemod))
+ sig_kind = "function";
+ else if (flags & METH_CLASS)
sig_kind = "classmethod";
else if (flags & METH_STATIC)
sig_kind = "staticmethod";
else
sig_kind = "method";
- return GetSignature_Cached(props, sig_kind, modifier);
+ PyObject *ret = GetSignature_Cached(props, sig_kind, modifier);
+ return ret;
}
static PyObject *
GetSignature_Wrapper(PyObject *ob, const char *modifier)
{
- PyObject *dict, *props;
- PyObject *func_name = PyObject_GetAttrString(ob, "__name__");
- PyObject *objclass = PyObject_GetAttrString(ob, "__objclass__");
- PyObject *class_name = PyObject_GetAttrString(objclass, "__name__");
- const char *sig_kind;
+ Shiboken::AutoDecRef func_name(PyObject_GetAttrString(ob, "__name__"));
+ Shiboken::AutoDecRef objclass(PyObject_GetAttrString(ob, "__objclass__"));
+ Shiboken::AutoDecRef class_key(GetClassKey(objclass));
- if (func_name == nullptr || objclass == nullptr || class_name == nullptr)
+ if (func_name.isNull() || objclass.isNull() || class_key.isNull())
return nullptr;
- dict = PyDict_GetItem(pyside_globals->arg_dict, class_name);
+ PyObject *dict = PyDict_GetItem(pyside_globals->arg_dict, class_key);
if (dict == NULL)
Py_RETURN_NONE;
if (PyTuple_Check(dict)) {
@@ -316,65 +353,51 @@ GetSignature_Wrapper(PyObject *ob, const char *modifier)
if (dict == NULL)
Py_RETURN_NONE;
}
- props = PyDict_GetItem(dict, func_name);
- Py_DECREF(func_name);
- Py_DECREF(objclass);
- Py_DECREF(class_name);
+ PyObject *props = PyDict_GetItem(dict, func_name);
if (props == NULL)
Py_RETURN_NONE;
- sig_kind = "method";
- return GetSignature_Cached(props, sig_kind, modifier);
+ return GetSignature_Cached(props, "method", modifier);
}
static PyObject *
GetSignature_TypeMod(PyObject *ob, const char *modifier)
{
- PyObject *ob_name, *dict, *props;
- const char *sig_kind;
+ Shiboken::AutoDecRef ob_name(PyObject_GetAttrString(ob, "__name__"));
+ Shiboken::AutoDecRef ob_key(GetClassKey(ob));
- ob_name = PyObject_GetAttrString(ob, "__name__");
- dict = PyDict_GetItem(pyside_globals->arg_dict, ob_name);
+ PyObject *dict = PyDict_GetItem(pyside_globals->arg_dict, ob_key);
if (dict == NULL)
Py_RETURN_NONE;
+
if (PyTuple_Check(dict)) {
dict = PySide_BuildSignatureProps(ob);
if (dict == NULL) {
Py_RETURN_NONE;
}
}
- props = PyDict_GetItem(dict, ob_name);
- Py_DECREF(ob_name);
+ PyObject *props = PyDict_GetItem(dict, ob_name);
if (props == NULL)
Py_RETURN_NONE;
- sig_kind = "method";
- return GetSignature_Cached(props, sig_kind, modifier);
+ return GetSignature_Cached(props, "method", modifier);
}
static PyObject *
GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier)
{
- PyObject *key, *value;
-
- if (modifier == nullptr)
- key = Py_BuildValue("s", sig_kind);
- else
- key = Py_BuildValue("(ss)", sig_kind, modifier);
- if (key == nullptr)
- return nullptr;
- value = PyDict_GetItem(props, key);
+ Shiboken::AutoDecRef key(modifier == nullptr
+ ? Py_BuildValue("s", sig_kind)
+ : Py_BuildValue("(ss)", sig_kind, modifier));
+ PyObject *value = PyDict_GetItem(props, key);
if (value == nullptr) {
// we need to compute a signature object
value = CreateSignature(props, key);
if (value != nullptr) {
- if (PyDict_SetItem(props, key, value) < 0) {
+ if (PyDict_SetItem(props, key, value) < 0)
// this is an error
- Py_DECREF(key);
return nullptr;
- }
}
else {
// key not found
- Py_DECREF(key);
Py_RETURN_NONE;
}
}
@@ -385,15 +408,14 @@ static const char PySide_PythonCode[] =
"from __future__ import print_function, absolute_import\n" R"~(if True:
import sys, os, traceback
-
- pyside_package_dir = os.environ.get('PYSIDE_PACKAGE_DIR')
- if pyside_package_dir is None:
- # This happens in shiboken running ctest.
- from distutils.sysconfig import get_python_lib
- pyside_package_dir = os.path.join(get_python_lib(), 'PySide2')
- __file__ = os.path.join(pyside_package_dir, 'support', 'signature', 'loader.py')
+ # We avoid imports in phase 1 that could fail. "import shiboken" of the
+ # binary would even crash in FinishSignatureInitialization.
def bootstrap():
+ global __file__
+ import PySide2 as root
+ rp = os.path.realpath(os.path.dirname(root.__file__))
+ __file__ = os.path.join(rp, 'support', 'signature', 'loader.py')
try:
with open(__file__) as _f:
exec(compile(_f.read(), __file__, 'exec'))
@@ -407,10 +429,9 @@ static const char PySide_PythonCode[] =
static safe_globals_struc *
init_phase_1(void)
{
- safe_globals_struc *p;
PyObject *d, *v;
-
- p = (safe_globals_struc *)malloc(sizeof(safe_globals_struc));
+ safe_globals_struc *p = (safe_globals_struc *)
+ malloc(sizeof(safe_globals_struc));
if (p == NULL)
goto error;
p->helper_module = PyImport_AddModule((char *) helper_module_name);
@@ -431,11 +452,10 @@ init_phase_1(void)
if (p->map_dict == NULL)
goto error;
- // Build a dict for the prepared arguments
+ // build a dict for the prepared arguments
p->arg_dict = PyDict_New();
- if (p->arg_dict == NULL)
- goto error;
- if (PyObject_SetAttrString(p->helper_module, arg_name, p->arg_dict) < 0)
+ if (p->arg_dict == NULL
+ || PyObject_SetAttrString(p->helper_module, arg_name, p->arg_dict) < 0)
goto error;
return p;
@@ -450,30 +470,25 @@ init_phase_2(safe_globals_struc *p, PyMethodDef *methods)
PyObject *bootstrap_func, *v = nullptr;
PyMethodDef *ml;
+ // The single function to be called, but maybe more to come.
+ for (ml = methods; ml->ml_name != NULL; ml++) {
+ v = PyCFunction_NewEx(ml, nullptr, nullptr);
+ if (v == nullptr
+ || PyObject_SetAttrString(p->helper_module, ml->ml_name, v) != 0)
+ goto error;
+ Py_DECREF(v);
+ }
bootstrap_func = PyObject_GetAttrString(p->helper_module, bootstrap_name);
- if (bootstrap_func == NULL)
+ if (bootstrap_func == NULL
+ || PyObject_CallFunction(bootstrap_func, (char *)"()") == NULL)
goto error;
- if (PyObject_CallFunction(bootstrap_func, (char *)"()") == NULL)
- goto error;
- // now the loader is initialized
+ // now the loader should be initialized
p->sigparse_func = PyObject_GetAttrString(p->helper_module, func_name);
if (p->sigparse_func == NULL)
goto error;
p->createsig_func = PyObject_GetAttrString(p->helper_module, "create_signature");
if (p->createsig_func == NULL)
goto error;
-
- // The single function to be called, but maybe more to come.
- for (ml = methods; ml->ml_name != NULL; ml++) {
- v = PyCFunction_NewEx(ml, nullptr, nullptr);
- if (v == nullptr) {
- goto error;
- }
- if (PyObject_SetAttrString(p->helper_module, ml->ml_name, v) != 0) {
- goto error;
- }
- Py_DECREF(v);
- }
return 0;
error:
@@ -485,23 +500,17 @@ error:
static int
add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp)
{
- PyObject *dict;
-
assert(PyType_Check(type));
PyType_Ready(type);
- dict = type->tp_dict;
+ PyObject *dict = type->tp_dict;
for (; gsp->name != NULL; gsp++) {
- PyObject *descr;
if (PyDict_GetItemString(dict, gsp->name))
continue;
- descr = PyDescr_NewGetSet(type, gsp);
- if (descr == NULL)
+ Shiboken::AutoDecRef descr(PyDescr_NewGetSet(type, gsp));
+ if (descr.isNull())
return -1;
- if (PyDict_SetItemString(dict, gsp->name, descr) < 0) {
- Py_DECREF(descr);
+ if (PyDict_SetItemString(dict, gsp->name, descr) < 0)
return -1;
- }
- Py_DECREF(descr);
}
return 0;
}
@@ -611,13 +620,12 @@ void handler(int sig) {
static int
PySideType_Ready(PyTypeObject *type)
{
- PyObject *md, *wd;
static int init_done = 0;
if (!init_done) {
- md = PyObject_GetAttrString((PyObject *)&PyString_Type, "split"); // method-descriptor
- wd = PyObject_GetAttrString((PyObject *)Py_TYPE(Py_True), "__add__"); // wrapper-descriptor
- if (md == nullptr || wd == nullptr
+ Shiboken::AutoDecRef md(PyObject_GetAttrString((PyObject *)&PyString_Type, "split")); // method-descriptor
+ Shiboken::AutoDecRef wd(PyObject_GetAttrString((PyObject *)Py_TYPE(Py_True), "__add__")); // wrapper-descriptor
+ if (md.isNull() || wd.isNull()
|| PyType_Ready(Py_TYPE(md)) < 0
|| add_more_getsets(PepMethodDescr_TypePtr, new_PyMethodDescr_getsets) < 0
|| add_more_getsets(&PyCFunction_Type, new_PyCFunction_getsets) < 0
@@ -626,8 +634,6 @@ PySideType_Ready(PyTypeObject *type)
|| add_more_getsets(Py_TYPE(wd), new_PyWrapperDescr_getsets) < 0
)
return -1;
- Py_DECREF(md);
- Py_DECREF(wd);
#ifndef _WIN32
// We enable the stack trace in CI, only.
const char *testEnv = getenv("QTEST_ENVIRONMENT");
@@ -639,31 +645,6 @@ PySideType_Ready(PyTypeObject *type)
return PyType_Ready(type);
}
-static int
-build_func_to_type(PyObject *obtype)
-{
- PyTypeObject *type = (PyTypeObject *)obtype;
- PyObject *dict = type->tp_dict;
- PyMethodDef *meth = type->tp_methods;
-
- if (meth == 0)
- return 0;
-
- for (; meth->ml_name != NULL; meth++) {
- if (meth->ml_flags & METH_STATIC) {
- PyObject *descr = PyDict_GetItemString(dict, meth->ml_name);
- if (descr == NULL)
- return -1;
- PyObject *func = PyObject_GetAttrString(descr, "__func__");
- if (func == NULL ||
- PyDict_SetItem(pyside_globals->map_dict, func, obtype) < 0)
- return -1;
- Py_DECREF(func);
- }
- }
- return 0;
-}
-
static void
init_module_1(void)
{
@@ -680,20 +661,26 @@ static int
PySide_BuildSignatureArgs(PyObject *module, PyObject *type,
const char *signatures)
{
- PyObject *type_name, *arg_tup;
- const char *name = NULL;
+ PyObject *type_key, *arg_tup;
- init_module_1();;
+ init_module_1();
arg_tup = Py_BuildValue("(Os)", type, signatures);
if (arg_tup == NULL)
return -1;
- if (!PyModule_Check(module))
- return 0;
- name = PyModule_GetName(module);
- if (name == NULL)
- return -1;
- if (strncmp(name, "PySide2.Qt", 10) != 0)
- return 0;
+ /*
+ * We either get a module name or the dict of an EnclosingObject.
+ * We can ignore the EnclosingObject since we get full name info
+ * from the type.
+ */
+ if (PyModule_Check(module)) {
+ const char *name = PyModule_GetName(module);
+ if (name == NULL)
+ return -1;
+ if (strcmp(name, "testbinding") == 0)
+ return 0;
+ }
+ else
+ assert(PyDict_Check(module));
/*
* Normally, we would now just call the Python function with the
* arguments and then continue processing.
@@ -705,16 +692,17 @@ PySide_BuildSignatureArgs(PyObject *module, PyObject *type,
* - by calling the python function late, we can freely import PySide
* without recursion problems.
*/
- type_name = PyObject_GetAttrString(type, "__name__");
- if (type_name == NULL)
+ type_key = GetClassKey(type);
+ if (type_key == nullptr)
return -1;
- if (PyDict_SetItem(pyside_globals->arg_dict, type_name, arg_tup) < 0)
+ if (PyDict_SetItem(pyside_globals->arg_dict, type_key, arg_tup) < 0)
return -1;
/*
- * We record also a mapping from type name to type. This helps to lazily
- * initialize the Py_LIMITED_API in qualname_to_func().
+ * We record also a mapping from type key to type. This helps to lazily
+ * initialize the Py_LIMITED_API in name_key_to_func().
*/
- if (PyDict_SetItem(pyside_globals->map_dict, type_name, type) < 0)
+
+ if (PyDict_SetItem(pyside_globals->map_dict, type_key, type) < 0)
return -1;
return 0;
}
@@ -728,18 +716,20 @@ static PyMethodDef signature_methods[] = {
static void
init_module_2(void)
{
- static int init_done = 0;
+ static int init_done = 0, initializing = 0;
if (!init_done) {
+ if (initializing)
+ Py_FatalError("Init 2 called recursively!");
init_phase_2(pyside_globals, signature_methods);
init_done = 1;
+ initializing = 0;
}
}
static PyObject *
PySide_BuildSignatureProps(PyObject *classmod)
{
- PyObject *arg_tup, *dict, *type_name;
/*
* Here is the second part of the function.
* This part will be called on-demand when needed by some attribute.
@@ -747,20 +737,19 @@ PySide_BuildSignatureProps(PyObject *classmod)
* them by the function result.
*/
init_module_2();
- type_name = PyObject_GetAttrString(classmod, "__name__");
- if (type_name == NULL)
- return NULL;
- arg_tup = PyDict_GetItem(pyside_globals->arg_dict, type_name);
- if (arg_tup == NULL)
- return NULL;
- dict = PyObject_CallObject(pyside_globals->sigparse_func, arg_tup);
- if (dict == NULL)
- return NULL;
+ Shiboken::AutoDecRef type_key(GetClassKey(classmod));
+ if (type_key.isNull())
+ return nullptr;
+ PyObject *arg_tup = PyDict_GetItem(pyside_globals->arg_dict, type_key);
+ if (arg_tup == nullptr)
+ return nullptr;
+ PyObject *dict = PyObject_CallObject(pyside_globals->sigparse_func, arg_tup);
+ if (dict == nullptr)
+ return nullptr;
// We replace the arguments by the result dict.
- if (PyDict_SetItem(pyside_globals->arg_dict, type_name, dict) < 0)
- return NULL;
- Py_DECREF(type_name);
+ if (PyDict_SetItem(pyside_globals->arg_dict, type_key, dict) < 0)
+ return nullptr;
return dict;
}
@@ -779,17 +768,22 @@ SbkSpecial_Type_Ready(PyObject *module, PyTypeObject *type,
return ret;
}
+static int _finish_nested_classes(PyObject *dict);
+static int _build_func_to_type(PyObject *obtype);
+
static int
PySide_FinishSignatures(PyObject *module, const char *signatures)
{
- const char *name = NULL;
+ /*
+ * Initialization of module functions and resolving of static methods.
+ */
// CRUCIAL: Do not call this on "testbinding":
// The module is different and should not get signatures, anyway.
- name = PyModule_GetName(module);
+ const char *name = PyModule_GetName(module);
if (name == NULL)
return -1;
- if (strncmp(name, "PySide2.Qt", 10) != 0)
+ if (strcmp(name, "testbinding") == 0)
return 0;
// we abuse the call for types, since they both have a __name__ attribute.
@@ -797,9 +791,6 @@ PySide_FinishSignatures(PyObject *module, const char *signatures)
return -1;
/*
- * Python2 does not abuse the 'm_self' field for the type. So we need to
- * supply this for all static methods.
- *
* Note: This function crashed when called from PySide_BuildSignatureArgs.
* Probably this was too early.
*
@@ -807,20 +798,72 @@ PySide_FinishSignatures(PyObject *module, const char *signatures)
* to the PyCFunction attributes. Therefore I simplified things
* and always use our own mapping.
*/
- {
- PyObject *key, *value;
- Py_ssize_t pos = 0;
- PyObject *dict = PyModule_GetDict(module);
+ PyObject *key, *func, *obdict = PyModule_GetDict(module);
+ Py_ssize_t pos = 0;
- if (dict == NULL)
- return -1;
+ while (PyDict_Next(obdict, &pos, &key, &func))
+ if (PyCFunction_Check(func))
+ if (PyDict_SetItem(pyside_globals->map_dict, func, module) < 0)
+ return -1;
+ if (_finish_nested_classes(obdict) < 0)
+ return -1;
+ return 0;
+}
+
+static int
+_finish_nested_classes(PyObject *obdict)
+{
+ PyObject *key, *value, *obtype;
+ PyTypeObject *subtype;
+ Py_ssize_t pos = 0;
- while (PyDict_Next(dict, &pos, &key, &value)) {
- if (PyType_Check(value)) {
- PyObject *type = value;
- if (build_func_to_type(type) < 0)
- return -1;
- }
+ if (obdict == NULL)
+ return -1;
+ while (PyDict_Next(obdict, &pos, &key, &value)) {
+ if (PyType_Check(value)) {
+ obtype = value;
+ if (_build_func_to_type(obtype) < 0)
+ return -1;
+ // now continue with nested cases
+ subtype = reinterpret_cast<PyTypeObject *>(obtype);
+ if (_finish_nested_classes(subtype->tp_dict) < 0)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+_build_func_to_type(PyObject *obtype)
+{
+ /*
+ * There is no general way to directly get the type of a static method.
+ * On Python 3, the type is hidden in an unused pointer in the
+ * PyCFunction structure, but the Limited API does not allow to access
+ * this, either.
+ *
+ * In the end, it was easier to avoid such tricks and build an explicit
+ * mapping from function to type.
+ *
+ * We walk through the method list of the type
+ * and record the mapping from function to this type in a dict.
+ */
+ PyTypeObject *type = reinterpret_cast<PyTypeObject *>(obtype);
+ PyObject *dict = type->tp_dict;
+ PyMethodDef *meth = type->tp_methods;
+
+ if (meth == 0)
+ return 0;
+
+ for (; meth->ml_name != NULL; meth++) {
+ if (meth->ml_flags & METH_STATIC) {
+ PyObject *descr = PyDict_GetItemString(dict, meth->ml_name);
+ if (descr == NULL)
+ return -1;
+ Shiboken::AutoDecRef func(PyObject_GetAttrString(descr, "__func__"));
+ if (func.isNull() ||
+ PyDict_SetItem(pyside_globals->map_dict, func, obtype) < 0)
+ return -1;
}
}
return 0;
@@ -829,6 +872,12 @@ PySide_FinishSignatures(PyObject *module, const char *signatures)
void
FinishSignatureInitialization(PyObject *module, const char *signatures)
{
+ /*
+ * This function is called at the very end of a module
+ * initialization. SbkSpecial_Type_Ready has already been run
+ * with all the types.
+ * We now initialize module functions and resolve static methods.
+ */
if (PySide_FinishSignatures(module, signatures) < 0) {
PyErr_Print();
PyErr_SetNone(PyExc_ImportError);