diff options
-rw-r--r-- | sources/pyside2/tests/QtCore/qenum_test.py | 33 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/pep384impl.cpp | 32 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/pep384impl.h | 9 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/sbkenum.cpp | 102 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/sbkenum.h | 2 | ||||
-rw-r--r-- | sources/shiboken2/shibokenmodule/typesystem_shiboken.xml | 6 |
6 files changed, 183 insertions, 1 deletions
diff --git a/sources/pyside2/tests/QtCore/qenum_test.py b/sources/pyside2/tests/QtCore/qenum_test.py index dd91d1581..ed58f4f20 100644 --- a/sources/pyside2/tests/QtCore/qenum_test.py +++ b/sources/pyside2/tests/QtCore/qenum_test.py @@ -32,13 +32,15 @@ import os import sys +import pickle import unittest sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from init_paths import init_test_paths init_test_paths(False) -from PySide2.QtCore import * +from PySide2.QtCore import Qt, QIODevice + class TestEnum(unittest.TestCase): @@ -73,6 +75,7 @@ class TestEnum(unittest.TestCase): with self.assertRaises(TypeError): a = k*2.0 + class TestQFlags(unittest.TestCase): def testToItn(self): om = QIODevice.NotOpen @@ -94,5 +97,33 @@ class TestQFlags(unittest.TestCase): except: pass + +# PYSIDE-15: Pickling of enums +class TestEnumPickling(unittest.TestCase): + def testPickleEnum(self): + + # Pickling of enums with different depth works. + ret = pickle.loads(pickle.dumps(QIODevice.Append)) + self.assertEqual(ret, QIODevice.Append) + + ret = pickle.loads(pickle.dumps(Qt.Key.Key_Asterisk)) + self.assertEqual(ret, Qt.Key.Key_Asterisk) + self.assertEqual(ret, Qt.Key(42)) + + # We can also pickle the whole enum class (built in): + ret = pickle.loads(pickle.dumps(QIODevice)) + + # This works also with nested classes for Python 3, after we + # introduced the correct __qualname__ attribute. + + # Note: For Python 2, we would need quite strange patches. + func = lambda: pickle.loads(pickle.dumps(Qt.Key)) + if sys.version_info[0] < 3: + with self.assertRaises(pickle.PicklingError): + func() + else: + func() + + if __name__ == '__main__': unittest.main() diff --git a/sources/shiboken2/libshiboken/pep384impl.cpp b/sources/shiboken2/libshiboken/pep384impl.cpp index c04848eb3..6405d777a 100644 --- a/sources/shiboken2/libshiboken/pep384impl.cpp +++ b/sources/shiboken2/libshiboken/pep384impl.cpp @@ -638,6 +638,38 @@ PepType_GetNameStr(PyTypeObject *type) /***************************************************************************** * + * Newly introduced convenience functions + * + */ +#if PY_VERSION_HEX < 0x03070000 + +PyObject * +PyImport_GetModule(PyObject *name) +{ + PyObject *m; + PyObject *modules = PyImport_GetModuleDict(); + if (modules == NULL) { + PyErr_SetString(PyExc_RuntimeError, "unable to get sys.modules"); + return NULL; + } + Py_INCREF(modules); + if (PyDict_CheckExact(modules)) { + m = PyDict_GetItemWithError(modules, name); /* borrowed */ + Py_XINCREF(m); + } + else { + m = PyObject_GetItem(modules, name); + if (m == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + } + } + Py_DECREF(modules); + return m; +} + +#endif // PY_VERSION_HEX < 0x03070000 +/***************************************************************************** + * * Extra support for name mangling * */ diff --git a/sources/shiboken2/libshiboken/pep384impl.h b/sources/shiboken2/libshiboken/pep384impl.h index e735095e8..c180a06c1 100644 --- a/sources/shiboken2/libshiboken/pep384impl.h +++ b/sources/shiboken2/libshiboken/pep384impl.h @@ -523,6 +523,15 @@ extern LIBSHIBOKEN_API PyTypeObject *PepMethodDescr_TypePtr; /***************************************************************************** * + * Newly introduced convenience functions + * + */ +#if PY_VERSION_HEX < 0x03070000 +LIBSHIBOKEN_API PyObject *PyImport_GetModule(PyObject *name); +#endif // PY_VERSION_HEX < 0x03070000 + +/***************************************************************************** + * * Runtime support for Python 3.8 incompatibilities * */ diff --git a/sources/shiboken2/libshiboken/sbkenum.cpp b/sources/shiboken2/libshiboken/sbkenum.cpp index 2b80da112..5b9718738 100644 --- a/sources/shiboken2/libshiboken/sbkenum.cpp +++ b/sources/shiboken2/libshiboken/sbkenum.cpp @@ -361,6 +361,107 @@ PyObject *SbkEnumTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwd } // extern "C" +/////////////////////////////////////////////////////////////// +// +// PYSIDE-15: Pickling Support for Qt Enum objects +// This works very well and fixes the issue. +// +extern "C" { + +static void init_enum(); // forward + +static PyObject *enum_unpickler = nullptr; + +// Pickling: reduce the Qt Enum object +static PyObject *enum___reduce__(PyObject *obj) +{ + init_enum(); + return Py_BuildValue("O(Ni)", + enum_unpickler, + Py_BuildValue("s", Py_TYPE(obj)->tp_name), + PyInt_AS_LONG(obj)); +} + +} // extern "C" + +namespace Shiboken { namespace Enum { + +// Unpickling: rebuild the Qt Enum object +PyObject *unpickleEnum(PyObject *enum_class_name, PyObject *value) +{ + Shiboken::AutoDecRef parts(PyObject_CallMethod(enum_class_name, + const_cast<char *>("split"), const_cast<char *>("s"), ".")); + if (parts.isNull()) + return nullptr; + PyObject *top_name = PyList_GetItem(parts, 0); // borrowed ref + if (top_name == nullptr) + return nullptr; + PyObject *module = PyImport_GetModule(top_name); + if (module == nullptr) { + PyErr_Format(PyExc_ImportError, "could not import module %.200s", + Shiboken::String::toCString(top_name)); + return nullptr; + } + Shiboken::AutoDecRef cur_thing(module); + int len = PyList_Size(parts); + for (int idx = 1; idx < len; ++idx) { + PyObject *name = PyList_GetItem(parts, idx); // borrowed ref + PyObject *thing = PyObject_GetAttr(cur_thing, name); + if (thing == nullptr) { + PyErr_Format(PyExc_ImportError, "could not import Qt Enum type %.200s", + Shiboken::String::toCString(enum_class_name)); + return nullptr; + } + cur_thing.reset(thing); + } + PyObject *klass = cur_thing; + return PyObject_CallFunctionObjArgs(klass, value, nullptr); +} + +} // namespace Enum +} // namespace Shiboken + +extern "C" { + +// Initialization +static bool _init_enum() +{ + static PyObject *shiboken_name = Py_BuildValue("s", "shiboken2"); + if (shiboken_name == nullptr) + return false; + Shiboken::AutoDecRef shibo(PyImport_GetModule(shiboken_name)); + if (shibo.isNull()) + return false; + Shiboken::AutoDecRef sub(PyObject_GetAttr(shibo, shiboken_name)); + PyObject *mod = sub.object(); + if (mod == nullptr) { + // We are in the build dir and already in shiboken. + PyErr_Clear(); + mod = shibo.object(); + } + enum_unpickler = PyObject_GetAttrString(mod, "_unpickle_enum"); + if (enum_unpickler == nullptr) + return false; + return true; +} + +static void init_enum() +{ + if (!(enum_unpickler || _init_enum())) + Py_FatalError("could not load enum helper functions"); +} + +static PyMethodDef SbkEnumObject_Methods[] = { + {const_cast<char *>("__reduce__"), reinterpret_cast<PyCFunction>(enum___reduce__), + METH_NOARGS, nullptr}, + {nullptr, nullptr, 0, nullptr} // Sentinel +}; + +} // extern "C" + +// +/////////////////////////////////////////////////////////////// + namespace Shiboken { class DeclaredEnumTypes @@ -521,6 +622,7 @@ static PyType_Slot SbkNewType_slots[] = { {Py_tp_repr, (void *)SbkEnumObject_repr}, {Py_tp_str, (void *)SbkEnumObject_repr}, {Py_tp_getset, (void *)SbkEnumGetSetList}, + {Py_tp_methods, (void *)SbkEnumObject_Methods}, {Py_tp_new, (void *)SbkEnum_tp_new}, {Py_nb_add, (void *)enum_add}, {Py_nb_subtract, (void *)enum_subtract}, diff --git a/sources/shiboken2/libshiboken/sbkenum.h b/sources/shiboken2/libshiboken/sbkenum.h index 759d72636..c294c17d9 100644 --- a/sources/shiboken2/libshiboken/sbkenum.h +++ b/sources/shiboken2/libshiboken/sbkenum.h @@ -114,6 +114,8 @@ namespace Enum LIBSHIBOKEN_API void setTypeConverter(PyTypeObject *enumType, SbkConverter *converter); /// Returns the converter assigned to the enum \p type. LIBSHIBOKEN_API SbkConverter *getTypeConverter(PyTypeObject *enumType); + + LIBSHIBOKEN_API PyObject *unpickleEnum(PyObject *, PyObject *); } } // namespace Shiboken diff --git a/sources/shiboken2/shibokenmodule/typesystem_shiboken.xml b/sources/shiboken2/shibokenmodule/typesystem_shiboken.xml index bdb0c9338..3eba557fb 100644 --- a/sources/shiboken2/shibokenmodule/typesystem_shiboken.xml +++ b/sources/shiboken2/shibokenmodule/typesystem_shiboken.xml @@ -103,6 +103,12 @@ </inject-code> </add-function> + <add-function signature="_unpickle_enum(PyObject*, PyObject*)" return-type="PyObject*"> + <inject-code> + %PYARG_0 = Shiboken::Enum::unpickleEnum(%1, %2); + </inject-code> + </add-function> + <extra-includes> <include file-name="sbkversion.h" location="local"/> <include file-name="voidptr.h" location="local"/> |