aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/libshiboken/sbkenum.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/libshiboken/sbkenum.cpp')
-rw-r--r--sources/shiboken6/libshiboken/sbkenum.cpp979
1 files changed, 334 insertions, 645 deletions
diff --git a/sources/shiboken6/libshiboken/sbkenum.cpp b/sources/shiboken6/libshiboken/sbkenum.cpp
index a2e6401ca..d39369979 100644
--- a/sources/shiboken6/libshiboken/sbkenum.cpp
+++ b/sources/shiboken6/libshiboken/sbkenum.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt for Python.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "sbkenum.h"
#include "sbkstring.h"
@@ -47,717 +11,442 @@
#include "sbkpython.h"
#include "signature.h"
-#include <string.h>
#include <cstring>
#include <vector>
+#include <sstream>
-#define SbkEnumType_Check(o) (Py_TYPE(Py_TYPE(o)) == SbkEnumType_TypeF())
-typedef PyObject *(*enum_func)(PyObject *, PyObject *);
+using namespace Shiboken;
extern "C"
{
-struct SbkEnumTypePrivate
-{
- SbkConverter **converterPtr;
- SbkConverter *converter;
- const char *cppName;
-};
-
struct SbkEnumType
{
PyTypeObject type;
};
-struct SbkEnumObject
-{
- PyObject_HEAD
- long ob_value;
- PyObject *ob_name;
-};
-
-static PyTypeObject *SbkEnum_TypeF(); // forward
-
-static PyObject *SbkEnumObject_repr(PyObject *self)
-{
- const SbkEnumObject *enumObj = reinterpret_cast<SbkEnumObject *>(self);
- if (enumObj->ob_name)
- return Shiboken::String::fromFormat("%s.%s", (Py_TYPE(self))->tp_name, PyBytes_AS_STRING(enumObj->ob_name));
- else
- return Shiboken::String::fromFormat("%s(%ld)", (Py_TYPE(self))->tp_name, enumObj->ob_value);
-}
-
-static PyObject *SbkEnumObject_name(PyObject *self, void *)
-{
- auto *enum_self = reinterpret_cast<SbkEnumObject *>(self);
-
- if (enum_self->ob_name == nullptr)
- Py_RETURN_NONE;
-
- Py_INCREF(enum_self->ob_name);
- return enum_self->ob_name;
-}
-
-static PyObject *SbkEnum_tp_new(PyTypeObject *type, PyObject *args, PyObject *)
-{
- long itemValue = 0;
- if (!PyArg_ParseTuple(args, "|l:__new__", &itemValue))
- return nullptr;
-
- if (type == SbkEnum_TypeF()) {
- PyErr_Format(PyExc_TypeError, "You cannot use %s directly", type->tp_name);
- return nullptr;
- }
-
- SbkEnumObject *self = PyObject_New(SbkEnumObject, type);
- if (!self)
- return nullptr;
- self->ob_value = itemValue;
- Shiboken::AutoDecRef item(Shiboken::Enum::getEnumItemFromValue(type, itemValue));
- self->ob_name = item.object() ? SbkEnumObject_name(item, nullptr) : nullptr;
- return reinterpret_cast<PyObject *>(self);
-}
-
-static const char *SbkEnum_SignatureStrings[] = {
- "Shiboken.Enum(self,itemValue:int=0)",
- nullptr}; // Sentinel
-
-void enum_object_dealloc(PyObject *ob)
+// Initialization
+static bool _init_enum()
{
- auto self = reinterpret_cast<SbkEnumObject *>(ob);
- Py_XDECREF(self->ob_name);
- Sbk_object_dealloc(ob);
-}
-
-static PyObject *_enum_op(enum_func f, PyObject *a, PyObject *b) {
- PyObject *valA = a;
- PyObject *valB = b;
- PyObject *result = nullptr;
- bool enumA = false;
- bool enumB = false;
-
- // We are not allowing floats
- if (!PyFloat_Check(valA) && !PyFloat_Check(valB)) {
- // Check if both variables are SbkEnumObject
- if (SbkEnumType_Check(valA)) {
- valA = PyLong_FromLong(reinterpret_cast<SbkEnumObject *>(valA)->ob_value);
- enumA = true;
- }
- if (SbkEnumType_Check(valB)) {
- valB = PyLong_FromLong(reinterpret_cast<SbkEnumObject *>(valB)->ob_value);
- enumB = true;
+ AutoDecRef shibo(PyImport_ImportModule("shiboken6.Shiboken"));
+ return !shibo.isNull();
+}
+
+static PyObject *PyEnumModule{};
+static PyObject *PyEnumMeta{};
+static PyObject *PyEnum{};
+static PyObject *PyIntEnum{};
+static PyObject *PyFlag{};
+static PyObject *PyIntFlag{};
+static PyObject *PyFlag_KEEP{};
+
+bool PyEnumMeta_Check(PyObject *ob)
+{
+ return Py_TYPE(ob) == reinterpret_cast<PyTypeObject *>(PyEnumMeta);
+}
+
+PyTypeObject *getPyEnumMeta()
+{
+ if (PyEnumMeta)
+ return reinterpret_cast<PyTypeObject *>(PyEnumMeta);
+
+ static auto *mod = PyImport_ImportModule("enum");
+ if (mod) {
+ PyEnumModule = mod;
+ PyEnumMeta = PyObject_GetAttrString(mod, "EnumMeta");
+ if (PyEnumMeta && PyType_Check(PyEnumMeta))
+ PyEnum = PyObject_GetAttrString(mod, "Enum");
+ if (PyEnum && PyType_Check(PyEnum))
+ PyIntEnum = PyObject_GetAttrString(mod, "IntEnum");
+ if (PyIntEnum && PyType_Check(PyIntEnum))
+ PyFlag = PyObject_GetAttrString(mod, "Flag");
+ if (PyFlag && PyType_Check(PyFlag))
+ PyIntFlag = PyObject_GetAttrString(mod, "IntFlag");
+ if (PyIntFlag && PyType_Check(PyIntFlag)) {
+ // KEEP is defined from Python 3.11 on.
+ PyFlag_KEEP = PyObject_GetAttrString(mod, "KEEP");
+ PyErr_Clear();
+ return reinterpret_cast<PyTypeObject *>(PyEnumMeta);
}
}
-
- // Without an enum we are not supporting the operation
- if (!(enumA || enumB)) {
- Py_INCREF(Py_NotImplemented);
- return Py_NotImplemented;
- } else {
- result = f(valA, valB);
- }
-
- // Decreasing the reference of the used variables a and b.
- if (enumA)
- Py_DECREF(valA);
- if (enumB)
- Py_DECREF(valB);
- return result;
-}
-
-/* Notes:
- * On Py3k land we use long type when using integer numbers. However, on older
- * versions of Python (version 2) we need to convert it to int type,
- * respectively.
- *
- * Thus calling PyInt_FromLong() will result in calling PyLong_FromLong in
- * Py3k.
- */
-static PyObject *enum_int(PyObject *v)
-{
- return PyInt_FromLong(reinterpret_cast<SbkEnumObject *>(v)->ob_value);
-}
-
-static PyObject *enum_and(PyObject *self, PyObject *b)
-{
- return _enum_op(PyNumber_And, self, b);
-}
-
-static PyObject *enum_or(PyObject *self, PyObject *b)
-{
- return _enum_op(PyNumber_Or, self, b);
+ Py_FatalError("Python module 'enum' not found");
+ return nullptr;
}
-static PyObject *enum_xor(PyObject *self, PyObject *b)
+void init_enum()
{
- return _enum_op(PyNumber_Xor, self, b);
+ static bool isInitialized = false;
+ if (isInitialized)
+ return;
+ if (!(isInitialized || _init_enum()))
+ Py_FatalError("could not init enum");
+
+ // PYSIDE-1735: Determine whether we should use the old or the new enum implementation.
+ static PyObject *option = PySys_GetObject("pyside6_option_python_enum");
+ if (!option || !PyLong_Check(option)) {
+ PyErr_Clear();
+ option = PyLong_FromLong(1);
+ }
+ int ignoreOver{};
+ Enum::enumOption = PyLong_AsLongAndOverflow(option, &ignoreOver);
+ getPyEnumMeta();
+ isInitialized = true;
}
-static int enum_bool(PyObject *v)
+// PYSIDE-1735: Helper function supporting QEnum
+int enumIsFlag(PyObject *ob_type)
{
- return (reinterpret_cast<SbkEnumObject *>(v)->ob_value > 0);
-}
+ init_enum();
-static PyObject *enum_add(PyObject *self, PyObject *v)
-{
- return _enum_op(PyNumber_Add, self, v);
+ auto *metatype = Py_TYPE(ob_type);
+ if (metatype != reinterpret_cast<PyTypeObject *>(PyEnumMeta))
+ return -1;
+ auto *mro = reinterpret_cast<PyTypeObject *>(ob_type)->tp_mro;
+ const Py_ssize_t n = PyTuple_GET_SIZE(mro);
+ for (Py_ssize_t idx = 0; idx < n; ++idx) {
+ auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+ if (sub_type == reinterpret_cast<PyTypeObject *>(PyFlag))
+ return 1;
+ }
+ return 0;
}
-static PyObject *enum_subtract(PyObject *self, PyObject *v)
-{
- return _enum_op(PyNumber_Subtract, self, v);
-}
+///////////////////////////////////////////////////////////////////////
+//
+// Support for Missing Values
+// ==========================
+//
+// Qt enums sometimes use undefined values in enums.
+// The enum module handles this by the option "KEEP" for Flag and
+// IntFlag. The handling of missing enum values is still strict.
+//
+// We changed that (also for compatibility with some competitor)
+// and provide a `_missing_` function that creates the missing value.
+//
+// The idea:
+// ---------
+// We cannot modify the already created class.
+// But we can create a one-element class with the new value and
+// pretend that this is the already existing class.
+//
+// We create each constant only once and keep the result in a dict
+// "_sbk_missing_". This is similar to a competitor's "_sip_missing_".
+//
-static PyObject *enum_multiply(PyObject *self, PyObject *v)
+static PyObject *missing_func(PyObject * /* self */ , PyObject *args)
{
- return _enum_op(PyNumber_Multiply, self, v);
-}
+ // In order to relax matters to be more compatible with C++, we need
+ // to create a pseudo-member with that value.
+ static auto *const _sbk_missing = Shiboken::String::createStaticString("_sbk_missing_");
+ static auto *const _name = Shiboken::String::createStaticString("__name__");
+ static auto *const _mro = Shiboken::String::createStaticString("__mro__");
+ static auto *const _class = Shiboken::String::createStaticString("__class__");
-static PyObject *enum_richcompare(PyObject *self, PyObject *other, int op)
-{
- PyObject *valA = self;
- PyObject *valB = other;
- PyObject *result = nullptr;
- bool enumA = false;
- bool enumB = false;
-
- // We are not allowing floats
- if (!PyFloat_Check(valA) && !PyFloat_Check(valB)) {
-
- // Check if both variables are SbkEnumObject
- if (SbkEnumType_Check(valA)) {
- valA = PyLong_FromLong(reinterpret_cast<SbkEnumObject *>(valA)->ob_value);
- enumA = true;
- }
- if (SbkEnumType_Check(valB)) {
- valB = PyLong_FromLong(reinterpret_cast<SbkEnumObject *>(valB)->ob_value);
- enumB =true;
- }
+ PyObject *klass{}, *value{};
+ if (!PyArg_UnpackTuple(args, "missing", 2, 2, &klass, &value))
+ Py_RETURN_NONE;
+ if (!PyLong_Check(value))
+ Py_RETURN_NONE;
+ auto *type = reinterpret_cast<PyTypeObject *>(klass);
+ AutoDecRef tpDict(PepType_GetDict(type));
+ auto *sbk_missing = PyDict_GetItem(tpDict.object(), _sbk_missing);
+ if (!sbk_missing) {
+ sbk_missing = PyDict_New();
+ PyDict_SetItem(tpDict.object(), _sbk_missing, sbk_missing);
}
-
- // Without an enum we are not supporting the operation
- if (!(enumA || enumB)) {
- Py_INCREF(Py_NotImplemented);
- return Py_NotImplemented;
+ // See if the value is already in the dict.
+ AutoDecRef val_str(PyObject_CallMethod(value, "__str__", nullptr));
+ auto *ret = PyDict_GetItem(sbk_missing, val_str);
+ if (ret) {
+ Py_INCREF(ret);
+ return ret;
}
- result = PyObject_RichCompare(valA, valB, op);
-
- // Decreasing the reference of the used variables a and b.
- if (enumA)
- Py_DECREF(valA);
- if (enumB)
- Py_DECREF(valB);
-
- return result;
-}
-
-static Py_hash_t enum_hash(PyObject *pyObj)
-{
- Py_hash_t val = reinterpret_cast<SbkEnumObject *>(pyObj)->ob_value;
- if (val == -1)
- val = -2;
- return val;
-}
-
-static PyGetSetDef SbkEnumGetSetList[] = {
- {const_cast<char *>("name"), &SbkEnumObject_name, nullptr, nullptr, nullptr},
- {nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel
+ // No, we must create a new object and insert it into the dict.
+ AutoDecRef cls_name(PyObject_GetAttr(klass, _name));
+ AutoDecRef mro(PyObject_GetAttr(klass, _mro));
+ auto *baseClass(PyTuple_GetItem(mro, 1));
+ AutoDecRef param(PyDict_New());
+ PyDict_SetItem(param, val_str, value);
+ AutoDecRef fake(PyObject_CallFunctionObjArgs(baseClass, cls_name.object(), param.object(),
+ nullptr));
+ ret = PyObject_GetAttr(fake, val_str);
+ PyDict_SetItem(sbk_missing, val_str, ret);
+ // Now the real fake: Pretend that the type is our original type!
+ PyObject_SetAttr(ret, _class, klass);
+ return ret;
+}
+
+static struct PyMethodDef dummy_methods[] = {
+ {"_missing_", reinterpret_cast<PyCFunction>(missing_func), METH_VARARGS|METH_STATIC, nullptr},
+ {nullptr, nullptr, 0, nullptr}
};
-static void SbkEnumTypeDealloc(PyObject *pyObj);
-static PyObject *SbkEnumTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds);
-
-static PyType_Slot SbkEnumType_Type_slots[] = {
- {Py_tp_dealloc, (void *)SbkEnumTypeDealloc},
- {Py_tp_base, (void *)&PyType_Type},
- {Py_tp_alloc, (void *)PyType_GenericAlloc},
- {Py_tp_new, (void *)SbkEnumTypeTpNew},
- {Py_tp_free, (void *)PyObject_GC_Del},
+static PyType_Slot dummy_slots[] = {
+ {Py_tp_base, reinterpret_cast<void *>(&PyType_Type)},
+ {Py_tp_methods, reinterpret_cast<void *>(dummy_methods)},
{0, nullptr}
};
-static PyType_Spec SbkEnumType_Type_spec = {
- "1:Shiboken.EnumMeta",
- 0, // filled in later
- sizeof(PyMemberDef),
- Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES,
- SbkEnumType_Type_slots,
-};
-
-PyTypeObject *SbkEnumType_TypeF(void)
-{
- static PyTypeObject *type = nullptr;
- if (!type) {
- SbkEnumType_Type_spec.basicsize =
- PepHeapType_SIZE + sizeof(SbkEnumTypePrivate);
- type = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&SbkEnumType_Type_spec));
- }
- return type;
-}
+static PyType_Spec dummy_spec = {
+ "1:builtins.EnumType",
+ 0,
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ dummy_slots,
+};
-void SbkEnumTypeDealloc(PyObject *pyObj)
+static PyObject *create_missing_func(PyObject *klass)
{
- auto sbkType = reinterpret_cast<SbkEnumType *>(pyObj);
-
- PyObject_GC_UnTrack(pyObj);
-#ifndef Py_LIMITED_API
- Py_TRASHCAN_SAFE_BEGIN(pyObj);
-#endif
- if (PepType_SETP(sbkType)->converter) {
- Shiboken::Conversions::deleteConverter(PepType_SETP(sbkType)->converter);
- }
-#ifndef Py_LIMITED_API
- Py_TRASHCAN_SAFE_END(pyObj);
-#endif
- if (PepRuntime_38_flag) {
- // PYSIDE-939: Handling references correctly.
- // This was not needed before Python 3.8 (Python issue 35810)
- Py_DECREF(Py_TYPE(pyObj));
- }
-}
-
-PyObject *SbkEnumTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
-{
- auto type_new = reinterpret_cast<newfunc>(PyType_GetSlot(&PyType_Type, Py_tp_new));
- auto newType = reinterpret_cast<SbkEnumType *>(type_new(metatype, args, kwds));
- if (!newType)
- return nullptr;
- return reinterpret_cast<PyObject *>(newType);
+ // When creating the class, memorize it in the missing function by
+ // a partial function argument.
+ static auto *const type = SbkType_FromSpec(&dummy_spec);
+ static auto *const obType = reinterpret_cast<PyObject *>(type);
+ static auto *const _missing = Shiboken::String::createStaticString("_missing_");
+ static auto *const func = PyObject_GetAttr(obType, _missing);
+ static auto *const partial = Pep_GetPartialFunction();
+ return PyObject_CallFunctionObjArgs(partial, func, klass, nullptr);
}
+//
+////////////////////////////////////////////////////////////////////////
} // extern "C"
-///////////////////////////////////////////////////////////////
-//
-// PYSIDE-15: Pickling Support for Qt Enum objects
-// This works very well and fixes the issue.
-//
-extern "C" {
+namespace Shiboken::Enum {
-static PyObject *enum_unpickler = nullptr;
+int enumOption{};
-// Pickling: reduce the Qt Enum object
-static PyObject *enum___reduce__(PyObject *obj)
+bool check(PyObject *pyObj)
{
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 {
+ static PyTypeObject *meta = getPyEnumMeta();
+ return Py_TYPE(Py_TYPE(pyObj)) == reinterpret_cast<PyTypeObject *>(meta);
+}
-// Unpickling: rebuild the Qt Enum object
-PyObject *unpickleEnum(PyObject *enum_class_name, PyObject *value)
+PyObject *getEnumItemFromValue(PyTypeObject *enumType, EnumValueType itemValue)
{
- 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));
+ init_enum();
+
+ auto *obEnumType = reinterpret_cast<PyObject *>(enumType);
+ AutoDecRef val2members(PyObject_GetAttrString(obEnumType, "_value2member_map_"));
+ if (val2members.isNull()) {
+ PyErr_Clear();
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()
-{
- Shiboken::AutoDecRef shibo(PyImport_ImportModule("shiboken6.Shiboken"));
- auto mod = shibo.object();
- // publish Shiboken.Enum so that the signature gets initialized
- if (PyObject_SetAttrString(mod, "Enum", reinterpret_cast<PyObject *>(SbkEnum_TypeF())) < 0)
- return false;
- if (InitSignatureStrings(SbkEnum_TypeF(), SbkEnum_SignatureStrings) < 0)
- return false;
- enum_unpickler = PyObject_GetAttrString(mod, "_unpickle_enum");
- if (enum_unpickler == nullptr)
- return false;
- return true;
+ AutoDecRef ob_value(PyLong_FromLongLong(itemValue));
+ auto *result = PyDict_GetItem(val2members, ob_value);
+ Py_XINCREF(result);
+ return result;
}
-void init_enum()
+PyObject *newItem(PyTypeObject *enumType, EnumValueType itemValue,
+ const char *itemName)
{
- static bool is_initialized = false;
- if (!(is_initialized || enum_unpickler || _init_enum()))
- Py_FatalError("could not load enum pickling helper function");
- is_initialized = true;
-}
-
-static PyMethodDef SbkEnumObject_Methods[] = {
- {const_cast<char *>("__reduce__"), reinterpret_cast<PyCFunction>(enum___reduce__),
- METH_NOARGS, nullptr},
- {nullptr, nullptr, 0, nullptr} // Sentinel
-};
-
-} // extern "C"
+ init_enum();
-//
-///////////////////////////////////////////////////////////////
+ auto *obEnumType = reinterpret_cast<PyObject *>(enumType);
+ if (!itemName)
+ return PyObject_CallFunction(obEnumType, "L", itemValue);
-namespace Shiboken {
+ static PyObject *const _member_map_ = String::createStaticString("_member_map_");
+ AutoDecRef tpDict(PepType_GetDict(enumType));
+ auto *member_map = PyDict_GetItem(tpDict.object(), _member_map_);
+ if (!(member_map && PyDict_Check(member_map)))
+ return nullptr;
+ auto *result = PyDict_GetItemString(member_map, itemName);
+ Py_XINCREF(result);
+ return result;
+}
-class DeclaredEnumTypes
+EnumValueType getValue(PyObject *enumItem)
{
-public:
- DeclaredEnumTypes(const DeclaredEnumTypes &) = delete;
- DeclaredEnumTypes(DeclaredEnumTypes &&) = delete;
- DeclaredEnumTypes &operator=(const DeclaredEnumTypes &) = delete;
- DeclaredEnumTypes &operator=(DeclaredEnumTypes &&) = delete;
-
- DeclaredEnumTypes();
- ~DeclaredEnumTypes();
- static DeclaredEnumTypes &instance();
- void addEnumType(PyTypeObject *type);
-
-private:
- std::vector<PyTypeObject *> m_enumTypes;
-};
+ init_enum();
-namespace Enum {
+ assert(Enum::check(enumItem));
-bool check(PyObject *pyObj)
-{
- return Py_TYPE(Py_TYPE(pyObj)) == SbkEnumType_TypeF();
+ AutoDecRef pyValue(PyObject_GetAttrString(enumItem, "value"));
+ return PyLong_AsLongLong(pyValue);
}
-PyObject *getEnumItemFromValue(PyTypeObject *enumType, long itemValue)
+void setTypeConverter(PyTypeObject *type, SbkConverter *converter)
{
- PyObject *key, *value;
- Py_ssize_t pos = 0;
- PyObject *values = PyDict_GetItem(enumType->tp_dict, Shiboken::PyName::values());
-
- while (PyDict_Next(values, &pos, &key, &value)) {
- auto *obj = reinterpret_cast<SbkEnumObject *>(value);
- if (obj->ob_value == itemValue) {
- Py_INCREF(value);
- return value;
- }
- }
- return nullptr;
+ auto *enumType = reinterpret_cast<SbkEnumType *>(type);
+ PepType_SETP(enumType)->converter = converter;
}
-static PyTypeObject *createEnum(const char *fullName, const char *cppName,
- PyTypeObject *flagsType)
+static PyTypeObject *createEnumForPython(PyObject *scopeOrModule,
+ const char *fullName,
+ PyObject *pyEnumItems)
{
- PyTypeObject *enumType = newTypeWithName(fullName, cppName, flagsType);
- if (PyType_Ready(enumType) < 0) {
- Py_XDECREF(enumType);
- return nullptr;
- }
- return enumType;
-}
+ const char *colon = strchr(fullName, ':');
+ assert(colon);
+ int package_level = atoi(fullName);
+ const char *mod = colon + 1;
-PyTypeObject *createGlobalEnum(PyObject *module, const char *name, const char *fullName, const char *cppName, PyTypeObject *flagsType)
-{
- PyTypeObject *enumType = createEnum(fullName, cppName, flagsType);
- if (enumType && PyModule_AddObject(module, name, reinterpret_cast<PyObject *>(enumType)) < 0) {
- Py_DECREF(enumType);
- return nullptr;
+ const char *qual = mod;
+ for (int idx = package_level; idx > 0; --idx) {
+ const char *dot = strchr(qual, '.');
+ if (!dot)
+ break;
+ qual = dot + 1;
}
- if (flagsType && PyModule_AddObject(module, PepType_GetNameStr(flagsType),
- reinterpret_cast<PyObject *>(flagsType)) < 0) {
- Py_DECREF(enumType);
- return nullptr;
+ int mlen = qual - mod - 1;
+ AutoDecRef module(Shiboken::String::fromCString(mod, mlen));
+ AutoDecRef qualname(Shiboken::String::fromCString(qual));
+ const char *dot = strrchr(qual, '.');
+ AutoDecRef name(Shiboken::String::fromCString(dot ? dot + 1 : qual));
+
+ static PyObject *enumName = String::createStaticString("IntEnum");
+ if (PyType_Check(scopeOrModule)) {
+ // For global objects, we have no good solution, yet where to put the int info.
+ auto type = reinterpret_cast<PyTypeObject *>(scopeOrModule);
+ auto *sotp = PepType_SOTP(type);
+ if (!sotp->enumFlagsDict)
+ initEnumFlagsDict(type);
+ enumName = PyDict_GetItem(sotp->enumTypeDict, name);
}
- return enumType;
-}
-PyTypeObject *createScopedEnum(SbkObjectType *scope, const char *name, const char *fullName, const char *cppName, PyTypeObject *flagsType)
-{
- PyTypeObject *enumType = createEnum(fullName, cppName, flagsType);
- if (enumType && PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(scope)->tp_dict, name,
- reinterpret_cast<PyObject *>(enumType)) < 0) {
- Py_DECREF(enumType);
- return nullptr;
- }
- if (flagsType && PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(scope)->tp_dict,
- PepType_GetNameStr(flagsType),
- reinterpret_cast<PyObject *>(flagsType)) < 0) {
- Py_DECREF(enumType);
- return nullptr;
- }
- return enumType;
-}
+ AutoDecRef PyEnumType(PyObject_GetAttr(PyEnumModule, enumName));
+ assert(PyEnumType.object());
+ bool isFlag = PyObject_IsSubclass(PyEnumType, PyFlag);
-static PyObject *createEnumItem(PyTypeObject *enumType, const char *itemName, long itemValue)
-{
- PyObject *enumItem = newItem(enumType, itemValue, itemName);
- if (PyDict_SetItemString(enumType->tp_dict, itemName, enumItem) < 0) {
- Py_DECREF(enumItem);
- return nullptr;
+ // See if we should use the Int versions of the types, again
+ bool useIntInheritance = Enum::enumOption & Enum::ENOPT_INHERIT_INT;
+ if (useIntInheritance) {
+ auto *surrogate = PyObject_IsSubclass(PyEnumType, PyFlag) ? PyIntFlag : PyIntEnum;
+ Py_INCREF(surrogate);
+ PyEnumType.reset(surrogate);
}
- return enumItem;
-}
-
-bool createGlobalEnumItem(PyTypeObject *enumType, PyObject *module, const char *itemName, long itemValue)
-{
- PyObject *enumItem = createEnumItem(enumType, itemName, itemValue);
- if (!enumItem)
- return false;
- int ok = PyModule_AddObject(module, itemName, enumItem);
- Py_DECREF(enumItem);
- return ok >= 0;
-}
-bool createScopedEnumItem(PyTypeObject *enumType, PyTypeObject *scope,
- const char *itemName, long itemValue)
-{
- PyObject *enumItem = createEnumItem(enumType, itemName, itemValue);
- if (!enumItem)
- return false;
- int ok = PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(scope)->tp_dict, itemName, enumItem);
- Py_DECREF(enumItem);
- return ok >= 0;
-}
-
-bool createScopedEnumItem(PyTypeObject *enumType, SbkObjectType *scope, const char *itemName, long itemValue)
-{
- return createScopedEnumItem(enumType, reinterpret_cast<PyTypeObject *>(scope), itemName, itemValue);
-}
+ // Walk the enumItemStrings and create a Python enum type.
+ auto *pyName = name.object();
+
+ // We now create the new type. Since Python 3.11, we need to pass in
+ // `boundary=KEEP` because the default STRICT crashes on us.
+ // See QDir.Filter.Drives | QDir.Filter.Files
+ AutoDecRef callArgs(Py_BuildValue("(OO)", pyName, pyEnumItems));
+ AutoDecRef callDict(PyDict_New());
+ static PyObject *boundary = String::createStaticString("boundary");
+ if (PyFlag_KEEP)
+ PyDict_SetItem(callDict, boundary, PyFlag_KEEP);
+ auto *obNewType = PyObject_Call(PyEnumType, callArgs, callDict);
+ if (!obNewType || PyObject_SetAttr(scopeOrModule, pyName, obNewType) < 0)
+ return nullptr;
-PyObject *
-newItem(PyTypeObject *enumType, long itemValue, const char *itemName)
-{
- bool newValue = true;
- SbkEnumObject *enumObj;
- if (!itemName) {
- enumObj = reinterpret_cast<SbkEnumObject *>(
- getEnumItemFromValue(enumType, itemValue));
- if (enumObj)
- return reinterpret_cast<PyObject *>(enumObj);
-
- newValue = false;
+ // For compatibility with Qt enums, provide a permissive missing method for (Int)?Enum.
+ if (!isFlag) {
+ bool supportMissing = !(Enum::enumOption & Enum::ENOPT_NO_MISSING);
+ if (supportMissing) {
+ AutoDecRef enum_missing(create_missing_func(obNewType));
+ PyObject_SetAttrString(obNewType, "_missing_", enum_missing);
+ }
}
- enumObj = PyObject_New(SbkEnumObject, enumType);
- if (!enumObj)
- return nullptr;
-
- enumObj->ob_name = itemName ? PyBytes_FromString(itemName) : nullptr;
- enumObj->ob_value = itemValue;
-
- if (newValue) {
- auto dict = enumType->tp_dict; // Note: 'values' is borrowed
- PyObject *values = PyDict_GetItemWithError(dict, Shiboken::PyName::values());
- if (values == nullptr) {
- if (PyErr_Occurred())
- return nullptr;
- Shiboken::AutoDecRef new_values(values = PyDict_New());
- if (values == nullptr)
- return nullptr;
- if (PyDict_SetItem(dict, Shiboken::PyName::values(), values) < 0)
- return nullptr;
+ auto *newType = reinterpret_cast<PyTypeObject *>(obNewType);
+ PyObject_SetAttr(obNewType, PyMagicName::qualname(), qualname);
+ PyObject_SetAttr(obNewType, PyMagicName::module(), module);
+
+ // See if we should re-introduce shortcuts in the enclosing object.
+ const bool useGlobalShortcut = (Enum::enumOption & Enum::ENOPT_GLOBAL_SHORTCUT) != 0;
+ const bool useScopedShortcut = (Enum::enumOption & Enum::ENOPT_SCOPED_SHORTCUT) != 0;
+ if (useGlobalShortcut || useScopedShortcut) {
+ // We have to use the iterator protokol because the values dict is a mappingproxy.
+ AutoDecRef values(PyObject_GetAttr(obNewType, PyMagicName::members()));
+ AutoDecRef mapIterator(PyObject_GetIter(values));
+ AutoDecRef mapKey{};
+ bool isModule = PyModule_Check(scopeOrModule);
+ while ((mapKey.reset(PyIter_Next(mapIterator))), mapKey.object()) {
+ if ((useGlobalShortcut && isModule) || (useScopedShortcut && !isModule)) {
+ AutoDecRef value(PyObject_GetItem(values, mapKey));
+ if (PyObject_SetAttr(scopeOrModule, mapKey, value) < 0)
+ return nullptr;
+ }
}
- PyDict_SetItemString(values, itemName, reinterpret_cast<PyObject *>(enumObj));
}
- return reinterpret_cast<PyObject *>(enumObj);
+ return newType;
}
-} // namespace Shiboken
-} // namespace Enum
-
-static PyType_Slot SbkNewEnum_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},
- {Py_nb_multiply, (void *)enum_multiply},
- {Py_nb_positive, (void *)enum_int},
- {Py_nb_bool, (void *)enum_bool},
- {Py_nb_and, (void *)enum_and},
- {Py_nb_xor, (void *)enum_xor},
- {Py_nb_or, (void *)enum_or},
- {Py_nb_int, (void *)enum_int},
- {Py_nb_index, (void *)enum_int},
- {Py_tp_richcompare, (void *)enum_richcompare},
- {Py_tp_hash, (void *)enum_hash},
- {Py_tp_dealloc, (void *)enum_object_dealloc},
- {0, nullptr}
-};
-static PyType_Spec SbkNewEnum_spec = {
- "1:Shiboken.Enum",
- sizeof(SbkEnumObject),
- 0,
- Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES,
- SbkNewEnum_slots,
-};
-
-static PyTypeObject *SbkEnum_TypeF()
+template <typename IntT>
+static PyObject *toPyObject(IntT v)
{
- static auto type = SbkType_FromSpec(&SbkNewEnum_spec);
- return reinterpret_cast<PyTypeObject *>(type);
+ if constexpr (sizeof(IntT) == 8) {
+ if constexpr (std::is_unsigned_v<IntT>)
+ return PyLong_FromUnsignedLongLong(v);
+ return PyLong_FromLongLong(v);
+ }
+ if constexpr (std::is_unsigned_v<IntT>)
+ return PyLong_FromUnsignedLong(v);
+ return PyLong_FromLong(v);
+}
+
+template <typename IntT>
+static PyTypeObject *createPythonEnumHelper(PyObject *module,
+ const char *fullName, const char *enumItemStrings[], const IntT enumValues[])
+{
+ AutoDecRef args(PyList_New(0));
+ auto *pyEnumItems = args.object();
+ for (size_t idx = 0; enumItemStrings[idx] != nullptr; ++idx) {
+ const char *kv = enumItemStrings[idx];
+ auto *key = PyUnicode_FromString(kv);
+ auto *value = toPyObject(enumValues[idx]);
+ auto *key_value = PyTuple_New(2);
+ PyTuple_SET_ITEM(key_value, 0, key);
+ PyTuple_SET_ITEM(key_value, 1, value);
+ PyList_Append(pyEnumItems, key_value);
+ }
+ return createEnumForPython(module, fullName, pyEnumItems);
}
-namespace Shiboken { namespace Enum {
+// Now we have to concretize these functions explicitly,
+// otherwise templates will not work across modules.
-static void
-copyNumberMethods(PyTypeObject *flagsType,
- PyType_Slot number_slots[],
- int *pidx)
+PyTypeObject *createPythonEnum(PyObject *module,
+ const char *fullName, const char *enumItemStrings[], const int64_t enumValues[])
{
- int idx = *pidx;
-#define PUT_SLOT(name) \
- number_slots[idx].slot = (name); \
- number_slots[idx].pfunc = PyType_GetSlot(flagsType, (name)); \
- ++idx;
-
- PUT_SLOT(Py_nb_absolute);
- PUT_SLOT(Py_nb_add);
- PUT_SLOT(Py_nb_and);
- PUT_SLOT(Py_nb_bool);
- PUT_SLOT(Py_nb_divmod);
- PUT_SLOT(Py_nb_float);
- PUT_SLOT(Py_nb_floor_divide);
- PUT_SLOT(Py_nb_index);
- PUT_SLOT(Py_nb_inplace_add);
- PUT_SLOT(Py_nb_inplace_and);
- PUT_SLOT(Py_nb_inplace_floor_divide);
- PUT_SLOT(Py_nb_inplace_lshift);
- PUT_SLOT(Py_nb_inplace_multiply);
- PUT_SLOT(Py_nb_inplace_or);
- PUT_SLOT(Py_nb_inplace_power);
- PUT_SLOT(Py_nb_inplace_remainder);
- PUT_SLOT(Py_nb_inplace_rshift);
- PUT_SLOT(Py_nb_inplace_subtract);
- PUT_SLOT(Py_nb_inplace_true_divide);
- PUT_SLOT(Py_nb_inplace_xor);
- PUT_SLOT(Py_nb_int);
- PUT_SLOT(Py_nb_invert);
- PUT_SLOT(Py_nb_lshift);
- PUT_SLOT(Py_nb_multiply);
- PUT_SLOT(Py_nb_negative);
- PUT_SLOT(Py_nb_or);
- PUT_SLOT(Py_nb_positive);
- PUT_SLOT(Py_nb_power);
- PUT_SLOT(Py_nb_remainder);
- PUT_SLOT(Py_nb_rshift);
- PUT_SLOT(Py_nb_subtract);
- PUT_SLOT(Py_nb_true_divide);
- PUT_SLOT(Py_nb_xor);
-#undef PUT_SLOT
- *pidx = idx;
-}
-
-PyTypeObject *
-newTypeWithName(const char *name,
- const char *cppName,
- PyTypeObject *numbers_fromFlag)
-{
- // Careful: SbkType_FromSpec does not allocate the string.
- PyType_Slot newslots[99] = {}; // enough but not too big for the stack
- PyType_Spec newspec;
- newspec.name = strdup(name);
- newspec.basicsize = SbkNewEnum_spec.basicsize;
- newspec.itemsize = SbkNewEnum_spec.itemsize;
- newspec.flags = SbkNewEnum_spec.flags;
- // we must append all the number methods, so rebuild everything:
- int idx = 0;
- while (SbkNewEnum_slots[idx].slot) {
- newslots[idx].slot = SbkNewEnum_slots[idx].slot;
- newslots[idx].pfunc = SbkNewEnum_slots[idx].pfunc;
- ++idx;
- }
- if (numbers_fromFlag)
- copyNumberMethods(numbers_fromFlag, newslots, &idx);
- newspec.slots = newslots;
- Shiboken::AutoDecRef bases(PyTuple_New(1));
- static auto basetype = reinterpret_cast<PyObject *>(SbkEnum_TypeF());
- Py_INCREF(basetype);
- PyTuple_SetItem(bases, 0, basetype);
- auto *type = reinterpret_cast<PyTypeObject *>(SbkType_FromSpecWithBases(&newspec, bases));
- Py_TYPE(type) = SbkEnumType_TypeF();
-
- auto *enumType = reinterpret_cast<SbkEnumType *>(type);
- PepType_SETP(enumType)->cppName = cppName;
- PepType_SETP(enumType)->converterPtr = &PepType_SETP(enumType)->converter;
- DeclaredEnumTypes::instance().addEnumType(type);
- return type;
+ return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues);
}
-const char *getCppName(PyTypeObject *enumType)
+PyTypeObject *createPythonEnum(PyObject *module,
+ const char *fullName, const char *enumItemStrings[], const uint64_t enumValues[])
{
- assert(Py_TYPE(enumType) == SbkEnumType_TypeF());
- return PepType_SETP(reinterpret_cast<SbkEnumType *>(enumType))->cppName;
+ return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues);
}
-long int getValue(PyObject *enumItem)
+PyTypeObject *createPythonEnum(PyObject *module,
+ const char *fullName, const char *enumItemStrings[], const int32_t enumValues[])
{
- assert(Shiboken::Enum::check(enumItem));
- return reinterpret_cast<SbkEnumObject *>(enumItem)->ob_value;
+ return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues);
}
-void setTypeConverter(PyTypeObject *enumType, SbkConverter *converter)
+PyTypeObject *createPythonEnum(PyObject *module,
+ const char *fullName, const char *enumItemStrings[], const uint32_t enumValues[])
{
- //reinterpret_cast<SbkEnumType *>(enumType)->converter = converter;
- *PepType_SGTP(enumType)->converter = converter;
+ return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues);
}
-SbkConverter *getTypeConverter(PyTypeObject *enumType)
+PyTypeObject *createPythonEnum(PyObject *module,
+ const char *fullName, const char *enumItemStrings[], const int16_t enumValues[])
{
- //return reinterpret_cast<SbkEnumType *>(enumType)->converter;
- return *PepType_SGTP(enumType)->converter;
+ return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues);
}
-} // namespace Enum
-
-DeclaredEnumTypes &DeclaredEnumTypes::instance()
+PyTypeObject *createPythonEnum(PyObject *module,
+ const char *fullName, const char *enumItemStrings[], const uint16_t enumValues[])
{
- static DeclaredEnumTypes me;
- return me;
+ return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues);
}
-DeclaredEnumTypes::DeclaredEnumTypes() = default;
-
-DeclaredEnumTypes::~DeclaredEnumTypes()
+PyTypeObject *createPythonEnum(PyObject *module,
+ const char *fullName, const char *enumItemStrings[], const int8_t enumValues[])
{
- /*
- * PYSIDE-595: This was "delete *it;" before introducing 'SbkType_FromSpec'.
- * XXX what should I do now?
- * Refcounts in tests are 30 or 0 at end.
- * When I add the default tp_dealloc, we get negative refcounts!
- * So right now I am doing nothing. Surely wrong but no crash.
- * See also the comment in function 'createGlobalEnumItem'.
- */
- // for (PyTypeObject *o : m_enumTypes)
- // fprintf(stderr, "ttt %d %s\n", Py_REFCNT(o), o->tp_name);
- m_enumTypes.clear();
+ return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues);
}
-void DeclaredEnumTypes::addEnumType(PyTypeObject *type)
+PyTypeObject *createPythonEnum(PyObject *module,
+ const char *fullName, const char *enumItemStrings[], const uint8_t enumValues[])
{
- m_enumTypes.push_back(type);
+ return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues);
}
-}
+} // namespace Shiboken::Enum