From a5a2e97aaaf7b5c2ea7d9547cc0fdfbc9b09c97a Mon Sep 17 00:00:00 2001 From: Christian Tismer Date: Wed, 27 Mar 2024 18:06:28 +0100 Subject: Implement multiple inheritance correctly, 2nd. amendment When a Python class does _not_ implement __init__, then we might get the default of object.__init__, which must be skipped like the object class alone. Change-Id: I0416c97854e8d1c9edf0b9ac44d3df58223fef84 Fixes: PYSIDE-2654 Task-number: PYSIDE-2294 Pick-to: 6.6 6.5 Reviewed-by: Qt CI Bot Reviewed-by: Friedemann Kleint (cherry picked from commit 7b709cf594d9c308b57eacd784845beff9c72c2f) Reviewed-by: Qt Cherry-pick Bot --- .../tests/pysidetest/multiple_inheritance_test.py | 40 +++++++++++++++++++--- sources/shiboken6/libshiboken/bindingmanager.cpp | 6 ++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/sources/pyside6/tests/pysidetest/multiple_inheritance_test.py b/sources/pyside6/tests/pysidetest/multiple_inheritance_test.py index fe8e14f9f..49550ba55 100644 --- a/sources/pyside6/tests/pysidetest/multiple_inheritance_test.py +++ b/sources/pyside6/tests/pysidetest/multiple_inheritance_test.py @@ -12,7 +12,7 @@ init_test_paths(False) from helper.usesqapplication import UsesQApplication from PySide6 import QtCore, QtGui, QtWidgets -from PySide6.QtWidgets import QMainWindow, QLabel +from PySide6.QtWidgets import QMainWindow, QLabel, QWidget def xprint(*args, **kw): @@ -71,7 +71,8 @@ class C(A, B): xprint('C: after init') -# mro ('F', 'D', 'QCursor', 'E', 'QLabel', 'QFrame', 'QWidget', 'QObject', 'QPaintDevice', 'Object', 'object') +# mro ('F', 'D', 'QCursor', 'E', 'QLabel', 'QFrame', 'QWidget', 'QObject', +# 'QPaintDevice', 'Object', 'object') class D(QtGui.QCursor): def __init__(self, anna=77, **kw): xprint(f'D: before init kw = {kw}') @@ -94,7 +95,8 @@ class F(D, E, QtWidgets.QLabel): xprint('F: after init') -# mro ('I', 'G', 'QTextDocument', 'H', 'QLabel', 'QFrame', 'QWidget', 'QObject', 'QPaintDevice', 'Object', 'object') +# mro ('I', 'G', 'QTextDocument', 'H', 'QLabel', 'QFrame', 'QWidget', 'QObject', +# 'QPaintDevice', 'Object', 'object') # Similar, but this time we want to reach `H` without support from `super`. class G(QtGui.QTextDocument): pass @@ -108,7 +110,7 @@ class H: xprint('H: after init') -class I(G, H, QtWidgets.QLabel): +class II(G, H, QtWidgets.QLabel): pass @@ -145,7 +147,7 @@ class AdditionalMultipleInheritanceTest(UsesQApplication): def testGHI(self): xprint() - res = I(age=7) + res = II(age=7) self.assertEqual(res.age, 7) xprint() @@ -155,5 +157,33 @@ class AdditionalMultipleInheritanceTest(UsesQApplication): MainWindow() +# PYSIDE-2654: Additional missing init test. +# This must work if no __init__ is defined (Ui_Form) +class Ui_Form(object): + pass + + +class Mixin: + def __init__(self, **kwargs) -> None: + super().__init__(**kwargs) + + +class Card(Mixin, QWidget): + def __init__(self, parent=None) -> None: + super().__init__(parent=parent) + + +class Demo(Card, Ui_Form): + def __init__(self) -> None: + super().__init__() + + +class MissingInitFunctionTest(UsesQApplication): + def testMissing(self): + Demo() + # Tests if this works. Would crash without the extra + # check for object.__init__ + + if __name__ == "__main__": unittest.main() diff --git a/sources/shiboken6/libshiboken/bindingmanager.cpp b/sources/shiboken6/libshiboken/bindingmanager.cpp index a9b87f7f4..a0acc4e4b 100644 --- a/sources/shiboken6/libshiboken/bindingmanager.cpp +++ b/sources/shiboken6/libshiboken/bindingmanager.cpp @@ -408,6 +408,8 @@ bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds, using Shiboken::AutoDecRef; static PyObject *const _init = String::createStaticString("__init__"); + static PyObject *objectInit = + PyObject_GetAttr(reinterpret_cast(&PyBaseObject_Type), _init); // A native C++ self cannot have multiple inheritance. if (!Object::isUserType(self)) @@ -441,6 +443,10 @@ bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds, if (subType == &PyBaseObject_Type) return false; AutoDecRef func(PyObject_GetAttr(obSubType, _init)); + // PYSIDE-2654: If this has no implementation then we get object.__init__ + // but that is the same case like above. + if (func == objectInit) + return false; // PYSIDE-2294: We need to explicitly ignore positional args in a mixin class. SBK_UNUSED(args); AutoDecRef newArgs(PyTuple_New(1)); -- cgit v1.2.3