diff options
-rw-r--r-- | sources/pyside2/PySide2/__init__.py.in | 2 | ||||
-rw-r--r-- | sources/pyside2/PySide2/support/signature/__init__.py | 2 | ||||
-rw-r--r-- | sources/pyside2/PySide2/support/signature/layout.py | 16 | ||||
-rw-r--r-- | sources/pyside2/PySide2/support/signature/lib/enum_sig.py | 62 | ||||
-rw-r--r-- | sources/pyside2/PySide2/support/signature/mapping.py | 42 | ||||
-rw-r--r-- | sources/pyside2/tests/registry/init_platform.py | 10 | ||||
-rw-r--r-- | sources/shiboken2/generator/shiboken2/cppgenerator.cpp | 31 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/signature.cpp | 575 |
8 files changed, 447 insertions, 293 deletions
diff --git a/sources/pyside2/PySide2/__init__.py.in b/sources/pyside2/PySide2/__init__.py.in index 631f5f13a..ac75f52b6 100644 --- a/sources/pyside2/PySide2/__init__.py.in +++ b/sources/pyside2/PySide2/__init__.py.in @@ -21,8 +21,6 @@ def _setupQtDirectories(): import shiboken2 pyside_package_dir = os.path.abspath(os.path.dirname(__file__)) - # Used by signature module. - os.environ["PYSIDE_PACKAGE_DIR"] = pyside_package_dir if sys.platform == 'win32': # PATH has to contain the package directory, otherwise plugins diff --git a/sources/pyside2/PySide2/support/signature/__init__.py b/sources/pyside2/PySide2/support/signature/__init__.py index 14e63a5fb..49224bf92 100644 --- a/sources/pyside2/PySide2/support/signature/__init__.py +++ b/sources/pyside2/PySide2/support/signature/__init__.py @@ -42,5 +42,5 @@ from __future__ import print_function, absolute_import from .loader import inspect from PySide2 import QtCore if QtCore.QProcess.__signature__: - pass # trigger initialization + pass # trigger initialization phase 2, so we can import: from signature_loader import get_signature diff --git a/sources/pyside2/PySide2/support/signature/layout.py b/sources/pyside2/PySide2/support/signature/layout.py index ac7833f03..e18cb2172 100644 --- a/sources/pyside2/PySide2/support/signature/layout.py +++ b/sources/pyside2/PySide2/support/signature/layout.py @@ -58,6 +58,7 @@ used literally as strings like "signature", "existence", etc. from textwrap import dedent from .loader import inspect + class SimpleNamespace(object): # From types.rst, because the builtin is implemented in Python 3, only. def __init__(self, **kwargs): @@ -71,6 +72,7 @@ class SimpleNamespace(object): def __eq__(self, other): return self.__dict__ == other.__dict__ + class SignatureLayout(SimpleNamespace): """ Configure a signature. @@ -140,6 +142,7 @@ typeerror = SignatureLayout(definition=False, return_annotation=False, parameter_names=False) + def define_nameless_parameter(): """ Create Nameless Parameters @@ -168,8 +171,10 @@ def define_nameless_parameter(): body["__str__"] = __str__ return type(newname, bases, body) + NamelessParameter = define_nameless_parameter() + def make_signature_nameless(signature): """ Make a Signature Nameless @@ -178,7 +183,8 @@ def make_signature_nameless(signature): The signature looks different, but is totally intact. """ for key in signature.parameters.keys(): - Signature.parameters[key].__class__ = NamelessParameter + signature.parameters[key].__class__ = NamelessParameter + def create_signature(props, key): if not props: @@ -193,7 +199,7 @@ def create_signature(props, key): else: sig_kind, modifier = key, "signature" - layout = globals()[modifier] # lookup of the modifier, here + layout = globals()[modifier] # lookup of the modifier in this module if not isinstance(layout, SignatureLayout): raise SystemError("Modifiers must be names of a SignatureLayout " "instance") @@ -201,14 +207,16 @@ def create_signature(props, key): # this is the basic layout of a signature varnames = props["varnames"] if layout.definition: - if sig_kind == "method": + if sig_kind == "function": + pass + elif sig_kind == "method": varnames = ("self",) + varnames elif sig_kind == "staticmethod": pass elif sig_kind == "classmethod": varnames = ("klass",) + varnames else: - raise SystemError("Methods must be normal, staticmethod or " + raise SystemError("Methods must be function, method, staticmethod or " "classmethod") # calculate the modifications defaults = props["defaults"][:] diff --git a/sources/pyside2/PySide2/support/signature/lib/enum_sig.py b/sources/pyside2/PySide2/support/signature/lib/enum_sig.py index 702ee7ebd..c043f04f8 100644 --- a/sources/pyside2/PySide2/support/signature/lib/enum_sig.py +++ b/sources/pyside2/PySide2/support/signature/lib/enum_sig.py @@ -37,6 +37,8 @@ ## ############################################################################# +from __future__ import print_function, absolute_import + import sys from PySide2.support.signature import inspect, get_signature @@ -58,7 +60,11 @@ class ExactEnumerator(object): with self.fmt.module(mod_name): module = sys.modules[mod_name] members = inspect.getmembers(module, inspect.isclass) + functions = inspect.getmembers(module, inspect.isroutine) ret = self.result_type() + self.fmt.class_name = None + for func_name, func in functions: + ret.update(self.function(func_name, func)) for class_name, klass in members: ret.update(self.klass(class_name, klass)) return ret @@ -79,8 +85,15 @@ class ExactEnumerator(object): # class_members = inspect.getmembers(klass) # gives us also the inherited things. class_members = sorted(list(klass.__dict__.items())) - for func_name, func in class_members: - ret.update(self.function(func_name, func)) + subclasses = [] + for thing_name, thing in class_members: + if inspect.isclass(thing): + subclass_name = ".".join((class_name, thing_name)) + subclasses.append((subclass_name, thing)) + else: + ret.update(self.function(thing_name, thing)) + for subclass_name, subclass in subclasses: + ret.update(self.klass(subclass_name, subclass)) return ret def function(self, func_name, func): @@ -92,6 +105,27 @@ class ExactEnumerator(object): return ret +def simplify(signature): + if isinstance(signature, list): + # remove duplicates which still sometimes occour: + ret = set(simplify(sig) for sig in signature) + return sorted(ret) if len(ret) > 1 else list(ret)[0] + ret = [] + for pv in signature.parameters.values(): + txt = str(pv) + if ":" not in txt: # 'self' or '*args' + continue + txt = txt[txt.index(":") + 1:] + if "=" in txt: + txt = txt[:txt.index("=")] + quote = txt[0] + if quote in ("'", '"') and txt[-1] == quote: + txt = txt[1:-1] + ret.append(txt.strip()) + return tuple(ret) + + +### disabled for now: class SimplifyingEnumerator(ExactEnumerator): """ SimplifyingEnumerator enumerates all signatures in a module filtered. @@ -109,5 +143,27 @@ class SimplifyingEnumerator(ExactEnumerator): signature = get_signature(func, 'existence') if signature is not None and func_name not in ("next", "__next__"): with self.fmt.function(func_name, signature) as key: - ret[key] = signature + ret[key] = str(signature) + return ret + + +class SimplifyingEnumerator(ExactEnumerator): + """ + SimplifyingEnumerator enumerates all signatures in a module filtered. + + There are no default values, no variable + names and no self parameter. Only types are present after simplification. + The functions 'next' resp. '__next__' are removed + to make the output identical for Python 2 and 3. + An appropriate formatter should be supplied, if printable output + is desired. + """ + + def function(self, func_name, func): + ret = self.result_type() + signature = getattr(func, '__signature__', None) + sig = simplify(signature) if signature is not None else None + if sig is not None and func_name not in ("next", "__next__", "__div__"): + with self.fmt.function(func_name, sig) as key: + ret[key] = sig return ret diff --git a/sources/pyside2/PySide2/support/signature/mapping.py b/sources/pyside2/PySide2/support/signature/mapping.py index 6b7d1ad01..23ba6a7f1 100644 --- a/sources/pyside2/PySide2/support/signature/mapping.py +++ b/sources/pyside2/PySide2/support/signature/mapping.py @@ -56,6 +56,11 @@ import sys import struct import PySide2 try: + import sample +except ImportError: + pass + +try: from . import typing except ImportError: import typing @@ -64,10 +69,12 @@ ellipsis = "..." Char = typing.Union[str, int] # how do I model the limitation to 1 char? StringList = typing.List[str] IntList = typing.List[int] +IntMatrix = typing.List[IntList] Variant = typing.Any ModelIndexList = typing.List[int] QImageCleanupFunction = typing.Callable -FloatMatrix = typing.List[typing.List[float]] +FloatList = typing.List[float] +FloatMatrix = typing.List[FloatList] # Pair could be more specific, but we loose the info in the generator. Pair = typing.Tuple[typing.Any, typing.Any] MultiMap = typing.DefaultDict[str, typing.List[str]] @@ -132,7 +139,7 @@ class Instance(_NotCalled): class Reloader(object): def __init__(self): self.sys_module_count = 0 - self.uninitialized = PySide2.__all__[:] + self.uninitialized = PySide2.__all__[:] + ["sample"] def update(self): if self.sys_module_count == len(sys.modules): @@ -140,7 +147,7 @@ class Reloader(object): self.sys_module_count = len(sys.modules) g = globals() for mod_name in self.uninitialized[:]: - if "PySide2." + mod_name in sys.modules: + if "PySide2." + mod_name in sys.modules or mod_name == "sample": self.uninitialized.remove(mod_name) proc_name = "init_" + mod_name if proc_name in g: @@ -289,6 +296,7 @@ def init_QtCore(): "PySide2.QtCore.QAbstractItemModel.CheckIndexOptions.NoOption"), # 5.11 "QVariantMap": dict, "PySide2.QtCore.QCborStreamReader.StringResult": typing.AnyStr, + "PySide2.QtCore.double": float, }) try: type_map.update({ @@ -299,6 +307,7 @@ def init_QtCore(): pass return locals() + def init_QtGui(): import PySide2.QtGui type_map.update({ @@ -328,6 +337,7 @@ def init_QtGui(): }) return locals() + def init_QtWidgets(): import PySide2.QtWidgets from PySide2.QtWidgets import QWidget, QMessageBox, QStyleOption, QStyleHintReturn, QStyleOptionComplex @@ -364,6 +374,7 @@ def init_QtWidgets(): }) return locals() + def init_QtSql(): import PySide2.QtSql from PySide2.QtSql import QSqlDatabase @@ -373,6 +384,7 @@ def init_QtSql(): }) return locals() + def init_QtNetwork(): import PySide2.QtNetwork type_map.update({ @@ -383,6 +395,7 @@ def init_QtNetwork(): }) return locals() + def init_QtXmlPatterns(): import PySide2.QtXmlPatterns from PySide2.QtXmlPatterns import QXmlName @@ -392,6 +405,7 @@ def init_QtXmlPatterns(): }) return locals() + def init_QtMultimedia(): import PySide2.QtMultimedia import PySide2.QtMultimediaWidgets @@ -401,6 +415,7 @@ def init_QtMultimedia(): }) return locals() + def init_QtOpenGL(): import PySide2.QtOpenGL type_map.update({ @@ -417,6 +432,7 @@ def init_QtOpenGL(): }) return locals() + def init_QtQml(): import PySide2.QtQml type_map.update({ @@ -429,6 +445,7 @@ def init_QtQml(): }) return locals() + def init_QtQuick(): import PySide2.QtQuick type_map.update({ @@ -440,6 +457,7 @@ def init_QtQuick(): }) return locals() + def init_QtScript(): import PySide2.QtScript type_map.update({ @@ -447,6 +465,7 @@ def init_QtScript(): }) return locals() + def init_QtTest(): import PySide2.QtTest type_map.update({ @@ -471,6 +490,23 @@ def init_QtWinExtras(): }) return locals() +def init_sample(): + type_map.update({ + "sample.int": int, + "Complex": complex, + "sample.OddBool": bool, + "sample.bool": bool, + "sample.PStr": str, + "double[]": FloatList, + "OddBool": bool, + "PStr": str, + "sample.char": Char, + "double[][]": FloatMatrix, + "int[]": IntList, + "int[][]": IntMatrix, + }) + return locals() + # Here was testbinding, actually the source of all evil. # end of file diff --git a/sources/pyside2/tests/registry/init_platform.py b/sources/pyside2/tests/registry/init_platform.py index 22875a63e..66ec6f566 100644 --- a/sources/pyside2/tests/registry/init_platform.py +++ b/sources/pyside2/tests/registry/init_platform.py @@ -143,7 +143,10 @@ class Formatter(object): @contextmanager def function(self, func_name, signature): - key = viskey = "{}.{}".format(self.class_name, func_name) + if self.class_name is None: + key = viskey = "{}".format(func_name) + else: + key = viskey = "{}.{}".format(self.class_name, func_name) if key.endswith("lY"): # Some classes like PySide2.QtGui.QContextMenuEvent have functions # globalX and the same with Y. The gerrit robot thinks that this @@ -176,8 +179,9 @@ def generate_all(): This file contains the simplified signatures for all functions in PySide for module '{}'. There are no default values, no variable names and no self parameter. Only types are present after simplification. - The functions 'next' resp. '__next__' are removed - to make the output identical for Python 2 and 3. + The functions 'next' resp. '__next__' are removed to make the output + identical for Python 2 and 3. '__div__' is also removed, + since it exists in Python 2, only. """ '''.format(module))) fmt.print("import sys") diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 99947d347..393f8a850 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -5415,10 +5415,10 @@ bool CppGenerator::finishGeneration() s << "#include <sbkpython.h>" << endl; s << "#include <shiboken.h>" << endl; s << "#include <algorithm>" << endl; + s << "#include <signature.h>" << endl; if (usePySideExtensions()) { s << includeQDebug; s << "#include <pyside.h>" << endl; - s << "#include <signature.h>" << endl; s << "#include <qapp_macro.h>" << endl; } @@ -5714,22 +5714,25 @@ bool CppGenerator::finishGeneration() // cleanup staticMetaObject attribute s << INDENT << "PySide::registerCleanupFunction(cleanTypesAttributes);" << endl << endl; + } - // PYSIDE-510: Create a signatures string for the introspection feature. - s << "// The signatures string for the global functions." << endl; - s << "// Multiple signatures have their index \"n:\" in front." << endl; - s << "const char " << moduleName() << "_SignaturesString[] = \"\"" << endl; - QString line; - while (signatureStream.readLineInto(&line)) - s << INDENT << '"' << line << "\\n\"" << endl; - s << ';' << endl; - // finish the rest of __signature__ initialization. - s << INDENT << "FinishSignatureInitialization(module, " << moduleName() - << "_SignaturesString);" << endl; + // PYSIDE-510: Create a signatures string for the introspection feature. + s << "// The signatures string for the global functions." << endl; + s << "// Multiple signatures have their index \"n:\" in front." << endl; + s << "const char " << moduleName() << "_SignaturesString[] = \"\"" << endl; + QString line; + while (signatureStream.readLineInto(&line)) + s << INDENT << '"' << line << "\\n\"" << endl; + s << ';' << endl; + // finish the rest of __signature__ initialization. + s << INDENT << "FinishSignatureInitialization(module, " << moduleName() + << "_SignaturesString);" << endl; + + if (usePySideExtensions()) { // initialize the qApp module. - s << INDENT << "NotifyModuleForQApp(module);" << endl << endl; + s << INDENT << "NotifyModuleForQApp(module);" << endl; } - + s << endl; s << "SBK_MODULE_INIT_FUNCTION_END" << endl; return file.done() != FileOut::Failure; 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); |