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.cpp790
1 files changed, 654 insertions, 136 deletions
diff --git a/sources/pyside6/libpyside/pyside.cpp b/sources/pyside6/libpyside/pyside.cpp
index 14b86f322..cff74c260 100644
--- a/sources/pyside6/libpyside/pyside.cpp
+++ b/sources/pyside6/libpyside/pyside.cpp
@@ -1,44 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 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) 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 "pyside_numpy.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"
@@ -53,6 +22,7 @@
#include "pysidemetafunction.h"
#include "dynamicqmetaobject.h"
#include "feature_select.h"
+#include "pysidelogging_p.h"
#include <autodecref.h>
#include <basewrapper.h>
@@ -61,20 +31,28 @@
#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/QSharedPointer>
+#include <QtCore/QMetaMethod>
+#include <QtCore/QMutex>
#include <QtCore/QStack>
#include <QtCore/QThread>
#include <algorithm>
#include <cstring>
#include <cctype>
+#include <memory>
+#include <optional>
#include <typeinfo>
+using namespace Qt::StringLiterals;
+
static QStack<PySide::CleanupFunction> cleanupFunctionList;
static void *qobjectNextAddr;
@@ -83,13 +61,14 @@ extern bool qRegisterResourceData(int, const unsigned char *, const unsigned cha
const unsigned char *);
QT_END_NAMESPACE
+Q_LOGGING_CATEGORY(lcPySide, "qt.pyside.libpyside", QtCriticalMsg)
+
namespace PySide
{
void init(PyObject *module)
{
qobjectNextAddr = nullptr;
- Numpy::init();
ClassInfo::init(module);
Signal::init(module);
Slot::init(module);
@@ -101,56 +80,282 @@ void init(PyObject *module)
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));
- propName[0] = std::toupper(propName[0]);
- propName.prepend("set");
+ 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{};
- Shiboken::AutoDecRef propSetter(PyObject_GetAttrString(qObj, propName.constData()));
- if (!propSetter.isNull()) {
+ 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;
- Shiboken::AutoDecRef args(PyTuple_Pack(1, value));
- Shiboken::AutoDecRef retval(PyObject_CallObject(propSetter, args));
+ AutoDecRef args(PyTuple_Pack(1, value));
+ AutoDecRef retval(PyObject_CallObject(propSetter, args));
if (retval.isNull())
return false;
} else {
PyErr_Clear();
- Shiboken::AutoDecRef attr(PyObject_GenericGetAttr(qObj, name));
+ 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)
+ if (PySide::Property::setValue(reinterpret_cast<PySideProperty *>(
+ attr.object()), qObj, value) < 0)
return false;
}
}
return true;
}
-bool fillQtProperties(PyObject *qObj, const QMetaObject *metaObj, PyObject *kwds)
+// 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)) {
- QByteArray propName(Shiboken::String::toCString(key));
+ const QByteArray propName = Shiboken::String::toCString(key);
+ QByteArray unmangledName = _sigWithOrigName(propName, snake_flag);
bool accept = false;
- if (metaObj->indexOfProperty(propName) != -1) {
- if (!_setProperty(qObj, key, value, &accept))
- return false;
- } else {
- propName.append("()");
- if (metaObj->indexOfSignal(propName) != -1) {
- accept = true;
- propName.prepend('2');
- if (!PySide::Signal::connect(qObj, propName, value))
+ // 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 (!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",
@@ -218,12 +423,12 @@ void destroyQCoreApplication()
Py_DECREF(MakeQAppWrapper(nullptr));
}
-std::size_t getSizeOfQObject(SbkObjectType *type)
+std::size_t getSizeOfQObject(PyTypeObject *type)
{
return retrieveTypeUserData(type)->cppObjSize;
}
-void initDynamicMetaObject(SbkObjectType *type, const QMetaObject *base, std::size_t 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);
@@ -237,17 +442,14 @@ void initDynamicMetaObject(SbkObjectType *type, const QMetaObject *base, std::si
return;
Shiboken::AutoDecRef pyMetaObject(Shiboken::Conversions::pointerToPython(converter, metaObjectPtr));
PyObject_SetAttr(reinterpret_cast<PyObject *>(type),
- PySide::PyName::qtStaticMetaObject(), pyMetaObject);
-}
-
-TypeUserData *retrieveTypeUserData(SbkObjectType *sbkTypeObj)
-{
- return reinterpret_cast<TypeUserData *>(Shiboken::ObjectType::getTypeUserData(sbkTypeObj));
+ Shiboken::PyName::qtStaticMetaObject(), pyMetaObject);
}
TypeUserData *retrieveTypeUserData(PyTypeObject *pyTypeObj)
{
- return retrieveTypeUserData(reinterpret_cast<SbkObjectType *>(pyTypeObj));
+ if (!SbkObjectType_Check(pyTypeObj))
+ return nullptr;
+ return reinterpret_cast<TypeUserData *>(Shiboken::ObjectType::getTypeUserData(pyTypeObj));
}
TypeUserData *retrieveTypeUserData(PyObject *pyObj)
@@ -270,10 +472,9 @@ const QMetaObject *retrieveMetaObject(PyObject *pyObj)
return retrieveMetaObject(pyTypeObj);
}
-void initQObjectSubType(SbkObjectType *type, PyObject *args, PyObject * /* kwds */)
+void initQObjectSubType(PyTypeObject *type, PyObject *args, PyObject * /* kwds */)
{
PyTypeObject *qObjType = Shiboken::Conversions::getPythonTypeObject("QObject*");
- QByteArray className(Shiboken::String::toCString(PyTuple_GET_ITEM(args, 0)));
PyObject *bases = PyTuple_GET_ITEM(args, 1);
int numBases = PyTuple_GET_SIZE(bases);
@@ -288,7 +489,9 @@ void initQObjectSubType(SbkObjectType *type, PyObject *args, PyObject * /* kwds
}
}
if (!userData) {
- qWarning("Sub class of QObject not inheriting QObject!? Crash will happen when using %s.", className.constData());
+ 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.
@@ -318,8 +521,11 @@ void initQApp()
setDestroyQApplication(destroyQCoreApplication);
}
-PyObject *getMetaDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *name)
+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;
@@ -332,27 +538,63 @@ PyObject *getMetaDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *nam
attr = value;
}
- //mutate native signals to signal instance type
- if (attr && PyObject_TypeCheck(attr, PySideSignalTypeF())) {
- PyObject *signal = reinterpret_cast<PyObject *>(Signal::initialize(reinterpret_cast<PySideSignal *>(attr), name, self));
- PyObject_SetAttr(self, name, reinterpret_cast<PyObject *>(signal));
- return signal;
- }
-
- //search on metaobject (avoid internal attributes started with '__')
+ // 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)) {
+ if (std::strncmp("__", cname, 2) != 0) {
const QMetaObject *metaObject = cppSelf->metaObject();
- //signal
QList<QMetaMethod> signalList;
- for(int i=0, i_max = metaObject->methodCount(); i < i_max; i++) {
+ // 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);
- const QByteArray methSig_ = method.methodSignature();
+ // 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 methMacth = !std::strncmp(cname, methSig, cnameLen) && methSig[cnameLen] == '(';
- if (methMacth) {
+ bool methMatch = std::strncmp(cname, methSig, cnameLen) == 0
+ && methSig[cnameLen] == '(';
+ if (methMatch) {
if (method.methodType() == QMetaMethod::Signal) {
signalList.append(method);
} else {
@@ -365,12 +607,14 @@ PyObject *getMetaDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *nam
}
}
}
- if (!signalList.empty()) {
- PyObject *pySignal = reinterpret_cast<PyObject *>(Signal::newObjectFromMethod(self, signalList));
+ 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;
}
@@ -387,6 +631,12 @@ bool inherits(PyTypeObject *objType, const char *class_name)
return inherits(base, class_name);
}
+QMutex &nextQObjectMemoryAddrMutex()
+{
+ static QMutex mutex;
+ return mutex;
+}
+
void *nextQObjectMemoryAddr()
{
return qobjectNextAddr;
@@ -399,17 +649,23 @@ void setNextQObjectMemoryAddr(void *addr)
} // namespace PySide
-// A QSharedPointer is used with a deletion function to invalidate a pointer
+// 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
-typedef char any_t;
-Q_DECLARE_METATYPE(QSharedPointer<any_t>);
+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);
@@ -432,6 +688,13 @@ static const char *typeName(const QObject *cppSelf)
typeName = name;
break;
}
+ // PYSIDE-2404: Did not find the name. Load the lazy classes
+ // which have this name and try again.
+ Shiboken::Module::loadLazyClassesWithName(name);
+ if (Shiboken::Conversions::getConverter(name)) {
+ typeName = name;
+ break;
+ }
}
}
return typeName;
@@ -451,7 +714,7 @@ PyTypeObject *getTypeForQObject(const QObject *cppSelf)
return nullptr;
}
-PyObject *getWrapperForQObject(QObject *cppSelf, SbkObjectType *sbk_type)
+PyObject *getWrapperForQObject(QObject *cppSelf, PyTypeObject *sbk_type)
{
PyObject *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppSelf));
if (pyOut) {
@@ -465,7 +728,7 @@ PyObject *getWrapperForQObject(QObject *cppSelf, SbkObjectType *sbk_type)
QVariant existing = cppSelf->property(invalidatePropertyName);
if (!existing.isValid()) {
if (cppSelf->thread() == QThread::currentThread()) {
- QSharedPointer<any_t> shared_with_del(reinterpret_cast<any_t *>(cppSelf), invalidatePtr);
+ 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));
@@ -475,24 +738,33 @@ PyObject *getWrapperForQObject(QObject *cppSelf, SbkObjectType *sbk_type)
}
}
- pyOut = Shiboken::Object::newObject(sbk_type, cppSelf, false, false, typeName(cppSelf));
+ pyOut = Shiboken::Object::newObjectWithHeuristics(sbk_type, cppSelf, false, typeName(cppSelf));
return pyOut;
}
-#ifdef PYSIDE_QML_SUPPORT
-static QuickRegisterItemFunction quickRegisterItem;
-
-QuickRegisterItemFunction getQuickRegisterItemFunction()
+QString pyUnicodeToQString(PyObject *str)
{
- return quickRegisterItem;
+ 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);
}
-void setQuickRegisterItemFunction(QuickRegisterItemFunction function)
+PyObject *qStringToPyUnicode(QStringView s)
{
- quickRegisterItem = function;
+ const QByteArray ba = s.toUtf8();
+ return PyUnicode_FromStringAndSize(ba.constData(), ba.size());
}
-#endif // PYSIDE_QML_SUPPORT
// Inspired by Shiboken::String::toCString;
QString pyStringToQString(PyObject *str)
@@ -500,11 +772,9 @@ QString pyStringToQString(PyObject *str)
if (str == Py_None)
return QString();
- if (PyUnicode_Check(str)) {
- const char *unicodeBuffer = _PepUnicode_AsString(str);
- if (unicodeBuffer)
- return QString::fromUtf8(unicodeBuffer);
- }
+ if (PyUnicode_Check(str) != 0)
+ return pyUnicodeToQString(str);
+
if (PyBytes_Check(str)) {
const char *asciiBuffer = PyBytes_AS_STRING(str);
if (asciiBuffer)
@@ -532,6 +802,13 @@ QString pyPathToQString(PyObject *path)
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,
@@ -565,9 +842,9 @@ bool registerInternalQtConf()
{
// Guard to ensure single registration.
#ifdef PYSIDE_QT_CONF_PREFIX
- static bool registrationAttempted = false;
+ static bool registrationAttempted = false;
#else
- static bool registrationAttempted = true;
+ static bool registrationAttempted = true;
#endif
static bool isRegistered = false;
if (registrationAttempted)
@@ -578,19 +855,33 @@ bool registerInternalQtConf()
// PyInstaller executable.
// This will disable the internal qt.conf which points to the PySide6 subdirectory (due to the
// subdirectory not existing anymore).
- QString executablePath =
- QString::fromWCharArray(Py_GetProgramFullPath());
+#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(QStringLiteral("qt.conf"));
- bool executableQtConfAvailable = QFileInfo::exists(maybeQtConfPath);
+
+ 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;
- if (disableInternalQtConf || executableQtConfAvailable) {
+ 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;
}
@@ -620,28 +911,20 @@ bool registerInternalQtConf()
#ifdef PYSIDE_QT_CONF_PREFIX
setupPrefix = QStringLiteral(PYSIDE_QT_CONF_PREFIX);
#endif
- const QString prefixPathStr = pysideDir.absoluteFilePath(setupPrefix);
-#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
- const QByteArray prefixPath = prefixPathStr.toLocal8Bit();
-#else
- // PYSIDE-972, QSettings used by QtCore uses Latin1
- const QByteArray prefixPath = prefixPathStr.toLatin1();
-#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
+ 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.
- + QByteArray("\nLibraryExecutables = ") + prefixPath
+ // 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
- ;
- rccData.append('\n');
// The RCC data structure expects a 4-byte size value representing the actual data.
- int size = rccData.size();
+ qsizetype size = rccData.size();
for (int i = 0; i < 4; ++i) {
rccData.prepend((size & 0xff));
@@ -656,7 +939,242 @@ bool registerInternalQtConf()
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';
+ }
+}
-} //namespace PySide
+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