aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken2/libshiboken/signature.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken2/libshiboken/signature.cpp')
-rw-r--r--sources/shiboken2/libshiboken/signature.cpp820
1 files changed, 524 insertions, 296 deletions
diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp
index f0bb8e609..922f85906 100644
--- a/sources/shiboken2/libshiboken/signature.cpp
+++ b/sources/shiboken2/libshiboken/signature.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt for Python.
@@ -38,6 +38,7 @@
****************************************************************************/
#include "basewrapper.h"
+#include "autodecref.h"
extern "C"
{
@@ -77,18 +78,25 @@ typedef struct safe_globals_struc {
static safe_globals pyside_globals = 0;
-static PyObject *GetSignature_Function(PyCFunctionObject *);
-static PyObject *GetSignature_TypeMod(PyObject *);
+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);
static PyObject *PySide_BuildSignatureProps(PyObject *class_mod);
+static void init_module_1(void);
+static void init_module_2(void);
+
const char helper_module_name[] = "signature_loader";
const char bootstrap_name[] = "bootstrap";
const char arg_name[] = "pyside_arg_dict";
const char func_name[] = "pyside_type_init";
static PyObject *
-CreateSignature(PyObject *props, const char *sig_kind)
+CreateSignature(PyObject *props, PyObject *key)
{
/*
* Here is the new function to create all signatures. It simply calls
@@ -97,30 +105,95 @@ CreateSignature(PyObject *props, const char *sig_kind)
* to support '_signature_is_functionlike()'.
*/
return PyObject_CallFunction(pyside_globals->createsig_func,
- (char *)"(Os)", props, sig_kind);
+ (char *)"(OO)", props, key);
}
static PyObject *
-pyside_cf_get___signature__(PyObject *func)
+pyside_cf_get___signature__(PyObject *func, const char *modifier)
{
- return GetSignature_Function((PyCFunctionObject *)func);
+ init_module_2();
+ return GetSignature_Function(func, modifier);
}
static PyObject *
-pyside_sm_get___signature__(PyObject *sm)
+pyside_sm_get___signature__(PyObject *sm, const char *modifier)
{
- PyObject *func, *ret;
+ init_module_2();
+ Shiboken::AutoDecRef func(PyObject_GetAttrString(sm, "__func__"));
+ if (Py_TYPE(func) == PepFunction_TypePtr)
+ Py_RETURN_NONE;
+ return GetSignature_Function(func, modifier);
+}
- func = PyObject_GetAttrString(sm, "__func__");
- ret = GetSignature_Function((PyCFunctionObject *)func);
- 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;
@@ -129,230 +202,245 @@ 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) {
- return -1;
- }
- if (PyDict_SetItem(pyside_globals->map_dict, qualname, func) < 0) {
+ 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;
- }
- 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)
-{
- 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)
+{
+ init_module_2();
+ 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);
- Py_DECREF(func);
- return result;
+ return pyside_cf_get___signature__(func, modifier);
}
static PyObject *
-pyside_tp_get___signature__(PyObject *typemod)
+pyside_wd_get___signature__(PyObject *ob, const char *modifier)
{
- return GetSignature_TypeMod(typemod);
+ init_module_2();
+ return GetSignature_Wrapper(ob, modifier);
}
static PyObject *
-GetSignature_Function(PyCFunctionObject *func)
+pyside_tp_get___signature__(PyObject *typemod, const char *modifier)
{
- PyObject *typemod, *type_name, *dict, *props, *value, *selftype;
- PyObject *func_name = PyObject_GetAttrString((PyObject *)func, "__name__");
- const char *sig_kind;
- int flags;
+ init_module_2();
+ return GetSignature_TypeMod(typemod, modifier);
+}
- selftype = PyCFunction_GET_SELF((PyObject *)func);
- if (selftype == NULL)
- selftype = PyDict_GetItem(pyside_globals->map_dict, (PyObject *)func);
- if (selftype == NULL) {
- if (!PyErr_Occurred()) {
- PyErr_Format(PyExc_SystemError,
- "the signature for \"%s\" should exist",
- PepCFunction_GET_NAMESTR(func)
- );
- }
- return NULL;
+// forward
+static PyObject *
+GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier);
+
+static PyObject *
+GetClassKey(PyObject *ob)
+{
+ 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__"));
+
+ 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 *empty_dict = nullptr;
+
+static PyObject *
+TypeKey_to_PropsDict(PyObject *type_key, PyObject *obtype)
+{
+ PyObject *dict = PyDict_GetItem(pyside_globals->arg_dict, type_key);
+ if (dict == nullptr) {
+ if (empty_dict == nullptr)
+ empty_dict = PyDict_New();
+ dict = empty_dict;
}
- 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 (PyTuple_Check(dict))
+ dict = PySide_BuildSignatureProps(obtype);
+ return dict;
+}
+
+static PyObject *
+GetSignature_Function(PyObject *ob_func, const char *modifier)
+{
+ // make sure that we look into PyCFunction, only...
+ if (Py_TYPE(ob_func) == PepFunction_TypePtr)
Py_RETURN_NONE;
- dict = PyDict_GetItem(pyside_globals->arg_dict, type_name);
- Py_DECREF(type_name);
- if (dict == NULL)
+ Shiboken::AutoDecRef typemod(GetClassOfFunc(ob_func));
+ Shiboken::AutoDecRef type_key(GetClassKey(typemod));
+ if (type_key.isNull())
Py_RETURN_NONE;
- if (PyTuple_Check(dict)) {
- /*
- * We do the initialization lazily.
- * This has also the advantage that we can freely import PySide.
- */
- dict = PySide_BuildSignatureProps(typemod);
- if (dict == NULL)
- Py_RETURN_NONE;
- }
- props = PyDict_GetItem(dict, func_name);
- if (props == NULL)
+ PyObject *dict = TypeKey_to_PropsDict(type_key, typemod);
+ if (dict == nullptr)
+ return nullptr;
+ Shiboken::AutoDecRef func_name(PyObject_GetAttrString(ob_func, "__name__"));
+ PyObject *props = !func_name.isNull() ? PyDict_GetItem(dict, func_name) : nullptr;
+ if (props == nullptr)
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";
- value = PyDict_GetItemString(props, sig_kind);
- if (value == NULL) {
- // we need to compute a signature object
- value = CreateSignature(props, sig_kind);
- if (value != NULL) {
- if (PyDict_SetItemString(props, sig_kind, value) < 0)
- return NULL;
- }
- else
- Py_RETURN_NONE;
- }
- return Py_INCREF(value), value;
+ return GetSignature_Cached(props, sig_kind, modifier);
}
static PyObject *
-GetSignature_TypeMod(PyObject *ob)
+GetSignature_Wrapper(PyObject *ob, const char *modifier)
{
- PyObject *ob_name, *dict, *props, *value;
- const char *sig_kind;
-
- ob_name = PyObject_GetAttrString(ob, "__name__");
- dict = PyDict_GetItem(pyside_globals->arg_dict, ob_name);
- if (dict == NULL)
+ Shiboken::AutoDecRef func_name(PyObject_GetAttrString(ob, "__name__"));
+ Shiboken::AutoDecRef objclass(PyObject_GetAttrString(ob, "__objclass__"));
+ Shiboken::AutoDecRef class_key(GetClassKey(objclass));
+
+ if (func_name.isNull() || objclass.isNull() || class_key.isNull())
+ return nullptr;
+ PyObject *dict = TypeKey_to_PropsDict(class_key, objclass);
+ if (dict == nullptr)
+ return nullptr;
+ PyObject *props = PyDict_GetItem(dict, func_name);
+ if (props == nullptr)
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);
- if (props == NULL)
+ return GetSignature_Cached(props, "method", modifier);
+}
+
+static PyObject *
+GetSignature_TypeMod(PyObject *ob, const char *modifier)
+{
+ Shiboken::AutoDecRef ob_name(PyObject_GetAttrString(ob, "__name__"));
+ Shiboken::AutoDecRef ob_key(GetClassKey(ob));
+
+ PyObject *dict = TypeKey_to_PropsDict(ob_key, ob);
+ if (dict == nullptr)
+ return nullptr;
+ PyObject *props = PyDict_GetItem(dict, ob_name);
+ if (props == nullptr)
Py_RETURN_NONE;
- sig_kind = "method";
- value = PyDict_GetItemString(props, sig_kind);
- if (value == NULL) {
+ return GetSignature_Cached(props, "method", modifier);
+}
+
+static PyObject *
+GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier)
+{
+ 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, sig_kind);
- if (value != NULL) {
- if (PyDict_SetItemString(props, sig_kind, value) < 0)
- return NULL;
+ value = CreateSignature(props, key);
+ if (value != nullptr) {
+ if (PyDict_SetItem(props, key, value) < 0)
+ // this is an error
+ return nullptr;
}
- else
+ else {
+ // key not found
Py_RETURN_NONE;
+ }
}
return Py_INCREF(value), value;
}
-
static const char PySide_PythonCode[] =
- "from __future__ import print_function, absolute_import\n"
- "import sys, os, traceback\n"
-
- "pyside_package_dir = os.environ.get('PYSIDE_PACKAGE_DIR', '.')\n"
- "__file__ = os.path.join(pyside_package_dir, 'support', 'signature', 'loader.py')\n"
-
- "def bootstrap():\n"
- " try:\n"
- " with open(__file__) as _f:\n"
- " exec(compile(_f.read(), __file__, 'exec'))\n"
- " except Exception as e:\n"
- " print('Exception:', e)\n"
- " traceback.print_exc(file=sys.stdout)\n"
- " globals().update(locals())\n"
- ;
+ "from __future__ import print_function, absolute_import\n" R"~(if True:
+
+ # This is becoming the 'signature_loader' module.
+
+ import sys, os, traceback
+ # We avoid imports in phase 1 that could fail. "import shiboken" of the
+ # binary would even crash in FinishSignatureInitialization.
+
+ def bootstrap():
+ global __file__
+ try:
+ import shiboken2 as root
+ except ImportError:
+ # uninstalled case without ctest, try only this one which has __init__:
+ from shibokenmodule import shiboken2 as root
+ rp = os.path.realpath(os.path.dirname(root.__file__))
+ # This can be the shiboken2 directory or the binary module, so search.
+ while len(rp) > 3 and not os.path.exists(os.path.join(rp, 'support')):
+ rp = os.path.abspath(os.path.join(rp, '..'))
+ __file__ = os.path.join(rp, 'support', 'signature', 'loader.py')
+ try:
+ with open(__file__) as _f:
+ exec(compile(_f.read(), __file__, 'exec'))
+ except Exception as e:
+ print('Exception:', e)
+ traceback.print_exc(file=sys.stdout)
+ globals().update(locals())
+
+ )~";
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);
@@ -373,11 +461,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;
@@ -387,16 +474,24 @@ error:
}
static int
-init_phase_2(safe_globals_struc *p)
+init_phase_2(safe_globals_struc *p, PyMethodDef *methods)
{
- PyObject *bootstrap_func;
-
+ 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;
@@ -406,6 +501,8 @@ init_phase_2(safe_globals_struc *p)
return 0;
error:
+ Py_XDECREF(v);
+ PyErr_Print();
PyErr_SetString(PyExc_SystemError, "could not initialize part 2");
return -1;
}
@@ -413,20 +510,17 @@ error:
static int
add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp)
{
+ assert(PyType_Check(type));
+ PyType_Ready(type);
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;
}
@@ -463,6 +557,47 @@ static PyGetSetDef new_PyType_getsets[] = {
{0}
};
+static PyGetSetDef new_PyWrapperDescr_getsets[] = {
+ {(char *) "__signature__", (getter)pyside_wd_get___signature__},
+ {0}
+};
+
+////////////////////////////////////////////////////////////////////////////
+//
+// get_signature -- providing a superior interface
+//
+// Additionally to the interface via __signature__, we also provide
+// a general function, which allows for different signature layouts.
+// The "modifier" argument is a string that is passed in from loader.py .
+// Configuration what the modifiers mean is completely in Python.
+//
+
+static PyObject *
+get_signature(PyObject *self, PyObject *args)
+{
+ PyObject *ob;
+ const char *modifier = nullptr;
+
+ init_module_1();
+
+ if (!PyArg_ParseTuple(args, "O|s", &ob, &modifier))
+ return NULL;
+ if (Py_TYPE(ob) == PepFunction_TypePtr)
+ Py_RETURN_NONE;
+
+ if (Py_TYPE(ob) == &PyCFunction_Type)
+ return pyside_cf_get___signature__(ob, modifier);
+ if (Py_TYPE(ob) == PepStaticMethod_TypePtr)
+ return pyside_sm_get___signature__(ob, modifier);
+ if (Py_TYPE(ob) == PepMethodDescr_TypePtr)
+ return pyside_md_get___signature__(ob, modifier);
+ if (PyType_Check(ob))
+ return pyside_tp_get___signature__(ob, modifier);
+ if (Py_TYPE(ob) == &PyWrapperDescr_Type)
+ return pyside_wd_get___signature__(ob, modifier);
+ Py_RETURN_NONE;
+}
+
////////////////////////////////////////////////////////////////////////////
//
// This special Type_Ready does certain initializations earlier with
@@ -495,25 +630,22 @@ void handler(int sig) {
#endif // _WIN32
static int
-PySideType_Ready(PyTypeObject *type)
+PySide_PatchTypes(void)
{
- PyObject *md;
static int init_done = 0;
if (!init_done) {
- // Python2 does not expose certain types. We look them up:
- // PyMethodDescr_Type 'type(str.__dict__["split"])'
- // PyClassMethodDescr_Type. 'type(dict.__dict__["fromkeys"])'
- // The latter is not needed until we use class methods in PySide.
- md = PyObject_GetAttrString((PyObject *)&PyString_Type, "split");
- if (md == NULL
+ 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(Py_TYPE(md), new_PyMethodDescr_getsets) < 0
+ || add_more_getsets(PepMethodDescr_TypePtr, new_PyMethodDescr_getsets) < 0
|| add_more_getsets(&PyCFunction_Type, new_PyCFunction_getsets) < 0
|| add_more_getsets(PepStaticMethod_TypePtr, new_PyStaticMethod_getsets) < 0
- || add_more_getsets(&PyType_Type, new_PyType_getsets) < 0)
+ || add_more_getsets(&PyType_Type, new_PyType_getsets) < 0
+ || add_more_getsets(Py_TYPE(wd), new_PyWrapperDescr_getsets) < 0
+ )
return -1;
- Py_DECREF(md);
#ifndef _WIN32
// We enable the stack trace in CI, only.
const char *testEnv = getenv("QTEST_ENVIRONMENT");
@@ -522,58 +654,38 @@ PySideType_Ready(PyTypeObject *type)
#endif // _WIN32
init_done = 1;
}
- return PyType_Ready(type);
+ return 0;
}
-static int
-build_func_to_type(PyObject *obtype)
+static void
+init_module_1(void)
{
- PyTypeObject *type = (PyTypeObject *)obtype;
- PyObject *dict = type->tp_dict;
- PyMethodDef *meth = type->tp_methods;
-
- if (meth == 0)
- return 0;
+ static int init_done = 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);
- }
+ if (!init_done) {
+ pyside_globals = init_phase_1();
+ if (pyside_globals != nullptr)
+ init_done = 1;
}
- return 0;
}
static int
PySide_BuildSignatureArgs(PyObject *module, PyObject *type,
const char *signatures)
{
- PyObject *type_name, *arg_tup;
- const char *name = NULL;
- static int init_done = 0;
+ PyObject *type_key, *arg_tup;
- if (!init_done) {
- pyside_globals = init_phase_1();
- if (pyside_globals == NULL)
- return -1;
- init_done = 1;
- }
+ init_module_1();
arg_tup = Py_BuildValue("(Os)", type, signatures);
if (arg_tup == NULL)
return -1;
+ /*
+ * 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))
- return 0;
- name = PyModule_GetName(module);
- if (name == NULL)
- return -1;
- if (strncmp(name, "PySide2.Qt", 10) != 0)
- return 0;
+ assert(PyDict_Check(module));
/*
* Normally, we would now just call the Python function with the
* arguments and then continue processing.
@@ -585,51 +697,69 @@ 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;
}
-static PyObject *
-PySide_BuildSignatureProps(PyObject *classmod)
+static PyMethodDef signature_methods[] = {
+ {"get_signature", (PyCFunction)get_signature, METH_VARARGS,
+ "get the __signature__, but pass an optional string parameter"},
+ {NULL, NULL}
+};
+
+static void
+init_module_2(void)
{
- PyObject *arg_tup, *dict, *type_name;
static int init_done = 0;
if (!init_done) {
- if (init_phase_2(pyside_globals) < 0)
- return NULL;
+ // Phase 2 will call __init__.py which touches a signature, itself.
+ // Therefore we set init_done prior to init_phase_2().
init_done = 1;
+ init_phase_2(pyside_globals, signature_methods);
}
+}
+
+static PyObject *
+PySide_BuildSignatureProps(PyObject *classmod)
+{
/*
* Here is the second part of the function.
* This part will be called on-demand when needed by some attribute.
* We simply pick up the arguments that we stored here and replace
* them by the function result.
*/
- 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;
+ init_module_2();
+ 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) {
+ if (PyErr_Occurred())
+ return nullptr;
+ // No error: return an empty dict.
+ if (empty_dict == nullptr)
+ empty_dict = PyDict_New();
+ return empty_dict;
+ }
// 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;
}
@@ -638,7 +768,7 @@ SbkSpecial_Type_Ready(PyObject *module, PyTypeObject *type,
const char *signatures)
{
int ret;
- if (PySideType_Ready(type) < 0)
+ if (PyType_Ready(type) < 0)
return -1;
ret = PySide_BuildSignatureArgs(module, (PyObject *)type, signatures);
if (ret < 0) {
@@ -648,27 +778,25 @@ 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)
- return 0;
// we abuse the call for types, since they both have a __name__ attribute.
if (PySide_BuildSignatureArgs(module, module, signatures) < 0)
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.
*
@@ -676,20 +804,111 @@ 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;
+
+ 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;
+
+ 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 static method to this type in a dict.
+ * We also check for hidden methods, see below.
+ */
+ PyTypeObject *type = reinterpret_cast<PyTypeObject *>(obtype);
+ PyObject *dict = type->tp_dict;
+ PyMethodDef *meth = type->tp_methods;
- if (dict == NULL)
+ if (meth == 0)
+ return 0;
+
+ for (; meth->ml_name != NULL; meth++) {
+ /*
+ * It is possible that a method is overwritten by another
+ * attribute with the same name. This case was obviously provoked
+ * explicitly in "testbinding.TestObject.staticMethodDouble",
+ * where instead of the method a "PySide2.QtCore.Signal" object
+ * was in the dict.
+ * This overlap is also found in regular PySide under
+ * "PySide2.QtCore.QProcess.error" where again a signal object is
+ * returned. These hidden methods will be opened for the
+ * signature module by adding them under the name
+ * "{name}.overload".
+ */
+ PyObject *descr = PyDict_GetItemString(dict, meth->ml_name);
+ const char *look_attr = meth->ml_flags & METH_STATIC ? "__func__" : "__name__";
+ int check_name = meth->ml_flags & METH_STATIC ? 0 : 1;
+ if (descr == NULL)
return -1;
- while (PyDict_Next(dict, &pos, &key, &value)) {
- if (PyType_Check(value)) {
- PyObject *type = value;
- if (build_func_to_type(type) < 0)
- return -1;
- }
+ // We first check all methods if one is hidden by something else.
+ Shiboken::AutoDecRef look(PyObject_GetAttrString(descr, look_attr));
+ Shiboken::AutoDecRef given(Py_BuildValue("s", meth->ml_name));
+ if (look.isNull()
+ || (check_name && PyObject_RichCompareBool(look, given, Py_EQ) != 1)) {
+ PyErr_Clear();
+ Shiboken::AutoDecRef cfunc(PyCFunction_NewEx(meth, (PyObject*)type, NULL));
+ if (cfunc.isNull())
+ return -1;
+ if (meth->ml_flags & METH_STATIC)
+ descr = PyStaticMethod_New(cfunc);
+ else
+ descr = PyDescr_NewMethod(type, meth);
+ if (descr == nullptr)
+ return -1;
+ char mangled_name[200];
+ strcpy(mangled_name, meth->ml_name);
+ strcat(mangled_name, ".overload");
+ if (PyDict_SetItemString(dict, mangled_name, descr) < 0)
+ return -1;
+ if (PyDict_SetItemString(pyside_globals->map_dict, mangled_name, obtype) < 0)
+ return -1;
+ continue;
+ }
+ // Then we insert the mapping for static methods.
+ if (meth->ml_flags & METH_STATIC) {
+ if (PyDict_SetItem(pyside_globals->map_dict, look, obtype) < 0)
+ return -1;
}
}
return 0;
@@ -698,6 +917,15 @@ 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.
+ * We now patch certain types to support the __signature__ attribute,
+ * initialize module functions and resolve static methods.
+ *
+ * Still, it is not possible to call init phase 2 from here,
+ * because the import is still running. Do it from Python!
+ */
+ PySide_PatchTypes();
if (PySide_FinishSignatures(module, signatures) < 0) {
PyErr_Print();
PyErr_SetNone(PyExc_ImportError);