at master 14 kB view raw
1From: Christian Tismer <tismer@stackless.com> 2Date: Tue, 14 Feb 2023 14:46:22 +0100 3Subject: Support running PySide on Python 3.12 4 5Builtin types no longer have tp_dict set. We need to 6use PyType_GetDict, instead. This works without Limited API 7at the moment. 8 9With some great cheating, this works with Limited API, too. 10We emulate PyType_GetDict by tp_dict if that is not 0. 11Otherwise we create an empty dict. 12 13Some small changes to Exception handling and longer 14warm-up in leaking tests were found, too. 15 16Pick-to: 6.6 6.5 6.2 17Task-number: PYSIDE-2230 18Change-Id: I8a56de6208ec00979255b39b5784dfc9b4b92def 19Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> 20(cherry picked from commit 441ffbd4fc622e67acd81e9c1c6d3a0b0fbcacf0) 21--- 22 build_scripts/config.py | 3 +- 23 sources/pyside2/PySide2/support/generate_pyi.py | 8 ++++-- 24 sources/pyside2/libpyside/feature_select.cpp | 10 ++++--- 25 sources/pyside2/libpyside/pysideproperty.cpp | 4 +-- 26 sources/pyside2/libpyside/pysidesignal.cpp | 4 +-- 27 sources/pyside2/tests/QtWidgets/bug_662.py | 3 +- 28 sources/pyside2/tests/signals/bug_79.py | 5 ++++ 29 sources/shiboken2/libshiboken/pep384impl.cpp | 33 ++++++++++++++++++++++ 30 sources/shiboken2/libshiboken/pep384impl.h | 8 ++++++ 31 .../shiboken2/libshiboken/signature/signature.cpp | 2 +- 32 .../libshiboken/signature/signature_helper.cpp | 6 ++-- 33 .../shibokensupport/signature/errorhandler.py | 6 ++++ 34 sources/shiboken2/tests/samplebinding/enum_test.py | 2 +- 35 13 files changed, 78 insertions(+), 16 deletions(-) 36 37diff --git a/build_scripts/config.py b/build_scripts/config.py 38index f2b4c40..5fc23d4 100644 39--- a/build_scripts/config.py 40+++ b/build_scripts/config.py 41@@ -94,7 +94,8 @@ class Config(object): 42 'Programming Language :: Python :: 3.8', 43 'Programming Language :: Python :: 3.9', 44 'Programming Language :: Python :: 3.10', 45- 'Programming Language :: Python :: 3.11' 46+ 'Programming Language :: Python :: 3.11', 47+ 'Programming Language :: Python :: 3.12', 48 ] 49 50 self.setup_script_dir = None 51diff --git a/sources/pyside2/PySide2/support/generate_pyi.py b/sources/pyside2/PySide2/support/generate_pyi.py 52index 1956533..fd05b1f 100644 53--- a/sources/pyside2/PySide2/support/generate_pyi.py 54+++ b/sources/pyside2/PySide2/support/generate_pyi.py 55@@ -116,8 +116,12 @@ class Formatter(Writer): 56 """ 57 def _typevar__repr__(self): 58 return "typing." + self.__name__ 59- typing.TypeVar.__repr__ = _typevar__repr__ 60- 61+ # This is no longer necessary for modern typing versions. 62+ # Ignore therefore if the repr is read-only and cannot be changed. 63+ try: 64+ typing.TypeVar.__repr__ = _typevar__repr__ 65+ except TypeError: 66+ pass 67 # Adding a pattern to substitute "Union[T, NoneType]" by "Optional[T]" 68 # I tried hard to replace typing.Optional by a simple override, but 69 # this became _way_ too much. 70diff --git a/sources/pyside2/libpyside/feature_select.cpp b/sources/pyside2/libpyside/feature_select.cpp 71index b9e1470..533c09d 100644 72--- a/sources/pyside2/libpyside/feature_select.cpp 73+++ b/sources/pyside2/libpyside/feature_select.cpp 74@@ -358,7 +358,8 @@ static bool SelectFeatureSetSubtype(PyTypeObject *type, PyObject *select_id) 75 * This is the selector for one sublass. We need to call this for 76 * every subclass until no more subclasses or reaching the wanted id. 77 */ 78- if (Py_TYPE(type->tp_dict) == Py_TYPE(PyType_Type.tp_dict)) { 79+ static const auto *pyTypeType_tp_dict = PepType_GetDict(&PyType_Type); 80+ if (Py_TYPE(type->tp_dict) == Py_TYPE(pyTypeType_tp_dict)) { 81 // On first touch, we initialize the dynamic naming. 82 // The dict type will be replaced after the first call. 83 if (!replaceClassDict(type)) { 84@@ -385,7 +386,8 @@ static inline PyObject *SelectFeatureSet(PyTypeObject *type) 85 * Generated functions call this directly. 86 * Shiboken will assign it via a public hook of `basewrapper.cpp`. 87 */ 88- if (Py_TYPE(type->tp_dict) == Py_TYPE(PyType_Type.tp_dict)) { 89+ static const auto *pyTypeType_tp_dict = PepType_GetDict(&PyType_Type); 90+ if (Py_TYPE(type->tp_dict) == Py_TYPE(pyTypeType_tp_dict)) { 91 // We initialize the dynamic features by using our own dict type. 92 if (!replaceClassDict(type)) 93 return nullptr; 94@@ -721,11 +723,11 @@ static bool patch_property_impl() 95 // Turn `__doc__` into a computed attribute without changing writability. 96 auto gsp = property_getset; 97 auto type = &PyProperty_Type; 98- auto dict = type->tp_dict; 99+ AutoDecRef dict(PepType_GetDict(type)); 100 AutoDecRef descr(PyDescr_NewGetSet(type, gsp)); 101 if (descr.isNull()) 102 return false; 103- if (PyDict_SetItemString(dict, gsp->name, descr) < 0) 104+ if (PyDict_SetItemString(dict.object(), gsp->name, descr) < 0) 105 return false; 106 // Replace property_descr_get/set by slightly changed versions 107 return true; 108diff --git a/sources/pyside2/libpyside/pysideproperty.cpp b/sources/pyside2/libpyside/pysideproperty.cpp 109index 86909d3..d2e2c68 100644 110--- a/sources/pyside2/libpyside/pysideproperty.cpp 111+++ b/sources/pyside2/libpyside/pysideproperty.cpp 112@@ -445,8 +445,8 @@ namespace { 113 114 static PyObject *getFromType(PyTypeObject *type, PyObject *name) 115 { 116- PyObject *attr = nullptr; 117- attr = PyDict_GetItem(type->tp_dict, name); 118+ AutoDecRef tpDict(PepType_GetDict(type)); 119+ auto *attr = PyDict_GetItem(tpDict.object(), name); 120 if (!attr) { 121 PyObject *bases = type->tp_bases; 122 int size = PyTuple_GET_SIZE(bases); 123diff --git a/sources/pyside2/libpyside/pysidesignal.cpp b/sources/pyside2/libpyside/pysidesignal.cpp 124index 6824a71..f15d7aa 100644 125--- a/sources/pyside2/libpyside/pysidesignal.cpp 126+++ b/sources/pyside2/libpyside/pysidesignal.cpp 127@@ -670,8 +670,8 @@ void updateSourceObject(PyObject *source) 128 Py_ssize_t pos = 0; 129 PyObject *value; 130 PyObject *key; 131- 132- while (PyDict_Next(objType->tp_dict, &pos, &key, &value)) { 133+ Shiboken::AutoDecRef tpDict(PepType_GetDict(objType)); 134+ while (PyDict_Next(tpDict, &pos, &key, &value)) { 135 if (PyObject_TypeCheck(value, PySideSignalTypeF())) { 136 Shiboken::AutoDecRef signalInstance(reinterpret_cast<PyObject *>(PyObject_New(PySideSignalInstance, PySideSignalInstanceTypeF()))); 137 instanceInitialize(signalInstance.cast<PySideSignalInstance *>(), key, reinterpret_cast<PySideSignal *>(value), source, 0); 138diff --git a/sources/pyside2/tests/QtWidgets/bug_662.py b/sources/pyside2/tests/QtWidgets/bug_662.py 139index 7fb97de..ec0e6f9 100644 140--- a/sources/pyside2/tests/QtWidgets/bug_662.py 141+++ b/sources/pyside2/tests/QtWidgets/bug_662.py 142@@ -40,7 +40,8 @@ from PySide2.QtWidgets import QTextEdit, QApplication 143 import sys 144 145 class testQTextBlock(unittest.TestCase): 146- def tesIterator(self): 147+ 148+ def testIterator(self): 149 edit = QTextEdit() 150 cursor = edit.textCursor() 151 fmt = QTextCharFormat() 152diff --git a/sources/pyside2/tests/signals/bug_79.py b/sources/pyside2/tests/signals/bug_79.py 153index ca25fb3..b70c8c5 100644 154--- a/sources/pyside2/tests/signals/bug_79.py 155+++ b/sources/pyside2/tests/signals/bug_79.py 156@@ -60,6 +60,11 @@ class ConnectTest(unittest.TestCase): 157 gc.collect() 158 # if this is no debug build, then we check at least that 159 # we do not crash any longer. 160+ for idx in range(200): 161+ # PYSIDE-2230: Warm-up is necessary before measuring, because 162+ # the code changes the constant parts after some time. 163+ o.selectionModel().destroyed.connect(self.callback) 164+ o.selectionModel().destroyed.disconnect(self.callback) 165 if not skiptest: 166 total = gettotalrefcount() 167 for idx in range(1000): 168diff --git a/sources/shiboken2/libshiboken/pep384impl.cpp b/sources/shiboken2/libshiboken/pep384impl.cpp 169index d12dae3..fed2716 100644 170--- a/sources/shiboken2/libshiboken/pep384impl.cpp 171+++ b/sources/shiboken2/libshiboken/pep384impl.cpp 172@@ -810,6 +810,39 @@ init_PepRuntime() 173 PepRuntime_38_flag = 1; 174 } 175 176+#ifdef Py_LIMITED_API 177+static PyObject *emulatePyType_GetDict(PyTypeObject *type) 178+{ 179+ if (_PepRuntimeVersion() < 0x030C00 || type->tp_dict) { 180+ auto *res = type->tp_dict; 181+ Py_XINCREF(res); 182+ return res; 183+ } 184+ // PYSIDE-2230: Here we are really cheating. We don't know how to 185+ // access an internal dict, and so we simply pretend 186+ // it were an empty dict. This works great for our types. 187+ // This was an unexpectedly simple solution :D 188+ return PyDict_New(); 189+} 190+#endif 191+ 192+// PyType_GetDict: replacement for <static type>.tp_dict, which is 193+// zero for builtin types since 3.12. 194+PyObject *PepType_GetDict(PyTypeObject *type) 195+{ 196+#if !defined(Py_LIMITED_API) 197+# if PY_VERSION_HEX >= 0x030C0000 198+ return PyType_GetDict(type); 199+# else 200+ // pre 3.12 fallback code, mimicking the addref-behavior. 201+ Py_XINCREF(type->tp_dict); 202+ return type->tp_dict; 203+# endif 204+#else 205+ return emulatePyType_GetDict(type); 206+#endif // Py_LIMITED_API 207+} 208+ 209 /***************************************************************************** 210 * 211 * Module Initialization 212diff --git a/sources/shiboken2/libshiboken/pep384impl.h b/sources/shiboken2/libshiboken/pep384impl.h 213index a870d6b..440784e 100644 214--- a/sources/shiboken2/libshiboken/pep384impl.h 215+++ b/sources/shiboken2/libshiboken/pep384impl.h 216@@ -567,6 +567,14 @@ extern LIBSHIBOKEN_API PyObject *PepMapping_Items(PyObject *o); 217 218 extern LIBSHIBOKEN_API int PepRuntime_38_flag; 219 220+/***************************************************************************** 221+ * 222+ * Runtime support for Python 3.12 incompatibility 223+ * 224+ */ 225+ 226+LIBSHIBOKEN_API PyObject *PepType_GetDict(PyTypeObject *type); 227+ 228 /***************************************************************************** 229 * 230 * Module Initialization 231diff --git a/sources/shiboken2/libshiboken/signature/signature.cpp b/sources/shiboken2/libshiboken/signature/signature.cpp 232index 191af3d..f817e47 100644 233--- a/sources/shiboken2/libshiboken/signature/signature.cpp 234+++ b/sources/shiboken2/libshiboken/signature/signature.cpp 235@@ -482,7 +482,7 @@ static PyObject *adjustFuncName(const char *func_name) 236 237 // Find the feature flags 238 auto type = reinterpret_cast<PyTypeObject *>(obtype.object()); 239- auto dict = type->tp_dict; 240+ AutoDecRef dict(PepType_GetDict(type)); 241 int id = SbkObjectType_GetReserved(type); 242 id = id < 0 ? 0 : id; // if undefined, set to zero 243 auto lower = id & 0x01; 244diff --git a/sources/shiboken2/libshiboken/signature/signature_helper.cpp b/sources/shiboken2/libshiboken/signature/signature_helper.cpp 245index 0246ec6..05eaa14 100644 246--- a/sources/shiboken2/libshiboken/signature/signature_helper.cpp 247+++ b/sources/shiboken2/libshiboken/signature/signature_helper.cpp 248@@ -105,7 +105,8 @@ int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp, PyObject **doc_descr) 249 */ 250 assert(PyType_Check(type)); 251 PyType_Ready(type); 252- PyObject *dict = type->tp_dict; 253+ AutoDecRef tpDict(PepType_GetDict(type)); 254+ auto *dict = tpDict.object(); 255 for (; gsp->name != nullptr; gsp++) { 256 PyObject *have_descr = PyDict_GetItemString(dict, gsp->name); 257 if (have_descr != nullptr) { 258@@ -346,7 +347,8 @@ static int _build_func_to_type(PyObject *obtype) 259 * We also check for hidden methods, see below. 260 */ 261 auto *type = reinterpret_cast<PyTypeObject *>(obtype); 262- PyObject *dict = type->tp_dict; 263+ AutoDecRef tpDict(PepType_GetDict(type)); 264+ auto *dict = tpDict.object(); 265 PyMethodDef *meth = type->tp_methods; 266 267 if (meth == nullptr) 268diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py 269index 47ab89a..3e1266c 100644 270--- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py 271+++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py 272@@ -113,6 +113,12 @@ def seterror_argument(args, func_name, info): 273 msg = "{func_name}(): {info}".format(**locals()) 274 err = AttributeError 275 return err, msg 276+ if isinstance(info, Exception): 277+ # PYSIDE-2230: Python 3.12 seems to always do normalization. 278+ err = type(info) 279+ info = info.args[0] 280+ msg = f"{func_name}(): {info}" 281+ return err, msg 282 if info and type(info) is dict: 283 keyword = tuple(info)[0] 284 msg = "{func_name}(): unsupported keyword '{keyword}'".format(**locals()) 285diff --git a/sources/shiboken2/tests/samplebinding/enum_test.py b/sources/shiboken2/tests/samplebinding/enum_test.py 286index 0beb720..f2606a4 100644 287--- a/sources/shiboken2/tests/samplebinding/enum_test.py 288+++ b/sources/shiboken2/tests/samplebinding/enum_test.py 289@@ -95,7 +95,7 @@ class EnumTest(unittest.TestCase): 290 291 def testEnumConstructorWithTooManyParameters(self): 292 '''Calling the constructor of non-extensible enum with the wrong number of parameters.''' 293- self.assertRaises(TypeError, SampleNamespace.InValue, 13, 14) 294+ self.assertRaises((TypeError, ValueError), SampleNamespace.InValue, 13, 14) 295 296 def testEnumConstructorWithNonNumberParameter(self): 297 '''Calling the constructor of non-extensible enum with a string.''' 298