diff options
author | Christian Tismer <tismer@stackless.com> | 2021-10-29 12:33:24 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2022-02-10 23:06:36 +0000 |
commit | 18a93ae18a486c2d49427186f72c88453a55d024 (patch) | |
tree | 77ec504a7c70062c7b53de3868e201bf1ac46e7b | |
parent | 47372eb977d9a480eb7b08290dff5c419acb44ee (diff) |
__feature__: Fix snake_case handling on user defined classes
The snake case feature filters candidate methods and turns
them into snake case. This works fine for built-in classes.
The assumption is that all methods come from the tp_methods
list.
This assumption is not correct when applied to user defined
classes. The methods have no static source in this case.
To distinguish here, we inspect the tp_methods list. If it
is empty, we assume a user defined class and do nothing.
A forgotten initialization in feature.py was added, too.
As a note: RHEL has such an old Python version that does
not have MethodDescriptorType in the types module.
[ChangeLog][PySide6] snake_case handling now does explicitly
not touch user defined classes.
Fixes: PYSIDE-1702
Change-Id: Idfa16cdc50cb7234c1d2f473dfae3a568887547e
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
(cherry picked from commit 42695262f0ab4c73377b9d638dd28636ab1a3668)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
3 files changed, 47 insertions, 6 deletions
diff --git a/sources/pyside6/libpyside/feature_select.cpp b/sources/pyside6/libpyside/feature_select.cpp index c82cccce1..9459e1ca9 100644 --- a/sources/pyside6/libpyside/feature_select.cpp +++ b/sources/pyside6/libpyside/feature_select.cpp @@ -499,10 +499,17 @@ static PyObject *methodWithNewName(PyTypeObject *type, static bool feature_01_addLowerNames(PyTypeObject *type, PyObject *prev_dict, int id) { + PyMethodDef *meth = type->tp_methods; + PyObject *lower_dict = type->tp_dict; + + // PYSIDE-1702: A user-defined class in Python has no internal method list. + // We are not going to change anything. + if (!meth) + return PyDict_Update(lower_dict, prev_dict) >= 0; + /* * Add objects with lower names to `type->tp_dict` from 'prev_dict`. */ - PyObject *lower_dict = type->tp_dict; PyObject *key, *value; Py_ssize_t pos = 0; @@ -515,11 +522,9 @@ static bool feature_01_addLowerNames(PyTypeObject *type, PyObject *prev_dict, in continue; } } + // Then we walk over the tp_methods to get all methods and insert // them with changed names. - PyMethodDef *meth = type->tp_methods; - if (!meth) - return true; for (; meth != nullptr && meth->ml_name != nullptr; ++meth) { const char *name = String::toCString(String::getSnakeCaseName(meth->ml_name, true)); @@ -648,11 +653,18 @@ static bool feature_02_true_property(PyTypeObject *type, PyObject *prev_dict, in * Use the property info to create true Python property objects. */ - // The empty `tp_dict` gets populated by the previous dict. + PyMethodDef *meth = type->tp_methods; PyObject *prop_dict = type->tp_dict; + + // The empty `tp_dict` gets populated by the previous dict. if (PyDict_Update(prop_dict, prev_dict) < 0) return false; + // PYSIDE-1702: A user-defined class in Python has no internal method list. + // We are not going to change anything. + if (!meth) + return true; + // For speed, we establish a helper dict that maps the removed property // method names to property name. PyObject *prop_methods = PyDict_GetItem(prop_dict, PyMagicName::property_methods()); diff --git a/sources/pyside6/tests/QtCore/snake_prop_feature_test.py b/sources/pyside6/tests/QtCore/snake_prop_feature_test.py index 7925c4c0c..18a5e4058 100644 --- a/sources/pyside6/tests/QtCore/snake_prop_feature_test.py +++ b/sources/pyside6/tests/QtCore/snake_prop_feature_test.py @@ -67,7 +67,7 @@ class Window(QWidget): class FeatureTest(unittest.TestCase): def setUp(self): qApp or QApplication() - __feature__.set_selection(0) + __feature__.set_selection(0x80) # FIXME: 0 is insecure def tearDown(self): __feature__.set_selection(0) @@ -122,6 +122,34 @@ class FeatureTest(unittest.TestCase): self.assertEqual(qApp.quit_on_last_window_closed, QApplication.quit_on_last_window_closed) + def testUserClassNotAffected(self): + FunctionType = type(lambda: 42) + # Note: the types module does not have MethodDescriptorType in low versions. + MethodDescriptorType = type(str.split) + + class UserClass(QWidget): + + def someFunc1(self): + pass + + @staticmethod + def someFunc2(a, b): + pass + + inspect = UserClass.__dict__ + self.assertTrue(isinstance(inspect["someFunc1"], FunctionType)) + self.assertTrue(isinstance(inspect["someFunc2"], staticmethod)) + self.assertTrue(isinstance(UserClass.someFunc2, FunctionType)) + self.assertTrue(isinstance(UserClass.addAction, MethodDescriptorType)) + + from __feature__ import snake_case + + inspect = UserClass.__dict__ + self.assertTrue(isinstance(inspect["someFunc1"], FunctionType)) + self.assertTrue(isinstance(inspect["someFunc2"], staticmethod)) + self.assertTrue(isinstance(UserClass.someFunc2, FunctionType)) + self.assertTrue(isinstance(UserClass.add_action, MethodDescriptorType)) + if __name__ == '__main__': unittest.main() diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/feature.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/feature.py index 8776c7de9..a2b3699cf 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/feature.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/feature.py @@ -159,6 +159,7 @@ def __init__(): # Initialize all prior imported modules for name in sys.modules: pyside_feature_dict.setdefault(name, -1) + _is_initialized = True def set_selection(select_id, mod_name=None): |