aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/libpyside/dynamicqmetaobject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside6/libpyside/dynamicqmetaobject.cpp')
-rw-r--r--sources/pyside6/libpyside/dynamicqmetaobject.cpp675
1 files changed, 675 insertions, 0 deletions
diff --git a/sources/pyside6/libpyside/dynamicqmetaobject.cpp b/sources/pyside6/libpyside/dynamicqmetaobject.cpp
new file mode 100644
index 000000000..048001f81
--- /dev/null
+++ b/sources/pyside6/libpyside/dynamicqmetaobject.cpp
@@ -0,0 +1,675 @@
+// Copyright (C) 2017 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 "dynamicqmetaobject.h"
+#include "pysideqobject.h"
+#include "pysidesignal.h"
+#include "pysidesignal_p.h"
+#include "pysideproperty.h"
+#include "pysideproperty_p.h"
+#include "pysideslot_p.h"
+#include "pysideqenum.h"
+#include "pyside_p.h"
+#include "pysidestaticstrings.h"
+
+#include <shiboken.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QObject>
+#include <QtCore/QStringList>
+#include <QtCore/QTextStream>
+#include <QtCore/QList>
+#include <private/qmetaobjectbuilder_p.h>
+
+#include <cstring>
+#include <vector>
+
+using namespace Qt::StringLiterals;
+
+using namespace PySide;
+
+// MetaObjectBuilder: Provides the QMetaObject's returned by
+// QObject::metaObject() for PySide6 objects. There are several
+// scenarios to consider:
+// 1) A plain Qt class (say QTimer) is instantiated. In that case,
+// return the base meta object until a modification is made by
+// adding methods, properties or class info (cf qmetaobject_test.py).
+// In that case, instantiate a QMetaObjectBuilder inheriting the
+// base meta meta object, add the method and return the result
+// of QMetaObjectBuilder::toMetaObject() (with dirty handling should
+// further modifications be made).
+// 2) A Python class inheriting a Qt class is instantiated. For this,
+// instantiate a QMetaObjectBuilder and add the methods/properties
+// found by inspecting the Python class.
+
+class MetaObjectBuilderPrivate
+{
+public:
+ using MetaObjects = std::vector<const QMetaObject *>;
+
+ QMetaObjectBuilder *ensureBuilder();
+ void parsePythonType(PyTypeObject *type);
+ int indexOfMethod(QMetaMethod::MethodType mtype,
+ const QByteArray &signature) const;
+ int indexOfProperty(const QByteArray &name) const;
+ int addSlot(const QByteArray &signature);
+ int addSlot(const QByteArray &signature, const QByteArray &type,
+ const QByteArray &tag = {});
+ int addSignal(const QByteArray &signature);
+ void removeMethod(QMetaMethod::MethodType mtype, int index);
+ int getPropertyNotifyId(PySideProperty *property) const;
+ int addProperty(const QByteArray &property, PyObject *data);
+ void addInfo(const QByteArray &key, const QByteArray &value);
+ void addInfo(const QMap<QByteArray, QByteArray> &info);
+ void addEnumerator(const char *name, bool flag, bool scoped,
+ const MetaObjectBuilder::EnumValues &entries);
+ void removeProperty(int index);
+ const QMetaObject *update();
+
+ QMetaObjectBuilder *m_builder = nullptr;
+
+ const QMetaObject *m_baseObject = nullptr;
+ MetaObjects m_cachedMetaObjects;
+ bool m_dirty = true;
+
+private:
+ QMetaPropertyBuilder
+ createProperty(PySideProperty *property, const QByteArray &propertyName);
+};
+
+QMetaObjectBuilder *MetaObjectBuilderPrivate::ensureBuilder()
+{
+ if (!m_builder) {
+ m_builder = new QMetaObjectBuilder();
+ m_builder->setClassName(m_baseObject->className());
+ m_builder->setSuperClass(m_baseObject);
+ }
+ return m_builder;
+}
+
+MetaObjectBuilder::MetaObjectBuilder(const char *className, const QMetaObject *metaObject) :
+ m_d(new MetaObjectBuilderPrivate)
+{
+ m_d->m_baseObject = metaObject;
+ m_d->m_builder = new QMetaObjectBuilder();
+ m_d->m_builder->setClassName(className);
+ m_d->m_builder->setSuperClass(metaObject);
+ m_d->m_builder->setClassName(className);
+}
+
+MetaObjectBuilder::MetaObjectBuilder(PyTypeObject *type, const QMetaObject *metaObject)
+ : m_d(new MetaObjectBuilderPrivate)
+{
+ m_d->m_baseObject = metaObject;
+ const char *className = type->tp_name;
+ if (const char *lastDot = strrchr(type->tp_name, '.'))
+ className = lastDot + 1;
+ // Different names indicate a Python class inheriting a Qt class.
+ // Parse the type.
+ if (strcmp(className, metaObject->className()) != 0) {
+ m_d->m_builder = new QMetaObjectBuilder();
+ m_d->m_builder->setClassName(className);
+ m_d->m_builder->setSuperClass(metaObject);
+ m_d->parsePythonType(type);
+ }
+}
+
+MetaObjectBuilder::~MetaObjectBuilder()
+{
+ for (auto *metaObject : m_d->m_cachedMetaObjects)
+ free(const_cast<QMetaObject*>(metaObject));
+ delete m_d->m_builder;
+ delete m_d;
+}
+
+int MetaObjectBuilderPrivate::indexOfMethod(QMetaMethod::MethodType mtype,
+ const QByteArray &signature) const
+{
+ int result = -1;
+ if (m_builder) {
+ switch (mtype) {
+ case QMetaMethod::Signal:
+ result = m_builder->indexOfSignal(signature);
+ break;
+ case QMetaMethod::Slot:
+ result = m_builder->indexOfSlot(signature);
+ break;
+ case QMetaMethod::Constructor:
+ result = m_builder->indexOfConstructor(signature);
+ break;
+ case QMetaMethod::Method:
+ result = m_builder->indexOfMethod(signature);
+ break;
+ }
+ if (result >= 0)
+ return result + m_baseObject->methodCount();
+ }
+ switch (mtype) {
+ case QMetaMethod::Signal:
+ result = m_baseObject->indexOfSignal(signature);
+ break;
+ case QMetaMethod::Slot:
+ result = m_baseObject->indexOfSlot(signature);
+ break;
+ case QMetaMethod::Constructor:
+ result = m_baseObject->indexOfConstructor(signature);
+ break;
+ case QMetaMethod::Method:
+ result = m_baseObject->indexOfMethod(signature);
+ break;
+ }
+ return result;
+}
+
+int MetaObjectBuilder::indexOfMethod(QMetaMethod::MethodType mtype,
+ const QByteArray &signature) const
+{
+ return m_d->indexOfMethod(mtype, signature);
+}
+
+int MetaObjectBuilderPrivate::indexOfProperty(const QByteArray &name) const
+{
+ if (m_builder) {
+ const int result = m_builder->indexOfProperty(name);
+ if (result >= 0)
+ return m_baseObject->propertyCount() + result;
+ }
+ return m_baseObject->indexOfProperty(name);
+}
+
+int MetaObjectBuilder::indexOfProperty(const QByteArray &name) const
+{
+ return m_d->indexOfProperty(name);
+}
+
+static bool checkMethodSignature(const QByteArray &signature)
+{
+ // Common mistake not to add parentheses to the signature.
+ const auto openParen = signature.indexOf('(');
+ const auto closingParen = signature.lastIndexOf(')');
+ const bool ok = openParen != -1 && closingParen != -1 && openParen < closingParen;
+ if (!ok) {
+ const QByteArray message =
+ "MetaObjectBuilder::addMethod: Invalid method signature provided for \""
+ + signature + '"';
+ PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0);
+ }
+ return ok;
+}
+
+int MetaObjectBuilderPrivate::addSlot(const QByteArray &signature)
+{
+ if (!checkMethodSignature(signature))
+ return -1;
+ m_dirty = true;
+ return m_baseObject->methodCount()
+ + ensureBuilder()->addSlot(signature).index();
+}
+
+int MetaObjectBuilder::addSlot(const char *signature)
+{
+ return m_d->addSlot(signature);
+}
+
+int MetaObjectBuilderPrivate::addSlot(const QByteArray &signature,
+ const QByteArray &type,
+ const QByteArray &tag)
+{
+ if (!checkMethodSignature(signature))
+ return -1;
+ m_dirty = true;
+ QMetaMethodBuilder methodBuilder = ensureBuilder()->addSlot(signature);
+ if (!type.isEmpty() && type != "void"_ba)
+ methodBuilder.setReturnType(type);
+ if (!tag.isEmpty())
+ methodBuilder.setTag(tag);
+ return m_baseObject->methodCount() + methodBuilder.index();
+}
+
+int MetaObjectBuilder::addSlot(const char *signature, const char *type)
+{
+ return m_d->addSlot(signature, type);
+}
+
+int MetaObjectBuilderPrivate::addSignal(const QByteArray &signature)
+{
+ if (!checkMethodSignature(signature))
+ return -1;
+ m_dirty = true;
+ return m_baseObject->methodCount()
+ + ensureBuilder()->addSignal(signature).index();
+}
+
+int MetaObjectBuilder::addSignal(const char *signature)
+{
+ return m_d->addSignal(signature);
+}
+
+void MetaObjectBuilderPrivate::removeMethod(QMetaMethod::MethodType mtype,
+ int index)
+{
+ index -= m_baseObject->methodCount();
+ auto builder = ensureBuilder();
+ Q_ASSERT(index >= 0 && index < builder->methodCount());
+ switch (mtype) {
+ case QMetaMethod::Constructor:
+ builder->removeConstructor(index);
+ break;
+ default:
+ builder->removeMethod(index);
+ break;
+ }
+ m_dirty = true;
+}
+
+void MetaObjectBuilder::removeMethod(QMetaMethod::MethodType mtype, int index)
+{
+ m_d->removeMethod(mtype, index);
+}
+
+int MetaObjectBuilderPrivate::getPropertyNotifyId(PySideProperty *property) const
+{
+ int notifyId = -1;
+ if (property->d->notify) {
+ if (const char *signalNotify = PySide::Property::getNotifyName(property))
+ notifyId = indexOfMethod(QMetaMethod::Signal, signalNotify);
+ }
+ return notifyId;
+}
+
+QMetaPropertyBuilder
+ MetaObjectBuilderPrivate::createProperty(PySideProperty *property,
+ const QByteArray &propertyName)
+{
+ int propertyNotifyId = getPropertyNotifyId(property);
+ if (propertyNotifyId >= 0)
+ propertyNotifyId -= m_baseObject->methodCount();
+
+ // For QObject-derived Python types, retrieve the meta type registered
+ // by name from the qmlRegisterType, if there is one. This is required for
+ // grouped QML properties to work.
+ auto *builder = ensureBuilder();
+ auto *typeObject = Property::getTypeObject(property);
+ if (typeObject != nullptr && PyType_Check(typeObject)) {
+ auto *pyTypeObject = reinterpret_cast<PyTypeObject *>(typeObject);
+ if (qstrncmp(pyTypeObject->tp_name, "PySide", 6) != 0
+ && PySide::isQObjectDerived(pyTypeObject, false)) {
+ const QByteArray pyType(pyTypeObject->tp_name);
+ const auto metaType = QMetaType::fromName(pyType + '*');
+ if (metaType.isValid()) {
+ return builder->addProperty(propertyName, pyType,
+ metaType, propertyNotifyId);
+ }
+ }
+ }
+ return builder->addProperty(propertyName, property->d->typeName,
+ propertyNotifyId);
+}
+
+int MetaObjectBuilderPrivate::addProperty(const QByteArray &propertyName,
+ PyObject *data)
+{
+ int index = indexOfProperty(propertyName);
+ if (index != -1)
+ return index;
+
+ auto *property = reinterpret_cast<PySideProperty *>(data);
+ auto newProperty = createProperty(property, propertyName);
+
+ // Adding property attributes
+ newProperty.setReadable(PySide::Property::isReadable(property));
+ newProperty.setWritable(PySide::Property::isWritable(property));
+ newProperty.setResettable(PySide::Property::hasReset(property));
+ newProperty.setDesignable(PySide::Property::isDesignable(property));
+ newProperty.setScriptable(PySide::Property::isScriptable(property));
+ newProperty.setStored(PySide::Property::isStored(property));
+ newProperty.setUser(PySide::Property::isUser(property));
+ newProperty.setConstant(PySide::Property::isConstant(property));
+ newProperty.setFinal(PySide::Property::isFinal(property));
+
+ index = newProperty.index() + m_baseObject->propertyCount();
+ m_dirty = true;
+ return index;
+}
+
+int MetaObjectBuilder::addProperty(const char *property, PyObject *data)
+{
+ return m_d->addProperty(property, data);
+}
+
+void MetaObjectBuilderPrivate::addInfo(const QByteArray &key,
+ const QByteArray &value)
+{
+ ensureBuilder()->addClassInfo(key, value);
+ m_dirty = true;
+}
+
+void MetaObjectBuilder::addInfo(const char *key, const char *value)
+{
+ m_d->addInfo(key, value);
+}
+
+void MetaObjectBuilderPrivate::addInfo(const QMap<QByteArray, QByteArray> &info)
+{
+ auto builder = ensureBuilder();
+ for (auto i = info.constBegin(), end = info.constEnd(); i != end; ++i)
+ builder->addClassInfo(i.key(), i.value());
+ m_dirty = true;
+}
+
+void MetaObjectBuilder::addInfo(const QMap<QByteArray, QByteArray> &info)
+{
+ m_d->addInfo(info);
+}
+
+void MetaObjectBuilder::addEnumerator(const char *name, bool flag, bool scoped,
+ const EnumValues &entries)
+{
+ m_d->addEnumerator(name, flag, scoped, entries);
+}
+
+void MetaObjectBuilderPrivate::addEnumerator(const char *name, bool flag, bool scoped,
+ const MetaObjectBuilder::EnumValues &entries)
+{
+ auto builder = ensureBuilder();
+ int have_already = builder->indexOfEnumerator(name);
+ if (have_already >= 0)
+ builder->removeEnumerator(have_already);
+ auto enumbuilder = builder->addEnumerator(name);
+ enumbuilder.setIsFlag(flag);
+ enumbuilder.setIsScoped(scoped);
+
+ for (const auto &item : entries)
+ enumbuilder.addKey(item.first, item.second);
+ m_dirty = true;
+}
+
+void MetaObjectBuilderPrivate::removeProperty(int index)
+{
+ index -= m_baseObject->propertyCount();
+ auto builder = ensureBuilder();
+ Q_ASSERT(index >= 0 && index < builder->propertyCount());
+ builder->removeProperty(index);
+ m_dirty = true;
+}
+
+void MetaObjectBuilder::removeProperty(int index)
+{
+ m_d->removeProperty(index);
+}
+
+// PYSIDE-315: Instead of sorting the items and maybe breaking indices, we
+// ensure that the signals and slots are sorted by the improved
+// parsePythonType() (signals must go before slots). The order can only
+// become distorted if the class is modified after creation. In that
+// case, we give a warning.
+
+static QString msgMethodSortOrder(const QMetaObject *mo, int offendingIndex)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "\n\n*** Sort Warning ***\nSignals and slots in QMetaObject '"
+ << mo->className()
+ << "' are not ordered correctly, this may lead to issues.\n";
+ const int methodOffset = mo->methodOffset();
+ for (int m = methodOffset, methodCount = mo->methodCount(); m < methodCount; ++m) {
+ const auto method = mo->method(m);
+ str << (m - methodOffset + 1) << (m > offendingIndex ? '!' : ' ')
+ << (method.methodType() == QMetaMethod::Signal ? " Signal " : " Slot ")
+ << method.methodSignature() << '\n';
+ }
+ return result;
+}
+
+static void checkMethodOrder(const QMetaObject *metaObject)
+{
+ const int lastMethod = metaObject->methodCount() - 1;
+ for (int m = metaObject->methodOffset(); m < lastMethod; ++m) {
+ if (metaObject->method(m).methodType() == QMetaMethod::Slot
+ && metaObject->method(m + 1).methodType() == QMetaMethod::Signal) {
+ const auto message = msgMethodSortOrder(metaObject, m);
+ PyErr_WarnEx(PyExc_RuntimeWarning, qPrintable(message), 0);
+ // Prevent a warning from being turned into an error. We cannot easily unwind.
+ PyErr_Clear();
+ break;
+ }
+ }
+}
+
+const QMetaObject *MetaObjectBuilderPrivate::update()
+{
+ if (!m_builder)
+ return m_baseObject;
+ if (m_cachedMetaObjects.empty() || m_dirty) {
+ // PYSIDE-803: The dirty branch needs to be protected by the GIL.
+ // This was moved from SignalManager::retrieveMetaObject to here,
+ // which is only the update in "return builder->update()".
+ Shiboken::GilState gil;
+ m_cachedMetaObjects.push_back(m_builder->toMetaObject());
+ checkMethodOrder(m_cachedMetaObjects.back());
+ m_dirty = false;
+ }
+ return m_cachedMetaObjects.back();
+}
+
+const QMetaObject *MetaObjectBuilder::update()
+{
+ return m_d->update();
+}
+
+static void formatEnum(QTextStream &str, const QMetaEnum &e)
+{
+ str << '"' << e.name() << "\" {";
+ for (int k = 0, cnt = e.keyCount(); k < cnt; ++k) {
+ if (k)
+ str << ", ";
+ str << e.key(k);
+ }
+ str << "}";
+}
+
+static void formatProperty(QTextStream &str, const QMetaProperty &p)
+{
+ str << '"' << p.name() << "\", " << p.typeName();
+ if (p.isWritable())
+ str << " [writeable]";
+ if (p.isResettable())
+ str << " [resettable]";
+ if (p.isConstant())
+ str << " [constant]";
+ if (p.isFinal())
+ str << " [final]";
+ if (p.isDesignable())
+ str << " [designable]";
+ auto sig = p.notifySignal();
+ if (sig.isValid())
+ str << ", notify=" << sig.name();
+}
+
+static void formatMethod(QTextStream &str, const QMetaMethod &m)
+{
+ str << "type=";
+ switch (m.methodType()) {
+ case QMetaMethod::Method:
+ str << "Method";
+ break;
+ case QMetaMethod::Signal:
+ str << "Signal";
+ break;
+ case QMetaMethod::Slot:
+ str << "Slot";
+ break;
+ case QMetaMethod::Constructor:
+ str << "Constructor";
+ break;
+ }
+
+ str << ", signature="
+ << m.methodSignature();
+ const QByteArrayList parms = m.parameterTypes();
+ if (!parms.isEmpty())
+ str << ", parameters=" << parms.join(", ");
+}
+
+QString MetaObjectBuilder::formatMetaObject(const QMetaObject *metaObject)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "PySide" << QT_VERSION_MAJOR << ".QtCore.QMetaObject(\""
+ << metaObject->className() << '"';
+ if (auto *s = metaObject->superClass())
+ str << " inherits \"" << s->className() << '"';
+ str << ":\n";
+
+ int offset = metaObject->enumeratorOffset();
+ int count = metaObject->enumeratorCount();
+ if (offset < count) {
+ str << "Enumerators:\n";
+ for (int e = offset; e < count; ++e) {
+ str << " #" << e << ' ';
+ formatEnum(str, metaObject->enumerator(e));
+ str << '\n';
+ }
+ }
+
+ offset = metaObject->propertyOffset();
+ count = metaObject->propertyCount();
+ if (offset < count) {
+ str << "Properties:\n";
+ for (int p = offset; p < count; ++p) {
+ str << " #" << p << ' ';
+ formatProperty(str, metaObject->property(p));
+ str << '\n';
+ }
+ }
+
+ offset = metaObject->methodOffset();
+ count = metaObject->methodCount();
+ if (offset < count) {
+ str << "Methods:\n";
+ for (int m = offset; m < count; ++m) {
+ str << " #" << m << ' ';
+ formatMethod(str, metaObject->method(m));
+ str << '\n';
+ }
+ }
+
+ str << ')';
+ return result;
+}
+
+using namespace Shiboken;
+
+void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
+{
+ // Get all non-QObject-derived base types in method resolution order, filtering out the types
+ // that can't have signals, slots or properties.
+ // This enforces registering of all signals and slots at type parsing time, and not later at
+ // signal connection time, thus making sure no method indices change which would break
+ // existing connections.
+ const PyObject *mro = type->tp_mro;
+ const Py_ssize_t basesCount = PyTuple_GET_SIZE(mro);
+
+ std::vector<PyTypeObject *> basesToCheck;
+ // Prepend the actual type that we are parsing.
+ basesToCheck.reserve(1u + basesCount);
+ basesToCheck.push_back(type);
+
+ auto sbkObjTypeF = SbkObject_TypeF();
+ auto baseObjType = reinterpret_cast<PyTypeObject *>(&PyBaseObject_Type);
+ for (Py_ssize_t i = 0; i < basesCount; ++i) {
+ auto baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, i));
+ if (baseType != sbkObjTypeF && baseType != baseObjType
+ && !PySide::isQObjectDerived(baseType, false)) {
+ basesToCheck.push_back(baseType);
+ }
+ }
+
+ // PYSIDE-315: Handle all signals first, in all involved types.
+ // Leave the properties to be registered after signals because they may depend on
+ // notify signals.
+ for (PyTypeObject *baseType : basesToCheck) {
+ AutoDecRef tpDict(PepType_GetDict(baseType));
+ PyObject *attrs = tpDict.object();
+ PyObject *key = nullptr;
+ PyObject *value = nullptr;
+ Py_ssize_t pos = 0;
+
+ while (PyDict_Next(attrs, &pos, &key, &value)) {
+ if (Signal::checkType(value)) {
+ // Register signals.
+ auto *data = reinterpret_cast<PySideSignal *>(value)->data;
+ if (data->signalName.isEmpty())
+ data->signalName = String::toCString(key);
+ for (const auto &s : data->signatures) {
+ const auto sig = data->signalName + '(' + s.signature + ')';
+ if (m_baseObject->indexOfSignal(sig) == -1) {
+ // Registering the parameterNames to the QMetaObject (PYSIDE-634)
+ // from:
+ // Signal(..., arguments=['...', ...]
+ // the arguments are now on data-data->signalArguments
+ auto builder = m_builder->addSignal(sig);
+ if (!data->signalArguments.isEmpty())
+ builder.setParameterNames(data->signalArguments);
+ }
+ }
+ }
+ }
+ }
+
+ PyObject *slotAttrName = PySide::PySideMagicName::slot_list_attr();
+ // PYSIDE-315: Now take care of the rest.
+ // Signals and slots should be separated, unless the types are modified, later.
+ // We check for this using "is_sorted()". Sorting no longer happens at all.
+ for (PyTypeObject *baseType : basesToCheck) {
+ AutoDecRef tpDict(PepType_GetDict(baseType));
+ PyObject *attrs = tpDict.object();
+ PyObject *key = nullptr;
+ PyObject *value = nullptr;
+ Py_ssize_t pos = 0;
+
+ while (PyDict_Next(attrs, &pos, &key, &value)) {
+ if (Property::checkType(value)) {
+ const QByteArray name = String::toCString(key);
+ const int index = m_baseObject->indexOfProperty(name);
+ if (index == -1)
+ addProperty(name, value);
+ } else if (PepType_GetSlot(Py_TYPE(value), Py_tp_call) != nullptr) {
+ // PYSIDE-198: PyFunction_Check does not work with Nuitka.
+ // Register slots.
+ if (PyObject_HasAttr(value, slotAttrName)) {
+ auto *capsule = PyObject_GetAttr(value, slotAttrName);
+ const auto *entryList = PySide::Slot::dataListFromCapsule(capsule);
+ for (const auto &e : *entryList) {
+ if (m_baseObject->indexOfSlot(e.signature) == -1)
+ addSlot(e.signature, e.resultType, e.tag);
+ }
+ }
+ }
+ }
+ }
+ // PYSIDE-957: Collect the delayed QEnums
+ auto collectedEnums = PySide::QEnum::resolveDelayedQEnums(type);
+ for (PyObject *obEnumType : collectedEnums) {
+ bool isFlag = PySide::QEnum::isFlag(obEnumType);
+ AutoDecRef obName(PyObject_GetAttr(obEnumType, PyMagicName::name()));
+ // Everything has been checked already in resolveDelayedQEnums.
+ // Therefore, we don't need to error-check here again.
+ auto name = String::toCString(obName);
+ AutoDecRef members(PyObject_GetAttr(obEnumType, PyMagicName::members()));
+ AutoDecRef items(PyMapping_Items(members));
+ Py_ssize_t nr_items = PySequence_Length(items);
+
+ QList<std::pair<QByteArray, int> > entries;
+ for (Py_ssize_t idx = 0; idx < nr_items; ++idx) {
+ AutoDecRef item(PySequence_GetItem(items, idx));
+ AutoDecRef key(PySequence_GetItem(item, 0));
+ AutoDecRef member(PySequence_GetItem(item, 1));
+ AutoDecRef value(PyObject_GetAttr(member, Shiboken::PyName::value()));
+ auto ckey = String::toCString(key);
+ auto ivalue = PyLong_AsSsize_t(value);
+ entries.push_back(std::make_pair(ckey, int(ivalue)));
+ }
+ addEnumerator(name, isFlag, true, entries);
+ }
+}