diff options
author | Christian Tismer <tismer@stackless.com> | 2022-05-23 12:33:29 +0200 |
---|---|---|
committer | Christian Tismer <tismer@stackless.com> | 2022-06-21 11:06:37 +0200 |
commit | 304582e59e63f7620f221d3a9748118479ede099 (patch) | |
tree | 52e516c29b8f7eb12036fcc374b90941db9314dd /sources | |
parent | 7eb12e4cd3be60b4aaf18530d0c927bef386e913 (diff) |
PyEnum: Remove the old duplication of Enums in the enclosing scope
When implementing Python enums, the behavior of old Qt enums
was copied: Every enum was also inserted into the enclosing scope.
This patch removes that for two reasons:
- it is inconsequent to keep an old quirk when we have all
enums renewed so much
- It is more consistent compared to our competitor
- it is a prerequisite to implement efficient lazy initialization
PROBLEM: Many constants (about 110) are no longer recognized, for
instance `Qt.AlignLeft` should be `Qt.AlignmentFlag.AlignLeft`.
The question is if that can be fixed easily in C++, or if
the file mapping.py should get ~100 new fixes?
SOLUTION: We allow the old enums, but tell nobody that they continue
to work. They also are not advertized in the PYI files.
[ChangeLog][PySide6] The duplication of enum values into the
enclosing scope, allowing to write Qt.AlignLeft instead of
Qt.Alignment.AlignLeft, is still implemented but no longer
advertized in PYI files or line completion.
Task-number: PYSIDE-1735
Change-Id: I79a90d08f2a5a3a069fa551e60d609ecad718239
Pick-to: 6.3
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Diffstat (limited to 'sources')
8 files changed, 70 insertions, 19 deletions
diff --git a/sources/pyside6/tests/QtCore/qinstallmsghandler_test.py b/sources/pyside6/tests/QtCore/qinstallmsghandler_test.py index 56bec5e65..9c5ae4370 100644 --- a/sources/pyside6/tests/QtCore/qinstallmsghandler_test.py +++ b/sources/pyside6/tests/QtCore/qinstallmsghandler_test.py @@ -12,9 +12,9 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import (QLibraryInfo, QtCriticalMsg, QtDebugMsg, QtInfoMsg, +from PySide6.QtCore import (QLibraryInfo, QtMsgType, QMessageLogContext, - QtWarningMsg, qCritical, qFormatLogMessage, qDebug, + qCritical, qFormatLogMessage, qDebug, qInstallMessageHandler, qWarning) @@ -56,18 +56,18 @@ class QInstallMsgHandlerTest(unittest.TestCase): rethandler = qInstallMessageHandler(handler) if QLibraryInfo.isDebugBuild(): qDebug("Test Debug") - self.assertEqual(param[0], QtDebugMsg) + self.assertEqual(param[0], QtMsgType.QtDebugMsg) self.assertEqual(param[2], "Test Debug") qWarning("Test Warning") - self.assertEqual(param[0], QtWarningMsg) + self.assertEqual(param[0], QtMsgType.QtWarningMsg) self.assertEqual(param[2], "Test Warning") qCritical("Test Critical") - self.assertEqual(param[0], QtCriticalMsg) + self.assertEqual(param[0], QtMsgType.QtCriticalMsg) self.assertEqual(param[2], "Test Critical") def testFormat(self): ctx = QMessageLogContext() - s = qFormatLogMessage(QtInfoMsg, ctx, 'bla') + s = qFormatLogMessage(QtMsgType.QtInfoMsg, ctx, 'bla') self.assertTrue(s) diff --git a/sources/shiboken6/libshiboken/sbkenum.cpp b/sources/shiboken6/libshiboken/sbkenum.cpp index b9427142d..3f432dcb2 100644 --- a/sources/shiboken6/libshiboken/sbkenum.cpp +++ b/sources/shiboken6/libshiboken/sbkenum.cpp @@ -618,7 +618,7 @@ bool createGlobalEnumItem(PyTypeObject *enumType, PyObject *module, const char * PyObject *enumItem = createEnumItem(enumType, itemName, itemValue); if (!enumItem) return false; - int ok = PyModule_AddObject(module, itemName, enumItem); + int ok = useOldEnum ? PyModule_AddObject(module, itemName, enumItem) : true; Py_DECREF(enumItem); return ok >= 0; } @@ -629,7 +629,7 @@ bool createScopedEnumItem(PyTypeObject *enumType, PyTypeObject *scope, PyObject *enumItem = createEnumItem(enumType, itemName, itemValue); if (!enumItem) return false; - int ok = PyDict_SetItemString(scope->tp_dict, itemName, enumItem); + int ok = useOldEnum ? PyDict_SetItemString(scope->tp_dict, itemName, enumItem) : true; Py_DECREF(enumItem); return ok >= 0; } @@ -995,13 +995,6 @@ PyTypeObject *morphLastEnumToPython() PyObject_SetAttr(obNewType, PyMagicName::qualname(), qual_name); AutoDecRef module(PyObject_GetAttr(obEnumType, PyMagicName::module())); PyObject_SetAttr(obNewType, PyMagicName::module(), module); - // As a last step, fix the item entries in the enclosing object. - pos = 0; - while (PyDict_Next(values, &pos, &key, &value)) { - AutoDecRef entry(PyObject_GetAttr(obNewType, key)); - if (PyObject_SetAttr(lec.scopeOrModule, key, entry) < 0) - return nullptr; - } // Protect against double initialization setp->replacementType = newType; #if PY_VERSION_HEX < 0x03080000 diff --git a/sources/shiboken6/libshiboken/sbkfeature_base.cpp b/sources/shiboken6/libshiboken/sbkfeature_base.cpp index 2b3eba19e..e191204e4 100644 --- a/sources/shiboken6/libshiboken/sbkfeature_base.cpp +++ b/sources/shiboken6/libshiboken/sbkfeature_base.cpp @@ -4,6 +4,7 @@ #include "basewrapper.h" #include "basewrapper_p.h" #include "autodecref.h" +#include "sbkenum_p.h" #include "sbkstring.h" #include "sbkstaticstrings.h" #include "sbkstaticstrings_p.h" @@ -23,8 +24,8 @@ extern "C" // Maybe the same function from feature_select.cpp will be replaced. // -static PyObject *cached_globals = nullptr; -static PyObject *last_select_id = nullptr; +static PyObject *cached_globals{}; +static PyObject *last_select_id{}; PyObject *getFeatureSelectId() { @@ -86,9 +87,54 @@ PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name) * What we change here is the meta class of `QObject`. */ static getattrofunc type_getattro = PyType_Type.tp_getattro; + static PyObject *ignAttr1 = PyName::qtStaticMetaObject(); + static PyObject *ignAttr2 = PyMagicName::get(); if (SelectFeatureSet != nullptr) type->tp_dict = SelectFeatureSet(type); - return type_getattro(reinterpret_cast<PyObject *>(type), name); + auto *ret = type_getattro(reinterpret_cast<PyObject *>(type), name); + + // PYSIDE-1735: Be forgiving with strict enums and fetch the enum, silently. + // The PYI files now look correct, but the old duplication is + // emulated here. This should be removed in Qt 7, see `parser.py`. + // + // FIXME PYSIDE7 should remove this forgivingness: + // + // The duplication of enum values into the enclosing scope, allowing to write + // Qt.AlignLeft instead of Qt.Alignment.AlignLeft, is still implemented but + // no longer advertized in PYI files or line completion. + + if (!ret && name != ignAttr1 && name != ignAttr2) { + PyObject *error_type, *error_value, *error_traceback; + PyErr_Fetch(&error_type, &error_value, &error_traceback); + + // This is similar to `find_name_in_mro`, but instead of looking directly into + // tp_dict, we search for the attribute in local classes of that dict. + PyObject *mro = type->tp_mro; + assert(PyTuple_Check(mro)); + size_t idx, n = PyTuple_GET_SIZE(mro); + for (idx = 0; idx < n; ++idx) { + // FIXME This loop should further be optimized by installing an extra + // <classname>_EnumInfo structure. This comes with the next compatibility patch. + auto *base = PyTuple_GET_ITEM(mro, idx); + auto *type_base = reinterpret_cast<PyTypeObject *>(base); + auto *dict = type_base->tp_dict; + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(dict, &pos, &key, &value)) { + static auto *EnumMeta = getPyEnumMeta(); + if (Py_TYPE(value) == EnumMeta) { + auto *valtype = reinterpret_cast<PyTypeObject *>(value); + auto *result = PyDict_GetItem(valtype->tp_dict, name); + if (result) { + Py_INCREF(result); + return result; + } + } + } + } + PyErr_Restore(error_type, error_value, error_traceback); + } + return ret; } PyObject *Sbk_TypeGet___dict__(PyTypeObject *type, void *context) diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py index 5dabc395f..4c8ade025 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py @@ -62,7 +62,6 @@ def _get_flag_enum_option(): flag = bool(int(opt)) elif hasattr(sys, sysname): flag = bool(getattr(sys, sysname)) - sysver = sys.version_info[:2] # PYSIDE-1797: Emit a warning when we may remove pep384_issue33738.cpp if pyminver and pyminver >= (3, 8): warnings.warn(f"\n *** Python is at version {'.'.join(map(str, pyminver))} now. " @@ -71,6 +70,10 @@ def _get_flag_enum_option(): if pymaxver and pymaxver > (3, 10): warnings.warn(f"\n *** Python is at version {'.'.join(map(str, pymaxver))} now. " f"Please check if enum_310.py should be updated! ***") + # PYSIDE-1735: Emit a warning when we may update enum_310.py + if ver[:2] >= (7, 0): + warnings.warn(f"\n *** PySide is at version {'.'.join(map(str, ver[:2]))} now. " + f"Please drop the forgiving Enum behavior in `mangled_type_getattro` ***") # modify the sys attribute to bool setattr(sys, sysname, flag) # modify the env attribute to "0" or "1" diff --git a/sources/shiboken6/tests/samplebinding/derived_test.py b/sources/shiboken6/tests/samplebinding/derived_test.py index 897b1654e..2d5eb4293 100644 --- a/sources/shiboken6/tests/samplebinding/derived_test.py +++ b/sources/shiboken6/tests/samplebinding/derived_test.py @@ -40,6 +40,7 @@ class DerivedTest(unittest.TestCase): 'id_', 'pureVirtual', 'unpureVirtual']) self.assertTrue(inherited_methods.issubset(dir(Derived))) + @unittest.skipIf(sys.pyside63_option_python_enum, "Makes no sense with strict Enums") def testOverloadedMethodCall(self): '''Test if the correct overloaded method is being called.''' derived = Derived() @@ -56,6 +57,7 @@ class DerivedTest(unittest.TestCase): self.assertEqual(type(result), OverloadedFuncEnum) self.assertEqual(result, sample.OverloadedFunc_d) + @unittest.skipIf(sys.pyside63_option_python_enum, "Makes no sense with strict Enums") def testOtherOverloadedMethodCall(self): '''Another test to check overloaded method calling, just to double check.''' derived = Derived() @@ -68,6 +70,7 @@ class DerivedTest(unittest.TestCase): self.assertEqual(type(result), Derived.OtherOverloadedFuncEnum) self.assertEqual(result, Derived.OtherOverloadedFunc_id) + @unittest.skipIf(sys.pyside63_option_python_enum, "Makes no sense with strict Enums") def testOverloadedMethodCallWithDifferentNumericTypes(self): '''Test if the correct overloaded method accepts a different numeric type as argument.''' derived = Derived() diff --git a/sources/shiboken6/tests/samplebinding/enumfromremovednamespace_test.py b/sources/shiboken6/tests/samplebinding/enumfromremovednamespace_test.py index 8cf21d4ff..f70621c59 100644 --- a/sources/shiboken6/tests/samplebinding/enumfromremovednamespace_test.py +++ b/sources/shiboken6/tests/samplebinding/enumfromremovednamespace_test.py @@ -21,6 +21,8 @@ from shibokensupport.signature import get_signature class TestEnumFromRemovedNamespace(unittest.TestCase): + + @unittest.skipIf(sys.pyside63_option_python_enum, "Makes no sense with strict Enums") def testEnumPromotedToGlobal(self): sample.RemovedNamespace1_Enum self.assertEqual(sample.RemovedNamespace1_Enum_Value0, 0) diff --git a/sources/shiboken6/tests/samplebinding/modifications_test.py b/sources/shiboken6/tests/samplebinding/modifications_test.py index 478e0700d..d1e70004f 100644 --- a/sources/shiboken6/tests/samplebinding/modifications_test.py +++ b/sources/shiboken6/tests/samplebinding/modifications_test.py @@ -41,6 +41,7 @@ class ModificationsTest(unittest.TestCase): # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion gc.collect() + @unittest.skipIf(sys.pyside63_option_python_enum, "Makes no sense with strict Enums") def testClassMembersAvailability(self): '''Test if Modified class really have the expected members.''' expected_members = set(['OverloadedModFunc', 'OverloadedNone', diff --git a/sources/shiboken6/tests/samplebinding/sample_test.py b/sources/shiboken6/tests/samplebinding/sample_test.py index 35595649d..52abc968f 100644 --- a/sources/shiboken6/tests/samplebinding/sample_test.py +++ b/sources/shiboken6/tests/samplebinding/sample_test.py @@ -18,6 +18,7 @@ import sample class ModuleTest(unittest.TestCase): '''Test case for module and global functions''' + @unittest.skipIf(sys.pyside63_option_python_enum, "Makes no sense with strict Enums") def testModuleMembers(self): '''Test availability of classes, global functions and other members on binding''' expected_members = set(['Abstract', 'Derived', 'Point', @@ -30,12 +31,14 @@ class ModuleTest(unittest.TestCase): 'GlobalEnum', 'NoThing']) self.assertTrue(expected_members.issubset(dir(sample))) + @unittest.skipIf(sys.pyside63_option_python_enum, "Makes no sense with strict Enums") def testAbstractPrintFormatEnum(self): '''Test availability of PrintFormat enum from Abstract class''' enum_members = set(['PrintFormat', 'Short', 'Verbose', 'OnlyId', 'ClassNameAndId']) self.assertTrue(enum_members.issubset(dir(sample.Abstract))) + @unittest.skipIf(sys.pyside63_option_python_enum, "Makes no sense with strict Enums") def testSampleNamespaceOptionEnum(self): '''Test availability of Option enum from SampleNamespace namespace''' enum_members = set(['Option', 'None_', 'RandomNumber', 'UnixTime']) |