From cd6172063756a59e02f1a7857bc60a1737214ad1 Mon Sep 17 00:00:00 2001 From: Christian Tismer Date: Fri, 30 Aug 2019 06:27:49 +0200 Subject: Fix heaptype conflict with QtCore.QObject.__new__in Python 2.7 The patching of the type generation needs to become universal. Additional to the patch for SbkObjectType_TypeF, we now patch * SbkObjectTypeTpNew * introduceWrapperType which makes the modifications for the heaptype flag complete. Actually, the modification of tp_new_wrapper has to be more sophisticated in this general case: We follow the __mro__ and patch exactly those functions which have the old wrapper and add the new wrapper. Change-Id: I51d4d77c99bd18eed2e31d2ab21143c0f4e2ea6c Fixes: PYSIDE-1051 Reviewed-by: Qt CI Bot Reviewed-by: Cristian Maureira-Fredes --- sources/shiboken2/libshiboken/basewrapper.cpp | 94 +++++++++++++++------------ 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp index b9f6735d8..9d233b847 100644 --- a/sources/shiboken2/libshiboken/basewrapper.cpp +++ b/sources/shiboken2/libshiboken/basewrapper.cpp @@ -92,6 +92,7 @@ static PyType_Slot SbkObjectType_Type_slots[] = { {Py_tp_alloc, reinterpret_cast(PyType_GenericAlloc)}, {Py_tp_new, reinterpret_cast(SbkObjectTypeTpNew)}, {Py_tp_free, reinterpret_cast(PyObject_GC_Del)}, + {Py_tp_getset, reinterpret_cast(SbkObjectType_Type_getsetlist)}, {0, nullptr} }; static PyType_Spec SbkObjectType_Type_spec = { @@ -106,7 +107,7 @@ static PyType_Spec SbkObjectType_Type_spec = { #if PY_VERSION_HEX < 0x03000000 /***************************************************************************** * - * PYSIDE-816: Workaround for Python 2.7 + * PYSIDE-816: Workaround for Python 2.7 for SbkObjectType_TypeF(). * * This is an add-on for function typeobject.c:tp_new_wrapper from Python 2.7 . * Problem: @@ -121,9 +122,16 @@ static PyType_Spec SbkObjectType_Type_spec = { * The problem is that heap types have this unwanted dependency. * But we cannot get at static slot_tp_new, and so we have to use * the original function and patch Py_TPFLAGS_HEAPTYPE away during the call. + * + * PYSIDE-1051: The same problem holds for all dynamic metatypes generated by + * SbkObjectTypeTpNew() and all types generated by + * introduceWrapperType() . + * + * This led to a drastic overhaul of patch_tp_new_wrapper() which now adds + * the new wrapper to exactly those types which have the old wrapper. */ -static PyCFunction old_tp_new_wrapper = nullptr; +ternaryfunc old_tp_new_wrapper = nullptr; static PyObject * tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds) @@ -136,9 +144,9 @@ tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds) return ret; } -// This is intentionally the new docstring of Python 3.7 . +// This is intentionally the __new__ docstring of Python 3.7 . static struct PyMethodDef tp_new_methoddef[] = { - {"__new__", (PyCFunction)tp_new_wrapper, METH_VARARGS|METH_KEYWORDS, + {"__new__", reinterpret_cast(tp_new_wrapper), METH_VARARGS|METH_KEYWORDS, PyDoc_STR("__new__($type, *args, **kwargs)\n--\n\n" "Create and return a new object. " "See help(type) for accurate signature.")}, @@ -146,43 +154,39 @@ static struct PyMethodDef tp_new_methoddef[] = { }; static int -get_old_tp_new_wrapper(void) -{ - // We get the old tp_new_wrapper from any initialized type. - PyTypeObject *type = &PyType_Type; - PyObject *dict = type->tp_dict; - PyObject *key, *func = nullptr; - Py_ssize_t pos = 0; - while (PyDict_Next(dict, &pos, &key, &func)) { - char *name = PyString_AsString(key); - if (strcmp(name, "__new__") == 0) { - break; +patch_tp_new_wrapper(PyTypeObject *type) +{ + /* + * The old tp_new_wrapper is added to all types that have tp_new. + * We patch that with a version that ignores the heaptype flag. + */ + static PyObject *__new__ = nullptr; + if (old_tp_new_wrapper == nullptr) { + if ((__new__ = Py_BuildValue("s", "__new__")) == nullptr) + return -1; + PyObject *func = PyDict_GetItem(PyType_Type.tp_dict, __new__); + PyCFunctionObject *pycf_ob = reinterpret_cast(func); + old_tp_new_wrapper = reinterpret_cast(pycf_ob->m_ml->ml_meth); + } + PyObject *mro = type->tp_mro; + Py_ssize_t i, n = PyTuple_GET_SIZE(mro); + for (i = 0; i < n; i++) { + type = reinterpret_cast(PyTuple_GET_ITEM(mro, i)); + PyObject *existing = PyDict_GetItem(type->tp_dict, __new__); + if (existing && PyCFunction_Check(existing) + && type->tp_flags & Py_TPFLAGS_HEAPTYPE) { + auto *pycf_ob = reinterpret_cast(existing); + auto existing_wrapper = reinterpret_cast(pycf_ob->m_ml->ml_meth); + if (existing_wrapper == tp_new_wrapper) + break; + if (existing_wrapper == old_tp_new_wrapper) { + PyObject *ob_type = reinterpret_cast(type); + Shiboken::AutoDecRef func(PyCFunction_New(tp_new_methoddef, ob_type)); + if (func.isNull() || PyDict_SetItem(type->tp_dict, __new__, func)) + return -1; + } } } - if (func == nullptr) - return -1; - PyCFunctionObject *pycf_ob = reinterpret_cast(func); - old_tp_new_wrapper = pycf_ob->m_ml->ml_meth; - return 0; -} - -static int -add_tp_new_wrapper(PyTypeObject *type) -{ - // get the original tp_new_wrapper - if (old_tp_new_wrapper == nullptr && get_old_tp_new_wrapper() < 0) - return -1; - // initialize tp_dict - if (type->tp_dict == nullptr) - type->tp_dict = PyDict_New(); - if (type->tp_dict == nullptr) - return -1; - PyObject *ob_type = reinterpret_cast(type); - Shiboken::AutoDecRef func(PyCFunction_New(tp_new_methoddef, ob_type)); - if (func.isNull()) - return -1; - if (PyDict_SetItemString(type->tp_dict, "__new__", func)) - return -1; return 0; } /*****************************************************************************/ @@ -197,7 +201,7 @@ PyTypeObject *SbkObjectType_TypeF(void) PepHeapType_SIZE + sizeof(SbkObjectTypePrivate); type = reinterpret_cast(PyType_FromSpec(&SbkObjectType_Type_spec)); #if PY_VERSION_HEX < 0x03000000 - if (add_tp_new_wrapper(type) < 0) + if (patch_tp_new_wrapper(type) < 0) return nullptr; #endif } @@ -452,6 +456,11 @@ PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *k auto *newType = reinterpret_cast(type_new(metatype, args, kwds)); if (!newType) return nullptr; +#if PY_VERSION_HEX < 0x03000000 + // PYSIDE-1051: The newly created metatype needs the PYSIDE-816 wrapper, too. + if (patch_tp_new_wrapper(&newType->type) < 0) + return nullptr; +#endif Shiboken::ObjectType::initPrivateData(newType); SbkObjectTypePrivate *sotp = PepType_SOTP(newType); @@ -842,6 +851,11 @@ introduceWrapperType(PyObject *enclosingObject, Py_TYPE(heaptype) = SbkObjectType_TypeF(); Py_INCREF(Py_TYPE(heaptype)); auto *type = reinterpret_cast(heaptype); +#if PY_VERSION_HEX < 0x03000000 + // PYSIDE-1051: The newly created type needs the PYSIDE-816 wrapper, too. + if (patch_tp_new_wrapper(&type->type) < 0) + return nullptr; +#endif if (baseType) { if (baseTypes) { for (int i = 0; i < PySequence_Fast_GET_SIZE(baseTypes); ++i) -- cgit v1.2.3