aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/libpyside/pyside.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside6/libpyside/pyside.cpp')
-rw-r--r--sources/pyside6/libpyside/pyside.cpp1215
1 files changed, 1215 insertions, 0 deletions
diff --git a/sources/pyside6/libpyside/pyside.cpp b/sources/pyside6/libpyside/pyside.cpp
new file mode 100644
index 000000000..9e12e3cd7
--- /dev/null
+++ b/sources/pyside6/libpyside/pyside.cpp
@@ -0,0 +1,1215 @@
+// Copyright (C) 2016 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 "pyside.h"
+#include "pysideinit.h"
+#include "pysidecleanup.h"
+#include "pysidemetatype.h"
+#include "pysideqapp.h"
+#include "pysideqobject.h"
+#include "pysideutils.h"
+#include "pyside_p.h"
+#include "signalmanager.h"
+#include "pysideclassinfo_p.h"
+#include "pysideproperty_p.h"
+#include "class_property.h"
+#include "pysideproperty.h"
+#include "pysidesignal.h"
+#include "pysidesignal_p.h"
+#include "pysidestaticstrings.h"
+#include "pysideslot_p.h"
+#include "pysidemetafunction_p.h"
+#include "pysidemetafunction.h"
+#include "dynamicqmetaobject.h"
+#include "feature_select.h"
+#include "pysidelogging_p.h"
+
+#include <autodecref.h>
+#include <basewrapper.h>
+#include <bindingmanager.h>
+#include <gilstate.h>
+#include <sbkconverter.h>
+#include <sbkstring.h>
+#include <sbkstaticstrings.h>
+#include <sbkfeature_base.h>
+#include <sbkmodule.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QMetaMethod>
+#include <QtCore/QMutex>
+#include <QtCore/QStack>
+#include <QtCore/QThread>
+#include <QtCore/private/qobject_p.h>
+
+#include <algorithm>
+#include <cstring>
+#include <cctype>
+#include <memory>
+#include <optional>
+#include <typeinfo>
+
+using namespace Qt::StringLiterals;
+
+static QStack<PySide::CleanupFunction> cleanupFunctionList;
+static void *qobjectNextAddr;
+
+QT_BEGIN_NAMESPACE
+extern bool qRegisterResourceData(int, const unsigned char *, const unsigned char *,
+ const unsigned char *);
+QT_END_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcPySide, "qt.pyside.libpyside", QtCriticalMsg)
+
+static QObjectData *qt_object_private(const QObject *o)
+{
+ class FriendlyQObject : public QObject {
+ public:
+ using QObject::d_ptr;
+ };
+ return static_cast<const FriendlyQObject *>(o)->d_ptr.data();
+}
+
+static bool hasDynamicMetaObject(const QObject *o)
+{
+ return qt_object_private(o)->metaObject != nullptr;
+}
+
+namespace PySide
+{
+
+void init(PyObject *module)
+{
+ qobjectNextAddr = nullptr;
+ ClassInfo::init(module);
+ Signal::init(module);
+ Slot::init(module);
+ Property::init(module);
+ ClassProperty::init(module);
+ MetaFunction::init(module);
+ // Init signal manager, so it will register some meta types used by QVariant.
+ SignalManager::instance();
+ initQApp();
+}
+
+static const QByteArray _sigWithMangledName(const QByteArray &signature, bool mangle)
+{
+ if (!mangle)
+ return signature;
+ auto bracePos = signature.indexOf('(');
+ auto limit = bracePos >= 0 ? bracePos : signature.size();
+ if (limit < 3)
+ return signature;
+ QByteArray result;
+ result.reserve(signature.size() + 4);
+ for (auto i = 0; i < limit; ++i) {
+ const char c = signature.at(i);
+ if (std::isupper(c)) {
+ if (i > 0) {
+ if (std::isupper(signature.at(i - 1)))
+ return signature; // Give up at consecutive upper chars
+ result.append('_');
+ }
+ result.append(std::tolower(c));
+ } else {
+ result.append(c);
+ }
+ }
+ // Copy the rest after the opening brace (if any)
+ result.append(signature.mid(limit));
+ return result;
+}
+
+static const QByteArray _sigWithOrigName(const QByteArray &signature, bool mangle)
+{
+ if (!mangle)
+ return signature;
+ auto bracePos = signature.indexOf('(');
+ auto limit = bracePos >= 0 ? bracePos : signature.size();
+ QByteArray result;
+ result.reserve(signature.size());
+ for (auto i = 0; i < limit; ++i) {
+ const char c = signature.at(i);
+ if (std::isupper(c)) {
+ if (i > 0) {
+ if (std::isupper(signature.at(i - 1)))
+ return signature; // Give up at consecutive upper chars
+ return QByteArray{}; // Error, this was not converted!
+ }
+ }
+ if (std::islower(c) && i > 0 && signature.at(i - 1) == '_') {
+ result.chop(1);
+ result.append(std::toupper(c));
+ } else {
+ result.append(c);
+ }
+ }
+ // Copy the rest after the opening brace (if any)
+ result.append(signature.mid(limit));
+ return result;
+}
+
+/*****************************************************************************
+ *
+ * How do we find a property?
+ * --------------------------
+ *
+ * There are methods which are truly parts of properties, and there are
+ * other property-like methods which are not. True properties can be
+ * found by inspecting `SbkObjectType_GetPropertyStrings(type)`.
+ *
+ * Pseudo-properties have only a getter and a setter, and we must assume that
+ * the name of the getter is the property name, and the name of the setter
+ * is the uppercase of the getter with "set" prepended.
+ *
+ * We first walk the mro and search the property name and get the setter
+ * name. If that doesn't work, we use the heuristics for the setter.
+ * We then do the final mro lookup.
+ *
+ * Note that the true property lists have the original names, while the
+ * dict entries in the mro are already mangled.
+ */
+
+static const QByteArrayList parseFields(const char *propStr, int flags, bool *stdWrite)
+{
+ /*
+ * Break the string into subfields at ':' and add defaults.
+ */
+ if (stdWrite)
+ *stdWrite = true;
+ QByteArray s = QByteArray(propStr);
+ auto list = s.split(':');
+ assert(list.size() == 2 || list.size() == 3);
+ auto name = list[0];
+ auto read = list[1];
+ if (read.isEmpty())
+ list[1] = name;
+ if (list.size() == 2)
+ return list;
+ auto write = list[2];
+ if (stdWrite)
+ *stdWrite = write.isEmpty();
+ if (write.isEmpty()) {
+ auto snake_flag = flags & 0x01;
+ if (snake_flag) {
+ list[2] = ("set_") + name;
+ } else {
+ list[2] = QByteArray("set") + name;
+ list[2][3] = std::toupper(list[2][3]);
+ }
+ }
+ return list;
+}
+
+static QByteArrayList _SbkType_LookupProperty(PyTypeObject *type,
+ const QByteArray &name, int flags)
+{
+ /*
+ * Looks up a property and returns all fields.
+ */
+ int snake_flag = flags & 0x01;
+ QByteArray origName(_sigWithOrigName(name, snake_flag));
+ if (origName.isEmpty())
+ return QByteArrayList{};
+ PyObject *mro = type->tp_mro;
+ auto n = PyTuple_GET_SIZE(mro);
+ auto len = std::strlen(origName);
+ for (Py_ssize_t idx = 0; idx < n; idx++) {
+ PyTypeObject *base = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+ if (!SbkObjectType_Check(base))
+ continue;
+ auto props = SbkObjectType_GetPropertyStrings(base);
+ if (props == nullptr || *props == nullptr)
+ continue;
+ for (; *props != nullptr; ++props) {
+ QByteArray propStr(*props);
+ if (std::strncmp(propStr, origName, len) == 0) {
+ if (propStr[len] != ':')
+ continue;
+ // We found the property. Return the parsed fields.
+ propStr = _sigWithMangledName(propStr, snake_flag);
+ return parseFields(propStr, flags, nullptr);
+ }
+ }
+ }
+ return QByteArrayList{};
+}
+
+static QByteArrayList _SbkType_FakeProperty(const QByteArray &name, int flags)
+{
+ /*
+ * Handle a pseudo.property and return all fields.
+ */
+ int snake_flag = flags & 0x01;
+ QByteArray propStr(name);
+ propStr += "::";
+ propStr = _sigWithMangledName(propStr, snake_flag);
+ return parseFields(propStr, snake_flag, nullptr);
+}
+
+static bool _setProperty(PyObject *qObj, PyObject *name, PyObject *value, bool *accept)
+{
+ using Shiboken::AutoDecRef;
+
+ QByteArray propName(Shiboken::String::toCString(name));
+ auto type = Py_TYPE(qObj);
+ int flags = currentSelectId(type);
+ int prop_flag = flags & 0x02;
+ auto found = false;
+ QByteArray getterName{}, setterName{};
+
+ auto fields = _SbkType_LookupProperty(type, propName, flags);
+ if (!fields.isEmpty()) {
+ found = true;
+ bool haveWrite = fields.size() == 3;
+ if (!haveWrite)
+ return false;
+ } else {
+ fields = _SbkType_FakeProperty(propName, flags);
+ }
+
+ propName = fields[0];
+ getterName = fields[1];
+ setterName = fields[2];
+
+ // PYSIDE-1702: We do not use getattr, since that could trigger an action
+ // if we have a true property. Better to look inside the mro.
+ // That should return a descriptor or a property.
+ PyObject *look{};
+
+ if (found && prop_flag) {
+ // We have a property, and true_property is active.
+ // There must be a property object and we use it's fset.
+ AutoDecRef pyPropName(Shiboken::String::fromCString(propName.constData()));
+ look = _PepType_Lookup(Py_TYPE(qObj), pyPropName);
+ } else {
+ // We have a pseudo property or true_property is off, looking for a setter.
+ AutoDecRef pySetterName(Shiboken::String::fromCString(setterName.constData()));
+ look = _PepType_Lookup(Py_TYPE(qObj), pySetterName);
+ }
+
+ if (look) {
+ AutoDecRef propSetter{};
+ static PyObject *magicGet = Shiboken::PyMagicName::get();
+ if (found && prop_flag) {
+ // the indirection of the setter descriptor in a true property
+ AutoDecRef descr(PyObject_GetAttr(look, PySideName::fset()));
+ propSetter.reset(PyObject_CallMethodObjArgs(descr, magicGet, qObj, nullptr));
+ } else {
+ // look is already the descriptor
+ propSetter.reset(PyObject_CallMethodObjArgs(look, magicGet, qObj, nullptr));
+ }
+ *accept = true;
+ AutoDecRef args(PyTuple_Pack(1, value));
+ AutoDecRef retval(PyObject_CallObject(propSetter, args));
+ if (retval.isNull())
+ return false;
+ } else {
+ PyErr_Clear();
+ AutoDecRef attr(PyObject_GenericGetAttr(qObj, name));
+ if (PySide::Property::checkType(attr)) {
+ *accept = true;
+ if (PySide::Property::setValue(reinterpret_cast<PySideProperty *>(
+ attr.object()), qObj, value) < 0)
+ return false;
+ }
+ }
+ return true;
+}
+
+// PYSIDE-2329: Search a signal by name (Note: QMetaObject::indexOfSignal()
+// searches by signature).
+static std::optional<QMetaMethod> findSignal(const QMetaObject *mo,
+ const QByteArray &name)
+{
+ const auto count = mo->methodCount();
+ for (int i = mo->methodOffset(); i < count; ++i) {
+ const auto method = mo->method(i);
+ if (method.methodType() == QMetaMethod::Signal && method.name() == name)
+ return method;
+ }
+ auto *base = mo->superClass();
+ return base != nullptr ? findSignal(base, name) : std::nullopt;
+}
+
+bool fillQtProperties(PyObject *qObj, const QMetaObject *metaObj,
+ PyObject *kwds, bool allowErrors)
+{
+
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ int flags = currentSelectId(Py_TYPE(qObj));
+ int snake_flag = flags & 0x01;
+
+ while (PyDict_Next(kwds, &pos, &key, &value)) {
+ const QByteArray propName = Shiboken::String::toCString(key);
+ QByteArray unmangledName = _sigWithOrigName(propName, snake_flag);
+ bool accept = false;
+ // PYSIDE-1705: Make sure that un-mangled names are not recognized in snake_case mode.
+ if (!unmangledName.isEmpty()) {
+ if (metaObj->indexOfProperty(unmangledName) != -1) {
+ if (!_setProperty(qObj, key, value, &accept))
+ return false;
+ } else {
+ const auto methodO = findSignal(metaObj, propName);
+ if (methodO.has_value()) {
+ const auto signature = "2"_ba + methodO->methodSignature();
+ accept = true;
+ if (!PySide::Signal::connect(qObj, signature, value))
+ return false;
+ }
+ }
+ if (!accept) {
+ // PYSIDE-1019: Allow any existing attribute in the constructor.
+ if (!_setProperty(qObj, key, value, &accept))
+ return false;
+ }
+ }
+ if (allowErrors) {
+ PyErr_Clear();
+ continue;
+ }
+ if (!accept) {
+ PyErr_Format(PyExc_AttributeError, "'%s' is not a Qt property or a signal",
+ propName.constData());
+ return false;
+ }
+ }
+ return true;
+}
+
+void registerCleanupFunction(CleanupFunction func)
+{
+ cleanupFunctionList.push(func);
+}
+
+void runCleanupFunctions()
+{
+ while (!cleanupFunctionList.isEmpty()) {
+ CleanupFunction f = cleanupFunctionList.pop();
+ f();
+ }
+}
+
+static void destructionVisitor(SbkObject *pyObj, void *data)
+{
+ auto realData = reinterpret_cast<void **>(data);
+ auto pyQApp = reinterpret_cast<SbkObject *>(realData[0]);
+ auto pyQObjectType = reinterpret_cast<PyTypeObject *>(realData[1]);
+
+ if (pyObj != pyQApp && PyObject_TypeCheck(pyObj, pyQObjectType)) {
+ if (Shiboken::Object::hasOwnership(pyObj) && Shiboken::Object::isValid(pyObj, false)) {
+ Shiboken::Object::setValidCpp(pyObj, false);
+
+ Py_BEGIN_ALLOW_THREADS
+ Shiboken::callCppDestructor<QObject>(Shiboken::Object::cppPointer(pyObj, pyQObjectType));
+ Py_END_ALLOW_THREADS
+ }
+ }
+
+};
+
+void destroyQCoreApplication()
+{
+ QCoreApplication *app = QCoreApplication::instance();
+ if (!app)
+ return;
+ SignalManager::instance().clear();
+
+ Shiboken::BindingManager &bm = Shiboken::BindingManager::instance();
+ SbkObject *pyQApp = bm.retrieveWrapper(app);
+ PyTypeObject *pyQObjectType = Shiboken::Conversions::getPythonTypeObject("QObject*");
+ assert(pyQObjectType);
+
+ void *data[2] = {pyQApp, pyQObjectType};
+ bm.visitAllPyObjects(&destructionVisitor, &data);
+
+ // in the end destroy app
+ // Allow threads because the destructor calls
+ // QThreadPool::globalInstance().waitForDone() which may deadlock on the GIL
+ // if there is a worker working with python objects.
+ Py_BEGIN_ALLOW_THREADS
+ delete app;
+ Py_END_ALLOW_THREADS
+ // PYSIDE-571: make sure to create a singleton deleted qApp.
+ Py_DECREF(MakeQAppWrapper(nullptr));
+}
+
+std::size_t getSizeOfQObject(PyTypeObject *type)
+{
+ return retrieveTypeUserData(type)->cppObjSize;
+}
+
+void initDynamicMetaObject(PyTypeObject *type, const QMetaObject *base, std::size_t cppObjSize)
+{
+ //create DynamicMetaObject based on python type
+ auto userData = new TypeUserData(reinterpret_cast<PyTypeObject *>(type), base, cppObjSize);
+ userData->mo.update();
+ Shiboken::ObjectType::setTypeUserData(type, userData, Shiboken::callCppDestructor<TypeUserData>);
+
+ //initialize staticQMetaObject property
+ void *metaObjectPtr = const_cast<QMetaObject *>(userData->mo.update());
+ static SbkConverter *converter = Shiboken::Conversions::getConverter("QMetaObject");
+ if (!converter)
+ return;
+ Shiboken::AutoDecRef pyMetaObject(Shiboken::Conversions::pointerToPython(converter, metaObjectPtr));
+ PyObject_SetAttr(reinterpret_cast<PyObject *>(type),
+ Shiboken::PyName::qtStaticMetaObject(), pyMetaObject);
+}
+
+TypeUserData *retrieveTypeUserData(PyTypeObject *pyTypeObj)
+{
+ if (!SbkObjectType_Check(pyTypeObj))
+ return nullptr;
+ return reinterpret_cast<TypeUserData *>(Shiboken::ObjectType::getTypeUserData(pyTypeObj));
+}
+
+TypeUserData *retrieveTypeUserData(PyObject *pyObj)
+{
+ auto pyTypeObj = PyType_Check(pyObj)
+ ? reinterpret_cast<PyTypeObject *>(pyObj) : Py_TYPE(pyObj);
+ return retrieveTypeUserData(pyTypeObj);
+}
+
+const QMetaObject *retrieveMetaObject(PyTypeObject *pyTypeObj)
+{
+ TypeUserData *userData = retrieveTypeUserData(pyTypeObj);
+ return userData ? userData->mo.update() : nullptr;
+}
+
+const QMetaObject *retrieveMetaObject(PyObject *pyObj)
+{
+ auto pyTypeObj = PyType_Check(pyObj)
+ ? reinterpret_cast<PyTypeObject *>(pyObj) : Py_TYPE(pyObj);
+ return retrieveMetaObject(pyTypeObj);
+}
+
+void initQObjectSubType(PyTypeObject *type, PyObject *args, PyObject * /* kwds */)
+{
+ PyTypeObject *qObjType = Shiboken::Conversions::getPythonTypeObject("QObject*");
+
+ PyObject *bases = PyTuple_GET_ITEM(args, 1);
+ int numBases = PyTuple_GET_SIZE(bases);
+
+ TypeUserData *userData = nullptr;
+
+ for (int i = 0; i < numBases; ++i) {
+ auto base = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, i));
+ if (PyType_IsSubtype(base, qObjType)) {
+ userData = retrieveTypeUserData(base);
+ break;
+ }
+ }
+ if (!userData) {
+ const char *className = Shiboken::String::toCString(PyTuple_GET_ITEM(args, 0));
+ qWarning("Sub class of QObject not inheriting QObject!? Crash will happen when using %s.",
+ className);
+ return;
+ }
+ // PYSIDE-1463: Don't change feature selection durin subtype initialization.
+ // This behavior is observed with PySide 6.
+ PySide::Feature::Enable(false);
+ initDynamicMetaObject(type, userData->mo.update(), userData->cppObjSize);
+ PySide::Feature::Enable(true);
+}
+
+void initQApp()
+{
+ /*
+ * qApp will not be initialized when embedding is active.
+ * That means that qApp exists already when PySide is initialized.
+ * We could solve that by creating a qApp variable, but in embedded
+ * mode, we also have the effect that the first assignment to qApp
+ * is persistent! Therefore, we can never be sure to have created
+ * qApp late enough to get the right type for the instance.
+ *
+ * I would appreciate very much if someone could explain or even fix
+ * this issue. It exists only when a pre-existing application exists.
+ */
+ if (!qApp)
+ Py_DECREF(MakeQAppWrapper(nullptr));
+
+ // PYSIDE-1470: Register a function to destroy an application from shiboken.
+ setDestroyQApplication(destroyQCoreApplication);
+}
+
+PyObject *getHiddenDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *name)
+{
+ using Shiboken::AutoDecRef;
+
+ // PYSIDE-68-bis: This getattr finds signals early by `signalDescrGet`.
+ PyObject *attr = PyObject_GenericGetAttr(self, name);
+ if (!Shiboken::Object::isValid(reinterpret_cast<SbkObject *>(self), false))
+ return attr;
+
+ if (attr && Property::checkType(attr)) {
+ PyObject *value = Property::getValue(reinterpret_cast<PySideProperty *>(attr), self);
+ Py_DECREF(attr);
+ if (!value)
+ return nullptr;
+ attr = value;
+ }
+
+ // Search on metaobject (avoid internal attributes started with '__')
+ if (!attr) {
+ PyObject *type, *value, *traceback;
+ PyErr_Fetch(&type, &value, &traceback); // This was omitted for a loong time.
+
+ int flags = currentSelectId(Py_TYPE(self));
+ int snake_flag = flags & 0x01;
+ int propFlag = flags & 0x02;
+
+ if (propFlag) {
+ // PYSIDE-1889: If we have actually a Python property, return f(get|set|del).
+ // Do not store this attribute in the instance dict, because this
+ // would create confusion with overload.
+ // Note: before implementing this property handling, the meta function code
+ // below created meta functions which was quite wrong.
+ auto *subdict = _PepType_Lookup(Py_TYPE(self), PySideMagicName::property_methods());
+ PyObject *propName = PyDict_GetItem(subdict, name);
+ if (propName) {
+ // We really have a property name and need to fetch the fget or fset function.
+ static PyObject *const _fget = Shiboken::String::createStaticString("fget");
+ static PyObject *const _fset = Shiboken::String::createStaticString("fset");
+ static PyObject *const _fdel = Shiboken::String::createStaticString("fdel");
+ static PyObject *const arr[3] = {_fget, _fset, _fdel};
+ auto prop = _PepType_Lookup(Py_TYPE(self), propName);
+ for (int idx = 0; idx < 3; ++idx) {
+ auto *trial = arr[idx];
+ auto *res = PyObject_GetAttr(prop, trial);
+ if (res) {
+ AutoDecRef elemName(PyObject_GetAttr(res, PySideMagicName::name()));
+ // Note: This comparison works because of interned strings.
+ if (elemName == name)
+ return res;
+ Py_DECREF(res);
+ }
+ PyErr_Clear();
+ }
+ }
+ }
+
+ const char *cname = Shiboken::String::toCString(name);
+ uint cnameLen = qstrlen(cname);
+ if (std::strncmp("__", cname, 2) != 0) {
+ const QMetaObject *metaObject = cppSelf->metaObject();
+ QList<QMetaMethod> signalList;
+ // Caution: This inserts a meta function or a signal into the instance dict.
+ for (int i=0, imax = metaObject->methodCount(); i < imax; i++) {
+ QMetaMethod method = metaObject->method(i);
+ // PYSIDE-1753: Snake case names must be renamed here too, or they will be
+ // found unexpectedly when forgetting to rename them.
+ auto origSignature = method.methodSignature();
+ // Currently, we rename only methods but no signals. This might change.
+ bool use_lower = snake_flag and method.methodType() != QMetaMethod::Signal;
+ const QByteArray methSig_ = _sigWithMangledName(origSignature, use_lower);
+ const char *methSig = methSig_.constData();
+ bool methMatch = std::strncmp(cname, methSig, cnameLen) == 0
+ && methSig[cnameLen] == '(';
+ if (methMatch) {
+ if (method.methodType() == QMetaMethod::Signal) {
+ signalList.append(method);
+ } else {
+ PySideMetaFunction *func = MetaFunction::newObject(cppSelf, i);
+ if (func) {
+ PyObject *result = reinterpret_cast<PyObject *>(func);
+ PyObject_SetAttr(self, name, result);
+ return result;
+ }
+ }
+ }
+ }
+ if (!signalList.isEmpty()) {
+ PyObject *pySignal = reinterpret_cast<PyObject *>(
+ Signal::newObjectFromMethod(self, signalList));
+ PyObject_SetAttr(self, name, pySignal);
+ return pySignal;
+ }
+ }
+ PyErr_Restore(type, value, traceback);
+ }
+ return attr;
+}
+
+bool inherits(PyTypeObject *objType, const char *class_name)
+{
+ if (strcmp(objType->tp_name, class_name) == 0)
+ return true;
+
+ PyTypeObject *base = objType->tp_base;
+ if (base == nullptr)
+ return false;
+
+ return inherits(base, class_name);
+}
+
+QMutex &nextQObjectMemoryAddrMutex()
+{
+ static QMutex mutex;
+ return mutex;
+}
+
+void *nextQObjectMemoryAddr()
+{
+ return qobjectNextAddr;
+}
+
+void setNextQObjectMemoryAddr(void *addr)
+{
+ qobjectNextAddr = addr;
+}
+
+} // namespace PySide
+
+// A std::shared_ptr is used with a deletion function to invalidate a pointer
+// when the property value is cleared. This should be a QSharedPointer with
+// a void *pointer, but that isn't allowed
+using any_t = char;
+Q_DECLARE_METATYPE(std::shared_ptr<any_t>);
+
+
+namespace PySide
+{
+
+static void invalidatePtr(any_t *object)
+{
+ // PYSIDE-2254: Guard against QObjects outliving Python, for example the
+ // adopted main thread as returned by QObjects::thread().
+ if (Py_IsInitialized() == 0)
+ return;
+
+ Shiboken::GilState state;
+
+ SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(object);
+ if (wrapper != nullptr)
+ Shiboken::BindingManager::instance().releaseWrapper(wrapper);
+}
+
+static const char invalidatePropertyName[] = "_PySideInvalidatePtr";
+
+// PYSIDE-2749: Skip over internal QML classes and classes
+// with dynamic meta objects when looking for the best matching
+// type to avoid unnessarily triggering the lazy load mechanism
+// for classes that do not have a binding from things like eventFilter().
+static inline bool isInternalObject(const char *name)
+{
+ return std::strstr(name, "QMLTYPE") != nullptr || std::strstr(name, "QQmlPrivate") != nullptr;
+}
+
+static const QMetaObject *metaObjectCandidate(const QObject *o)
+{
+ auto *metaObject = o->metaObject();
+ // Skip QML helper types and Python objects
+ if (hasDynamicMetaObject(o)) {
+ if (auto *super = metaObject->superClass())
+ metaObject = super;
+ }
+ for (auto *candidate = metaObject; candidate != nullptr; candidate = candidate->superClass()) {
+ if (!isInternalObject(candidate->className())) {
+ metaObject = candidate;
+ break;
+ }
+ }
+ return metaObject;
+}
+
+// PYSIDE-1214, when creating new wrappers for classes inheriting QObject but
+// not exposed to Python, try to find the best-matching (most-derived) Qt
+// class by walking up the meta objects.
+static const char *typeName(const QObject *cppSelf)
+{
+ const char *typeName = typeid(*cppSelf).name();
+ if (!Shiboken::Conversions::getConverter(typeName)) {
+ auto *metaObject = metaObjectCandidate(cppSelf);
+ for (; metaObject != nullptr; metaObject = metaObject->superClass()) {
+ const char *name = metaObject->className();
+ if (Shiboken::Conversions::getConverter(name)) {
+ typeName = name;
+ break;
+ }
+ }
+ }
+ return typeName;
+}
+
+PyTypeObject *getTypeForQObject(const QObject *cppSelf)
+{
+ // First check if there are any instances of Python implementations
+ // inheriting a PySide class.
+ auto *existing = Shiboken::BindingManager::instance().retrieveWrapper(cppSelf);
+ if (existing != nullptr)
+ return reinterpret_cast<PyObject *>(existing)->ob_type;
+ // Find the best match (will return a PySide type)
+ auto *sbkObjectType = Shiboken::ObjectType::typeForTypeName(typeName(cppSelf));
+ if (sbkObjectType != nullptr)
+ return reinterpret_cast<PyTypeObject *>(sbkObjectType);
+ return nullptr;
+}
+
+PyObject *getWrapperForQObject(QObject *cppSelf, PyTypeObject *sbk_type)
+{
+ PyObject *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppSelf));
+ if (pyOut) {
+ Py_INCREF(pyOut);
+ return pyOut;
+ }
+
+ // Setting the property will trigger an QEvent notification, which may call into
+ // code that creates the wrapper so only set the property if it isn't already
+ // set and check if it's created after the set call
+ QVariant existing = cppSelf->property(invalidatePropertyName);
+ if (!existing.isValid()) {
+ if (cppSelf->thread() == QThread::currentThread()) {
+ std::shared_ptr<any_t> shared_with_del(reinterpret_cast<any_t *>(cppSelf), invalidatePtr);
+ cppSelf->setProperty(invalidatePropertyName, QVariant::fromValue(shared_with_del));
+ }
+ pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppSelf));
+ if (pyOut) {
+ Py_INCREF(pyOut);
+ return pyOut;
+ }
+ }
+
+ pyOut = Shiboken::Object::newObjectWithHeuristics(sbk_type, cppSelf, false, typeName(cppSelf));
+
+ return pyOut;
+}
+
+QString pyUnicodeToQString(PyObject *str)
+{
+ Q_ASSERT(PyUnicode_Check(str) != 0);
+
+ const void *data = _PepUnicode_DATA(str);
+ const Py_ssize_t len = PyUnicode_GetLength(str);
+ switch (_PepUnicode_KIND(str)) {
+ case PepUnicode_1BYTE_KIND:
+ return QString::fromLatin1(reinterpret_cast<const char *>(data), len);
+ case PepUnicode_2BYTE_KIND:
+ return QString::fromUtf16(reinterpret_cast<const char16_t *>(data), len);
+ case PepUnicode_4BYTE_KIND:
+ break;
+ }
+ return QString::fromUcs4(reinterpret_cast<const char32_t *>(data), len);
+}
+
+PyObject *qStringToPyUnicode(QStringView s)
+{
+ const QByteArray ba = s.toUtf8();
+ return PyUnicode_FromStringAndSize(ba.constData(), ba.size());
+}
+
+// Inspired by Shiboken::String::toCString;
+QString pyStringToQString(PyObject *str)
+{
+ if (str == Py_None)
+ return QString();
+
+ if (PyUnicode_Check(str) != 0)
+ return pyUnicodeToQString(str);
+
+ if (PyBytes_Check(str)) {
+ const char *asciiBuffer = PyBytes_AS_STRING(str);
+ if (asciiBuffer)
+ return QString::fromLatin1(asciiBuffer);
+ }
+ return QString();
+}
+
+// PySide-1499: Provide an efficient, correct PathLike interface
+QString pyPathToQString(PyObject *path)
+{
+ // For empty constructors path can be nullptr
+ // fallback to an empty QString in that case.
+ if (!path)
+ return QString();
+
+ // str or bytes pass through
+ if (PyUnicode_Check(path) || PyBytes_Check(path))
+ return pyStringToQString(path);
+
+ // Let PyOS_FSPath do its work and then fix the result for Windows.
+ Shiboken::AutoDecRef strPath(PyOS_FSPath(path));
+ if (strPath.isNull())
+ return QString();
+ return QDir::fromNativeSeparators(pyStringToQString(strPath));
+}
+
+bool isCompiledMethod(PyObject *callback)
+{
+ return PyObject_HasAttr(callback, PySide::PySideName::im_func())
+ && PyObject_HasAttr(callback, PySide::PySideName::im_self())
+ && PyObject_HasAttr(callback, PySide::PySideMagicName::code());
+}
+
+static const unsigned char qt_resource_name[] = {
+ // qt
+ 0x0,0x2,
+ 0x0,0x0,0x7,0x84,
+ 0x0,0x71,
+ 0x0,0x74,
+ // etc
+ 0x0,0x3,
+ 0x0,0x0,0x6c,0xa3,
+ 0x0,0x65,
+ 0x0,0x74,0x0,0x63,
+ // qt.conf
+ 0x0,0x7,
+ 0x8,0x74,0xa6,0xa6,
+ 0x0,0x71,
+ 0x0,0x74,0x0,0x2e,0x0,0x63,0x0,0x6f,0x0,0x6e,0x0,0x66
+};
+
+static const unsigned char qt_resource_struct[] = {
+ // :
+ 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+ // :/qt
+ 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,
+ // :/qt/etc
+ 0x0,0x0,0x0,0xa,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,
+ // :/qt/etc/qt.conf
+ 0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0
+};
+
+bool registerInternalQtConf()
+{
+ // Guard to ensure single registration.
+#ifdef PYSIDE_QT_CONF_PREFIX
+ static bool registrationAttempted = false;
+#else
+ static bool registrationAttempted = true;
+#endif
+ static bool isRegistered = false;
+ if (registrationAttempted)
+ return isRegistered;
+ registrationAttempted = true;
+
+ // Support PyInstaller case when a qt.conf file might be provided next to the generated
+ // PyInstaller executable.
+ // This will disable the internal qt.conf which points to the PySide6 subdirectory (due to the
+ // subdirectory not existing anymore).
+#ifndef PYPY_VERSION
+ QString executablePath = QString::fromWCharArray(Py_GetProgramFullPath());
+#else
+ // PYSIDE-535: FIXME: Add this function when available.
+ QString executablePath = QLatin1StringView("missing Py_GetProgramFullPath");
+#endif // PYPY_VERSION
+
+ QString appDirPath = QFileInfo(executablePath).absolutePath();
+
+ QString maybeQtConfPath = QDir(appDirPath).filePath(u"qt.conf"_s);
+ maybeQtConfPath = QDir::toNativeSeparators(maybeQtConfPath);
+ bool executableQtConfAvailable = QFileInfo::exists(maybeQtConfPath);
+
+ QString maybeQt6ConfPath = QDir(appDirPath).filePath(u"qt6.conf"_s);
+ maybeQt6ConfPath = QDir::toNativeSeparators(maybeQt6ConfPath);
+ bool executableQt6ConfAvailable = QFileInfo::exists(maybeQt6ConfPath);
+
+ // Allow disabling the usage of the internal qt.conf. This is necessary for tests to work,
+ // because tests are executed before the package is installed, and thus the Prefix specified
+ // in qt.conf would point to a not yet existing location.
+ bool disableInternalQtConf =
+ qEnvironmentVariableIntValue("PYSIDE_DISABLE_INTERNAL_QT_CONF") > 0;
+ bool runsInConda =
+ qEnvironmentVariableIsSet("CONDA_DEFAULT_ENV") || qEnvironmentVariableIsSet("CONDA_PREFIX");
+
+ if ((!runsInConda && (disableInternalQtConf || executableQtConfAvailable))
+ || (runsInConda && executableQt6ConfAvailable)) {
+ registrationAttempted = true;
+ return false;
+ }
+
+ PyObject *pysideModule = PyImport_ImportModule("PySide6");
+ if (!pysideModule)
+ return false;
+
+ // Querying __file__ should be done only for modules that have finished their initialization.
+ // Thus querying for the top-level PySide6 package works for us whenever any Qt-wrapped module
+ // is loaded.
+ PyObject *pysideInitFilePath = PyObject_GetAttr(pysideModule, Shiboken::PyMagicName::file());
+ Py_DECREF(pysideModule);
+ if (!pysideInitFilePath)
+ return false;
+
+ QString initPath = pyStringToQString(pysideInitFilePath);
+ Py_DECREF(pysideInitFilePath);
+ if (initPath.isEmpty())
+ return false;
+
+ // pysideDir - absolute path to the directory containing the init file, which also contains
+ // the rest of the PySide6 modules.
+ // prefixPath - absolute path to the directory containing the installed Qt (prefix).
+ QDir pysideDir = QFileInfo(QDir::fromNativeSeparators(initPath)).absoluteDir();
+ QString setupPrefix;
+#ifdef PYSIDE_QT_CONF_PREFIX
+ setupPrefix = QStringLiteral(PYSIDE_QT_CONF_PREFIX);
+#endif
+ const QByteArray prefixPath = pysideDir.absoluteFilePath(setupPrefix).toUtf8();
+
+ // rccData needs to be static, otherwise when it goes out of scope, the Qt resource system
+ // will point to invalid memory.
+ static QByteArray rccData = QByteArrayLiteral("[Paths]\nPrefix = ") + prefixPath + "\n";
+#ifdef Q_OS_WIN
+ // LibraryExecutables needs to point to Prefix instead of ./bin because we don't
+ // currently conform to the Qt default directory layout on Windows. This is necessary
+ // for QtWebEngineCore to find the location of QtWebEngineProcess.exe.
+ rccData += QByteArrayLiteral("LibraryExecutables = ") + prefixPath + "\n";
+#endif
+
+ // The RCC data structure expects a 4-byte size value representing the actual data.
+ qsizetype size = rccData.size();
+
+ for (int i = 0; i < 4; ++i) {
+ rccData.prepend((size & 0xff));
+ size >>= 8;
+ }
+
+ const int version = 0x01;
+ isRegistered = qRegisterResourceData(version, qt_resource_struct, qt_resource_name,
+ reinterpret_cast<const unsigned char *>(
+ rccData.constData()));
+
+ return isRegistered;
+}
+
+static PyTypeObject *qobjectType()
+{
+ static PyTypeObject * const result = Shiboken::Conversions::getPythonTypeObject("QObject*");
+ return result;
+}
+
+bool isQObjectDerived(PyTypeObject *pyType, bool raiseError)
+{
+ const bool result = PyType_IsSubtype(pyType, qobjectType());
+ if (!result && raiseError) {
+ PyErr_Format(PyExc_TypeError, "A type inherited from %s expected, got %s.",
+ qobjectType()->tp_name, pyType->tp_name);
+ }
+ return result;
+}
+
+QObject *convertToQObject(PyObject *object, bool raiseError)
+{
+ if (object == nullptr) {
+ if (raiseError)
+ PyErr_Format(PyExc_TypeError, "None passed for QObject");
+ return nullptr;
+ }
+
+ if (!isQObjectDerived(Py_TYPE(object), raiseError))
+ return nullptr;
+
+ auto *sbkObject = reinterpret_cast<SbkObject*>(object);
+ auto *ptr = Shiboken::Object::cppPointer(sbkObject, qobjectType());
+ if (ptr == nullptr) {
+ if (raiseError) {
+ PyErr_Format(PyExc_TypeError, "Conversion of %s to QObject failed.",
+ Py_TYPE(object)->tp_name);
+ }
+ return nullptr;
+ }
+ return reinterpret_cast<QObject*>(ptr);
+}
+
+QMetaType qMetaTypeFromPyType(PyTypeObject *pyType)
+{
+ if (Shiboken::String::checkType(pyType))
+ return QMetaType(QMetaType::QString);
+ if (pyType == &PyFloat_Type)
+ return QMetaType(QMetaType::Double);
+ if (pyType == &PyLong_Type)
+ return QMetaType(QMetaType::Int);
+ if (Shiboken::ObjectType::checkType(pyType))
+ return QMetaType::fromName(Shiboken::ObjectType::getOriginalName(pyType));
+ return QMetaType::fromName(pyType->tp_name);
+}
+
+debugPyTypeObject::debugPyTypeObject(const PyTypeObject *o) noexcept
+ : m_object(o)
+{
+}
+
+QDebug operator<<(QDebug debug, const debugPyTypeObject &o)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "PyTypeObject(";
+ if (o.m_object)
+ debug << '"' << o.m_object->tp_name << '"';
+ else
+ debug << '0';
+ debug << ')';
+ return debug;
+}
+
+static void formatPyObject(PyObject *obj, QDebug &debug);
+
+static void formatPySequence(PyObject *obj, QDebug &debug)
+{
+ const Py_ssize_t size = PySequence_Size(obj);
+ debug << size << " [";
+ for (Py_ssize_t i = 0; i < size; ++i) {
+ if (i)
+ debug << ", ";
+ Shiboken::AutoDecRef item(PySequence_GetItem(obj, i));
+ formatPyObject(item.object(), debug);
+ }
+ debug << ']';
+}
+
+static void formatPyDict(PyObject *obj, QDebug &debug)
+{
+ PyObject *key;
+ PyObject *value;
+ Py_ssize_t pos = 0;
+ bool first = true;
+ debug << '{';
+ while (PyDict_Next(obj, &pos, &key, &value) != 0) {
+ if (first)
+ first = false;
+ else
+ debug << ", ";
+ formatPyObject(key, debug);
+ debug << '=';
+ formatPyObject(value, debug);
+ }
+ debug << '}';
+}
+
+static inline const char *pyTypeName(PyObject *obj)
+{
+ return Py_TYPE(obj)->tp_name;
+}
+
+static QString getQualName(PyObject *obj)
+{
+ Shiboken::AutoDecRef result(PyObject_GetAttr(obj, Shiboken::PyMagicName::qualname()));
+ return result.object() != nullptr
+ ? pyStringToQString(result.object()) : QString{};
+}
+
+static void formatPyFunction(PyObject *obj, QDebug &debug)
+{
+ debug << '"' << getQualName(obj) << "()\"";
+}
+
+static void formatPyMethod(PyObject *obj, QDebug &debug)
+{
+ if (auto *func = PyMethod_Function(obj))
+ formatPyFunction(func, debug);
+ debug << ", instance=" << PyMethod_Self(obj);
+}
+
+static void formatPyObjectValue(PyObject *obj, QDebug &debug)
+{
+ if (PyType_Check(obj) != 0)
+ debug << "type: \"" << pyTypeName(obj) << '"';
+ else if (PyLong_Check(obj) != 0) {
+ const auto llv = PyLong_AsLongLong(obj);
+ if (PyErr_Occurred() != PyExc_OverflowError) {
+ debug << llv;
+ } else {
+ PyErr_Clear();
+ debug << "0x" << Qt::hex << PyLong_AsUnsignedLongLong(obj) << Qt::dec;
+ }
+ } else if (PyFloat_Check(obj) != 0)
+ debug << PyFloat_AsDouble(obj);
+ else if (PyUnicode_Check(obj) != 0)
+ debug << '"' << pyStringToQString(obj) << '"';
+ else if (PyFunction_Check(obj) != 0)
+ formatPyFunction(obj, debug);
+ else if (PyMethod_Check(obj) != 0)
+ formatPyMethod(obj, debug);
+ else if (PySequence_Check(obj) != 0)
+ formatPySequence(obj, debug);
+ else if (PyDict_Check(obj) != 0)
+ formatPyDict(obj, debug);
+ else
+ debug << obj;
+}
+
+static void formatPyObject(PyObject *obj, QDebug &debug)
+{
+ if (obj == nullptr) {
+ debug << '0';
+ return;
+ }
+ if (obj == Py_None) {
+ debug << "None";
+ return;
+ }
+ if (obj == Py_True) {
+ debug << "True";
+ return;
+ }
+ if (obj == Py_False) {
+ debug << "False";
+ return;
+ }
+ if (PyType_Check(obj) == 0)
+ debug << pyTypeName(obj) << ": ";
+ formatPyObjectValue(obj, debug);
+}
+
+debugPyObject::debugPyObject(PyObject *o) noexcept : m_object(o)
+{
+}
+
+QDebug operator<<(QDebug debug, const debugPyObject &o)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "PyObject(";
+ formatPyObject(o.m_object, debug);
+ debug << ')';
+ return debug;
+}
+
+debugPyBuffer::debugPyBuffer(Py_buffer *b) noexcept : m_buffer(b)
+{
+}
+
+static void formatPy_ssizeArray(QDebug &debug, const char *name, const Py_ssize_t *array, int len)
+{
+ debug << ", " << name << '=';
+ if (array != nullptr) {
+ debug << '[';
+ for (int i = 0; i < len; ++i)
+ debug << array[i] << ' ';
+ debug << ']';
+ } else {
+ debug << '0';
+ }
+}
+
+PYSIDE_API QDebug operator<<(QDebug debug, const debugPyBuffer &b)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "Py_buffer(";
+ if (b.m_buffer != nullptr) {
+ debug << "obj=" << b.m_buffer->obj
+ << ", buf=" << b.m_buffer->buf << ", len=" << b.m_buffer->len
+ << ", readonly=" << b.m_buffer->readonly
+ << ", itemsize=" << b.m_buffer->itemsize << ", format=";
+ if (b.m_buffer->format != nullptr)
+ debug << '"' << b.m_buffer->format << '"';
+ else
+ debug << '0';
+ debug << ", ndim=" << b.m_buffer->ndim;
+ formatPy_ssizeArray(debug, "shape", b.m_buffer->shape, b.m_buffer->ndim);
+ formatPy_ssizeArray(debug, "strides", b.m_buffer->strides, b.m_buffer->ndim);
+ formatPy_ssizeArray(debug, "suboffsets", b.m_buffer->suboffsets, b.m_buffer->ndim);
+ } else {
+ debug << '0';
+ }
+ debug << ')';
+ return debug;
+}
+
+} // namespace PySide