aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/libpyside
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside6/libpyside')
-rw-r--r--sources/pyside6/libpyside/CMakeLists.txt82
-rw-r--r--sources/pyside6/libpyside/class_property.cpp107
-rw-r--r--sources/pyside6/libpyside/class_property.h22
-rw-r--r--sources/pyside6/libpyside/dynamicqmetaobject.cpp65
-rw-r--r--sources/pyside6/libpyside/dynamicqmetaobject.h6
-rw-r--r--sources/pyside6/libpyside/feature_select.cpp82
-rw-r--r--sources/pyside6/libpyside/feature_select.h6
-rw-r--r--sources/pyside6/libpyside/globalreceiverv2.cpp198
-rw-r--r--sources/pyside6/libpyside/globalreceiverv2.h141
-rw-r--r--sources/pyside6/libpyside/pyside.cpp401
-rw-r--r--sources/pyside6/libpyside/pyside_numpy.cpp16
-rw-r--r--sources/pyside6/libpyside/pysideclassdecorator.cpp5
-rw-r--r--sources/pyside6/libpyside/pysideclassdecorator_p.h14
-rw-r--r--sources/pyside6/libpyside/pysideclassinfo.cpp75
-rw-r--r--sources/pyside6/libpyside/pysideclassinfo.h21
-rw-r--r--sources/pyside6/libpyside/pysideclassinfo_p.h8
-rw-r--r--sources/pyside6/libpyside/pysideinit.h3
-rw-r--r--sources/pyside6/libpyside/pysidemetafunction.cpp37
-rw-r--r--sources/pyside6/libpyside/pysidemetafunction.h5
-rw-r--r--sources/pyside6/libpyside/pysidemetafunction_p.h7
-rw-r--r--sources/pyside6/libpyside/pysidemetatype.h2
-rw-r--r--sources/pyside6/libpyside/pysideproperty.cpp113
-rw-r--r--sources/pyside6/libpyside/pysideproperty.h5
-rw-r--r--sources/pyside6/libpyside/pysideproperty_p.h10
-rw-r--r--sources/pyside6/libpyside/pysideqenum.cpp5
-rw-r--r--sources/pyside6/libpyside/pysideqenum.h5
-rw-r--r--sources/pyside6/libpyside/pysideqflags.cpp203
-rw-r--r--sources/pyside6/libpyside/pysideqflags.h43
-rw-r--r--sources/pyside6/libpyside/pysideqhash.h2
-rw-r--r--sources/pyside6/libpyside/pysideqobject.h14
-rw-r--r--sources/pyside6/libpyside/pysideqslotobject_p.cpp36
-rw-r--r--sources/pyside6/libpyside/pysideqslotobject_p.h39
-rw-r--r--sources/pyside6/libpyside/pysidesignal.cpp741
-rw-r--r--sources/pyside6/libpyside/pysidesignal.h26
-rw-r--r--sources/pyside6/libpyside/pysidesignal_p.h16
-rw-r--r--sources/pyside6/libpyside/pysideslot.cpp94
-rw-r--r--sources/pyside6/libpyside/pysideslot_p.h25
-rw-r--r--sources/pyside6/libpyside/pysidestaticstrings.cpp6
-rw-r--r--sources/pyside6/libpyside/pysidestaticstrings.h6
-rw-r--r--sources/pyside6/libpyside/pysideutils.h30
-rw-r--r--sources/pyside6/libpyside/pysideweakref.cpp42
-rw-r--r--sources/pyside6/libpyside/pysideweakref.h8
-rw-r--r--sources/pyside6/libpyside/qobjectconnect.cpp104
-rw-r--r--sources/pyside6/libpyside/qobjectconnect.h5
-rw-r--r--sources/pyside6/libpyside/signalmanager.cpp338
-rw-r--r--sources/pyside6/libpyside/signalmanager.h21
46 files changed, 1925 insertions, 1315 deletions
diff --git a/sources/pyside6/libpyside/CMakeLists.txt b/sources/pyside6/libpyside/CMakeLists.txt
index e5f163996..ebfe123dd 100644
--- a/sources/pyside6/libpyside/CMakeLists.txt
+++ b/sources/pyside6/libpyside/CMakeLists.txt
@@ -1,7 +1,48 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
project(libpyside)
set(libpyside_libraries Qt::Core Qt::CorePrivate)
+set(CMAKE_AUTOMOC ON)
+
+set(libpyside_HEADERS # installed below
+ class_property.h
+ dynamicqmetaobject.h
+ feature_select.h
+ globalreceiverv2.h
+ pysideclassdecorator_p.h
+ pysideclassinfo.h
+ pysideclassinfo_p.h
+ pysidecleanup.h
+ pyside.h
+ pysideinit.h
+ pysidelogging_p.h
+ pysidemacros.h
+ pysidemetafunction.h
+ pysidemetafunction_p.h
+ pysidemetatype.h
+ pyside_numpy.h
+ pyside_p.h
+ pysideproperty.h
+ pysideproperty_p.h
+ pysideqapp.h
+ pysideqenum.h
+ pysideqhash.h
+ pysideqmetatype.h
+ pysideqobject.h
+ pysideqslotobject_p.h
+ pysidesignal.h
+ pysidesignal_p.h
+ pysideslot_p.h
+ pysidestaticstrings.h
+ pysideutils.h
+ pysideweakref.h
+ qobjectconnect.h
+ signalmanager.h
+)
+
set(libpyside_SRC
class_property.cpp
dynamicqmetaobject.cpp
@@ -11,16 +52,17 @@ set(libpyside_SRC
pysideclassdecorator.cpp
pysideclassinfo.cpp
pysideqenum.cpp
+ pysideqslotobject_p.cpp
pysidemetafunction.cpp
pysidesignal.cpp
pysideslot.cpp
pysideproperty.cpp
- pysideqflags.cpp
pysideweakref.cpp
pyside.cpp
pyside_numpy.cpp
pysidestaticstrings.cpp
qobjectconnect.cpp
+ ${libpyside_HEADERS}
)
qt6_add_resources(libpyside_SRC libpyside.qrc)
@@ -58,7 +100,7 @@ target_include_directories(pyside6 PUBLIC
$<INSTALL_INTERFACE:include/PySide6>
)
-target_compile_definitions(pyside6 PRIVATE -DQT_LEAN_HEADERS=1)
+target_compile_definitions(pyside6 PRIVATE -DQT_LEAN_HEADERS=1 -DQT_NO_KEYWORDS=1)
target_link_libraries(pyside6
PRIVATE Shiboken6::libshiboken ${libpyside_libraries})
@@ -82,42 +124,6 @@ endif()
# install stuff
#
-set(libpyside_HEADERS
- class_property.h
- dynamicqmetaobject.h
- feature_select.h
- globalreceiverv2.h
- pysideclassdecorator_p.h
- pysideclassinfo.h
- pysideclassinfo_p.h
- pysidecleanup.h
- pyside.h
- pysideinit.h
- pysidelogging_p.h
- pysidemacros.h
- pysidemetafunction.h
- pysidemetafunction_p.h
- pysidemetatype.h
- pyside_numpy.h
- pyside_p.h
- pysideproperty.h
- pysideproperty_p.h
- pysideqapp.h
- pysideqenum.h
- pysideqflags.h
- pysideqhash.h
- pysideqmetatype.h
- pysideqobject.h
- pysidesignal.h
- pysidesignal_p.h
- pysideslot_p.h
- pysidestaticstrings.h
- pysideutils.h
- pysideweakref.h
- qobjectconnect.h
- signalmanager.h
-)
-
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(LIBRARY_OUTPUT_SUFFIX ${CMAKE_DEBUG_POSTFIX})
else()
diff --git a/sources/pyside6/libpyside/class_property.cpp b/sources/pyside6/libpyside/class_property.cpp
index c255ef1e9..2bed97ef5 100644
--- a/sources/pyside6/libpyside/class_property.cpp
+++ b/sources/pyside6/libpyside/class_property.cpp
@@ -5,6 +5,7 @@
#include "pysidestaticstrings.h"
#include "feature_select.h"
+#include <pep384ext.h>
#include <shiboken.h>
#include <sbkstaticstrings.h>
@@ -23,14 +24,54 @@ extern "C" {
// `class_property.__get__()`: Always pass the class instead of the instance.
static PyObject *PyClassProperty_descr_get(PyObject *self, PyObject * /*ob*/, PyObject *cls)
{
- return PyProperty_Type.tp_descr_get(self, cls, cls);
+ return PepExt_Type_GetDescrGetSlot(&PyProperty_Type)(self, cls, cls);
}
// `class_property.__set__()`: Just like the above `__get__()`.
static int PyClassProperty_descr_set(PyObject *self, PyObject *obj, PyObject *value)
{
PyObject *cls = PyType_Check(obj) ? obj : reinterpret_cast<PyObject *>(Py_TYPE(obj));
- return PyProperty_Type.tp_descr_set(self, cls, value);
+ return PepExt_Type_GetDescrSetSlot(&PyProperty_Type)(self, cls, value);
+}
+
+// PYSIDE-2230: Why is this metaclass necessary?
+//
+// The problem is that the property object already exists as a Python
+// object. We derive a subclass for class properties, without
+// repeating everything but just by adding something to support
+// the class-ness.
+//
+// But this Python property has as metaclass `type` which is incompatible
+// now with SbkObjectType, which generates physically larger types that
+// are incompatible with properties by using PEP 697.
+// Adding a compatible metaclass that is unrelated to `SbkObjectType`
+// is the correct solution. Re-using `SbkObjectType` was actually an abuse,
+// since Python properties are in no way PySide objects.
+
+static PyTypeObject *createClassPropertyTypeType()
+{
+ PyType_Slot PyClassPropertyType_Type_slots[] = {
+ {Py_tp_base, static_cast<void *>(&PyType_Type)},
+ {Py_tp_alloc, reinterpret_cast<void *>(PyType_GenericAlloc)},
+ {Py_tp_free, reinterpret_cast<void *>(PyObject_GC_Del)},
+ {0, nullptr}
+ };
+
+ PyType_Spec PyClassPropertyType_Type_spec = {
+ "1:Shiboken.ClassPropertyType",
+ 0,
+ 0, // sizeof(PyMemberDef), not for PyPy without a __len__ defined
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_TYPE_SUBCLASS,
+ PyClassPropertyType_Type_slots,
+ };
+
+ return SbkType_FromSpec(&PyClassPropertyType_Type_spec);
+}
+
+PyTypeObject *PyClassPropertyType_TypeF()
+{
+ static auto *type = createClassPropertyTypeType();
+ return type;
}
// The property `__doc__` default does not work for class properties
@@ -40,36 +81,38 @@ static int PyClassProperty_tp_init(PyObject *self, PyObject *args, PyObject *kwa
{
auto hold = Py_TYPE(self);
self->ob_type = &PyProperty_Type;
- auto ret = PyProperty_Type.tp_init(self, args, kwargs);
+ auto ret = PepExt_Type_GetInitSlot(&PyProperty_Type)(self, args, kwargs);
self->ob_type = hold;
return ret;
}
-static PyType_Slot PyClassProperty_slots[] = {
- {Py_tp_getset, nullptr}, // will be set below
- {Py_tp_base, reinterpret_cast<void *>(&PyProperty_Type)},
- {Py_tp_descr_get, reinterpret_cast<void *>(PyClassProperty_descr_get)},
- {Py_tp_descr_set, reinterpret_cast<void *>(PyClassProperty_descr_set)},
- {Py_tp_init, reinterpret_cast<void *>(PyClassProperty_tp_init)},
- {0, 0}
-};
-
-static PyType_Spec PyClassProperty_spec = {
- "2:PySide6.QtCore.PyClassProperty",
- sizeof(propertyobject),
- 0,
- Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
- PyClassProperty_slots,
-};
+static PyTypeObject *createPyClassPropertyType()
+{
+ PyType_Slot PyClassProperty_slots[] = {
+ {Py_tp_getset, reinterpret_cast<void *>(PyProperty_Type.tp_getset)}, // will be set below
+ {Py_tp_base, reinterpret_cast<void *>(&PyProperty_Type)},
+ {Py_tp_descr_get, reinterpret_cast<void *>(PyClassProperty_descr_get)},
+ {Py_tp_descr_set, reinterpret_cast<void *>(PyClassProperty_descr_set)},
+ {Py_tp_init, reinterpret_cast<void *>(PyClassProperty_tp_init)},
+ {0, nullptr}
+ };
+
+ PyType_Spec PyClassProperty_spec = {
+ "2:PySide6.QtCore.PyClassProperty",
+ sizeof(propertyobject),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ PyClassProperty_slots,
+ };
+
+ if (_PepRuntimeVersion() >= 0x030A00)
+ PyClassProperty_spec.basicsize = sizeof(propertyobject310);
+ return SbkType_FromSpecWithMeta(&PyClassProperty_spec, PyClassPropertyType_TypeF());
+}
PyTypeObject *PyClassProperty_TypeF()
{
- static PyTypeObject *type = nullptr;
- if (type == nullptr) {
- // Provide the same `tp_getset`, which is not inherited.
- PyClassProperty_slots[0].pfunc = PyProperty_Type.tp_getset;
- type = SbkType_FromSpec(&PyClassProperty_spec);
- }
+ static auto *type = createPyClassPropertyType();
return type;
}
@@ -96,11 +139,9 @@ static int SbkObjectType_meta_setattro(PyObject *obj, PyObject *name, PyObject *
&& !PyObject_IsInstance(value, class_prop);
if (call_descr_set) {
// Call `class_property.__set__()` instead of replacing the `class_property`.
- return Py_TYPE(descr)->tp_descr_set(descr, obj, value);
- } else {
- // Replace existing attribute.
- return PyType_Type.tp_setattro(obj, name, value);
- }
+ return PepExt_Type_GetDescrSetSlot(Py_TYPE(descr))(descr, obj, value);
+ } // Replace existing attribute.
+ return PepExt_Type_GetSetAttroSlot(&PyType_Type)(obj, name, value);
}
} // extern "C"
@@ -108,7 +149,7 @@ static int SbkObjectType_meta_setattro(PyObject *obj, PyObject *name, PyObject *
/*
* These functions are added to the SbkObjectType_TypeF() dynamically.
*/
-namespace PySide { namespace ClassProperty {
+namespace PySide::ClassProperty {
static const char *PyClassProperty_SignatureStrings[] = {
"PySide6.QtCore.PyClassProperty(cls,"
@@ -125,7 +166,6 @@ void init(PyObject *module)
{
PyTypeObject *type = SbkObjectType_TypeF();
type->tp_setattro = SbkObjectType_meta_setattro;
- reinterpret_cast<PyObject *>(type)->ob_type = type;
if (InitSignatureStrings(PyClassProperty_TypeF(), PyClassProperty_SignatureStrings) < 0)
return;
@@ -135,5 +175,4 @@ void init(PyObject *module)
PyModule_AddObject(module, "PyClassProperty", classproptype);
}
-} // namespace ClassProperty
-} // namespace PySide
+} // namespace PySide::ClassProperty
diff --git a/sources/pyside6/libpyside/class_property.h b/sources/pyside6/libpyside/class_property.h
index 10378294e..f2ed29f1f 100644
--- a/sources/pyside6/libpyside/class_property.h
+++ b/sources/pyside6/libpyside/class_property.h
@@ -9,25 +9,35 @@
extern "C" {
-typedef struct {
+struct propertyobject {
PyObject_HEAD
PyObject *prop_get;
PyObject *prop_set;
PyObject *prop_del;
PyObject *prop_doc;
int getter_doc;
-} propertyobject;
+};
+
+struct propertyobject310 {
+ PyObject_HEAD
+ PyObject *prop_get;
+ PyObject *prop_set;
+ PyObject *prop_del;
+ PyObject *prop_doc;
+ // Note: This is a problem with Limited API: We have no direct access.
+ // You need to pick it from runtime info.
+ PyObject *prop_name;
+ int getter_doc;
+};
PYSIDE_API PyTypeObject *PyClassProperty_TypeF();
} // extern "C"
-namespace PySide {
-namespace ClassProperty {
+namespace PySide::ClassProperty {
PYSIDE_API void init(PyObject *module);
-} // namespace ClassProperty
-} // namespace PySide
+} // namespace PySide::ClassProperty
#endif // CLASS_PROPERTY_H
diff --git a/sources/pyside6/libpyside/dynamicqmetaobject.cpp b/sources/pyside6/libpyside/dynamicqmetaobject.cpp
index a3a16b6a0..048001f81 100644
--- a/sources/pyside6/libpyside/dynamicqmetaobject.cpp
+++ b/sources/pyside6/libpyside/dynamicqmetaobject.cpp
@@ -10,6 +10,7 @@
#include "pysideslot_p.h"
#include "pysideqenum.h"
#include "pyside_p.h"
+#include "pysidestaticstrings.h"
#include <shiboken.h>
@@ -23,6 +24,8 @@
#include <cstring>
#include <vector>
+using namespace Qt::StringLiterals;
+
using namespace PySide;
// MetaObjectBuilder: Provides the QMetaObject's returned by
@@ -50,7 +53,8 @@ public:
const QByteArray &signature) const;
int indexOfProperty(const QByteArray &name) const;
int addSlot(const QByteArray &signature);
- int addSlot(const QByteArray &signature, const QByteArray &type);
+ 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;
@@ -181,8 +185,8 @@ int MetaObjectBuilder::indexOfProperty(const QByteArray &name) const
static bool checkMethodSignature(const QByteArray &signature)
{
// Common mistake not to add parentheses to the signature.
- const int openParen = signature.indexOf('(');
- const int closingParen = signature.lastIndexOf(')');
+ const auto openParen = signature.indexOf('(');
+ const auto closingParen = signature.lastIndexOf(')');
const bool ok = openParen != -1 && closingParen != -1 && openParen < closingParen;
if (!ok) {
const QByteArray message =
@@ -208,13 +212,17 @@ int MetaObjectBuilder::addSlot(const char *signature)
}
int MetaObjectBuilderPrivate::addSlot(const QByteArray &signature,
- const QByteArray &type)
+ const QByteArray &type,
+ const QByteArray &tag)
{
if (!checkMethodSignature(signature))
return -1;
m_dirty = true;
QMetaMethodBuilder methodBuilder = ensureBuilder()->addSlot(signature);
- methodBuilder.setReturnType(type);
+ if (!type.isEmpty() && type != "void"_ba)
+ methodBuilder.setReturnType(type);
+ if (!tag.isEmpty())
+ methodBuilder.setTag(tag);
return m_baseObject->methodCount() + methodBuilder.index();
}
@@ -581,7 +589,8 @@ void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
// Leave the properties to be registered after signals because they may depend on
// notify signals.
for (PyTypeObject *baseType : basesToCheck) {
- PyObject *attrs = baseType->tp_dict;
+ AutoDecRef tpDict(PepType_GetDict(baseType));
+ PyObject *attrs = tpDict.object();
PyObject *key = nullptr;
PyObject *value = nullptr;
Py_ssize_t pos = 0;
@@ -600,51 +609,40 @@ void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
// Signal(..., arguments=['...', ...]
// the arguments are now on data-data->signalArguments
auto builder = m_builder->addSignal(sig);
- if (data->signalArguments && !data->signalArguments->isEmpty())
- builder.setParameterNames(*data->signalArguments);
+ if (!data->signalArguments.isEmpty())
+ builder.setParameterNames(data->signalArguments);
}
}
}
}
}
- AutoDecRef slotAttrName(String::fromCString(PYSIDE_SLOT_LIST_ATTR));
+ 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) {
- PyObject *attrs = baseType->tp_dict;
+ 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 int index = m_baseObject->indexOfProperty(String::toCString(key));
+ const QByteArray name = String::toCString(key);
+ const int index = m_baseObject->indexOfProperty(name);
if (index == -1)
- addProperty(String::toCString(key), value);
- } else if (Py_TYPE(value)->tp_call != nullptr) {
+ 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)) {
- PyObject *signatureList = PyObject_GetAttr(value, slotAttrName);
- for (Py_ssize_t i = 0, i_max = PyList_Size(signatureList); i < i_max; ++i) {
- PyObject *pySignature = PyList_GET_ITEM(signatureList, i);
- QByteArray signature(String::toCString(pySignature));
- // Split the slot type and its signature.
- QByteArray type;
- const int spacePos = signature.indexOf(' ');
- if (spacePos != -1) {
- type = signature.left(spacePos);
- signature.remove(0, spacePos + 1);
- }
- const int index = m_baseObject->indexOfSlot(signature);
- if (index == -1) {
- if (type.isEmpty() || type == "void")
- addSlot(signature);
- else
- addSlot(signature, type);
- }
+ 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);
}
}
}
@@ -662,7 +660,7 @@ void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
AutoDecRef items(PyMapping_Items(members));
Py_ssize_t nr_items = PySequence_Length(items);
- QList<QPair<QByteArray, int> > entries;
+ 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));
@@ -670,8 +668,7 @@ void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
AutoDecRef value(PyObject_GetAttr(member, Shiboken::PyName::value()));
auto ckey = String::toCString(key);
auto ivalue = PyLong_AsSsize_t(value);
- auto thing = QPair<QByteArray, int>(ckey, int(ivalue));
- entries.push_back(thing);
+ entries.push_back(std::make_pair(ckey, int(ivalue)));
}
addEnumerator(name, isFlag, true, entries);
}
diff --git a/sources/pyside6/libpyside/dynamicqmetaobject.h b/sources/pyside6/libpyside/dynamicqmetaobject.h
index 311c545f5..dd33f65f7 100644
--- a/sources/pyside6/libpyside/dynamicqmetaobject.h
+++ b/sources/pyside6/libpyside/dynamicqmetaobject.h
@@ -10,6 +10,8 @@
#include <QtCore/QMetaObject>
#include <QtCore/QMetaMethod>
+#include <utility>
+
class MetaObjectBuilderPrivate;
namespace PySide
@@ -17,9 +19,9 @@ namespace PySide
class MetaObjectBuilder
{
- Q_DISABLE_COPY(MetaObjectBuilder)
+ Q_DISABLE_COPY_MOVE(MetaObjectBuilder)
public:
- using EnumValue = QPair<QByteArray, int>;
+ using EnumValue = std::pair<QByteArray, int>;
using EnumValues = QList<EnumValue>;
MetaObjectBuilder(const char *className, const QMetaObject *metaObject);
diff --git a/sources/pyside6/libpyside/feature_select.cpp b/sources/pyside6/libpyside/feature_select.cpp
index c12869777..cfd465267 100644
--- a/sources/pyside6/libpyside/feature_select.cpp
+++ b/sources/pyside6/libpyside/feature_select.cpp
@@ -89,11 +89,11 @@ This is everything that the following code does.
*****************************************************************************/
-namespace PySide { namespace Feature {
+namespace PySide::Feature {
using namespace Shiboken;
-typedef bool(*FeatureProc)(PyTypeObject *type, PyObject *prev_dict, int id);
+using FeatureProc = bool(*)(PyTypeObject *type, PyObject *prev_dict, int id);
static FeatureProc *featurePointer = nullptr;
@@ -107,7 +107,7 @@ createDerivedDictType()
PyObject *ChameleonDict = PepRun_GetResult(R"CPP(if True:
class ChameleonDict(dict):
- __slots__ = ("dict_ring", "select_id")
+ __slots__ = ("dict_ring", "select_id", "orig_dict")
result = ChameleonDict
@@ -129,18 +129,18 @@ static void ensureNewDictType()
static inline PyObject *nextInCircle(PyObject *dict)
{
// returns a borrowed ref
- AutoDecRef next_dict(PyObject_GetAttr(dict, PyName::dict_ring()));
+ AutoDecRef next_dict(PyObject_GetAttr(dict, PySideName::dict_ring()));
return next_dict;
}
static inline void setNextDict(PyObject *dict, PyObject *next_dict)
{
- PyObject_SetAttr(dict, PyName::dict_ring(), next_dict);
+ PyObject_SetAttr(dict, PySideName::dict_ring(), next_dict);
}
static inline void setSelectId(PyObject *dict, int select_id)
{
- PyObject_SetAttr(dict, PyName::select_id(), PyLong_FromLong(select_id));
+ PyObject_SetAttr(dict, PySideName::select_id(), PyLong_FromLong(select_id));
}
static inline int getSelectId(PyObject *dict)
@@ -162,7 +162,7 @@ static bool replaceClassDict(PyTypeObject *type)
* This is mandatory for all type dicts when they are touched.
*/
ensureNewDictType();
- auto *dict = type->tp_dict;
+ AutoDecRef dict(PepType_GetDict(type));
auto *ob_ndt = reinterpret_cast<PyObject *>(new_dict_type);
auto *new_dict = PyObject_CallObject(ob_ndt, nullptr);
if (new_dict == nullptr || PyDict_Update(new_dict, dict) < 0)
@@ -172,9 +172,9 @@ static bool replaceClassDict(PyTypeObject *type)
// insert the dict into itself as ring
setNextDict(new_dict, new_dict);
// We have now an exact copy of the dict with a new type.
- // Replace `__dict__` which usually has refcount 1 (but see cyclic_test.py)
- Py_DECREF(type->tp_dict);
- type->tp_dict = new_dict;
+ PepType_SetDict(type, new_dict);
+ // PYSIDE-2404: Retain the original dict for easy late init.
+ PyObject_SetAttr(new_dict, PySideName::orig_dict(), dict);
return true;
}
@@ -184,7 +184,8 @@ static bool addNewDict(PyTypeObject *type, int select_id)
* Add a new dict to the ring and set it as `type->tp_dict`.
* A 'false' return is fatal.
*/
- auto *dict = type->tp_dict;
+ AutoDecRef dict(PepType_GetDict(type));
+ AutoDecRef orig_dict(PyObject_GetAttr(dict, PySideName::orig_dict()));
auto *ob_ndt = reinterpret_cast<PyObject *>(new_dict_type);
auto *new_dict = PyObject_CallObject(ob_ndt, nullptr);
if (new_dict == nullptr)
@@ -194,7 +195,9 @@ static bool addNewDict(PyTypeObject *type, int select_id)
auto next_dict = nextInCircle(dict);
setNextDict(dict, new_dict);
setNextDict(new_dict, next_dict);
- type->tp_dict = new_dict;
+ PepType_SetDict(type, new_dict);
+ // PYSIDE-2404: Retain the original dict for easy late init.
+ PyObject_SetAttr(new_dict, PySideName::orig_dict(), orig_dict);
return true;
}
@@ -204,18 +207,19 @@ static inline bool moveToFeatureSet(PyTypeObject *type, int select_id)
* Rotate the ring to the given `select_id` and return `true`.
* If not found, stay at the current position and return `false`.
*/
- auto *initial_dict = type->tp_dict;
+ AutoDecRef tpDict(PepType_GetDict(type));
+ auto *initial_dict = tpDict.object();
auto *dict = initial_dict;
do {
int current_id = getSelectId(dict);
// This works because small numbers are singleton objects.
if (current_id == select_id) {
- type->tp_dict = dict;
+ PepType_SetDict(type, dict);
return true;
}
dict = nextInCircle(dict);
} while (dict != initial_dict);
- type->tp_dict = initial_dict;
+ PepType_SetDict(type, initial_dict);
return false;
}
@@ -234,8 +238,7 @@ static bool createNewFeatureSet(PyTypeObject *type, int select_id)
Q_UNUSED(ok);
assert(ok);
- AutoDecRef prev_dict(type->tp_dict);
- Py_INCREF(prev_dict); // keep the first ref unchanged
+ AutoDecRef prev_dict(PepType_GetDict(type));
if (!addNewDict(type, select_id))
return false;
int id = select_id;
@@ -245,13 +248,14 @@ static bool createNewFeatureSet(PyTypeObject *type, int select_id)
for (int idx = id; *proc != nullptr; ++proc, idx >>= 1) {
if (idx & 1) {
// clear the tp_dict that will get new content
- PyDict_Clear(type->tp_dict);
+ AutoDecRef tpDict(PepType_GetDict(type));
+ PyDict_Clear(tpDict);
// let the proc re-fill the tp_dict
if (!(*proc)(type, prev_dict, id))
return false;
// if there is still a step, prepare `prev_dict`
if (idx >> 1) {
- prev_dict.reset(PyDict_Copy(type->tp_dict));
+ prev_dict.reset(PyDict_Copy(tpDict.object()));
if (prev_dict.isNull())
return false;
}
@@ -266,7 +270,9 @@ static inline void SelectFeatureSetSubtype(PyTypeObject *type, int select_id)
* This is the selector for one sublass. We need to call this for
* every subclass until no more subclasses or reaching the wanted id.
*/
- if (Py_TYPE(type->tp_dict) == Py_TYPE(PyType_Type.tp_dict)) {
+ static const auto *pyTypeType_tp_dict = PepType_GetDict(&PyType_Type);
+ AutoDecRef tpDict(PepType_GetDict(type));
+ if (Py_TYPE(tpDict.object()) == Py_TYPE(pyTypeType_tp_dict)) {
// On first touch, we initialize the dynamic naming.
// The dict type will be replaced after the first call.
if (!replaceClassDict(type)) {
@@ -320,7 +326,9 @@ static inline void SelectFeatureSet(PyTypeObject *type)
* Generated functions call this directly.
* Shiboken will assign it via a public hook of `basewrapper.cpp`.
*/
- if (Py_TYPE(type->tp_dict) == Py_TYPE(PyType_Type.tp_dict)) {
+ static const auto *pyTypeType_tp_dict = PepType_GetDict(&PyType_Type);
+ AutoDecRef tpDict(PepType_GetDict(type));
+ if (Py_TYPE(tpDict.object()) == Py_TYPE(pyTypeType_tp_dict)) {
// We initialize the dynamic features by using our own dict type.
if (!replaceClassDict(type)) {
Py_FatalError("failed to replace class dict!");
@@ -388,12 +396,18 @@ static FeatureProc featureProcArray[] = {
static bool patch_property_impl();
static bool is_initialized = false;
+static void featureEnableCallback(bool enable)
+{
+ featurePointer = enable ? featureProcArray : nullptr;
+}
+
void init()
{
// This function can be called multiple times.
if (!is_initialized) {
featurePointer = featureProcArray;
initSelectableFeature(SelectFeatureSet);
+ setSelectableFeatureCallback(featureEnableCallback);
patch_property_impl();
is_initialized = true;
}
@@ -454,7 +468,8 @@ static PyObject *methodWithNewName(PyTypeObject *type,
static bool feature_01_addLowerNames(PyTypeObject *type, PyObject *prev_dict, int /* id */)
{
PyMethodDef *meth = type->tp_methods;
- PyObject *lower_dict = type->tp_dict;
+ AutoDecRef tpDict(PepType_GetDict(type));
+ PyObject *lower_dict = tpDict.object();
// PYSIDE-1702: A user-defined class in Python has no internal method list.
// We are not going to change anything.
@@ -501,12 +516,12 @@ static bool feature_01_addLowerNames(PyTypeObject *type, PyObject *prev_dict, in
// This is the Python 2 version for inspection of m_ml, only.
// The actual Python 3 version is larget.
-typedef struct {
+struct PyCFunctionObject {
PyObject_HEAD
PyMethodDef *m_ml; /* Description of the C function to call */
PyObject *m_self; /* Passed as 'self' arg to the C func, can be NULL */
PyObject *m_module; /* The __module__ attribute, can be anything */
-} PyCFunctionObject;
+};
static PyObject *modifyStaticToClassMethod(PyTypeObject *type, PyObject *sm)
{
@@ -585,7 +600,7 @@ PyObject *adjustPropertyName(PyObject *dict, PyObject *name)
if (PyList_CheckExact(sig)) {
name_clash = true;
} else {
- Shiboken::AutoDecRef params(PyObject_GetAttr(sig, PyName::parameters()));
+ Shiboken::AutoDecRef params(PyObject_GetAttr(sig, PySideName::parameters()));
// Are there parameters except self or cls?
if (PyObject_Size(params.object()) > 1)
name_clash = true;
@@ -611,9 +626,9 @@ static QByteArrayList GetPropertyStringsMro(PyTypeObject *type)
auto res = QByteArrayList();
PyObject *mro = type->tp_mro;
- Py_ssize_t idx, n = PyTuple_GET_SIZE(mro);
+ const Py_ssize_t n = PyTuple_GET_SIZE(mro);
// We leave 'Shiboken.Object' and 'object' alone, therefore "n - 2".
- for (idx = 0; idx < n - 2; idx++) {
+ for (Py_ssize_t idx = 0; idx < n - 2; idx++) {
auto *subType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
auto props = SbkObjectType_GetPropertyStrings(subType);
if (props != nullptr)
@@ -630,7 +645,8 @@ static bool feature_02_true_property(PyTypeObject *type, PyObject *prev_dict, in
*/
PyMethodDef *meth = type->tp_methods;
- PyObject *prop_dict = type->tp_dict;
+ AutoDecRef tpDict(PepType_GetDict(type));
+ PyObject *prop_dict = tpDict.object();
// The empty `tp_dict` gets populated by the previous dict.
if (PyDict_Update(prop_dict, prev_dict) < 0)
@@ -744,11 +760,11 @@ static bool patch_property_impl()
// Turn `__doc__` into a computed attribute without changing writability.
auto gsp = property_getset;
auto *type = &PyProperty_Type;
- auto *dict = type->tp_dict;
+ AutoDecRef dict(PepType_GetDict(type));
AutoDecRef descr(PyDescr_NewGetSet(type, gsp));
if (descr.isNull())
return false;
- if (PyDict_SetItemString(dict, gsp->name, descr) < 0)
+ if (PyDict_SetItemString(dict.object(), gsp->name, descr) < 0)
return false;
return true;
}
@@ -763,7 +779,8 @@ static bool patch_property_impl()
#define SIMILAR_FEATURE(xx) \
static bool feature_##xx##_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int /* id */) \
{ \
- PyObject *dict = type->tp_dict; \
+ AutoDecRef tpDict(PepType_GetDict(type)); \
+ PyObject *dict = tpDict.object(); \
if (PyDict_Update(dict, prev_dict) < 0) \
return false; \
if (PyDict_SetItemString(dict, "fake_feature_" #xx, Py_None) < 0) \
@@ -778,5 +795,4 @@ SIMILAR_FEATURE(20)
SIMILAR_FEATURE(40)
SIMILAR_FEATURE(80)
-} // namespace PySide
-} // namespace Feature
+} // namespace PySide::Feature
diff --git a/sources/pyside6/libpyside/feature_select.h b/sources/pyside6/libpyside/feature_select.h
index af8f2c324..bf5a1b56b 100644
--- a/sources/pyside6/libpyside/feature_select.h
+++ b/sources/pyside6/libpyside/feature_select.h
@@ -7,15 +7,13 @@
#include "pysidemacros.h"
#include <sbkpython.h>
-namespace PySide {
-namespace Feature {
+namespace PySide::Feature {
PYSIDE_API void init();
PYSIDE_API void Select(PyObject *obj);
PYSIDE_API void Select(PyTypeObject *type);
PYSIDE_API void Enable(bool);
-} // namespace Feature
-} // namespace PySide
+} // namespace PySide::Feature
#endif // FEATURE_SELECT_H
diff --git a/sources/pyside6/libpyside/globalreceiverv2.cpp b/sources/pyside6/libpyside/globalreceiverv2.cpp
index c843a40ac..2c75e39e1 100644
--- a/sources/pyside6/libpyside/globalreceiverv2.cpp
+++ b/sources/pyside6/libpyside/globalreceiverv2.cpp
@@ -9,35 +9,23 @@
#include <autodecref.h>
#include <gilstate.h>
+#include <pep384ext.h>
-#include <QtCore/qhashfunctions.h>
#include <QtCore/QMetaMethod>
#include <QtCore/QSet>
+#include <QtCore/QDebug>
#include <cstring>
#define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)"
-namespace
-{
- static int DESTROY_SIGNAL_ID = 0;
- static int DESTROY_SLOT_ID = 0;
-}
namespace PySide
{
-size_t qHash(const GlobalReceiverKey &k, size_t seed)
-{
- QtPrivate::QHashCombine hash;
- seed = hash(seed, k.object);
- seed = hash(seed, k.method);
- return seed;
-}
-
class DynamicSlotDataV2
{
- Q_DISABLE_COPY(DynamicSlotDataV2)
+ Q_DISABLE_COPY_MOVE(DynamicSlotDataV2)
public:
DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *parent);
~DynamicSlotDataV2();
@@ -51,6 +39,8 @@ class DynamicSlotDataV2
static void onCallbackDestroyed(void *data);
static GlobalReceiverKey key(PyObject *callback);
+ void formatDebug(QDebug &debug) const;
+
private:
bool m_isMethod;
PyObject *m_callback;
@@ -61,6 +51,32 @@ class DynamicSlotDataV2
GlobalReceiverV2 *m_parent;
};
+void DynamicSlotDataV2::formatDebug(QDebug &debug) const
+{
+ debug << "method=" << m_isMethod << ", m_callback=" << m_callback;
+ if (m_callback != nullptr)
+ debug << '/' << Py_TYPE(m_callback)->tp_name;
+ debug << ", self=" << m_pythonSelf;
+ if (m_pythonSelf != nullptr)
+ debug << '/' << Py_TYPE(m_pythonSelf)->tp_name;
+ debug << ", m_pyClass=" << m_pyClass;
+ if (m_pyClass != nullptr)
+ debug << '/' << Py_TYPE(m_pyClass)->tp_name;
+ debug << ", signatures=" << m_signatures.keys();
+}
+
+QDebug operator<<(QDebug debug, const DynamicSlotDataV2 *d)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "DynamicSlotDataV2(";
+ if (d)
+ d->formatDebug(debug);
+ else
+ debug << '0';
+ debug << ')';
+ return debug;
}
using namespace PySide;
@@ -83,10 +99,10 @@ DynamicSlotDataV2::DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *paren
// PYSIDE-1523: PyMethod_Check is not accepting compiled form, we just go by attributes.
m_isMethod = true;
- m_callback = PyObject_GetAttr(callback, PySide::PyName::im_func());
+ m_callback = PyObject_GetAttr(callback, PySide::PySideName::im_func());
Py_DECREF(m_callback);
- m_pythonSelf = PyObject_GetAttr(callback, PySide::PyName::im_self());
+ m_pythonSelf = PyObject_GetAttr(callback, PySide::PySideName::im_self());
Py_DECREF(m_pythonSelf);
//monitor class from method lifetime
@@ -107,8 +123,8 @@ GlobalReceiverKey DynamicSlotDataV2::key(PyObject *callback)
return {PyMethod_GET_SELF(callback), PyMethod_GET_FUNCTION(callback)};
} else if (PySide::isCompiledMethod(callback)) {
// PYSIDE-1589: Fix for slots in compiled functions
- Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PyName::im_self()));
- Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PyName::im_func()));
+ Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PySideName::im_self()));
+ Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PySideName::im_func()));
return {self, func};
}
return {nullptr, callback};
@@ -120,7 +136,7 @@ PyObject *DynamicSlotDataV2::callback()
//create a callback based on method data
if (m_isMethod)
- callback = Py_TYPE(m_callback)->tp_descr_get(m_callback, m_pythonSelf, nullptr);
+ callback = PepExt_Type_CallDescrGet(m_callback, m_pythonSelf, nullptr);
else
Py_INCREF(callback);
@@ -146,7 +162,7 @@ void DynamicSlotDataV2::onCallbackDestroyed(void *data)
auto self = reinterpret_cast<DynamicSlotDataV2 *>(data);
self->m_weakRef = nullptr;
Py_BEGIN_ALLOW_THREADS
- delete self->m_parent;
+ SignalManager::instance().deleteGlobalReceiver(self->m_parent);
Py_END_ALLOW_THREADS
}
@@ -160,31 +176,20 @@ DynamicSlotDataV2::~DynamicSlotDataV2()
Py_DECREF(m_callback);
}
-GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, GlobalReceiverV2MapPtr map) :
+const char *GlobalReceiverV2::senderDynamicProperty = "_q_pyside_sender";
+
+GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, QObject *receiver) :
QObject(nullptr),
m_metaObject("__GlobalReceiver__", &QObject::staticMetaObject),
- m_sharedMap(std::move(map))
+ m_receiver(receiver)
{
m_data = new DynamicSlotDataV2(callback, this);
- m_metaObject.addSlot(RECEIVER_DESTROYED_SLOT_NAME);
- m_metaObject.update();
- m_refs.append(nullptr);
-
-
- if (DESTROY_SIGNAL_ID == 0)
- DESTROY_SIGNAL_ID = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
-
- if (DESTROY_SLOT_ID == 0)
- DESTROY_SLOT_ID = m_metaObject.indexOfMethod(QMetaMethod::Slot, RECEIVER_DESTROYED_SLOT_NAME);
-
-
}
GlobalReceiverV2::~GlobalReceiverV2()
{
m_refs.clear();
// Remove itself from map.
- m_sharedMap->remove(m_data->key());
// Suppress handling of destroyed() for objects whose last reference is contained inside
// the callback object that will now be deleted. The reference could be a default argument,
// a callback local variable, etc.
@@ -193,7 +198,7 @@ GlobalReceiverV2::~GlobalReceiverV2()
// leading to the object being deleted, which emits destroyed(), which would try to invoke
// the already deleted callback, and also try to delete the object again.
DynamicSlotDataV2 *data = m_data;
- m_data = Q_NULLPTR;
+ m_data = nullptr;
delete data;
}
@@ -204,69 +209,34 @@ int GlobalReceiverV2::addSlot(const char *signature)
void GlobalReceiverV2::incRef(const QObject *link)
{
- if (link) {
- if (!m_refs.contains(link)) {
- bool connected{};
- Py_BEGIN_ALLOW_THREADS
- connected = QMetaObject::connect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID);
- Py_END_ALLOW_THREADS
- if (connected)
- m_refs.append(link);
- else
- Q_ASSERT(false);
- } else {
- m_refs.append(link);
- }
- } else {
- m_refs.append(nullptr);
- }
+ Q_ASSERT(link);
+ m_refs.append(link);
}
void GlobalReceiverV2::decRef(const QObject *link)
{
- if (m_refs.isEmpty())
- return;
-
-
+ Q_ASSERT(link);
m_refs.removeOne(link);
- if (link) {
- if (!m_refs.contains(link)) {
- bool result{};
- Py_BEGIN_ALLOW_THREADS
- result = QMetaObject::disconnect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID);
- Py_END_ALLOW_THREADS
- Q_ASSERT(result);
- if (!result)
- return;
- }
- }
-
- if (m_refs.isEmpty())
- Py_BEGIN_ALLOW_THREADS
- delete this;
- Py_END_ALLOW_THREADS
+}
+void GlobalReceiverV2::notify()
+{
+ purgeDeletedSenders();
}
-int GlobalReceiverV2::refCount(const QObject *link) const
+static bool isNull(const QPointer<const QObject> &p)
{
- if (link)
- return m_refs.count(link);
+ return p.isNull();
+}
- return m_refs.size();
+void GlobalReceiverV2::purgeDeletedSenders()
+{
+ m_refs.erase(std::remove_if(m_refs.begin(), m_refs.end(), isNull), m_refs.end());
}
-void GlobalReceiverV2::notify()
+bool GlobalReceiverV2::isEmpty() const
{
- const QSet<const QObject *> objSet(m_refs.cbegin(), m_refs.cend());
- Py_BEGIN_ALLOW_THREADS
- for (const QObject *o : objSet) {
- if (o) {
- QMetaObject::disconnect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID);
- QMetaObject::connect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID);
- }
- }
- Py_END_ALLOW_THREADS
+ return std::all_of(m_refs.cbegin(), m_refs.cend(), isNull);
}
GlobalReceiverKey GlobalReceiverV2::key() const
@@ -294,26 +264,21 @@ int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args)
Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
if (!m_data) {
- if (id != DESTROY_SLOT_ID) {
- const QByteArray message = "PySide6 Warning: Skipping callback call "
- + slot.methodSignature() + " because the callback object is being destructed.";
- PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0);
- }
+ const QByteArray message = "PySide6 Warning: Skipping callback call "
+ + slot.methodSignature() + " because the callback object is being destructed.";
+ PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0);
return -1;
}
- if (id == DESTROY_SLOT_ID) {
- if (m_refs.isEmpty())
- return -1;
- auto obj = *reinterpret_cast<QObject **>(args[1]);
- incRef(); //keep the object live (safe ref)
- m_refs.removeAll(obj); // remove all refs to this object
- decRef(); //remove the safe ref
- } else {
- const bool isShortCuit = std::strchr(slot.methodSignature(), '(') == nullptr;
- Shiboken::AutoDecRef callback(m_data->callback());
- SignalManager::callPythonMetaMethod(slot, args, callback, isShortCuit);
- }
+ const bool setSenderDynamicProperty = !m_receiver.isNull();
+ if (setSenderDynamicProperty)
+ m_receiver->setProperty(senderDynamicProperty, QVariant::fromValue(sender()));
+
+ Shiboken::AutoDecRef callback(m_data->callback());
+ SignalManager::callPythonMetaMethod(slot, args, callback);
+
+ if (setSenderDynamicProperty)
+ m_receiver->setProperty(senderDynamicProperty, QVariant{});
// SignalManager::callPythonMetaMethod might have failed, in that case we have to print the
// error so it considered "handled".
@@ -330,3 +295,28 @@ int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args)
return -1;
}
+
+void GlobalReceiverV2::formatDebug(QDebug &debug) const
+{
+ debug << "receiver=" << m_receiver << ", slot=" << m_data;
+ if (isEmpty())
+ debug << ", empty";
+ else
+ debug << ", refs=" << m_refs;
+};
+
+QDebug operator<<(QDebug debug, const GlobalReceiverV2 *g)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "GlobalReceiverV2(";
+ if (g)
+ g->formatDebug(debug);
+ else
+ debug << '0';
+ debug << ')';
+ return debug;
+}
+
+} // namespace PySide
diff --git a/sources/pyside6/libpyside/globalreceiverv2.h b/sources/pyside6/libpyside/globalreceiverv2.h
index c4b621655..0e3bc562a 100644
--- a/sources/pyside6/libpyside/globalreceiverv2.h
+++ b/sources/pyside6/libpyside/globalreceiverv2.h
@@ -8,10 +8,16 @@
#include "dynamicqmetaobject.h"
+#include <QtCore/QtCompare>
#include <QtCore/QByteArray>
+#include <QtCore/QHashFunctions>
#include <QtCore/QObject>
+#include <QtCore/QPointer>
#include <QtCore/QMap>
-#include <QtCore/QSharedPointer>
+
+#include <memory>
+
+QT_FORWARD_DECLARE_CLASS(QDebug);
namespace PySide
{
@@ -23,110 +29,87 @@ struct GlobalReceiverKey
{
const PyObject *object;
const PyObject *method;
-};
-
-inline bool operator==(const GlobalReceiverKey &k1, const GlobalReceiverKey &k2)
-{
- return k1.object == k2.object && k1.method == k2.method;
-}
-
-inline bool operator!=(const GlobalReceiverKey &k1, const GlobalReceiverKey &k2)
-{
- return k1.object != k2.object || k1.method != k2.method;
-}
-
-size_t qHash(const GlobalReceiverKey &k, size_t seed = 0);
-
-using GlobalReceiverV2Map = QHash<GlobalReceiverKey, GlobalReceiverV2 *>;
-using GlobalReceiverV2MapPtr = QSharedPointer<GlobalReceiverV2Map>;
-/**
- * A class used to make the link between the C++ Signal/Slot and Python callback
- * This class is used internally by SignalManager
- **/
+ friend constexpr size_t qHash(GlobalReceiverKey k, size_t seed = 0) noexcept
+ {
+ return qHashMulti(seed, k.object, k.method);
+ }
+ friend constexpr bool comparesEqual(const GlobalReceiverKey &lhs,
+ const GlobalReceiverKey &rhs) noexcept
+ {
+ return lhs.object == rhs.object && lhs.method == rhs.method;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(GlobalReceiverKey)
+};
+/// A class used to link C++ Signals to non C++ slots (Python callbacks) by
+/// providing fake slots for QObject::connect().
+/// It keeps a Python callback and the list of QObject senders. It is stored
+/// in SignalManager by a hash of the Python callback.
class GlobalReceiverV2 : public QObject
{
public:
- /**
- * Create a GlobalReceiver object that will call 'callback' argumentent
- *
- * @param callback A Python callable object (can be a method or not)
- * @param ma A SharedPointer used on Signal manager that contains all instaces of GlobalReceiver
- **/
- GlobalReceiverV2(PyObject *callback, GlobalReceiverV2MapPtr map);
-
- /**
- * Destructor
- **/
+ Q_DISABLE_COPY_MOVE(GlobalReceiverV2)
+
+ /// Create a GlobalReceiver object that will call 'callback'
+ /// @param callback A Python callable object (can be a method or not)
+ explicit GlobalReceiverV2(PyObject *callback, QObject *receiver = nullptr);
+
~GlobalReceiverV2() override;
- /**
- * Reimplemented function from QObject
- **/
+ /// Reimplemented function from QObject
int qt_metacall(QMetaObject::Call call, int id, void **args) override;
const QMetaObject *metaObject() const override;
- /**
- * Add a extra slot to this object
- *
- * @param signature The signature of the slot to be added
- * @return The index of this slot on metaobject
- **/
+ /// Add a extra slot to this object
+ /// @param signature The signature of the slot to be added
+ /// @return The index of this slot on metaobject
int addSlot(const char *signature);
- /**
- * Notify to GlobalReceiver about when a new connection was made
- **/
+ /// Notify to GlobalReceiver about when a new connection was made
void notify();
- /**
- * Used to increment the reference of the GlobalReceiver object
- *
- * @param link This is a optional paramenter used to link the ref to some QObject life
- **/
- void incRef(const QObject *link = nullptr);
-
- /**
- * Used to decrement the reference of the GlobalReceiver object
- *
- * @param link This is a optional paramenter used to dismiss the link ref to some QObject
- **/
- void decRef(const QObject *link = nullptr);
-
- /*
- * Return the count of refs which the GlobalReceiver has
- *
- * @param link If any QObject was passed, the function return the number of references relative to this 'link' object
- * @return The number of references
- **/
- int refCount(const QObject *link) const;
-
- /**
- * Use to retrieve the unique hash of this GlobalReceiver object
- *
- * @return a string with a unique id based on GlobalReceiver contents
- **/
+ /// Used to increment the reference of the GlobalReceiver object
+ /// @param link This is a parameter used to link the ref to
+ /// some QObject life.
+ void incRef(const QObject *link);
+
+ /// Used to decrement the reference of the GlobalReceiver object.
+ /// @param link This is a parameter used to dismiss the link
+ /// ref to some QObject.
+ void decRef(const QObject *link);
+
+ /// Returns whether any senders are registered.
+ bool isEmpty() const;
+
+ /// Use to retrieve the unique hash of this GlobalReceiver object
+ /// @return hash key
GlobalReceiverKey key() const;
- /**
- * Use to retrieve the unique hash of the PyObject based on GlobalReceiver rules
- *
- * @param callback The Python callable object used to calculate the id
- * @return a string with a unique id based on GlobalReceiver contents
- **/
+ /// Use to retrieve the unique hash of the PyObject based on GlobalReceiver rules
+ /// @param callback The Python callable object used to calculate the id
+ /// @return hash key
static GlobalReceiverKey key(PyObject *callback);
const MetaObjectBuilder &metaObjectBuilder() const { return m_metaObject; }
MetaObjectBuilder &metaObjectBuilder() { return m_metaObject; }
+ static const char *senderDynamicProperty;
+
+ void formatDebug(QDebug &debug) const;
+
private:
+ void purgeDeletedSenders();
+
MetaObjectBuilder m_metaObject;
DynamicSlotDataV2 *m_data;
- QList<const QObject *> m_refs;
- GlobalReceiverV2MapPtr m_sharedMap;
+ using QObjectPointer = QPointer<const QObject>;
+ QList<QObjectPointer> m_refs;
+ QPointer<QObject> m_receiver;
};
+QDebug operator<<(QDebug debug, const GlobalReceiverV2 *g);
+
}
#endif
diff --git a/sources/pyside6/libpyside/pyside.cpp b/sources/pyside6/libpyside/pyside.cpp
index a832db28a..9e12e3cd7 100644
--- a/sources/pyside6/libpyside/pyside.cpp
+++ b/sources/pyside6/libpyside/pyside.cpp
@@ -32,21 +32,28 @@
#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/QSharedPointer>
#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;
@@ -57,6 +64,20 @@ 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
{
@@ -198,6 +219,8 @@ static QByteArrayList _SbkType_LookupProperty(PyTypeObject *type,
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;
@@ -273,7 +296,7 @@ static bool _setProperty(PyObject *qObj, PyObject *name, PyObject *value, bool *
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, PyName::fset()));
+ AutoDecRef descr(PyObject_GetAttr(look, PySideName::fset()));
propSetter.reset(PyObject_CallMethodObjArgs(descr, magicGet, qObj, nullptr));
} else {
// look is already the descriptor
@@ -297,7 +320,23 @@ static bool _setProperty(PyObject *qObj, PyObject *name, PyObject *value, bool *
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;
@@ -306,7 +345,7 @@ bool fillQtProperties(PyObject *qObj, const QMetaObject *metaObj, PyObject *kwds
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;
// PYSIDE-1705: Make sure that un-mangled names are not recognized in snake_case mode.
@@ -315,11 +354,11 @@ bool fillQtProperties(PyObject *qObj, const QMetaObject *metaObj, PyObject *kwds
if (!_setProperty(qObj, key, value, &accept))
return false;
} else {
- propName.append("()");
- if (metaObj->indexOfSignal(propName) != -1) {
+ const auto methodO = findSignal(metaObj, propName);
+ if (methodO.has_value()) {
+ const auto signature = "2"_ba + methodO->methodSignature();
accept = true;
- propName.prepend('2');
- if (!PySide::Signal::connect(qObj, propName, value))
+ if (!PySide::Signal::connect(qObj, signature, value))
return false;
}
}
@@ -329,6 +368,10 @@ bool fillQtProperties(PyObject *qObj, const QMetaObject *metaObj, PyObject *kwds
return false;
}
}
+ if (allowErrors) {
+ PyErr_Clear();
+ continue;
+ }
if (!accept) {
PyErr_Format(PyExc_AttributeError, "'%s' is not a Qt property or a signal",
propName.constData());
@@ -419,6 +462,8 @@ void initDynamicMetaObject(PyTypeObject *type, const QMetaObject *base, std::siz
TypeUserData *retrieveTypeUserData(PyTypeObject *pyTypeObj)
{
+ if (!SbkObjectType_Check(pyTypeObj))
+ return nullptr;
return reinterpret_cast<TypeUserData *>(Shiboken::ObjectType::getTypeUserData(pyTypeObj));
}
@@ -445,7 +490,6 @@ const QMetaObject *retrieveMetaObject(PyObject *pyObj)
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);
@@ -460,7 +504,9 @@ void initQObjectSubType(PyTypeObject *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.
@@ -490,8 +536,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;
@@ -504,26 +553,51 @@ PyObject *getMetaDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *nam
attr = value;
}
- // Mutate native signals to signal instance type
- if (attr && PyObject_TypeCheck(attr, PySideSignal_TypeF())) {
- auto *inst = Signal::initialize(reinterpret_cast<PySideSignal *>(attr), name, self);
- PyObject *signalInst = reinterpret_cast<PyObject *>(inst);
- PyObject_SetAttr(self, name, signalInst);
- return signalInst;
- }
-
// 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.
- const char *cname = Shiboken::String::toCString(name);
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();
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
@@ -590,17 +664,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);
@@ -610,6 +690,32 @@ static void invalidatePtr(any_t *object)
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.
@@ -617,7 +723,8 @@ static const char *typeName(const QObject *cppSelf)
{
const char *typeName = typeid(*cppSelf).name();
if (!Shiboken::Conversions::getConverter(typeName)) {
- for (auto metaObject = cppSelf->metaObject(); metaObject; metaObject = metaObject->superClass()) {
+ auto *metaObject = metaObjectCandidate(cppSelf);
+ for (; metaObject != nullptr; metaObject = metaObject->superClass()) {
const char *name = metaObject->className();
if (Shiboken::Conversions::getConverter(name)) {
typeName = name;
@@ -656,7 +763,7 @@ PyObject *getWrapperForQObject(QObject *cppSelf, PyTypeObject *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));
@@ -666,7 +773,7 @@ PyObject *getWrapperForQObject(QObject *cppSelf, PyTypeObject *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;
}
@@ -732,9 +839,9 @@ QString pyPathToQString(PyObject *path)
bool isCompiledMethod(PyObject *callback)
{
- return PyObject_HasAttr(callback, PySide::PyName::im_func())
- && PyObject_HasAttr(callback, PySide::PyName::im_self())
- && PyObject_HasAttr(callback, PySide::PyMagicName::code());
+ 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[] = {
@@ -770,9 +877,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)
@@ -784,23 +891,32 @@ bool registerInternalQtConf()
// 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());
+ QString executablePath = QString::fromWCharArray(Py_GetProgramFullPath());
#else
// PYSIDE-535: FIXME: Add this function when available.
- QString executablePath = QLatin1String("missing Py_GetProgramFullPath");
+ 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;
}
@@ -830,25 +946,17 @@ 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.
qsizetype size = rccData.size();
@@ -918,5 +1026,190 @@ QMetaType qMetaTypeFromPyType(PyTypeObject *pyType)
return QMetaType::fromName(pyType->tp_name);
}
-} //namespace PySide
+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
diff --git a/sources/pyside6/libpyside/pyside_numpy.cpp b/sources/pyside6/libpyside/pyside_numpy.cpp
index a6634fc56..5f43cc5ce 100644
--- a/sources/pyside6/libpyside/pyside_numpy.cpp
+++ b/sources/pyside6/libpyside/pyside_numpy.cpp
@@ -45,10 +45,18 @@ QList<QPointF> xyDataToQPointFList(PyObject *pyXIn, PyObject *pyYIn)
if (size == 0)
return {};
switch (xv.type) {
+ case Shiboken::Numpy::View::Int16:
+ return xyDataToQPointHelper<int16_t, QPointF>(xv.data, yv.data, size);
+ case Shiboken::Numpy::View::Unsigned16:
+ return xyDataToQPointHelper<uint16_t, QPointF>(xv.data, yv.data, size);
case Shiboken::Numpy::View::Int:
return xyDataToQPointHelper<int, QPointF>(xv.data, yv.data, size);
case Shiboken::Numpy::View::Unsigned:
return xyDataToQPointHelper<unsigned, QPointF>(xv.data, yv.data, size);
+ case Shiboken::Numpy::View::Int64:
+ return xyDataToQPointHelper<int64_t, QPointF>(xv.data, yv.data, size);
+ case Shiboken::Numpy::View::Unsigned64:
+ return xyDataToQPointHelper<uint64_t, QPointF>(xv.data, yv.data, size);
case Shiboken::Numpy::View::Float:
return xyDataToQPointHelper<float, QPointF>(xv.data, yv.data, size);
case Shiboken::Numpy::View::Double:
@@ -67,10 +75,18 @@ QList<QPoint> xyDataToQPointList(PyObject *pyXIn, PyObject *pyYIn)
if (size == 0)
return {};
switch (xv.type) {
+ case Shiboken::Numpy::View::Int16:
+ return xyDataToQPointHelper<int16_t, QPoint>(xv.data, yv.data, size);
+ case Shiboken::Numpy::View::Unsigned16:
+ return xyDataToQPointHelper<uint16_t, QPoint>(xv.data, yv.data, size);
case Shiboken::Numpy::View::Int:
return xyDataToQPointHelper<int, QPoint>(xv.data, yv.data, size);
case Shiboken::Numpy::View::Unsigned:
return xyDataToQPointHelper<unsigned, QPoint>(xv.data, yv.data, size);
+ case Shiboken::Numpy::View::Int64:
+ return xyDataToQPointHelper<int64_t, QPoint>(xv.data, yv.data, size);
+ case Shiboken::Numpy::View::Unsigned64:
+ return xyDataToQPointHelper<uint64_t, QPoint>(xv.data, yv.data, size);
case Shiboken::Numpy::View::Float:
return xyFloatDataToQPointHelper<float>(xv.data, yv.data, size);
case Shiboken::Numpy::View::Double:
diff --git a/sources/pyside6/libpyside/pysideclassdecorator.cpp b/sources/pyside6/libpyside/pysideclassdecorator.cpp
index 1085153d9..ec69c5fe7 100644
--- a/sources/pyside6/libpyside/pysideclassdecorator.cpp
+++ b/sources/pyside6/libpyside/pysideclassdecorator.cpp
@@ -7,10 +7,11 @@
#include "pysideqobject.h"
#include <basewrapper.h>
-#include <sbkcppstring.h>
+#include <sbkstring.h>
namespace PySide::ClassDecorator {
+DecoratorPrivate::DecoratorPrivate() noexcept = default;
DecoratorPrivate::~DecoratorPrivate() = default;
DecoratorPrivate *DecoratorPrivate::getPrivate(PyObject *o)
@@ -62,7 +63,7 @@ int StringDecoratorPrivate::convertToString(PyObject *self, PyObject *args)
if (PyUnicode_Check(arg)) {
auto *pData = DecoratorPrivate::get<StringDecoratorPrivate>(self);
result = 0;
- Shiboken::String::toCppString(arg, &(pData->m_string));
+ pData->m_string.assign(Shiboken::String::toCString(arg));
}
}
return result;
diff --git a/sources/pyside6/libpyside/pysideclassdecorator_p.h b/sources/pyside6/libpyside/pysideclassdecorator_p.h
index 9a1dfc8a2..6068f6a2e 100644
--- a/sources/pyside6/libpyside/pysideclassdecorator_p.h
+++ b/sources/pyside6/libpyside/pysideclassdecorator_p.h
@@ -7,6 +7,9 @@
#include <pysidemacros.h>
#include <sbkpython.h>
+#include <pep384ext.h>
+
+#include <QtCore/QByteArray>
#include <array>
#include <string>
@@ -18,6 +21,8 @@ namespace PySide::ClassDecorator {
class PYSIDE_API DecoratorPrivate
{
public:
+ Q_DISABLE_COPY_MOVE(DecoratorPrivate)
+
virtual ~DecoratorPrivate();
/// Virtual function which is passed the decorated class type
@@ -41,6 +46,7 @@ protected:
/// Check mode for the arguments of the call operator
enum class CheckMode { None, WrappedType, QObjectType };
+ DecoratorPrivate() noexcept;
static DecoratorPrivate *getPrivate(PyObject *o);
/// Helper for checking the arguments of the call operator
@@ -59,7 +65,7 @@ public:
/// Init function that retrieves the string parameter using convertToString()
int tp_init(PyObject *self, PyObject *args, PyObject *kwds) override;
- const std::string &string() const { return m_string; }
+ QByteArray string() const { return m_string; }
protected:
/// Helper function that retrieves the string parameter
@@ -69,7 +75,7 @@ protected:
int convertToString(PyObject *self, PyObject *args);
private:
- std::string m_string;
+ QByteArray m_string;
};
/// Base class for private objects of class decorator with a type parameter
@@ -114,7 +120,7 @@ struct Methods
{
static PyObject *tp_new(PyTypeObject *subtype)
{
- auto *result = reinterpret_cast<PySideClassDecorator *>(subtype->tp_alloc(subtype, 0));
+ auto *result = PepExt_TypeCallAlloc<PySideClassDecorator>(subtype, 0);
result->d = new DecoratorPrivate;
return reinterpret_cast<PyObject *>(result);
}
@@ -124,7 +130,7 @@ struct Methods
auto pySelf = reinterpret_cast<PyObject *>(self);
auto decorator = reinterpret_cast<PySideClassDecorator *>(self);
delete decorator->d;
- Py_TYPE(pySelf)->tp_base->tp_free(self);
+ PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, self);
}
static PyObject *tp_call(PyObject *self, PyObject *args, PyObject *kwds)
diff --git a/sources/pyside6/libpyside/pysideclassinfo.cpp b/sources/pyside6/libpyside/pysideclassinfo.cpp
index 60413be20..698cb1c76 100644
--- a/sources/pyside6/libpyside/pysideclassinfo.cpp
+++ b/sources/pyside6/libpyside/pysideclassinfo.cpp
@@ -14,7 +14,7 @@
extern "C"
{
-static PyTypeObject *createClassInfoType(void)
+static PyTypeObject *createClassInfoType()
{
auto typeSlots =
PySide::ClassDecorator::Methods<PySide::ClassInfo::ClassInfoPrivate>::typeSlots();
@@ -36,7 +36,7 @@ PyTypeObject *PySideClassInfo_TypeF(void)
} // extern "C"
-namespace PySide { namespace ClassInfo {
+namespace PySide::ClassInfo {
const char *ClassInfoPrivate::name() const
{
@@ -51,25 +51,14 @@ PyObject *ClassInfoPrivate::tp_call(PyObject *self, PyObject *args, PyObject * /
auto *pData = DecoratorPrivate::get<ClassInfoPrivate>(self);
- if (pData->m_alreadyWrapped) {
- PyErr_SetString(PyExc_TypeError, "This instance of ClassInfo() was already used to wrap an object");
- return nullptr;
- }
-
- bool validClass = false;
+ if (pData->m_alreadyWrapped)
+ return PyErr_Format(PyExc_TypeError, "This instance of ClassInfo() was already used to wrap an object");
PyTypeObject *klassType = reinterpret_cast<PyTypeObject *>(klass);
- if (auto userData = PySide::retrieveTypeUserData(klassType)) {
- PySide::MetaObjectBuilder &mo = userData->mo;
- mo.addInfo(pData->m_data);
- pData->m_alreadyWrapped = true;
- validClass = true;
- }
+ if (!PySide::ClassInfo::setClassInfo(klassType, pData->m_data))
+ return PyErr_Format(PyExc_TypeError, "This decorator can only be used on classes that are subclasses of QObject");
- if (!validClass) {
- PyErr_SetString(PyExc_TypeError, "This decorator can only be used on classes that are subclasses of QObject");
- return nullptr;
- }
+ pData->m_alreadyWrapped = true;
Py_INCREF(klass);
return klass;
@@ -79,7 +68,7 @@ int ClassInfoPrivate::tp_init(PyObject *self, PyObject *args, PyObject *kwds)
{
PyObject *infoDict = nullptr;
auto size = PyTuple_Size(args);
- if (size == 1 && !kwds) {
+ if (size == 1 && kwds == nullptr) {
PyObject *tmp = PyTuple_GET_ITEM(args, 0);
if (PyDict_Check(tmp))
infoDict = tmp;
@@ -87,7 +76,7 @@ int ClassInfoPrivate::tp_init(PyObject *self, PyObject *args, PyObject *kwds)
infoDict = kwds;
}
- if (!infoDict) {
+ if (infoDict == nullptr) {
PyErr_Format(PyExc_TypeError, "ClassInfo() takes either keyword argument(s) or "
"a single dictionary argument");
return -1;
@@ -95,15 +84,17 @@ int ClassInfoPrivate::tp_init(PyObject *self, PyObject *args, PyObject *kwds)
auto *pData = DecoratorPrivate::get<ClassInfoPrivate>(self);
- PyObject *key;
- PyObject *value;
+ PyObject *key{};
+ PyObject *value{};
Py_ssize_t pos = 0;
// PyDict_Next causes a segfault if kwds is empty
if (PyDict_Size(infoDict) > 0) {
while (PyDict_Next(infoDict, &pos, &key, &value)) {
if (Shiboken::String::check(key) && Shiboken::String::check(value)) {
- pData->m_data[Shiboken::String::toCString(key)] = Shiboken::String::toCString(value);
+ ClassInfo info{Shiboken::String::toCString(key),
+ Shiboken::String::toCString(value)};
+ pData->m_data.append(info);
} else {
PyErr_SetString(PyExc_TypeError, "All keys and values provided to ClassInfo() "
"must be strings");
@@ -112,7 +103,7 @@ int ClassInfoPrivate::tp_init(PyObject *self, PyObject *args, PyObject *kwds)
}
}
- return PyErr_Occurred() ? -1 : 0;
+ return PyErr_Occurred() != nullptr ? -1 : 0;
}
static const char *ClassInfo_SignatureStrings[] = {
@@ -130,16 +121,38 @@ void init(PyObject *module)
bool checkType(PyObject *pyObj)
{
- if (pyObj)
- return PyType_IsSubtype(Py_TYPE(pyObj), PySideClassInfo_TypeF());
- return false;
+ return pyObj != nullptr
+ && PyType_IsSubtype(Py_TYPE(pyObj), PySideClassInfo_TypeF()) != 0;
}
-QMap<QByteArray, QByteArray> getMap(PyObject *obj)
+ClassInfoList getClassInfoList(PyObject *decorator)
{
- auto *pData = PySide::ClassDecorator::DecoratorPrivate::get<ClassInfoPrivate>(obj);
+ auto *pData = PySide::ClassDecorator::DecoratorPrivate::get<ClassInfoPrivate>(decorator);
return pData->m_data;
}
-} //namespace Property
-} //namespace PySide
+bool setClassInfo(PyTypeObject *type, const QByteArray &key,
+ const QByteArray &value)
+{
+ auto *userData = PySide::retrieveTypeUserData(type);
+ const bool result = userData != nullptr;
+ if (result) {
+ PySide::MetaObjectBuilder &mo = userData->mo;
+ mo.addInfo(key, value);
+ }
+ return result;
+}
+
+bool setClassInfo(PyTypeObject *type, const ClassInfoList &list)
+{
+ auto *userData = PySide::retrieveTypeUserData(type);
+ const bool result = userData != nullptr;
+ if (result) {
+ PySide::MetaObjectBuilder &mo = userData->mo;
+ for (const auto &info : list)
+ mo.addInfo(info.key.constData(), info.value.constData());
+ }
+ return result;
+}
+
+} //namespace PySide::ClassInfo
diff --git a/sources/pyside6/libpyside/pysideclassinfo.h b/sources/pyside6/libpyside/pysideclassinfo.h
index ecc2affbb..e04865829 100644
--- a/sources/pyside6/libpyside/pysideclassinfo.h
+++ b/sources/pyside6/libpyside/pysideclassinfo.h
@@ -8,15 +8,26 @@
#include <sbkpython.h>
-#include <QtCore/QMap>
#include <QtCore/QByteArray>
+#include <QtCore/QList>
-namespace PySide { namespace ClassInfo {
+namespace PySide::ClassInfo {
+
+struct ClassInfo
+{
+ QByteArray key;
+ QByteArray value;
+};
+
+using ClassInfoList = QList<ClassInfo>;
PYSIDE_API bool checkType(PyObject* pyObj);
-PYSIDE_API QMap<QByteArray, QByteArray> getMap(PyObject *obj);
+PYSIDE_API ClassInfoList getClassInfoList(PyObject *decorator);
+
+PYSIDE_API bool setClassInfo(PyTypeObject *type, const QByteArray &key,
+ const QByteArray &value);
+PYSIDE_API bool setClassInfo(PyTypeObject *type, const ClassInfoList &list);
-} //namespace ClassInfo
-} //namespace PySide
+} // namespace PySide::ClassInfo
#endif
diff --git a/sources/pyside6/libpyside/pysideclassinfo_p.h b/sources/pyside6/libpyside/pysideclassinfo_p.h
index 7d59e4be8..4ef456f76 100644
--- a/sources/pyside6/libpyside/pysideclassinfo_p.h
+++ b/sources/pyside6/libpyside/pysideclassinfo_p.h
@@ -8,7 +8,6 @@
#include "pysideclassdecorator_p.h"
#include "pysideclassinfo.h"
-#include "pysideclassinfo.h"
#include <QtCore/QMetaObject>
@@ -20,7 +19,7 @@ extern PYSIDE_API PyTypeObject *PySideClassInfo_TypeF(void);
} // extern "C"
-namespace PySide { namespace ClassInfo {
+namespace PySide::ClassInfo {
class ClassInfoPrivate : public PySide::ClassDecorator::DecoratorPrivate
{
@@ -29,7 +28,7 @@ public:
int tp_init(PyObject *self, PyObject *args, PyObject *kwds) override;
const char *name() const override;
- QMap<QByteArray, QByteArray> m_data;
+ ClassInfoList m_data;
bool m_alreadyWrapped = false;
};
@@ -39,7 +38,6 @@ public:
void init(PyObject* module);
-} // namespace ClassInfo
-} // namespace PySide
+} // namespace PySide::ClassInfo
#endif
diff --git a/sources/pyside6/libpyside/pysideinit.h b/sources/pyside6/libpyside/pysideinit.h
index 3a8f20158..c623a0d27 100644
--- a/sources/pyside6/libpyside/pysideinit.h
+++ b/sources/pyside6/libpyside/pysideinit.h
@@ -17,6 +17,9 @@ PYSIDE_API void init(PyObject *module);
///
/// This is used in a standalone build, to inform QLibraryInfo of the Qt prefix
/// (where Qt libraries are installed) so that plugins can be successfully loaded.
+///
+/// This is also used if PySide runs from inside a conda environment to solve
+/// conflicts with the qt.conf installed by Anaconda Qt packages.
PYSIDE_API bool registerInternalQtConf();
} //namespace PySide
diff --git a/sources/pyside6/libpyside/pysidemetafunction.cpp b/sources/pyside6/libpyside/pysidemetafunction.cpp
index eb2943ecf..e8173b97d 100644
--- a/sources/pyside6/libpyside/pysidemetafunction.cpp
+++ b/sources/pyside6/libpyside/pysidemetafunction.cpp
@@ -22,25 +22,30 @@ struct PySideMetaFunctionPrivate
static void functionFree(void *);
static PyObject *functionCall(PyObject *, PyObject *, PyObject *);
-static PyType_Slot PySideMetaFunctionType_slots[] = {
- {Py_tp_call, reinterpret_cast<void *>(functionCall)},
- {Py_tp_new, reinterpret_cast<void *>(PyType_GenericNew)},
- {Py_tp_free, reinterpret_cast<void *>(functionFree)},
- {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
- {0, nullptr}
-};
-static PyType_Spec PySideMetaFunctionType_spec = {
- "2:PySide6.QtCore.MetaFunction",
- sizeof(PySideMetaFunction),
- 0,
- Py_TPFLAGS_DEFAULT,
- PySideMetaFunctionType_slots,
-};
-
+static PyTypeObject *createMetaFunctionType()
+{
+ PyType_Slot PySideMetaFunctionType_slots[] = {
+ {Py_tp_call, reinterpret_cast<void *>(functionCall)},
+ {Py_tp_new, reinterpret_cast<void *>(PyType_GenericNew)},
+ {Py_tp_free, reinterpret_cast<void *>(functionFree)},
+ {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
+ {0, nullptr}
+ };
+
+ PyType_Spec PySideMetaFunctionType_spec = {
+ "2:PySide6.QtCore.MetaFunction",
+ sizeof(PySideMetaFunction),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ PySideMetaFunctionType_slots,
+ };
+
+ return SbkType_FromSpec(&PySideMetaFunctionType_spec);
+}
PyTypeObject *PySideMetaFunction_TypeF(void)
{
- static auto *type = SbkType_FromSpec(&PySideMetaFunctionType_spec);
+ static auto *type = createMetaFunctionType();
return type;
}
diff --git a/sources/pyside6/libpyside/pysidemetafunction.h b/sources/pyside6/libpyside/pysidemetafunction.h
index 659a14161..26a2cfd68 100644
--- a/sources/pyside6/libpyside/pysidemetafunction.h
+++ b/sources/pyside6/libpyside/pysidemetafunction.h
@@ -22,7 +22,7 @@ extern "C"
};
}; //extern "C"
-namespace PySide { namespace MetaFunction {
+namespace PySide::MetaFunction {
/**
* This function creates a MetaFunction object
@@ -33,7 +33,6 @@ namespace PySide { namespace MetaFunction {
**/
PYSIDE_API PySideMetaFunction *newObject(QObject *obj, int methodIndex);
-} //namespace MetaFunction
-} //namespace PySide
+} //namespace PySide::MetaFunction
#endif
diff --git a/sources/pyside6/libpyside/pysidemetafunction_p.h b/sources/pyside6/libpyside/pysidemetafunction_p.h
index db4aaa403..0207ec3a2 100644
--- a/sources/pyside6/libpyside/pysidemetafunction_p.h
+++ b/sources/pyside6/libpyside/pysidemetafunction_p.h
@@ -6,13 +6,13 @@
#include <sbkpython.h>
-#include <QtCore/QtGlobal>
+#include <QtCore/qtconfigmacros.h>
QT_BEGIN_NAMESPACE
class QObject;
QT_END_NAMESPACE
-namespace PySide { namespace MetaFunction {
+namespace PySide::MetaFunction {
void init(PyObject *module);
/**
@@ -20,7 +20,6 @@ namespace PySide { namespace MetaFunction {
*/
bool call(QObject *self, int methodIndex, PyObject *args, PyObject **retVal = nullptr);
-} //namespace MetaFunction
-} //namespace PySide
+} //namespace PySide::MetaFunction
#endif
diff --git a/sources/pyside6/libpyside/pysidemetatype.h b/sources/pyside6/libpyside/pysidemetatype.h
index b7c531570..85e70f7c9 100644
--- a/sources/pyside6/libpyside/pysidemetatype.h
+++ b/sources/pyside6/libpyside/pysidemetatype.h
@@ -8,7 +8,7 @@
#include <pysidemacros.h>
-#include <QtCore/QtGlobal>
+#include <QtCore/qtconfigmacros.h>
QT_FORWARD_DECLARE_CLASS(QMetaType)
diff --git a/sources/pyside6/libpyside/pysideproperty.cpp b/sources/pyside6/libpyside/pysideproperty.cpp
index a4f90b8fd..3720815db 100644
--- a/sources/pyside6/libpyside/pysideproperty.cpp
+++ b/sources/pyside6/libpyside/pysideproperty.cpp
@@ -8,6 +8,7 @@
#include "pysidesignal_p.h"
#include <shiboken.h>
+#include <pep384ext.h>
#include <signature.h>
using namespace Shiboken;
@@ -58,34 +59,39 @@ static PyGetSetDef PySidePropertyType_getset[] = {
{nullptr, nullptr, nullptr, nullptr, nullptr}
};
-static PyType_Slot PySidePropertyType_slots[] = {
- {Py_tp_dealloc, reinterpret_cast<void *>(qpropertyDeAlloc)},
- {Py_tp_call, reinterpret_cast<void *>(qPropertyCall)},
- {Py_tp_traverse, reinterpret_cast<void *>(qpropertyTraverse)},
- {Py_tp_clear, reinterpret_cast<void *>(qpropertyClear)},
- {Py_tp_methods, reinterpret_cast<void *>(PySidePropertyMethods)},
- {Py_tp_init, reinterpret_cast<void *>(qpropertyTpInit)},
- {Py_tp_new, reinterpret_cast<void *>(qpropertyTpNew)},
- {Py_tp_getset, PySidePropertyType_getset},
- {Py_tp_del, reinterpret_cast<void *>(PyObject_GC_Del)},
- {0, nullptr}
-};
+static PyTypeObject *createPropertyType()
+{
+ PyType_Slot PySidePropertyType_slots[] = {
+ {Py_tp_dealloc, reinterpret_cast<void *>(qpropertyDeAlloc)},
+ {Py_tp_call, reinterpret_cast<void *>(qPropertyCall)},
+ {Py_tp_traverse, reinterpret_cast<void *>(qpropertyTraverse)},
+ {Py_tp_clear, reinterpret_cast<void *>(qpropertyClear)},
+ {Py_tp_methods, reinterpret_cast<void *>(PySidePropertyMethods)},
+ {Py_tp_init, reinterpret_cast<void *>(qpropertyTpInit)},
+ {Py_tp_new, reinterpret_cast<void *>(qpropertyTpNew)},
+ {Py_tp_getset, PySidePropertyType_getset},
+ {Py_tp_del, reinterpret_cast<void *>(PyObject_GC_Del)},
+ {0, nullptr}
+ };
-static PyType_Spec PySidePropertyType_spec = {
- "2:PySide6.QtCore.Property",
- sizeof(PySideProperty),
- 0,
- Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE,
- PySidePropertyType_slots,
-};
+ PyType_Spec PySidePropertyType_spec = {
+ "2:PySide6.QtCore.Property",
+ sizeof(PySideProperty),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE,
+ PySidePropertyType_slots,
+ };
+ return SbkType_FromSpec(&PySidePropertyType_spec);
+}
PyTypeObject *PySideProperty_TypeF(void)
{
- static auto *type = SbkType_FromSpec(&PySidePropertyType_spec);
+ static auto *type = createPropertyType();
return type;
}
+PySidePropertyPrivate::PySidePropertyPrivate() noexcept = default;
PySidePropertyPrivate::~PySidePropertyPrivate() = default;
PyObject *PySidePropertyPrivate::getValue(PyObject *source)
@@ -137,20 +143,29 @@ void PySidePropertyPrivate::metaCall(PyObject *source, QMetaObject::Call call, v
{
switch (call) {
case QMetaObject::ReadProperty: {
- Shiboken::Conversions::SpecificConverter converter(typeName);
- Q_ASSERT(converter);
- if (PyObject *value = getValue(source)) {
- converter.toCpp(value, args[0]);
- Py_DECREF(value);
+ AutoDecRef value(getValue(source));
+ auto *obValue = value.object();
+ if (obValue) {
+ Conversions::SpecificConverter converter(typeName);
+ if (converter) {
+ converter.toCpp(obValue, args[0]);
+ } else {
+ // PYSIDE-2160: Report an unknown type name to the caller `qtPropertyMetacall`.
+ PyErr_SetObject(PyExc_StopIteration, obValue);
+ }
}
}
break;
case QMetaObject::WriteProperty: {
- Shiboken::Conversions::SpecificConverter converter(typeName);
- Q_ASSERT(converter);
- Shiboken::AutoDecRef value(converter.toPython(args[0]));
- setValue(source, value);
+ Conversions::SpecificConverter converter(typeName);
+ if (converter) {
+ AutoDecRef value(converter.toPython(args[0]));
+ setValue(source, value);
+ } else {
+ // PYSIDE-2160: Report an unknown type name to the caller `qtPropertyMetacall`.
+ PyErr_SetNone(PyExc_StopIteration);
+ }
}
break;
@@ -165,14 +180,14 @@ void PySidePropertyPrivate::metaCall(PyObject *source, QMetaObject::Call call, v
static PyObject *qpropertyTpNew(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */)
{
- PySideProperty *me = reinterpret_cast<PySideProperty *>(subtype->tp_alloc(subtype, 0));
+ auto *me = PepExt_TypeCallAlloc<PySideProperty>(subtype, 0);
me->d = new PySidePropertyPrivate;
return reinterpret_cast<PyObject *>(me);
}
static int qpropertyTpInit(PyObject *self, PyObject *args, PyObject *kwds)
{
- PyObject *type = nullptr;
+ PyObject *type{};
auto data = reinterpret_cast<PySideProperty *>(self);
PySidePropertyPrivate *pData = data->d;
@@ -181,6 +196,13 @@ static int qpropertyTpInit(PyObject *self, PyObject *args, PyObject *kwds)
"user", "constant", "final", nullptr};
char *doc{};
+ Py_CLEAR(pData->pyTypeObject);
+ Py_CLEAR(pData->fget);
+ Py_CLEAR(pData->fset);
+ Py_CLEAR(pData->freset);
+ Py_CLEAR(pData->fdel);
+ Py_CLEAR(pData->notify);
+
if (!PyArg_ParseTupleAndKeywords(args, kwds,
"O|OOOOsObbbbbb:QtCore.Property",
const_cast<char **>(kwlist),
@@ -237,7 +259,7 @@ static void qpropertyDeAlloc(PyObject *self)
Py_DECREF(Py_TYPE(self));
}
PyObject_GC_UnTrack(self);
- Py_TYPE(self)->tp_free(self);
+ PepExt_TypeCallFree(self);
}
// Create a copy of the property to prevent the @property.setter from modifying
@@ -246,7 +268,7 @@ static void qpropertyDeAlloc(PyObject *self)
static PyObject *
_property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *reset, PyObject *del)
{
- PySideProperty *pold = reinterpret_cast<PySideProperty *>(old);
+ auto *pold = reinterpret_cast<PySideProperty *>(old);
PySidePropertyPrivate *pData = pold->d;
AutoDecRef type(PyObject_Type(old));
@@ -400,6 +422,7 @@ static int qpropertyTraverse(PyObject *self, visitproc visit, void *arg)
Py_VISIT(data->freset);
Py_VISIT(data->fdel);
Py_VISIT(data->notify);
+ Py_VISIT(data->pyTypeObject);
return 0;
}
@@ -414,7 +437,7 @@ static int qpropertyClear(PyObject *self)
Py_CLEAR(data->freset);
Py_CLEAR(data->fdel);
Py_CLEAR(data->notify);
- Py_XDECREF(data->pyTypeObject);
+ Py_CLEAR(data->pyTypeObject);
delete data;
reinterpret_cast<PySideProperty *>(self)->d = nullptr;
@@ -423,16 +446,14 @@ static int qpropertyClear(PyObject *self)
} // extern "C"
-namespace {
-
static PyObject *getFromType(PyTypeObject *type, PyObject *name)
{
- PyObject *attr = nullptr;
- attr = PyDict_GetItem(type->tp_dict, name);
+ AutoDecRef tpDict(PepType_GetDict(type));
+ auto *attr = PyDict_GetItem(tpDict.object(), name);
if (!attr) {
PyObject *bases = type->tp_bases;
- int size = PyTuple_GET_SIZE(bases);
- for(int i=0; i < size; i++) {
+ const Py_ssize_t size = PyTuple_GET_SIZE(bases);
+ for (Py_ssize_t i = 0; i < size; ++i) {
PyObject *base = PyTuple_GET_ITEM(bases, i);
attr = getFromType(reinterpret_cast<PyTypeObject *>(base), name);
if (attr)
@@ -442,10 +463,7 @@ static PyObject *getFromType(PyTypeObject *type, PyObject *name)
return attr;
}
-} //namespace
-
-
-namespace PySide { namespace Property {
+namespace PySide::Property {
static const char *Property_SignatureStrings[] = {
"PySide6.QtCore.Property(self,type:type,fget:typing.Callable=None,fset:typing.Callable=None,"
@@ -457,6 +475,7 @@ static const char *Property_SignatureStrings[] = {
"PySide6.QtCore.Property.read(self,fget:typing.Callable)->PySide6.QtCore.Property",
"PySide6.QtCore.Property.setter(self,fset:typing.Callable)->PySide6.QtCore.Property",
"PySide6.QtCore.Property.write(self,fset:typing.Callable)->PySide6.QtCore.Property",
+ "PySide6.QtCore.Property.__call__(self, func:typing.Callable)->PySide6.QtCore.Property",
nullptr}; // Sentinel
void init(PyObject *module)
@@ -560,9 +579,8 @@ bool isFinal(const PySideProperty *self)
const char *getNotifyName(PySideProperty *self)
{
if (self->d->notifySignature.isEmpty()) {
- PyObject *str = PyObject_Str(self->d->notify);
+ AutoDecRef str(PyObject_Str(self->d->notify));
self->d->notifySignature = Shiboken::String::toCString(str);
- Py_DECREF(str);
}
return self->d->notifySignature.isEmpty()
@@ -579,5 +597,4 @@ PyObject *getTypeObject(const PySideProperty *self)
return self->d->pyTypeObject;
}
-} //namespace Property
-} //namespace PySide
+} //namespace PySide::Property
diff --git a/sources/pyside6/libpyside/pysideproperty.h b/sources/pyside6/libpyside/pysideproperty.h
index 5e4e89fcf..a572efe45 100644
--- a/sources/pyside6/libpyside/pysideproperty.h
+++ b/sources/pyside6/libpyside/pysideproperty.h
@@ -23,7 +23,7 @@ extern "C"
};
};
-namespace PySide { namespace Property {
+namespace PySide::Property {
PYSIDE_API bool checkType(PyObject *pyObj);
@@ -68,7 +68,6 @@ PYSIDE_API PySideProperty *getObject(PyObject *source, PyObject *name);
PYSIDE_API void setTypeName(PySideProperty *self, const char *typeName);
-} //namespace Property
-} //namespace PySide
+} //namespace PySide::Property
#endif
diff --git a/sources/pyside6/libpyside/pysideproperty_p.h b/sources/pyside6/libpyside/pysideproperty_p.h
index 95c716ca5..10cb3ce87 100644
--- a/sources/pyside6/libpyside/pysideproperty_p.h
+++ b/sources/pyside6/libpyside/pysideproperty_p.h
@@ -10,6 +10,7 @@
#include <pysidemacros.h>
#include <QtCore/QByteArray>
+#include <QtCore/qtclasshelpermacros.h>
#include <QtCore/QMetaObject>
struct PySideProperty;
@@ -17,6 +18,10 @@ struct PySideProperty;
class PYSIDE_API PySidePropertyPrivate
{
public:
+
+ Q_DISABLE_COPY_MOVE(PySidePropertyPrivate)
+
+ PySidePropertyPrivate() noexcept;
virtual ~PySidePropertyPrivate();
virtual void metaCall(PyObject *source, QMetaObject::Call call, void **args);
@@ -45,7 +50,7 @@ public:
bool final = false;
};
-namespace PySide { namespace Property {
+namespace PySide::Property {
/**
* Init PySide QProperty support system
@@ -159,7 +164,6 @@ bool isFinal(const PySideProperty* self);
/// @return type object
PyObject *getTypeObject(const PySideProperty* self);
-} // namespace Property
-} // namespace PySide
+} // namespace PySide::Property
#endif
diff --git a/sources/pyside6/libpyside/pysideqenum.cpp b/sources/pyside6/libpyside/pysideqenum.cpp
index 3f374b702..c0479160f 100644
--- a/sources/pyside6/libpyside/pysideqenum.cpp
+++ b/sources/pyside6/libpyside/pysideqenum.cpp
@@ -97,7 +97,7 @@ static bool is_module_code()
} // extern "C"
-namespace PySide { namespace QEnum {
+namespace PySide::QEnum {
static std::map<int, PyObject *> enumCollector;
@@ -191,8 +191,7 @@ std::vector<PyObject *> resolveDelayedQEnums(PyTypeObject *containerType)
return result;
}
-} // namespace Enum
-} // namespace Shiboken
+} // namespace Shiboken::Enum
//
///////////////////////////////////////////////////////////////
diff --git a/sources/pyside6/libpyside/pysideqenum.h b/sources/pyside6/libpyside/pysideqenum.h
index 459c2b1f0..c3483e63e 100644
--- a/sources/pyside6/libpyside/pysideqenum.h
+++ b/sources/pyside6/libpyside/pysideqenum.h
@@ -7,7 +7,7 @@
#include <pysidemacros.h>
#include <vector>
-namespace PySide { namespace QEnum {
+namespace PySide::QEnum {
// PYSIDE-957: Support the QEnum macro
PYSIDE_API PyObject *QEnumMacro(PyObject *, bool);
@@ -15,7 +15,6 @@ PYSIDE_API int isFlag(PyObject *);
PYSIDE_API std::vector<PyObject *> resolveDelayedQEnums(PyTypeObject *);
PYSIDE_API void init();
-} // namespace QEnum
-} // namespace PySide
+} // namespace PySide::QEnum
#endif
diff --git a/sources/pyside6/libpyside/pysideqflags.cpp b/sources/pyside6/libpyside/pysideqflags.cpp
deleted file mode 100644
index 965d843c1..000000000
--- a/sources/pyside6/libpyside/pysideqflags.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-// 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 "pysideqflags.h"
-
-#include <autodecref.h>
-#include <sbkenum.h>
-#include <sbkenum_p.h>
-
-extern "C" {
- struct SbkConverter;
-
- struct PySideQFlagsTypePrivate
- {
- SbkConverter *converter;
- // PYSIDE-1735: These fields are just there for comatibility with the enumstructure.
- // We need to switch between flags and enum at runtine.
- // This will vanish completely when we no longer support two implementations.
- const char *_cppName;
- PyTypeObject *_replacementType;
- };
- /**
- * Type of all QFlags
- */
- struct PySideQFlagsType
- {
- PyTypeObject type;
- };
-
- #define PYSIDE_QFLAGS(X) reinterpret_cast<PySideQFlagsObject *>(X)
-
- PyObject *PySideQFlags_tp_new(PyTypeObject *type, PyObject *args, PyObject * /* kwds */)
- {
- long val = 0;
- if (PyTuple_GET_SIZE(args)) {
- PyObject *arg = PyTuple_GET_ITEM(args, 0);
- if (Shiboken::isShibokenEnum(arg)) {// faster call
- val = Shiboken::Enum::getValue(arg);
- } else if (PyNumber_Check(arg)) {
- Shiboken::AutoDecRef number(PyNumber_Long(arg));
- val = PyLong_AsLong(number);
- } else {
- PyErr_SetString(PyExc_TypeError,"QFlags must be created using enums or numbers.");
- return nullptr;
- }
- }
- PySideQFlagsObject *self = PyObject_New(PySideQFlagsObject, type);
- self->ob_value = val;
- return reinterpret_cast<PyObject *>(self);
- }
-
- static long getNumberValue(PyObject *v)
- {
- Shiboken::AutoDecRef number(PyNumber_Long(v));
- return PyLong_AsLong(number);
- }
-
- static PyObject *qflag_nb_int(PyObject *self)
- {
- return PyLong_FromLong(reinterpret_cast<PySideQFlagsObject*>(self)->ob_value);
- }
-
- PyObject *PySideQFlags_tp_richcompare(PyObject *self, PyObject *other, int op)
- {
- int result = 0;
- if (!PyNumber_Check(other)) {
- switch (op) {
- case Py_EQ:
- Py_RETURN_FALSE;
- case Py_NE:
- Py_RETURN_TRUE;
- default:
- Py_RETURN_NOTIMPLEMENTED;
- }
- }
-
- if (self == other) {
- switch (op) {
- case Py_EQ:
- case Py_LE:
- case Py_GE:
- result = 1;
- break;
- }
- } else {
- const long valA = PYSIDE_QFLAGS(self)->ob_value;
- const long valB = getNumberValue(other);
- switch (op) {
- case Py_EQ:
- result = (valA == valB);
- break;
- case Py_NE:
- result = (valA != valB);
- break;
- case Py_LE:
- result = (valA <= valB);
- break;
- case Py_GE:
- result = (valA >= valB);
- break;
- case Py_LT:
- result = (valA < valB);
- break;
- case Py_GT:
- result = (valA > valB);
- break;
- default:
- PyErr_BadArgument();
- return nullptr;
- }
- }
- if (result)
- Py_RETURN_TRUE;
- Py_RETURN_FALSE;
- }
-
- static void PySideQFlags_tp_dealloc(PyObject *self)
- {
- auto *flagsType = reinterpret_cast<PySideQFlagsType *>(self);
- PepType_PFTP_delete(flagsType);
- Sbk_object_dealloc(self);
- }
-
- /// PYSIDE-1735: Support for redirection to the new Python enum.Flag .
- static PyTypeObject *getEnumMeta()
- {
- static auto *mod = PyImport_ImportModule("enum");
- if (mod) {
- static auto *EnumMeta = PyObject_GetAttrString(mod, "EnumMeta");
- if (EnumMeta)
- return reinterpret_cast<PyTypeObject *>(EnumMeta);
- }
- Py_FatalError("Python module 'enum' not found");
- return nullptr;
- }
-}
-
-namespace PySide
-{
-namespace QFlags
-{
- static PyType_Slot SbkNewQFlagsType_slots[] = {
- {Py_nb_bool, nullptr},
- {Py_nb_invert, nullptr},
- {Py_nb_and, nullptr},
- {Py_nb_xor, nullptr},
- {Py_nb_or, nullptr},
- {Py_nb_int, reinterpret_cast<void*>(qflag_nb_int)},
- {Py_nb_index, reinterpret_cast<void*>(qflag_nb_int)}, // same as nb_int
- {Py_tp_new, reinterpret_cast<void *>(PySideQFlags_tp_new)},
- {Py_tp_richcompare, reinterpret_cast<void *>(PySideQFlags_tp_richcompare)},
- {Py_tp_dealloc, reinterpret_cast<void *>(PySideQFlags_tp_dealloc)},
- {0, nullptr}
- };
- static PyType_Spec SbkNewQFlagsType_spec = {
- "missing QFlags name", // to be inserted later
- sizeof(PySideQFlagsObject),
- 0,
- Py_TPFLAGS_DEFAULT,
- SbkNewQFlagsType_slots,
- };
-
- PyTypeObject *create(const char *name, PyType_Slot numberMethods[])
- {
- char qualname[200];
- // PYSIDE-747: Here we insert now the full class name.
- strcpy(qualname, name);
- // Careful: SbkType_FromSpec does not allocate the string.
- PyType_Spec newspec;
- newspec.name = strdup(qualname);
- newspec.basicsize = SbkNewQFlagsType_spec.basicsize;
- newspec.itemsize = SbkNewQFlagsType_spec.itemsize;
- newspec.flags = SbkNewQFlagsType_spec.flags;
- int idx = -1;
- while (numberMethods[++idx].slot) {
- assert(SbkNewQFlagsType_slots[idx].slot == numberMethods[idx].slot);
- SbkNewQFlagsType_slots[idx].pfunc = numberMethods[idx].pfunc;
- }
- newspec.slots = SbkNewQFlagsType_spec.slots;
- return SbkType_FromSpec(&newspec);
- }
-
- PySideQFlagsObject *newObject(long value, PyTypeObject *type)
- {
- // PYSIDE-1735: In case of a new Python enum, we must redirect to the
- // enum.Flag implementation.
- static PyTypeObject *enumMeta = getEnumMeta();
- if (Py_TYPE(type) == enumMeta) {
- // We are cheating: This is an enum type.
- auto *flag_enum = PyObject_CallFunction(reinterpret_cast<PyObject *>(type), "i", value);
- return reinterpret_cast<PySideQFlagsObject *>(flag_enum);
- }
- PySideQFlagsObject *qflags = PyObject_New(PySideQFlagsObject, type);
- qflags->ob_value = value;
- return qflags;
- }
-
- long getValue(PySideQFlagsObject *self)
- {
- return self->ob_value;
- }
-}
-}
diff --git a/sources/pyside6/libpyside/pysideqflags.h b/sources/pyside6/libpyside/pysideqflags.h
deleted file mode 100644
index aca823edd..000000000
--- a/sources/pyside6/libpyside/pysideqflags.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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
-
-#ifndef PYSIDE_QFLAGS_H
-#define PYSIDE_QFLAGS_H
-
-#include <sbkpython.h>
-#include "pysidemacros.h"
-
-
-extern "C"
-{
- struct PYSIDE_API PySideQFlagsObject {
- PyObject_HEAD
- long ob_value;
- };
-
- PYSIDE_API PyObject* PySideQFlags_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
- PYSIDE_API PyObject* PySideQFlags_tp_richcompare(PyObject *self, PyObject *other, int op);
-}
-
-
-namespace PySide
-{
-namespace QFlags
-{
- /**
- * Creates a new QFlags type.
- */
- PYSIDE_API PyTypeObject *create(const char* name, PyType_Slot *numberMethods);
- /**
- * Creates a new QFlags instance of type \p type and value \p value.
- */
- PYSIDE_API PySideQFlagsObject* newObject(long value, PyTypeObject* type);
- /**
- * Returns the value held by a QFlag.
- */
- PYSIDE_API long getValue(PySideQFlagsObject* self);
-}
-}
-
-#endif
-
diff --git a/sources/pyside6/libpyside/pysideqhash.h b/sources/pyside6/libpyside/pysideqhash.h
index e6e1392a9..ae2d295f6 100644
--- a/sources/pyside6/libpyside/pysideqhash.h
+++ b/sources/pyside6/libpyside/pysideqhash.h
@@ -14,7 +14,7 @@ namespace PySide
/// Hash function used to enable hash on objects not supported by the native Qt
/// library which have a toString() function.
template<class T>
-inline Py_ssize_t hash(const T& value)
+[[deprecated]] inline Py_ssize_t hash(const T& value)
{
return qHash(value.toString());
}
diff --git a/sources/pyside6/libpyside/pysideqobject.h b/sources/pyside6/libpyside/pysideqobject.h
index 96834635d..f81c50399 100644
--- a/sources/pyside6/libpyside/pysideqobject.h
+++ b/sources/pyside6/libpyside/pysideqobject.h
@@ -8,7 +8,9 @@
#include <pysidemacros.h>
-#include <QtCore/QtGlobal>
+#include <QtCore/qtclasshelpermacros.h>
+
+#include <cstddef>
QT_FORWARD_DECLARE_CLASS(QObject)
QT_FORWARD_DECLARE_STRUCT(QMetaObject)
@@ -22,7 +24,8 @@ namespace PySide
/// \param metaObj QMetaObject of \p qObj.
/// \param kwds key->value dictonary.
/// \return True if everything goes well, false with a Python error set otherwise.
-PYSIDE_API bool fillQtProperties(PyObject *qObj, const QMetaObject *metaObj, PyObject *kwds);
+PYSIDE_API bool fillQtProperties(PyObject *qObj, const QMetaObject *metaObj,
+ PyObject *kwds, bool allowErrors);
PYSIDE_API void initDynamicMetaObject(PyTypeObject *type, const QMetaObject *base,
std::size_t cppObjSize);
@@ -40,13 +43,14 @@ PYSIDE_API bool isQObjectDerived(PyTypeObject *pyType, bool raiseError);
/// Convenience to convert a PyObject to QObject
PYSIDE_API QObject *convertToQObject(PyObject *object, bool raiseError);
-/// Check for properties and signals registered on MetaObject and return these
+/// Check for properties and signals registered on MetaObject and return these.
+/// Also handle Python properties when true_property was selected.
/// \param cppSelf Is the QObject which contains the metaobject
/// \param self Python object of cppSelf
/// \param name Name of the argument which the function will try retrieve from MetaData
/// \return The Python object which contains the Data obtained in metaObject or the Python
-/// attribute related with name
-PYSIDE_API PyObject *getMetaDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *name);
+/// method pulled out of a Python property.
+PYSIDE_API PyObject *getHiddenDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *name);
/// Mutex for accessing QObject memory helpers from multiple threads
PYSIDE_API QMutex &nextQObjectMemoryAddrMutex();
diff --git a/sources/pyside6/libpyside/pysideqslotobject_p.cpp b/sources/pyside6/libpyside/pysideqslotobject_p.cpp
new file mode 100644
index 000000000..914be898a
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideqslotobject_p.cpp
@@ -0,0 +1,36 @@
+// Copyright (C) 2024 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 "pysideqslotobject_p.h"
+
+#include <autodecref.h>
+#include <gilstate.h>
+
+namespace PySide
+{
+
+void PySideQSlotObject::impl(int which, QSlotObjectBase *this_, QObject *receiver,
+ void **args, bool *ret)
+{
+ auto self = static_cast<PySideQSlotObject *>(this_);
+ switch (which) {
+ case Destroy:
+ delete self;
+ break;
+ case Call:
+ {
+ Shiboken::GilState state;
+ Shiboken::AutoDecRef arglist(PyTuple_New(0));
+ Shiboken::AutoDecRef ret(PyObject_CallObject(self->callable, arglist));
+ break;
+ }
+ case Compare:
+ case NumOperations:
+ Q_UNUSED(receiver);
+ Q_UNUSED(args);
+ Q_UNUSED(ret);
+ break;
+ }
+}
+
+} // namespace PySide
diff --git a/sources/pyside6/libpyside/pysideqslotobject_p.h b/sources/pyside6/libpyside/pysideqslotobject_p.h
new file mode 100644
index 000000000..d7d258505
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideqslotobject_p.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2024 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
+
+#ifndef PYSIDEQSLOTOBJECT_P_H
+#define PYSIDEQSLOTOBJECT_P_H
+
+#include "pysidemacros.h"
+#include <sbkpython.h>
+
+#include <QtCore/QObject>
+#include <QtCore/qobjectdefs.h>
+
+namespace PySide
+{
+
+class PySideQSlotObject : public QtPrivate::QSlotObjectBase
+{
+ PyObject *callable;
+
+ static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **args, bool *ret);
+
+public:
+ PySideQSlotObject(PyObject *callable) : QtPrivate::QSlotObjectBase(&impl), callable(callable)
+ {
+ Py_INCREF(callable);
+ }
+
+ ~PySideQSlotObject()
+ {
+ auto gstate = PyGILState_Ensure();
+ Py_DECREF(callable);
+ PyGILState_Release(gstate);
+ }
+};
+
+
+} // namespace PySide
+
+#endif // PYSIDEQSLOTOBJECT_P_H
diff --git a/sources/pyside6/libpyside/pysidesignal.cpp b/sources/pyside6/libpyside/pysidesignal.cpp
index d7d38d081..35a6e7ef9 100644
--- a/sources/pyside6/libpyside/pysidesignal.cpp
+++ b/sources/pyside6/libpyside/pysidesignal.cpp
@@ -4,8 +4,10 @@
#include <sbkpython.h>
#include "pysidesignal.h"
#include "pysidesignal_p.h"
+#include "pysideqobject.h"
#include "pysideutils.h"
#include "pysidestaticstrings.h"
+#include "pysideweakref.h"
#include "signalmanager.h"
#include <shiboken.h>
@@ -16,14 +18,18 @@
#include <QtCore/QObject>
#include <QtCore/QMetaMethod>
#include <QtCore/QMetaObject>
+#include <pep384ext.h>
#include <signature.h>
#include <algorithm>
+#include <optional>
#include <utility>
#include <cstring>
#define QT_SIGNAL_SENTINEL '2'
+using namespace Qt::StringLiterals;
+
QDebug operator<<(QDebug debug, const PySideSignalData::Signature &s)
{
QDebugStateSaver saver(debug);
@@ -43,8 +49,8 @@ QDebug operator<<(QDebug debug, const PySideSignalData &d)
debug.nospace();
debug << "PySideSignalData(\"" << d.signalName << "\", "
<< d.signatures;
- if (d.signalArguments)
- debug << ", signalArguments=\"" << *d.signalArguments << '"';
+ if (!d.signalArguments.isEmpty())
+ debug << ", signalArguments=" << d.signalArguments;
debug << ')';
return debug;
}
@@ -74,29 +80,35 @@ static bool connection_Check(PyObject *o)
return std::strcmp(o->ob_type->tp_name, typeName.constData()) == 0;
}
-namespace PySide {
-namespace Signal {
- //aux
- class SignalSignature {
- public:
- SignalSignature() = default;
- explicit SignalSignature(QByteArray parameterTypes) :
- m_parameterTypes(std::move(parameterTypes)) {}
- explicit SignalSignature(QByteArray parameterTypes, QMetaMethod::Attributes attributes) :
- m_parameterTypes(std::move(parameterTypes)),
- m_attributes(attributes) {}
-
- QByteArray m_parameterTypes;
- QMetaMethod::Attributes m_attributes = QMetaMethod::Compatibility;
- };
+static std::optional<QByteArrayList> parseArgumentNames(PyObject *argArguments)
+{
+ QByteArrayList result;
+ if (argArguments == nullptr)
+ return result;
+ // Prevent a string from being split into a sequence of characters
+ if (PySequence_Check(argArguments) == 0 || PyUnicode_Check(argArguments) != 0)
+ return std::nullopt;
+ const Py_ssize_t argumentSize = PySequence_Size(argArguments);
+ result.reserve(argumentSize);
+ for (Py_ssize_t i = 0; i < argumentSize; ++i) {
+ Shiboken::AutoDecRef item(PySequence_GetItem(argArguments, i));
+ if (PyUnicode_Check(item.object()) == 0)
+ return std::nullopt;
+ Shiboken::AutoDecRef strObj(PyUnicode_AsUTF8String(item));
+ const char *s = PyBytes_AsString(strObj);
+ if (s == nullptr)
+ return std::nullopt;
+ result.append(QByteArray(s));
+ }
+ return result;
+}
+namespace PySide::Signal {
static QByteArray buildSignature(const QByteArray &, const QByteArray &);
- static void appendSignature(PySideSignal *, const SignalSignature &);
static void instanceInitialize(PySideSignalInstance *, PyObject *, PySideSignal *, PyObject *, int);
- static QByteArray parseSignature(PyObject *);
+ static PySideSignalData::Signature parseSignature(PyObject *);
static PyObject *buildQtCompatible(const QByteArray &);
-}
-}
+} // PySide::Signal
extern "C"
{
@@ -128,54 +140,64 @@ static PyMethodDef MetaSignal_tp_methods[] = {
{nullptr, nullptr, 0, nullptr}
};
-static PyType_Slot PySideMetaSignalType_slots[] = {
- {Py_tp_methods, reinterpret_cast<void *>(MetaSignal_tp_methods)},
- {Py_tp_base, reinterpret_cast<void *>(&PyType_Type)},
- {Py_tp_free, reinterpret_cast<void *>(PyObject_GC_Del)},
- {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
- {0, nullptr}
-};
-static PyType_Spec PySideMetaSignalType_spec = {
- "2:PySide6.QtCore.MetaSignal",
- 0,
- // sizeof(PyHeapTypeObject) is filled in by SbkType_FromSpec
- // which calls PyType_Ready which calls inherit_special.
- 0,
- Py_TPFLAGS_DEFAULT,
- PySideMetaSignalType_slots,
-};
+static PyTypeObject *createMetaSignalType()
+{
+ PyType_Slot PySideMetaSignalType_slots[] = {
+ {Py_tp_methods, reinterpret_cast<void *>(MetaSignal_tp_methods)},
+ {Py_tp_base, reinterpret_cast<void *>(&PyType_Type)},
+ {Py_tp_free, reinterpret_cast<void *>(PyObject_GC_Del)},
+ {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
+ {0, nullptr}
+ };
+
+ PyType_Spec PySideMetaSignalType_spec = {
+ "2:PySide6.QtCore.MetaSignal",
+ 0,
+ // sizeof(PyHeapTypeObject) is filled in by SbkType_FromSpec
+ // which calls PyType_Ready which calls inherit_special.
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ PySideMetaSignalType_slots,
+ };
+ return SbkType_FromSpec(&PySideMetaSignalType_spec);
+}
static PyTypeObject *PySideMetaSignal_TypeF(void)
{
- static auto *type = SbkType_FromSpec(&PySideMetaSignalType_spec);
+ static auto *type = createMetaSignalType();
return type;
}
-static PyType_Slot PySideSignalType_slots[] = {
- {Py_mp_subscript, reinterpret_cast<void *>(signalGetItem)},
- {Py_tp_getattro, reinterpret_cast<void *>(signalGetAttr)},
- {Py_tp_descr_get, reinterpret_cast<void *>(signalDescrGet)},
- {Py_tp_call, reinterpret_cast<void *>(signalCall)},
- {Py_tp_str, reinterpret_cast<void *>(signalToString)},
- {Py_tp_init, reinterpret_cast<void *>(signalTpInit)},
- {Py_tp_new, reinterpret_cast<void *>(PyType_GenericNew)},
- {Py_tp_free, reinterpret_cast<void *>(signalFree)},
- {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
- {0, nullptr}
-};
-static PyType_Spec PySideSignalType_spec = {
- "2:PySide6.QtCore.Signal",
- sizeof(PySideSignal),
- 0,
- Py_TPFLAGS_DEFAULT,
- PySideSignalType_slots,
-};
+static PyTypeObject *createSignalType()
+{
+ PyType_Slot PySideSignalType_slots[] = {
+ {Py_mp_subscript, reinterpret_cast<void *>(signalGetItem)},
+ {Py_tp_getattro, reinterpret_cast<void *>(signalGetAttr)},
+ {Py_tp_descr_get, reinterpret_cast<void *>(signalDescrGet)},
+ {Py_tp_call, reinterpret_cast<void *>(signalCall)},
+ {Py_tp_str, reinterpret_cast<void *>(signalToString)},
+ {Py_tp_init, reinterpret_cast<void *>(signalTpInit)},
+ {Py_tp_new, reinterpret_cast<void *>(PyType_GenericNew)},
+ {Py_tp_free, reinterpret_cast<void *>(signalFree)},
+ {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
+ {0, nullptr}
+ };
+
+ PyType_Spec PySideSignalType_spec = {
+ "2:PySide6.QtCore.Signal",
+ sizeof(PySideSignal),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ PySideSignalType_slots,
+ };
+ return SbkType_FromSpecWithMeta(&PySideSignalType_spec, PySideMetaSignal_TypeF());
+}
PyTypeObject *PySideSignal_TypeF(void)
{
- static auto *type = SbkType_FromSpecWithMeta(&PySideSignalType_spec, PySideMetaSignal_TypeF());
+ static auto *type = createSignalType();
return type;
}
@@ -184,7 +206,8 @@ static PyObject *signalInstanceRepr(PyObject *obSelf)
auto *self = reinterpret_cast<PySideSignalInstance *>(obSelf);
auto *typeName = Py_TYPE(obSelf)->tp_name;
return Shiboken::String::fromFormat("<%s %s at %p>", typeName,
- self->d->signature.constData(), obSelf);
+ self->d ? self->d->signature.constData()
+ : "(no signature)", obSelf);
}
static PyMethodDef SignalInstance_methods[] = {
@@ -195,28 +218,33 @@ static PyMethodDef SignalInstance_methods[] = {
{nullptr, nullptr, 0, nullptr} /* Sentinel */
};
-static PyType_Slot PySideSignalInstanceType_slots[] = {
- {Py_mp_subscript, reinterpret_cast<void *>(signalInstanceGetItem)},
- {Py_tp_call, reinterpret_cast<void *>(signalInstanceCall)},
- {Py_tp_methods, reinterpret_cast<void *>(SignalInstance_methods)},
- {Py_tp_repr, reinterpret_cast<void *>(signalInstanceRepr)},
- {Py_tp_new, reinterpret_cast<void *>(PyType_GenericNew)},
- {Py_tp_free, reinterpret_cast<void *>(signalInstanceFree)},
- {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
- {0, nullptr}
-};
-static PyType_Spec PySideSignalInstanceType_spec = {
- "2:PySide6.QtCore.SignalInstance",
- sizeof(PySideSignalInstance),
- 0,
- Py_TPFLAGS_DEFAULT,
- PySideSignalInstanceType_slots,
-};
+static PyTypeObject *createSignalInstanceType()
+{
+ PyType_Slot PySideSignalInstanceType_slots[] = {
+ {Py_mp_subscript, reinterpret_cast<void *>(signalInstanceGetItem)},
+ {Py_tp_call, reinterpret_cast<void *>(signalInstanceCall)},
+ {Py_tp_methods, reinterpret_cast<void *>(SignalInstance_methods)},
+ {Py_tp_repr, reinterpret_cast<void *>(signalInstanceRepr)},
+ {Py_tp_new, reinterpret_cast<void *>(PyType_GenericNew)},
+ {Py_tp_free, reinterpret_cast<void *>(signalInstanceFree)},
+ {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
+ {0, nullptr}
+ };
+ PyType_Spec PySideSignalInstanceType_spec = {
+ "2:PySide6.QtCore.SignalInstance",
+ sizeof(PySideSignalInstance),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ PySideSignalInstanceType_slots,
+ };
+
+ return SbkType_FromSpec(&PySideSignalInstanceType_spec);
+}
PyTypeObject *PySideSignalInstance_TypeF(void)
{
- static auto *type = SbkType_FromSpec(&PySideSignalInstanceType_spec);
+ static auto *type = createSignalInstanceType();
return type;
}
@@ -239,37 +267,23 @@ static int signalTpInit(PyObject *obSelf, PyObject *args, PyObject *kwds)
if (argName)
self->data->signalName = argName;
- const Py_ssize_t argument_size =
- argArguments != nullptr && PySequence_Check(argArguments)
- ? PySequence_Size(argArguments) : 0;
- if (argument_size > 0) {
- self->data->signalArguments = new QByteArrayList();
- self->data->signalArguments->reserve(argument_size);
- for (Py_ssize_t i = 0; i < argument_size; ++i) {
- Shiboken::AutoDecRef item(PySequence_GetItem(argArguments, i));
- Shiboken::AutoDecRef strObj(PyUnicode_AsUTF8String(item));
- if (char *s = PyBytes_AsString(strObj))
- self->data->signalArguments->append(QByteArray(s));
- }
+ auto argumentNamesOpt = parseArgumentNames(argArguments);
+ if (!argumentNamesOpt.has_value()) {
+ PyErr_SetString(PyExc_TypeError, "'arguments' must be a sequence of strings.");
+ return -1;
}
+ self->data->signalArguments = argumentNamesOpt.value();
for (Py_ssize_t i = 0, i_max = PyTuple_Size(args); i < i_max; i++) {
PyObject *arg = PyTuple_GET_ITEM(args, i);
if (PySequence_Check(arg) && !Shiboken::String::check(arg) && !PyEnumMeta_Check(arg)) {
tupledArgs = true;
- const auto sig = PySide::Signal::parseSignature(arg);
- PySide::Signal::appendSignature(
- self,
- PySide::Signal::SignalSignature(sig));
+ self->data->signatures.append(PySide::Signal::parseSignature(arg));
}
}
- if (!tupledArgs) {
- const auto sig = PySide::Signal::parseSignature(args);
- PySide::Signal::appendSignature(
- self,
- PySide::Signal::SignalSignature(sig));
- }
+ if (!tupledArgs)
+ self->data->signatures.append(PySide::Signal::parseSignature(args));
return 0;
}
@@ -279,14 +293,13 @@ static void signalFree(void *vself)
auto pySelf = reinterpret_cast<PyObject *>(vself);
auto self = reinterpret_cast<PySideSignal *>(vself);
if (self->data) {
- delete self->data->signalArguments;
delete self->data;
self->data = nullptr;
}
Py_XDECREF(self->homonymousMethod);
self->homonymousMethod = nullptr;
- Py_TYPE(pySelf)->tp_base->tp_free(self);
+ PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, self);
}
static PyObject *signalGetItem(PyObject *obSelf, PyObject *key)
@@ -294,7 +307,7 @@ static PyObject *signalGetItem(PyObject *obSelf, PyObject *key)
auto self = reinterpret_cast<PySideSignal *>(obSelf);
QByteArray sigKey;
if (key) {
- sigKey = PySide::Signal::parseSignature(key);
+ sigKey = PySide::Signal::parseSignature(key).signature;
} else {
sigKey = self->data == nullptr || self->data->signatures.isEmpty()
? PySide::Signal::voidType() : self->data->signatures.constFirst().signature;
@@ -303,9 +316,21 @@ static PyObject *signalGetItem(PyObject *obSelf, PyObject *key)
return Shiboken::String::fromCString(sig.constData());
}
-static PyObject *signalToString(PyObject *self)
+static PyObject *signalToString(PyObject *obSelf)
{
- return signalGetItem(self, nullptr);
+ auto self = reinterpret_cast<PySideSignal *>(obSelf);
+ QByteArray result;
+ if (self->data == nullptr || self->data->signatures.isEmpty()) {
+ result = "<invalid>"_ba;
+ } else {
+ for (const auto &signature : std::as_const(self->data->signatures)) {
+ if (!result.isEmpty())
+ result += "; "_ba;
+ result += PySide::Signal::buildSignature(self->data->signalName,
+ signature.signature);
+ }
+ }
+ return Shiboken::String::fromCString(result.constData());
}
static PyObject *signalGetAttr(PyObject *obSelf, PyObject *name)
@@ -333,93 +358,119 @@ static void signalInstanceFree(void *vself)
auto self = reinterpret_cast<PySideSignalInstance *>(vself);
PySideSignalInstancePrivate *dataPvt = self->d;
+ if (dataPvt) {
+ Py_XDECREF(dataPvt->homonymousMethod);
- Py_XDECREF(dataPvt->homonymousMethod);
-
- if (dataPvt->next) {
- Py_DECREF(dataPvt->next);
- dataPvt->next = nullptr;
+ if (dataPvt->next) {
+ Py_DECREF(dataPvt->next);
+ dataPvt->next = nullptr;
+ }
+ delete dataPvt;
+ self->d = nullptr;
}
- delete dataPvt;
- self->d = nullptr;
- Py_TYPE(pySelf)->tp_base->tp_free(self);
+ self->deleted = true;
+ PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, self);
}
// PYSIDE-1523: PyFunction_Check is not accepting compiled functions and
// PyMethod_Check is not allowing compiled methods, therefore also lookup
// "im_func" and "__code__" attributes, we allow for that with a dedicated
// function handling both.
-static void extractFunctionArgumentsFromSlot(PyObject *slot,
- PyObject *& function,
- PepCodeObject *& objCode,
- bool &isMethod,
- QByteArray *functionName)
+
+struct FunctionArgumentsResult
{
- isMethod = PyMethod_Check(slot);
- bool isFunction = PyFunction_Check(slot);
+ PyObject *function = nullptr;
+ PepCodeObject *objCode = nullptr;
+ PyObject *functionName = nullptr;
+ bool isMethod = false;
+};
- function = nullptr;
- objCode = nullptr;
+static FunctionArgumentsResult extractFunctionArgumentsFromSlot(PyObject *slot)
+{
+ FunctionArgumentsResult ret;
+ ret.isMethod = PyMethod_Check(slot);
+ const bool isFunction = PyFunction_Check(slot);
- if (isMethod || isFunction) {
- function = isMethod ? PyMethod_GET_FUNCTION(slot) : slot;
- objCode = reinterpret_cast<PepCodeObject *>(PyFunction_GET_CODE(function));
+ if (ret.isMethod || isFunction) {
+ ret.function = ret.isMethod ? PyMethod_GET_FUNCTION(slot) : slot;
+ ret.objCode = reinterpret_cast<PepCodeObject *>(PyFunction_GET_CODE(ret.function));
+ ret.functionName = PepFunction_GetName(ret.function);
- if (functionName != nullptr) {
- *functionName = Shiboken::String::toCString(PepFunction_GetName(function));
- }
} else if (PySide::isCompiledMethod(slot)) {
// PYSIDE-1523: PyFunction_Check and PyMethod_Check are not accepting compiled forms, we
// just go by attributes.
- isMethod = true;
-
- function = PyObject_GetAttr(slot, PySide::PyName::im_func());
+ ret.isMethod = true;
+ ret.function = PyObject_GetAttr(slot, PySide::PySideName::im_func());
// Not retaining a reference inline with what PyMethod_GET_FUNCTION does.
- Py_DECREF(function);
+ Py_DECREF(ret.function);
- if (functionName != nullptr) {
- PyObject *name = PyObject_GetAttr(function, PySide::PyMagicName::name());
- *functionName = Shiboken::String::toCString(name);
- // Not retaining a reference inline with what PepFunction_GetName does.
- Py_DECREF(name);
- }
+ ret.functionName = PyObject_GetAttr(ret.function, PySide::PySideMagicName::name());
+ // Not retaining a reference inline with what PepFunction_GetName does.
+ Py_DECREF(ret.functionName);
- objCode = reinterpret_cast<PepCodeObject *>(
- PyObject_GetAttr(function, PySide::PyMagicName::code()));
+ ret.objCode = reinterpret_cast<PepCodeObject *>(
+ PyObject_GetAttr(ret.function, PySide::PySideMagicName::code()));
// Not retaining a reference inline with what PyFunction_GET_CODE does.
- Py_XDECREF(objCode);
+ Py_XDECREF(ret.objCode);
- if (objCode == nullptr) {
- // Should not happen, but lets handle it gracefully, maybe Nuitka one day
- // makes these optional, or somebody defined a type named like it without
- // it being actually being that.
- function = nullptr;
- }
+ // Should not happen, but lets handle it gracefully, maybe Nuitka one day
+ // makes these optional, or somebody defined a type named like it without
+ // it being actually being that.
+ if (ret.objCode == nullptr)
+ ret.function = nullptr;
} else if (strcmp(Py_TYPE(slot)->tp_name, "compiled_function") == 0) {
- isMethod = false;
- function = slot;
-
- if (functionName != nullptr) {
- PyObject *name = PyObject_GetAttr(function, PySide::PyMagicName::name());
- *functionName = Shiboken::String::toCString(name);
- // Not retaining a reference inline with what PepFunction_GetName does.
- Py_DECREF(name);
- }
+ ret.isMethod = false;
+ ret.function = slot;
- objCode = reinterpret_cast<PepCodeObject *>(
- PyObject_GetAttr(function, PySide::PyMagicName::code()));
+ ret.functionName = PyObject_GetAttr(ret.function, PySide::PySideMagicName::name());
+ // Not retaining a reference inline with what PepFunction_GetName does.
+ Py_DECREF(ret.functionName);
+
+ ret.objCode = reinterpret_cast<PepCodeObject *>(
+ PyObject_GetAttr(ret.function, PySide::PySideMagicName::code()));
// Not retaining a reference inline with what PyFunction_GET_CODE does.
- Py_XDECREF(objCode);
+ Py_XDECREF(ret.objCode);
- if (objCode == nullptr) {
- // Should not happen, but lets handle it gracefully, maybe Nuitka one day
- // makes these optional, or somebody defined a type named like it without
- // it being actually being that.
- function = nullptr;
- }
+ // Should not happen, but lets handle it gracefully, maybe Nuitka one day
+ // makes these optional, or somebody defined a type named like it without
+ // it being actually being that.
+ if (ret.objCode == nullptr)
+ ret.function = nullptr;
}
// any other callback
+ return ret;
+}
+
+struct ArgCount
+{
+ int min;
+ int max;
+};
+
+// Return a pair of minimum / arg count "foo(p1, p2=0)" -> {1, 2}
+ArgCount argCount(const FunctionArgumentsResult &args)
+{
+ Q_ASSERT(args.objCode);
+ ArgCount result{-1, -1};
+ if ((PepCode_GET_FLAGS(args.objCode) & CO_VARARGS) == 0) {
+ result.min = result.max = PepCode_GET_ARGCOUNT(args.objCode);
+ if (args.function != nullptr) {
+ if (auto *defaultArgs = PepFunction_GetDefaults(args.function))
+ result.min -= PyTuple_Size(defaultArgs);
+ }
+ }
+ return result;
+}
+
+// Find Signal Instance for argument count.
+static PySideSignalInstance *findSignalInstance(PySideSignalInstance *source, int argCount)
+{
+ for (auto *si = source; si != nullptr; si = si->d->next) {
+ if (si->d->argCount == argCount)
+ return si;
+ }
+ return nullptr;
}
static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject *kwds)
@@ -433,6 +484,11 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject
return nullptr;
PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self);
+ if (!source->d)
+ return PyErr_Format(PyExc_RuntimeError, "cannot connect uninitialized SignalInstance");
+ if (source->deleted)
+ return PyErr_Format(PyExc_RuntimeError, "Signal source has been deleted");
+
Shiboken::AutoDecRef pyArgs(PyList_New(0));
bool match = false;
@@ -461,54 +517,32 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject
}
} else {
// Check signature of the slot (method or function) to match signal
- int slotArgs = -1;
- bool matchedSlot = false;
-
- PySideSignalInstance *it = source;
-
- PyObject *function = nullptr;
- PepCodeObject *objCode = nullptr;
- bool useSelf = false;
-
- extractFunctionArgumentsFromSlot(slot, function, objCode, useSelf, nullptr);
-
- if (function != nullptr) {
- slotArgs = PepCode_GET_FLAGS(objCode) & CO_VARARGS ? -1 : PepCode_GET_ARGCOUNT(objCode);
- if (useSelf)
- slotArgs -= 1;
+ const auto args = extractFunctionArgumentsFromSlot(slot);
+ PySideSignalInstance *matchedSlot = nullptr;
+
+ if (args.function != nullptr) {
+ auto slotArgRange = argCount(args);
+ if (args.isMethod) {
+ slotArgRange.min -= 1;
+ slotArgRange.max -= 1;
+ }
// Get signature args
- bool isShortCircuit = false;
- int signatureArgs = 0;
- QStringList argsSignature;
-
- argsSignature = PySide::Signal::getArgsFromSignature(it->d->signature,
- &isShortCircuit);
- signatureArgs = argsSignature.length();
-
// Iterate the possible types of connection for this signal and compare
// it with slot arguments
- if (signatureArgs != slotArgs) {
- while (it->d->next != nullptr) {
- it = it->d->next;
- argsSignature = PySide::Signal::getArgsFromSignature(it->d->signature,
- &isShortCircuit);
- signatureArgs = argsSignature.length();
- if (signatureArgs == slotArgs) {
- matchedSlot = true;
- break;
- }
- }
+ for (int slotArgs = slotArgRange.max;
+ slotArgs >= slotArgRange.min && matchedSlot == nullptr; --slotArgs) {
+ matchedSlot = findSignalInstance(source, slotArgs);
}
}
// Adding references to pyArgs
PyList_Append(pyArgs, source->d->source);
- if (matchedSlot) {
+ if (matchedSlot != nullptr) {
// If a slot matching the same number of arguments was found,
// include signature to the pyArgs
- Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(it->d->signature));
+ Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(matchedSlot->d->signature));
PyList_Append(pyArgs, signature);
} else {
// Try the first by default if the slot was not found
@@ -525,11 +559,9 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject
if (match) {
Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
- PySide::PyName::qtConnect()));
- if (pyMethod.isNull()) { // PYSIDE-79: check if pyMethod exists.
- PyErr_SetString(PyExc_RuntimeError, "method 'connect' vanished!");
- return nullptr;
- }
+ PySide::PySideName::qtConnect()));
+ if (pyMethod.isNull()) // PYSIDE-79: check if pyMethod exists.
+ return PyErr_Format(PyExc_RuntimeError, "method 'connect' vanished!");
PyObject *result = PyObject_CallObject(pyMethod, tupleArgs);
if (connection_Check(result))
return result;
@@ -549,6 +581,13 @@ static int argCountInSignature(const char *signature)
static PyObject *signalInstanceEmit(PyObject *self, PyObject *args)
{
PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self);
+ if (!source->d)
+ return PyErr_Format(PyExc_RuntimeError, "cannot emit uninitialized SignalInstance");
+
+ // PYSIDE-2201: Check if the object has vanished meanwhile.
+ // Tried to revive it without exception, but this gives problems.
+ if (source->deleted)
+ return PyErr_Format(PyExc_RuntimeError, "The SignalInstance object was already deleted");
Shiboken::AutoDecRef pyArgs(PyList_New(0));
int numArgsGiven = PySequence_Fast_GET_SIZE(args);
@@ -579,7 +618,7 @@ static PyObject *signalInstanceEmit(PyObject *self, PyObject *args)
PyList_Append(pyArgs, PyTuple_GetItem(args, i));
Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
- PySide::PyName::qtEmit()));
+ PySide::PySideName::qtEmit()));
Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
return PyObject_CallObject(pyMethod.object(), tupleArgs);
@@ -589,7 +628,7 @@ static PyObject *signalInstanceGetItem(PyObject *self, PyObject *key)
{
auto *firstSignal = reinterpret_cast<PySideSignalInstance *>(self);
const auto &sigName = firstSignal->d->signalName;
- const auto sigKey = PySide::Signal::parseSignature(key);
+ const auto sigKey = PySide::Signal::parseSignature(key).signature;
const auto sig = PySide::Signal::buildSignature(sigName, sigKey);
for (auto *data = firstSignal; data != nullptr; data = data->d->next) {
if (data->d->signature == sig) {
@@ -608,13 +647,26 @@ static PyObject *signalInstanceGetItem(PyObject *self, PyObject *key)
message += '"' + data->d->signature + '"';
}
- PyErr_SetString(PyExc_IndexError, message.constData());
- return nullptr;
+ return PyErr_Format(PyExc_IndexError, message.constData());
+}
+
+static inline void warnDisconnectFailed(PyObject *aSlot, const QByteArray &signature)
+{
+ if (PyErr_Occurred() != nullptr) { // avoid "%S" invoking str() when an error is set.
+ PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%s) from signal \"%s\".",
+ Py_TYPE(aSlot)->tp_name, signature.constData());
+ } else {
+ PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%S) from signal \"%s\".",
+ aSlot, signature.constData());
+ }
}
static PyObject *signalInstanceDisconnect(PyObject *self, PyObject *args)
{
auto source = reinterpret_cast<PySideSignalInstance *>(self);
+ if (!source->d)
+ return PyErr_Format(PyExc_RuntimeError, "cannot disconnect uninitialized SignalInstance");
+
Shiboken::AutoDecRef pyArgs(PyList_New(0));
PyObject *slot = Py_None;
@@ -653,16 +705,15 @@ static PyObject *signalInstanceDisconnect(PyObject *self, PyObject *args)
if (match) {
Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
- PySide::PyName::qtDisconnect()));
+ PySide::PySideName::qtDisconnect()));
PyObject *result = PyObject_CallObject(pyMethod, tupleArgs);
- if (!result || result == Py_True)
- return result;
- Py_DECREF(result);
+ if (result != Py_True)
+ warnDisconnectFailed(slot, source->d->signature);
+ return result;
}
- PyErr_Format(PyExc_RuntimeError, "Failed to disconnect signal %s.",
- source->d->signature.constData());
- return nullptr;
+ warnDisconnectFailed(slot, source->d->signature);
+ Py_RETURN_FALSE;
}
// PYSIDE-68: Supply the missing __get__ function
@@ -670,12 +721,23 @@ static PyObject *signalDescrGet(PyObject *self, PyObject *obj, PyObject * /*type
{
auto signal = reinterpret_cast<PySideSignal *>(self);
// Return the unbound signal if there is nothing to bind it to.
- if (obj == nullptr || obj == Py_None) {
+ if (obj == nullptr || obj == Py_None
+ || !PySide::isQObjectDerived(Py_TYPE(obj), true)) {
Py_INCREF(self);
return self;
}
+
+ // PYSIDE-68-bis: It is important to respect the already cached instance.
Shiboken::AutoDecRef name(Py_BuildValue("s", signal->data->signalName.data()));
- return reinterpret_cast<PyObject *>(PySide::Signal::initialize(signal, name, obj));
+ auto *dict = SbkObject_GetDict_NoRef(obj);
+ auto *inst = PyDict_GetItem(dict, name);
+ if (inst) {
+ Py_INCREF(inst);
+ return inst;
+ }
+ inst = reinterpret_cast<PyObject *>(PySide::Signal::initialize(signal, name, obj));
+ PyObject_SetAttr(obj, name, inst);
+ return inst;
}
static PyObject *signalCall(PyObject *self, PyObject *args, PyObject *kw)
@@ -686,23 +748,19 @@ static PyObject *signalCall(PyObject *self, PyObject *args, PyObject *kw)
// The only way calling a signal can succeed (the Python equivalent of C++'s operator() )
// is when a method with the same name as the signal is attached to an object.
// An example is QProcess::error() (don't check the docs, but the source code of qprocess.h).
- if (!signal->homonymousMethod) {
- PyErr_SetString(PyExc_TypeError, "native Qt signal is not callable");
- return nullptr;
- }
-
- descrgetfunc getDescriptor = Py_TYPE(signal->homonymousMethod)->tp_descr_get;
+ if (!signal->homonymousMethod)
+ return PyErr_Format(PyExc_TypeError, "native Qt signal is not callable");
// Check if there exists a method with the same name as the signal, which is also a static
// method in C++ land.
- Shiboken::AutoDecRef homonymousMethod(getDescriptor(signal->homonymousMethod,
- nullptr, nullptr));
+ Shiboken::AutoDecRef homonymousMethod(PepExt_Type_CallDescrGet(signal->homonymousMethod,
+ nullptr, nullptr));
if (PyCFunction_Check(homonymousMethod.object())
&& (PyCFunction_GET_FLAGS(homonymousMethod.object()) & METH_STATIC))
return PyObject_Call(homonymousMethod, args, kw);
// Assumes homonymousMethod is not a static method.
- ternaryfunc callFunc = Py_TYPE(signal->homonymousMethod)->tp_call;
+ ternaryfunc callFunc = PepExt_Type_GetCallSlot(Py_TYPE(signal->homonymousMethod));
return callFunc(homonymousMethod, args, kw);
}
@@ -736,11 +794,12 @@ static PyObject *_getHomonymousMethod(PySideSignalInstance *inst)
auto signalName = inst->d->signalName;
Shiboken::AutoDecRef name(Shiboken::String::fromCString(signalName));
auto *mro = Py_TYPE(inst->d->source)->tp_mro;
- Py_ssize_t idx, n = PyTuple_GET_SIZE(mro);
+ const Py_ssize_t n = PyTuple_GET_SIZE(mro);
- for (idx = 0; idx < n; idx++) {
+ for (Py_ssize_t idx = 0; idx < n; idx++) {
auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
- auto *hom = PyDict_GetItem(sub_type->tp_dict, name);
+ Shiboken::AutoDecRef tpDict(PepType_GetDict(sub_type));
+ auto *hom = PyDict_GetItem(tpDict, name);
PyObject *realFunc{};
if (hom && PyCallable_Check(hom) && (realFunc = _getRealCallable(hom)))
return realFunc;
@@ -758,8 +817,8 @@ static PyObject *signalInstanceCall(PyObject *self, PyObject *args, PyObject *kw
return nullptr;
}
- descrgetfunc getDescriptor = Py_TYPE(hom)->tp_descr_get;
- Shiboken::AutoDecRef homonymousMethod(getDescriptor(hom, PySideSignal->d->source, nullptr));
+ Shiboken::AutoDecRef homonymousMethod(PepExt_Type_CallDescrGet(hom, PySideSignal->d->source,
+ nullptr));
return PyObject_Call(homonymousMethod, args, kw);
}
@@ -772,15 +831,14 @@ static PyObject *metaSignalCheck(PyObject * /* klass */, PyObject *arg)
} // extern "C"
-namespace PySide {
-namespace Signal {
+namespace PySide::Signal {
static const char *MetaSignal_SignatureStrings[] = {
"PySide6.QtCore.MetaSignal.__instancecheck__(self,object:object)->bool",
nullptr}; // Sentinel
static const char *Signal_SignatureStrings[] = {
- "PySide6.QtCore.Signal(self,*types:type,name:str=nullptr,arguments:str=nullptr)",
+ "PySide6.QtCore.Signal(self,*types:type,name:str=nullptr,arguments:typing.List[str]=nullptr)",
"1:PySide6.QtCore.Signal.__get__(self,instance:None,owner:Optional[typing.Any])->"
"PySide6.QtCore.Signal",
"0:PySide6.QtCore.Signal.__get__(self,instance:PySide6.QtCore.QObject,"
@@ -788,8 +846,10 @@ static const char *Signal_SignatureStrings[] = {
nullptr}; // Sentinel
static const char *SignalInstance_SignatureStrings[] = {
- "PySide6.QtCore.SignalInstance.connect(self,slot:object,type:type=nullptr)",
- "PySide6.QtCore.SignalInstance.disconnect(self,slot:object=nullptr)",
+ "PySide6.QtCore.SignalInstance.connect(self,slot:object,"
+ "type:PySide6.QtCore.Qt.ConnectionType=PySide6.QtCore.Qt.ConnectionType.AutoConnection)"
+ "->PySide6.QtCore.QMetaObject.Connection",
+ "PySide6.QtCore.SignalInstance.disconnect(self,slot:object=nullptr)->bool",
"PySide6.QtCore.SignalInstance.emit(self,*args:typing.Any)",
nullptr}; // Sentinel
@@ -848,8 +908,8 @@ void updateSourceObject(PyObject *source)
Py_ssize_t pos = 0;
PyObject *key, *value;
auto *type = reinterpret_cast<PyTypeObject *>(mroItem.object());
-
- while (PyDict_Next(type->tp_dict, &pos, &key, &value)) {
+ Shiboken::AutoDecRef tpDict(PepType_GetDict(type));
+ while (PyDict_Next(tpDict, &pos, &key, &value)) {
if (PyObject_TypeCheck(value, PySideSignal_TypeF())) {
// PYSIDE-1751: We only insert an instance into the instance dict, if a signal
// of the same name is in the mro. This is the equivalent action
@@ -857,8 +917,9 @@ void updateSourceObject(PyObject *source)
if (!PyDict_GetItem(dict, key)) {
auto *inst = PyObject_New(PySideSignalInstance, PySideSignalInstance_TypeF());
Shiboken::AutoDecRef signalInstance(reinterpret_cast<PyObject *>(inst));
- instanceInitialize(signalInstance.cast<PySideSignalInstance *>(),
- key, reinterpret_cast<PySideSignal *>(value), source, 0);
+ auto *si = reinterpret_cast<PySideSignalInstance *>(signalInstance.object());
+ instanceInitialize(si, key, reinterpret_cast<PySideSignal *>(value),
+ source, 0);
if (PyDict_SetItem(dict, key, signalInstance) == -1)
return; // An error occurred while setting the attribute
}
@@ -889,8 +950,6 @@ QByteArray getTypeName(PyObject *obType)
return QByteArrayLiteral("QVariantList");
if (type == &PyDict_Type)
return QByteArrayLiteral("QVariantMap");
- if (Py_TYPE(type) == SbkEnumType_TypeF())
- return Shiboken::Enum::getCppName(type);
return QByteArrayLiteral("PyObject");
}
if (obType == Py_None) // Must be checked before as Shiboken::String::check accepts Py_None
@@ -909,32 +968,38 @@ static QByteArray buildSignature(const QByteArray &name, const QByteArray &signa
return QMetaObject::normalizedSignature(name + '(' + signature + ')');
}
-static QByteArray parseSignature(PyObject *args)
+static PySideSignalData::Signature parseSignature(PyObject *args)
{
- if (args && (Shiboken::String::check(args) || !PyTuple_Check(args)))
- return getTypeName(args);
+ PySideSignalData::Signature result{{}, QMetaMethod::Compatibility, 0};
+ if (args && (Shiboken::String::check(args) || !PyTuple_Check(args))) {
+ result.signature = getTypeName(args);
+ result.argCount = 1;
+ return result;
+ }
- QByteArray signature;
for (Py_ssize_t i = 0, i_max = PySequence_Size(args); i < i_max; i++) {
Shiboken::AutoDecRef arg(PySequence_GetItem(args, i));
const auto typeName = getTypeName(arg);
if (!typeName.isEmpty()) {
- if (!signature.isEmpty())
- signature += ',';
- signature += typeName;
+ if (!result.signature.isEmpty())
+ result.signature += ',';
+ result.signature += typeName;
+ ++result.argCount;
}
}
- return signature;
+ return result;
}
-static void appendSignature(PySideSignal *self, const SignalSignature &signature)
+static void sourceGone(void *data)
{
- self->data->signatures.append({signature.m_parameterTypes, signature.m_attributes});
+ auto *self = reinterpret_cast<PySideSignalInstance *>(data);
+ self->deleted = true;
}
static void instanceInitialize(PySideSignalInstance *self, PyObject *name, PySideSignal *signal, PyObject *source, int index)
{
self->d = new PySideSignalInstancePrivate;
+ self->deleted = false;
PySideSignalInstancePrivate *selfPvt = self->d;
selfPvt->next = nullptr;
if (signal->data->signalName.isEmpty())
@@ -944,12 +1009,17 @@ static void instanceInitialize(PySideSignalInstance *self, PyObject *name, PySid
selfPvt->source = source;
const auto &signature = signal->data->signatures.at(index);
selfPvt->signature = buildSignature(self->d->signalName, signature.signature);
+ selfPvt->argCount = signature.argCount;
selfPvt->attributes = signature.attributes;
selfPvt->homonymousMethod = nullptr;
if (signal->homonymousMethod) {
selfPvt->homonymousMethod = signal->homonymousMethod;
Py_INCREF(selfPvt->homonymousMethod);
}
+ // PYSIDE-2201: We have no reference to source. Let's take a weakref to get
+ // notified when source gets deleted.
+ PySide::WeakRef::create(source, sourceGone, self);
+
index++;
if (index < signal->data->signatures.size()) {
@@ -981,7 +1051,7 @@ PySideSignalInstance *initialize(PySideSignal *self, PyObject *name, PyObject *o
bool connect(PyObject *source, const char *signal, PyObject *callback)
{
Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source,
- PySide::PyName::qtConnect()));
+ PySide::PySideName::qtConnect()));
if (pyMethod.isNull())
return false;
@@ -1009,14 +1079,15 @@ PySideSignalInstance *newObjectFromMethod(PyObject *source, const QList<QMetaMet
previous->d->next = item;
item->d = new PySideSignalInstancePrivate;
+ item->deleted = false;
PySideSignalInstancePrivate *selfPvt = item->d;
selfPvt->source = source;
- Py_INCREF(selfPvt->source); // PYSIDE-79: an INCREF is missing.
QByteArray cppName(m.methodSignature());
cppName.truncate(cppName.indexOf('('));
- // separe SignalName
+ // separate SignalName
selfPvt->signalName = cppName;
selfPvt->signature = m.methodSignature();
+ selfPvt->argCount = int(m.parameterCount());
selfPvt->attributes = m.attributes();
selfPvt->homonymousMethod = nullptr;
selfPvt->next = nullptr;
@@ -1024,29 +1095,10 @@ PySideSignalInstance *newObjectFromMethod(PyObject *source, const QList<QMetaMet
return root;
}
-template<typename T>
-static typename T::value_type join(T t, const char *sep)
-{
- typename T::value_type res;
- if (t.isEmpty())
- return res;
-
- typename T::const_iterator it = t.begin();
- typename T::const_iterator end = t.end();
- res += *it;
- ++it;
-
- while (it != end) {
- res += sep;
- res += *it;
- ++it;
- }
- return res;
-}
-
static void _addSignalToWrapper(PyTypeObject *wrapperType, const char *signalName, PySideSignal *signal)
{
- auto typeDict = wrapperType->tp_dict;
+ Shiboken::AutoDecRef tpDict(PepType_GetDict(wrapperType));
+ auto typeDict = tpDict.object();
PyObject *homonymousMethod;
if ((homonymousMethod = PyDict_GetItemString(typeDict, signalName))) {
Py_INCREF(homonymousMethod);
@@ -1056,9 +1108,10 @@ static void _addSignalToWrapper(PyTypeObject *wrapperType, const char *signalNam
}
// This function is used by qStableSort to promote empty signatures
-static bool compareSignals(const SignalSignature &sig1, const SignalSignature &)
+static bool compareSignals(const PySideSignalData::Signature &sig1,
+ const PySideSignalData::Signature &sig2)
{
- return sig1.m_parameterTypes.isEmpty();
+ return sig1.signature.isEmpty() && !sig2.signature.isEmpty();
}
static PyObject *buildQtCompatible(const QByteArray &signature)
@@ -1069,39 +1122,46 @@ static PyObject *buildQtCompatible(const QByteArray &signature)
void registerSignals(PyTypeObject *pyObj, const QMetaObject *metaObject)
{
- using SignalSigMap = QHash<QByteArray, QList<SignalSignature> >;
- SignalSigMap signalsFound;
+ using Signature = PySideSignalData::Signature;
+ struct MetaSignal
+ {
+ QByteArray methodName;
+ QList<Signature> signatures;
+ };
+
+ QList<MetaSignal> signalsFound;
for (int i = metaObject->methodOffset(), max = metaObject->methodCount(); i < max; ++i) {
QMetaMethod method = metaObject->method(i);
if (method.methodType() == QMetaMethod::Signal) {
QByteArray methodName(method.methodSignature());
- methodName.chop(methodName.size() - methodName.indexOf('('));
- SignalSignature signature;
- signature.m_parameterTypes = join(method.parameterTypes(), ",");
+ methodName.truncate(methodName.indexOf('('));
+ Signature signature{method.parameterTypes().join(','), {},
+ short(method.parameterCount())};
if (method.attributes() & QMetaMethod::Cloned)
- signature.m_attributes = QMetaMethod::Cloned;
- signalsFound[methodName] << signature;
+ signature.attributes = QMetaMethod::Cloned;
+ auto it = std::find_if(signalsFound.begin(), signalsFound.end(),
+ [methodName](const MetaSignal &ms)
+ { return ms.methodName == methodName; });
+ if (it != signalsFound.end())
+ it->signatures << signature;
+ else
+ signalsFound.append(MetaSignal{methodName, {signature}});
}
}
- SignalSigMap::Iterator it = signalsFound.begin();
- SignalSigMap::Iterator end = signalsFound.end();
- for (; it != end; ++it) {
+ for (const auto &metaSignal : std::as_const(signalsFound)) {
PySideSignal *self = PyObject_New(PySideSignal, PySideSignal_TypeF());
self->data = new PySideSignalData;
- self->data->signalName = it.key();
+ self->data->signalName = metaSignal.methodName;
self->homonymousMethod = nullptr;
// Empty signatures comes first! So they will be the default signal signature
- std::stable_sort(it.value().begin(), it.value().end(), &compareSignals);
- const auto endJ = it.value().cend();
- for (auto j = it.value().cbegin(); j != endJ; ++j) {
- const SignalSignature &sig = *j;
- appendSignature(self, sig);
- }
+ self->data->signatures = metaSignal.signatures;
+ std::stable_sort(self->data->signatures.begin(),
+ self->data->signatures.end(), &compareSignals);
- _addSignalToWrapper(pyObj, it.key(), self);
+ _addSignalToWrapper(pyObj, metaSignal.methodName, self);
Py_DECREF(reinterpret_cast<PyObject *>(self));
}
}
@@ -1116,41 +1176,48 @@ const char *getSignature(PySideSignalInstance *signal)
return signal->d->signature;
}
-QStringList getArgsFromSignature(const char *signature, bool *isShortCircuit)
+EmitterData getEmitterData(PySideSignalInstance *signal)
{
- QString qsignature = QString::fromLatin1(signature).trimmed();
- QStringList result;
+ EmitterData result;
+ result.emitter = PySide::convertToQObject(getObject(signal), false);
+ if (result.emitter != nullptr) {
+ auto *mo = result.emitter->metaObject();
+ result.methodIndex = mo->indexOfMethod(getSignature(signal));
+ }
+ return result;
+}
- if (isShortCircuit)
- *isShortCircuit = !qsignature.contains(u'(');
- if (qsignature.contains(u"()") || qsignature.contains(u"(void)"))
+QByteArrayList getArgsFromSignature(const char *signature)
+{
+ QByteArray qsignature = QByteArray(signature).trimmed();
+ QByteArrayList result;
+
+ if (qsignature.contains("()") || qsignature.contains("(void)"))
return result;
- if (qsignature.endsWith(u')')) {
- const int paren = qsignature.indexOf(u'(');
+ if (qsignature.endsWith(')')) {
+ const auto paren = qsignature.indexOf('(');
if (paren >= 0) {
qsignature.chop(1);
qsignature.remove(0, paren + 1);
result = qsignature.split(u',');
- for (QString &type : result)
+ for (auto &type : result)
type = type.trimmed();
}
}
return result;
}
-QString getCallbackSignature(const char *signal, QObject *receiver, PyObject *callback, bool encodeName)
+QByteArray getCallbackSignature(const char *signal, QObject *receiver,
+ PyObject *callback, bool encodeName)
{
QByteArray functionName;
qsizetype numArgs = -1;
- PyObject *function = nullptr;
- PepCodeObject *objCode = nullptr;
- bool useSelf = false;
-
- extractFunctionArgumentsFromSlot(callback, function, objCode, useSelf, &functionName);
+ const auto slotArgs = extractFunctionArgumentsFromSlot(callback);
+ qsizetype useSelf = slotArgs.isMethod ? 1 : 0;
- if (function != nullptr) {
- numArgs = PepCode_GET_FLAGS(objCode) & CO_VARARGS ? -1 : PepCode_GET_ARGCOUNT(objCode);
+ if (slotArgs.function != nullptr) {
+ numArgs = argCount(slotArgs).max;
#ifdef PYPY_VERSION
} else if (Py_TYPE(callback) == PepBuiltinMethod_TypePtr) {
// PYSIDE-535: PyPy has a special builtin method that acts almost like PyCFunction.
@@ -1176,7 +1243,7 @@ QString getCallbackSignature(const char *signal, QObject *receiver, PyObject *ca
} else if (PyCFunction_Check(callback)) {
const PyCFunctionObject *funcObj = reinterpret_cast<const PyCFunctionObject *>(callback);
functionName = PepCFunction_GET_NAMESTR(funcObj);
- useSelf = PyCFunction_GET_SELF(funcObj);
+ useSelf = PyCFunction_GET_SELF(funcObj) != nullptr ? 1 : 0;
const int flags = PyCFunction_GET_FLAGS(funcObj);
if (receiver) {
@@ -1184,7 +1251,7 @@ QString getCallbackSignature(const char *signal, QObject *receiver, PyObject *ca
const QMetaObject *mo = receiver->metaObject();
QByteArray prefix(functionName);
prefix += '(';
- for (int i = 0; i < mo->methodCount(); i++) {
+ for (int i = 0, count = mo->methodCount(); i < count; ++i) {
QMetaMethod me = mo->method(i);
if ((strncmp(me.methodSignature(), prefix, prefix.size()) == 0) &&
QMetaObject::checkConnectArgs(signal, me.methodSignature())) {
@@ -1201,26 +1268,28 @@ QString getCallbackSignature(const char *signal, QObject *receiver, PyObject *ca
numArgs = 0;
}
} else if (PyCallable_Check(callback)) {
- functionName = "__callback" + QByteArray::number((qlonglong)callback);
+ functionName = "__callback" + QByteArray::number(quintptr(callback));
}
+ if (functionName.isEmpty() && slotArgs.functionName != nullptr)
+ functionName = Shiboken::String::toCString(slotArgs.functionName);
Q_ASSERT(!functionName.isEmpty());
- bool isShortCircuit = false;
-
- const QString functionNameS = QLatin1String(functionName);
- QString signature = encodeName ? codeCallbackName(callback, functionNameS) : functionNameS;
- QStringList args = getArgsFromSignature(signal, &isShortCircuit);
+ if (functionName.startsWith('<') && functionName.endsWith('>')) { // fix "<lambda>"
+ functionName[0] = '_';
+ functionName[functionName.size() - 1] = '_';
+ }
+ QByteArray signature = encodeName ? codeCallbackName(callback, functionName) : functionName;
+ QByteArrayList args = getArgsFromSignature(signal);
- if (!isShortCircuit) {
- signature.append(u'(');
- if (numArgs == -1)
- numArgs = std::numeric_limits<qsizetype>::max();
+ signature.append(u'(');
+ if (numArgs != -1) {
while (!args.isEmpty() && (args.size() > (numArgs - useSelf)))
args.removeLast();
- signature.append(args.join(u','));
- signature.append(u')');
}
+ signature.append(args.join(','));
+ signature.append(')');
+
return signature;
}
@@ -1238,21 +1307,21 @@ bool checkQtSignal(const char *signal)
return true;
}
-QString codeCallbackName(PyObject *callback, const QString &funcName)
+QByteArray codeCallbackName(PyObject *callback, const QByteArray &funcName)
{
if (PyMethod_Check(callback)) {
PyObject *self = PyMethod_GET_SELF(callback);
PyObject *func = PyMethod_GET_FUNCTION(callback);
- return funcName + QString::number(quint64(self), 16) + QString::number(quint64(func), 16);
+ return funcName + QByteArray::number(quint64(self), 16) + QByteArray::number(quint64(func), 16);
}
// PYSIDE-1523: Handle the compiled case.
if (PySide::isCompiledMethod(callback)) {
// Not retaining references inline with what PyMethod_GET_(SELF|FUNC) does.
- Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PyName::im_self()));
- Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PyName::im_func()));
- return funcName + QString::number(quint64(self), 16) + QString::number(quint64(func), 16);
+ Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PySideName::im_self()));
+ Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PySideName::im_func()));
+ return funcName + QByteArray::number(quint64(self), 16) + QByteArray::number(quint64(func), 16);
}
- return funcName + QString::number(quint64(callback), 16);
+ return funcName + QByteArray::number(quint64(callback), 16);
}
QByteArray voidType()
@@ -1260,6 +1329,4 @@ QByteArray voidType()
return QByteArrayLiteral("void");
}
-} //namespace Signal
-} //namespace PySide
-
+} //namespace PySide::Signal
diff --git a/sources/pyside6/libpyside/pysidesignal.h b/sources/pyside6/libpyside/pysidesignal.h
index c38371d81..7493f94b5 100644
--- a/sources/pyside6/libpyside/pysidesignal.h
+++ b/sources/pyside6/libpyside/pysidesignal.h
@@ -30,11 +30,11 @@ extern "C"
{
PyObject_HEAD
PySideSignalInstancePrivate *d;
+ bool deleted;
};
}; // extern "C"
-namespace PySide {
-namespace Signal {
+namespace PySide::Signal {
/**
* This function checks for the PySideSignal type.
@@ -92,6 +92,18 @@ PYSIDE_API PyObject *getObject(PySideSignalInstance *signal);
**/
PYSIDE_API const char *getSignature(PySideSignalInstance *signal);
+struct EmitterData
+{
+ QObject *emitter = nullptr;
+ int methodIndex = -1;
+};
+
+/// A convenience to retrieve the emitter data from a signal instance
+///
+/// @param signal The Signal object
+/// @return Data structure
+PYSIDE_API EmitterData getEmitterData(PySideSignalInstance *signal);
+
/**
* This function is used to retrieve the signal signature
*
@@ -123,20 +135,18 @@ PYSIDE_API bool checkQtSignal(const char *signature);
* @param encodeName Used to specify if the returned signature will be encoded with Qt signal/slot style
* @return Return the callback signature
**/
-PYSIDE_API QString getCallbackSignature(const char *signal, QObject *receiver, PyObject *callback, bool encodeName);
+PYSIDE_API QByteArray getCallbackSignature(const char *signal, QObject *receiver,
+ PyObject *callback, bool encodeName);
/**
* This function parses the signature and then returns a list of argument types.
*
* @param signature The signal signature
- * @param isShortCircuit If this is a shortCircuit(python<->python) signal
* @return Return true if this is a Qt Signal, otherwise return false
* @todo replace return type by QList<QByteArray>
**/
-QStringList getArgsFromSignature(const char *signature,
- bool *isShortCircuit = nullptr);
+QByteArrayList getArgsFromSignature(const char *signature);
-} // namespace Signal
-} // namespace PySide
+} // namespace PySide::Signal
#endif
diff --git a/sources/pyside6/libpyside/pysidesignal_p.h b/sources/pyside6/libpyside/pysidesignal_p.h
index 432f6b5a0..55a9a7a70 100644
--- a/sources/pyside6/libpyside/pysidesignal_p.h
+++ b/sources/pyside6/libpyside/pysidesignal_p.h
@@ -13,13 +13,14 @@ struct PySideSignalData
{
struct Signature
{
- QByteArray signature;
- int attributes;
+ QByteArray signature; // ','-separated list of parameter types
+ unsigned short attributes;
+ short argCount;
};
QByteArray signalName;
QList<Signature> signatures;
- QByteArrayList *signalArguments = nullptr;
+ QByteArrayList signalArguments;
};
extern "C"
@@ -39,20 +40,21 @@ struct PySideSignalInstancePrivate
{
QByteArray signalName;
QByteArray signature;
- int attributes = 0;
PyObject *source = nullptr;
PyObject *homonymousMethod = nullptr;
PySideSignalInstance *next = nullptr;
+ unsigned short attributes = 0;
+ short argCount = 0;
};
-namespace PySide { namespace Signal {
+namespace PySide::Signal {
void init(PyObject *module);
bool connect(PyObject *source, const char *signal, PyObject *callback);
QByteArray getTypeName(PyObject *);
- QString codeCallbackName(PyObject *callback, const QString &funcName);
+ QByteArray codeCallbackName(PyObject *callback, const QByteArray &funcName);
QByteArray voidType();
-}} //namespace PySide
+} // namespace PySide::Signal
#endif
diff --git a/sources/pyside6/libpyside/pysideslot.cpp b/sources/pyside6/libpyside/pysideslot.cpp
index 9fdfe489d..fa7e89f42 100644
--- a/sources/pyside6/libpyside/pysideslot.cpp
+++ b/sources/pyside6/libpyside/pysideslot.cpp
@@ -3,6 +3,7 @@
#include "pysidesignal_p.h"
#include "pysideslot_p.h"
+#include "pysidestaticstrings.h"
#include <shiboken.h>
@@ -17,55 +18,69 @@ struct SlotData
QByteArray name;
QByteArray args;
QByteArray resultType;
+ QByteArray tag; // QMetaMethod::tag()
};
-typedef struct
+struct PySideSlot
{
PyObject_HEAD
SlotData *slotData;
-} PySideSlot;
+};
extern "C"
{
+static void slotDataListDestructor(PyObject *o)
+{
+ delete PySide::Slot::dataListFromCapsule(o);
+}
+
static int slotTpInit(PyObject *, PyObject *, PyObject *);
static PyObject *slotCall(PyObject *, PyObject *, PyObject *);
// Class Definition -----------------------------------------------
-static PyType_Slot PySideSlotType_slots[] = {
- {Py_tp_call, reinterpret_cast<void *>(slotCall)},
- {Py_tp_init, reinterpret_cast<void *>(slotTpInit)},
- {Py_tp_new, reinterpret_cast<void *>(PyType_GenericNew)},
- {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
- {0, nullptr}
-};
-static PyType_Spec PySideSlotType_spec = {
- "2:PySide6.QtCore.Slot",
- sizeof(PySideSlot),
- 0,
- Py_TPFLAGS_DEFAULT,
- PySideSlotType_slots,
-};
+static PyTypeObject *createSlotType()
+{
+ PyType_Slot PySideSlotType_slots[] = {
+ {Py_tp_call, reinterpret_cast<void *>(slotCall)},
+ {Py_tp_init, reinterpret_cast<void *>(slotTpInit)},
+ {Py_tp_new, reinterpret_cast<void *>(PyType_GenericNew)},
+ {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
+ {0, nullptr}
+ };
+
+ PyType_Spec PySideSlotType_spec = {
+ "2:PySide6.QtCore.Slot",
+ sizeof(PySideSlot),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ PySideSlotType_slots,
+ };
+
+ return SbkType_FromSpec(&PySideSlotType_spec);
+}
static PyTypeObject *PySideSlot_TypeF()
{
- static auto *type = SbkType_FromSpec(&PySideSlotType_spec);
+ static auto *type = createSlotType();
return type;
}
int slotTpInit(PyObject *self, PyObject *args, PyObject *kw)
{
static PyObject *emptyTuple = nullptr;
- static const char *kwlist[] = {"name", "result", nullptr};
+ static const char *kwlist[] = {"name", "result", "tag", nullptr};
char *argName = nullptr;
PyObject *argResult = nullptr;
+ char *tag = nullptr;
if (emptyTuple == nullptr)
emptyTuple = PyTuple_New(0);
- if (!PyArg_ParseTupleAndKeywords(emptyTuple, kw, "|sO:QtCore.Slot",
- const_cast<char **>(kwlist), &argName, &argResult)) {
+ if (!PyArg_ParseTupleAndKeywords(emptyTuple, kw, "|sOs:QtCore.Slot",
+ const_cast<char **>(kwlist),
+ &argName, &argResult, &tag)) {
return -1;
}
@@ -87,6 +102,9 @@ int slotTpInit(PyObject *self, PyObject *args, PyObject *kw)
if (argName)
data->slotData->name = argName;
+ if (tag)
+ data->slotData->tag = tag;
+
data->slotData->resultType = argResult
? PySide::Signal::getTypeName(argResult) : PySide::Signal::voidType();
@@ -95,7 +113,6 @@ int slotTpInit(PyObject *self, PyObject *args, PyObject *kw)
PyObject *slotCall(PyObject *self, PyObject *args, PyObject * /* kw */)
{
- static PyObject *pySlotName = nullptr;
PyObject *callback = nullptr;
if (!PyArg_UnpackTuple(args, "Slot.__call__", 1, 1, &callback))
@@ -114,24 +131,20 @@ PyObject *slotCall(PyObject *self, PyObject *args, PyObject * /* kw */)
data->slotData->name = funcName.isNull() ? "<no name>" : String::toCString(funcName);
}
const QByteArray returnType = QMetaObject::normalizedType(data->slotData->resultType);
- const QByteArray signature =
- returnType + ' ' + data->slotData->name + '(' + data->slotData->args + ')';
-
- if (!pySlotName)
- pySlotName = String::fromCString(PYSIDE_SLOT_LIST_ATTR);
+ const QByteArray signature = data->slotData->name + '(' + data->slotData->args + ')';
- PyObject *pySignature = String::fromCString(signature);
- PyObject *signatureList = nullptr;
+ PyObject *pySlotName = PySide::PySideMagicName::slot_list_attr();
+ PySide::Slot::DataList *entryList = nullptr;
if (PyObject_HasAttr(callback, pySlotName)) {
- signatureList = PyObject_GetAttr(callback, pySlotName);
+ auto *capsule = PyObject_GetAttr(callback, pySlotName);
+ entryList = PySide::Slot::dataListFromCapsule(capsule);
} else {
- signatureList = PyList_New(0);
- PyObject_SetAttr(callback, pySlotName, signatureList);
- Py_DECREF(signatureList);
+ entryList = new PySide::Slot::DataList{};
+ auto *capsule = PyCapsule_New(entryList, nullptr /* name */, slotDataListDestructor);
+ Py_INCREF(capsule);
+ PyObject_SetAttr(callback, pySlotName, capsule);
}
-
- PyList_Append(signatureList, pySignature);
- Py_DECREF(pySignature);
+ entryList->append({signature, returnType, data->slotData->tag});
//clear data
delete data->slotData;
@@ -144,8 +157,17 @@ PyObject *slotCall(PyObject *self, PyObject *args, PyObject * /* kw */)
namespace PySide::Slot {
+DataList *dataListFromCapsule(PyObject *capsule)
+{
+ if (capsule != nullptr && PyCapsule_CheckExact(capsule) != 0) {
+ if (void *v = PyCapsule_GetPointer(capsule, nullptr))
+ return reinterpret_cast<DataList *>(v);
+ }
+ return nullptr;
+}
+
static const char *Slot_SignatureStrings[] = {
- "PySide6.QtCore.Slot(self,*types:type,name:str=nullptr,result:str=nullptr)",
+ "PySide6.QtCore.Slot(self,*types:type,name:str=nullptr,result:type=nullptr)",
"PySide6.QtCore.Slot.__call__(self,function:typing.Callable)->typing.Any",
nullptr}; // Sentinel
diff --git a/sources/pyside6/libpyside/pysideslot_p.h b/sources/pyside6/libpyside/pysideslot_p.h
index 695b7935c..9852301ee 100644
--- a/sources/pyside6/libpyside/pysideslot_p.h
+++ b/sources/pyside6/libpyside/pysideslot_p.h
@@ -4,10 +4,25 @@
#define PYSIDE_SLOT_P_H
#include <sbkpython.h>
-#define PYSIDE_SLOT_LIST_ATTR "_slots"
-namespace PySide { namespace Slot {
- void init(PyObject* module);
-}}
+#include <QtCore/qbytearray.h>
+#include <QtCore/qlist.h>
-#endif
+namespace PySide::Slot {
+
+struct Data {
+ QByteArray signature;
+ QByteArray resultType;
+ QByteArray tag; // QMetaMethod::tag()
+};
+
+// This list is set as an attribute named PySide::PySideMagicName::slot_list_attr()
+// by the decorator for usage by MetaObjectBuilder.
+using DataList = QList<Data>;
+
+DataList *dataListFromCapsule(PyObject *capsule);
+
+void init(PyObject* module);
+} // namespace PySide::Slot
+
+#endif // PYSIDE_SLOT_P_H
diff --git a/sources/pyside6/libpyside/pysidestaticstrings.cpp b/sources/pyside6/libpyside/pysidestaticstrings.cpp
index 637e184be..3bddc84c2 100644
--- a/sources/pyside6/libpyside/pysidestaticstrings.cpp
+++ b/sources/pyside6/libpyside/pysidestaticstrings.cpp
@@ -13,7 +13,7 @@ PyObject *funcName() \
namespace PySide
{
-namespace PyName
+namespace PySideName
{
STATIC_STRING_IMPL(qtConnect, "connect")
STATIC_STRING_IMPL(qtDisconnect, "disconnect")
@@ -23,16 +23,18 @@ STATIC_STRING_IMPL(fset, "fset")
STATIC_STRING_IMPL(im_func, "im_func")
STATIC_STRING_IMPL(im_self, "im_self")
STATIC_STRING_IMPL(name, "name")
+STATIC_STRING_IMPL(orig_dict, "orig_dict")
STATIC_STRING_IMPL(parameters, "parameters")
STATIC_STRING_IMPL(property, "property")
STATIC_STRING_IMPL(select_id, "select_id")
} // namespace PyName
-namespace PyMagicName
+namespace PySideMagicName
{
STATIC_STRING_IMPL(code, "__code__")
STATIC_STRING_IMPL(doc, "__doc__")
STATIC_STRING_IMPL(func, "__func__")
STATIC_STRING_IMPL(name, "__name__")
STATIC_STRING_IMPL(property_methods, "__property_methods__")
+STATIC_STRING_IMPL(slot_list_attr, "_slots")
} // namespace PyMagicName
} // namespace PySide
diff --git a/sources/pyside6/libpyside/pysidestaticstrings.h b/sources/pyside6/libpyside/pysidestaticstrings.h
index eea0813a7..b4bc61800 100644
--- a/sources/pyside6/libpyside/pysidestaticstrings.h
+++ b/sources/pyside6/libpyside/pysidestaticstrings.h
@@ -9,7 +9,7 @@
namespace PySide
{
-namespace PyName
+namespace PySideName
{
PYSIDE_API PyObject *qtConnect();
PYSIDE_API PyObject *qtDisconnect();
@@ -19,17 +19,19 @@ PYSIDE_API PyObject *fset();
PYSIDE_API PyObject *im_func();
PYSIDE_API PyObject *im_self();
PYSIDE_API PyObject *name();
+PYSIDE_API PyObject *orig_dict();
PYSIDE_API PyObject *parameters();
PYSIDE_API PyObject *property();
PYSIDE_API PyObject *select_id();
} // namespace PyName
-namespace PyMagicName
+namespace PySideMagicName
{
PYSIDE_API PyObject *code();
PYSIDE_API PyObject *doc();
PYSIDE_API PyObject *func();
PYSIDE_API PyObject *name();
PYSIDE_API PyObject *property_methods();
+PYSIDE_API PyObject *slot_list_attr();
} // namespace PyMagicName
} // namespace PySide
diff --git a/sources/pyside6/libpyside/pysideutils.h b/sources/pyside6/libpyside/pysideutils.h
index 4a4062bfc..47c2f2c1b 100644
--- a/sources/pyside6/libpyside/pysideutils.h
+++ b/sources/pyside6/libpyside/pysideutils.h
@@ -8,8 +8,9 @@
#include <pysidemacros.h>
-#include <QtCore/QtGlobal>
+#include <QtCore/qtclasshelpermacros.h>
+QT_FORWARD_DECLARE_CLASS(QDebug)
QT_FORWARD_DECLARE_CLASS(QString)
QT_FORWARD_DECLARE_CLASS(QStringView)
@@ -36,6 +37,33 @@ PYSIDE_API QString pyPathToQString(PyObject *path);
PYSIDE_API bool isCompiledMethod(PyObject *callback);
+struct debugPyTypeObject
+{
+ PYSIDE_API explicit debugPyTypeObject(const PyTypeObject *o) noexcept;
+
+ const PyTypeObject *m_object;
+};
+
+PYSIDE_API QDebug operator<<(QDebug debug, const debugPyTypeObject &o);
+
+struct debugPyObject
+{
+ PYSIDE_API explicit debugPyObject(PyObject *o) noexcept;
+
+ PyObject *m_object;
+};
+
+PYSIDE_API QDebug operator<<(QDebug debug, const debugPyObject &o);
+
+struct debugPyBuffer
+{
+ PYSIDE_API explicit debugPyBuffer(Py_buffer *b) noexcept;
+
+ Py_buffer *m_buffer;
+};
+
+PYSIDE_API QDebug operator<<(QDebug debug, const debugPyBuffer &b);
+
} //namespace PySide
#endif // PYSIDESTRING_H
diff --git a/sources/pyside6/libpyside/pysideweakref.cpp b/sources/pyside6/libpyside/pysideweakref.cpp
index 79a19fec8..5f3ca59e4 100644
--- a/sources/pyside6/libpyside/pysideweakref.cpp
+++ b/sources/pyside6/libpyside/pysideweakref.cpp
@@ -6,32 +6,37 @@
#include <sbkpython.h>
#include <shiboken.h>
-typedef struct {
+struct PySideCallableObject {
PyObject_HEAD
/* Type-specific fields go here. */
PySideWeakRefFunction weakref_func;
void *user_data;
-} PySideCallableObject;
+};
static PyObject *CallableObject_call(PyObject *callable_object, PyObject *args, PyObject *kw);
-static PyType_Slot PySideCallableObjectType_slots[] = {
- {Py_tp_call, reinterpret_cast<void *>(CallableObject_call)},
- {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
- {0, nullptr}
-};
-static PyType_Spec PySideCallableObjectType_spec = {
- "1:PySide.Callable",
- sizeof(PySideCallableObject),
- 0,
- Py_TPFLAGS_DEFAULT,
- PySideCallableObjectType_slots,
-};
-
+static PyTypeObject *createCallableObjectType()
+{
+ PyType_Slot PySideCallableObjectType_slots[] = {
+ {Py_tp_call, reinterpret_cast<void *>(CallableObject_call)},
+ {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
+ {0, nullptr}
+ };
+
+ PyType_Spec PySideCallableObjectType_spec = {
+ "1:PySide.Callable",
+ sizeof(PySideCallableObject),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ PySideCallableObjectType_slots,
+ };
+
+ return SbkType_FromSpec(&PySideCallableObjectType_spec);
+}
static PyTypeObject *PySideCallableObject_TypeF()
{
- static auto *type = SbkType_FromSpec(&PySideCallableObjectType_spec);
+ static auto *type = createCallableObjectType();
return type;
}
@@ -44,7 +49,7 @@ static PyObject *CallableObject_call(PyObject *callable_object, PyObject *args,
Py_RETURN_NONE;
}
-namespace PySide { namespace WeakRef {
+namespace PySide::WeakRef {
PyObject *create(PyObject *obj, PySideWeakRefFunction func, void *userData)
{
@@ -74,5 +79,4 @@ PyObject *create(PyObject *obj, PySideWeakRefFunction func, void *userData)
return reinterpret_cast<PyObject *>(weak);
}
-} } //namespace
-
+} // namespace PySide::WeakRef
diff --git a/sources/pyside6/libpyside/pysideweakref.h b/sources/pyside6/libpyside/pysideweakref.h
index 71386903f..e29c73455 100644
--- a/sources/pyside6/libpyside/pysideweakref.h
+++ b/sources/pyside6/libpyside/pysideweakref.h
@@ -7,14 +7,12 @@
#include <pysidemacros.h>
#include <sbkpython.h>
-typedef void (*PySideWeakRefFunction)(void* userData);
+using PySideWeakRefFunction = void (*)(void *userData);
-namespace PySide { namespace WeakRef {
+namespace PySide::WeakRef {
PYSIDE_API PyObject* create(PyObject* ob, PySideWeakRefFunction func, void* userData);
-} //PySide
-} //WeakRef
-
+} // namespace PySide::WeakRef
#endif
diff --git a/sources/pyside6/libpyside/qobjectconnect.cpp b/sources/pyside6/libpyside/qobjectconnect.cpp
index 2d97a68d1..3c5b75953 100644
--- a/sources/pyside6/libpyside/qobjectconnect.cpp
+++ b/sources/pyside6/libpyside/qobjectconnect.cpp
@@ -3,6 +3,7 @@
#include "qobjectconnect.h"
#include "pysideqobject.h"
+#include "pysideqslotobject_p.h"
#include "pysidesignal.h"
#include "pysideutils.h"
#include "signalmanager.h"
@@ -15,6 +16,10 @@
#include <QtCore/QMetaMethod>
#include <QtCore/QObject>
+#include <QtCore/private/qobject_p.h>
+
+#include <string_view>
+
static bool isMethodDecorator(PyObject *method, bool is_pymethod, PyObject *self)
{
Shiboken::AutoDecRef methodName(PyObject_GetAttr(method, Shiboken::PyMagicName::name()));
@@ -29,6 +34,8 @@ static bool isMethodDecorator(PyObject *method, bool is_pymethod, PyObject *self
function1 = PyMethod_GET_FUNCTION(otherMethod.object());
} else {
function1 = PyObject_GetAttr(otherMethod.object(), Shiboken::PyName::im_func());
+ if (function1 == nullptr)
+ return false;
Py_DECREF(function1);
// Not retaining a reference in line with what PyMethod_GET_FUNCTION does.
}
@@ -61,12 +68,31 @@ QDebug operator<<(QDebug d, const GetReceiverResult &r)
d.noquote();
d.nospace();
d << "GetReceiverResult(receiver=" << r.receiver << ", self=" << r.self
- << ", sig=" << r.callbackSig << "slotIndex=" << r.slotIndex
+ << ", sig=\"" << r.callbackSig << "\", slotIndex=" << r.slotIndex
<< ", usingGlobalReceiver=" << r.usingGlobalReceiver << ')';
return d;
}
#endif // QT_NO_DEBUG_STREAM
+static const char *getQualifiedName(PyObject *ob)
+{
+ Shiboken::AutoDecRef qualNameP(PyObject_GetAttr(ob, Shiboken::PyMagicName::qualname()));
+ return qualNameP.isNull()
+ ? nullptr : Shiboken::String::toCString(qualNameP.object());
+}
+
+// Determine whether a method is declared in a class using qualified name lookup.
+static bool isDeclaredIn(PyObject *method, const char *className)
+{
+ bool result = false;
+ if (auto *qualifiedNameC = getQualifiedName(PyMethod_Function(method))) {
+ std::string_view qualifiedName(qualifiedNameC);
+ if (const auto dot = qualifiedName.rfind('.'); dot != std::string::npos)
+ result = qualifiedName.substr(0, dot) == className;
+ }
+ return result;
+}
+
static GetReceiverResult getReceiver(QObject *source, const char *signal,
PyObject *callback)
{
@@ -99,31 +125,40 @@ static GetReceiverResult getReceiver(QObject *source, const char *signal,
result.usingGlobalReceiver = !result.receiver || forceGlobalReceiver;
- // Check if this callback is a overwrite of a non-virtual Qt slot.
+ // Check if this callback is a overwrite of a non-virtual Qt slot (pre-Jira bug 1019).
+ // Make it possible to connect to a MyWidget.show() although QWidget.show()
+ // is a non-virtual slot which would be found by QMetaObject search.
+ // FIXME PYSIDE7: This is arguably a bit of a misguided "feature", remove?
if (!result.usingGlobalReceiver && result.receiver && result.self) {
result.callbackSig =
PySide::Signal::getCallbackSignature(signal, result.receiver, callback,
- result.usingGlobalReceiver).toLatin1();
+ result.usingGlobalReceiver);
const QMetaObject *metaObject = result.receiver->metaObject();
result.slotIndex = metaObject->indexOfSlot(result.callbackSig.constData());
- if (result.slotIndex != -1 && result.slotIndex < metaObject->methodOffset()
- && PyMethod_Check(callback)) {
- result.usingGlobalReceiver = true;
- }
+ if (PyMethod_Check(callback) != 0 && result.slotIndex != -1
+ && result.slotIndex < metaObject->methodOffset()) {
+ // Find the class in which the slot is declared.
+ while (result.slotIndex < metaObject->methodOffset())
+ metaObject = metaObject->superClass();
+ // If the Python callback is not declared in the same class, assume it is
+ // a Python override. Resort to global receiver (PYSIDE-2418).
+ if (!isDeclaredIn(callback, metaObject->className()))
+ result.usingGlobalReceiver = true;
+ }
}
const auto receiverThread = result.receiver ? result.receiver->thread() : nullptr;
if (result.usingGlobalReceiver) {
PySide::SignalManager &signalManager = PySide::SignalManager::instance();
- result.receiver = signalManager.globalReceiver(source, callback);
+ result.receiver = signalManager.globalReceiver(source, callback, result.receiver);
// PYSIDE-1354: Move the global receiver to the original receivers's thread
// so that autoconnections work correctly.
if (receiverThread && receiverThread != result.receiver->thread())
result.receiver->moveToThread(receiverThread);
result.callbackSig =
PySide::Signal::getCallbackSignature(signal, result.receiver, callback,
- result.usingGlobalReceiver).toLatin1();
+ result.usingGlobalReceiver);
const QMetaObject *metaObject = result.receiver->metaObject();
result.slotIndex = metaObject->indexOfSlot(result.callbackSig.constData());
}
@@ -208,7 +243,50 @@ QMetaObject::Connection qobjectConnectCallback(QObject *source, const char *sign
}
}
- auto connection = QMetaObject::connect(source, signalIndex, receiver.receiver, slotIndex, type);
+ QMetaObject::Connection connection{};
+ Py_BEGIN_ALLOW_THREADS // PYSIDE-2367, prevent threading deadlocks with connectNotify()
+ connection = QMetaObject::connect(source, signalIndex, receiver.receiver, slotIndex, type);
+ Py_END_ALLOW_THREADS
+ if (!connection) {
+ if (receiver.usingGlobalReceiver)
+ signalManager.releaseGlobalReceiver(source, receiver.receiver);
+ return {};
+ }
+
+ Q_ASSERT(receiver.receiver);
+ if (receiver.usingGlobalReceiver)
+ signalManager.notifyGlobalReceiver(receiver.receiver);
+
+ const QMetaMethod signalMethod = receiver.receiver->metaObject()->method(signalIndex);
+ static_cast<FriendlyQObject *>(source)->connectNotify(signalMethod);
+ return connection;
+}
+
+QMetaObject::Connection qobjectConnectCallback(QObject *source, const char *signal, QObject *context,
+ PyObject *callback, Qt::ConnectionType type)
+{
+ if (!signal || !PySide::Signal::checkQtSignal(signal))
+ return {};
+
+ const int signalIndex =
+ PySide::SignalManager::registerMetaMethodGetIndex(source, signal + 1,
+ QMetaMethod::Signal);
+ if (signalIndex == -1)
+ return {};
+
+ // Extract receiver from callback
+ const GetReceiverResult receiver = getReceiver(source, signal + 1, callback);
+ if (receiver.receiver == nullptr && receiver.self == nullptr)
+ return {};
+
+ PySide::SignalManager &signalManager = PySide::SignalManager::instance();
+
+ PySideQSlotObject *slotObject = new PySideQSlotObject(callback);
+
+ QMetaObject::Connection connection{};
+ Py_BEGIN_ALLOW_THREADS // PYSIDE-2367, prevent threading deadlocks with connectNotify()
+ connection = QObjectPrivate::connect(source, signalIndex, context, slotObject, type);
+ Py_END_ALLOW_THREADS
if (!connection) {
if (receiver.usingGlobalReceiver)
signalManager.releaseGlobalReceiver(source, receiver.receiver);
@@ -237,7 +315,11 @@ bool qobjectDisconnectCallback(QObject *source, const char *signal, PyObject *ca
const int signalIndex = source->metaObject()->indexOfSignal(signal + 1);
const int slotIndex = receiver.slotIndex;
- if (!QMetaObject::disconnectOne(source, signalIndex, receiver.receiver, slotIndex))
+ bool ok{};
+ Py_BEGIN_ALLOW_THREADS // PYSIDE-2367, prevent threading deadlocks with disconnectNotify()
+ ok = QMetaObject::disconnectOne(source, signalIndex, receiver.receiver, slotIndex);
+ Py_END_ALLOW_THREADS
+ if (!ok)
return false;
Q_ASSERT(receiver.receiver);
diff --git a/sources/pyside6/libpyside/qobjectconnect.h b/sources/pyside6/libpyside/qobjectconnect.h
index 70b862233..c99b8006e 100644
--- a/sources/pyside6/libpyside/qobjectconnect.h
+++ b/sources/pyside6/libpyside/qobjectconnect.h
@@ -33,6 +33,11 @@ PYSIDE_API QMetaObject::Connection
qobjectConnectCallback(QObject *source, const char *signal,
PyObject *callback, Qt::ConnectionType type);
+/// Helpers for QObject::connect(): Make a connection to a Python callback and a context object
+PYSIDE_API QMetaObject::Connection
+ qobjectConnectCallback(QObject *source, const char *signal, QObject *context,
+ PyObject *callback, Qt::ConnectionType type);
+
/// Helpers for QObject::disconnect(): Disconnect a Python callback
PYSIDE_API bool qobjectDisconnectCallback(QObject *source, const char *signal,
PyObject *callback);
diff --git a/sources/pyside6/libpyside/signalmanager.cpp b/sources/pyside6/libpyside/signalmanager.cpp
index 9a9d1c19b..f4c2bbf43 100644
--- a/sources/pyside6/libpyside/signalmanager.cpp
+++ b/sources/pyside6/libpyside/signalmanager.cpp
@@ -19,16 +19,21 @@
#include <sbkconverter.h>
#include <sbkstring.h>
#include <sbkstaticstrings.h>
+#include <sbkerrors.h>
+#include <QtCore/QCoreApplication>
#include <QtCore/QByteArrayView>
#include <QtCore/QDebug>
#include <QtCore/QHash>
#include <QtCore/QScopedPointer>
+#include <QtCore/QTimerEvent>
#include <algorithm>
#include <limits>
#include <memory>
+using namespace Qt::StringLiterals;
+
#if QSLOT_CODE != 1 || QSIGNAL_CODE != 2
#error QSLOT_CODE and/or QSIGNAL_CODE changed! change the hardcoded stuff to the correct value!
#endif
@@ -36,21 +41,19 @@
#define PYSIDE_SIGNAL '2'
#include "globalreceiverv2.h"
-namespace {
- static PyObject *metaObjectAttr = nullptr;
+static PyObject *metaObjectAttr = nullptr;
+static PyObject *parseArguments(const QMetaMethod &method, void **args);
- static PyObject *parseArguments(const QList< QByteArray >& paramTypes, void **args);
- static bool emitShortCircuitSignal(QObject *source, int signalIndex, PyObject *args);
+static bool qAppRunning = false;
- static void destroyMetaObject(PyObject *obj)
- {
- void *ptr = PyCapsule_GetPointer(obj, nullptr);
- auto meta = reinterpret_cast<PySide::MetaObjectBuilder *>(ptr);
- SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(meta);
- if (wrapper)
- Shiboken::BindingManager::instance().releaseWrapper(wrapper);
- delete meta;
- }
+static void destroyMetaObject(PyObject *obj)
+{
+ void *ptr = PyCapsule_GetPointer(obj, nullptr);
+ auto meta = reinterpret_cast<PySide::MetaObjectBuilder *>(ptr);
+ SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(meta);
+ if (wrapper)
+ Shiboken::BindingManager::instance().releaseWrapper(wrapper);
+ delete meta;
}
static const char *metaCallName(QMetaObject::Call call)
@@ -71,6 +74,29 @@ static const char *metaCallName(QMetaObject::Call call)
return it != mapping.constEnd() ? it.value() : "<Unknown>";
}
+static QByteArray methodSignature(const QMetaMethod &method)
+{
+ QByteArray result;
+ if (auto *t = method.typeName()) {
+ result += t;
+ result += ' ';
+ }
+ result += method.methodSignature();
+ return result;
+}
+
+static QByteArray msgCannotConvertParameter(const QMetaMethod &method, qsizetype p)
+{
+ return "Cannot call meta function \""_ba + methodSignature(method)
+ + "\" because parameter " + QByteArray::number(p) + " of type \""_ba
+ + method.parameterTypeName(p) + "\" cannot be converted."_ba;
+}
+
+static QByteArray msgCannotConvertReturn(const QMetaMethod &method)
+{
+ return "The return value of \""_ba + methodSignature(method) + "\" cannot be converted."_ba;
+}
+
namespace PySide {
PyObjectWrapper::PyObjectWrapper()
@@ -128,6 +154,14 @@ PyObjectWrapper::operator PyObject *() const
return m_me;
}
+
+int PyObjectWrapper::toInt() const
+{
+ // hold the GIL
+ Shiboken::GilState state;
+ return Shiboken::Enum::check(m_me) ? Shiboken::Enum::getValue(m_me) : -1;
+}
+
QDataStream &operator<<(QDataStream &out, const PyObjectWrapper &myObj)
{
if (Py_IsInitialized() == 0) {
@@ -187,33 +221,66 @@ QDataStream &operator>>(QDataStream &in, PyObjectWrapper &myObj)
};
+namespace PySide {
+using GlobalReceiverV2Ptr = std::shared_ptr<GlobalReceiverV2>;
+using GlobalReceiverV2Map = QHash<PySide::GlobalReceiverKey, GlobalReceiverV2Ptr>;
+}
+
using namespace PySide;
-struct SignalManager::SignalManagerPrivate
+// Listen for destroy() of main thread objects and ensure cleanup
+class SignalManagerDestroyListener : public QObject
{
- GlobalReceiverV2MapPtr m_globalReceivers;
- static SignalManager::QmlMetaCallErrorHandler m_qmlMetaCallErrorHandler;
+ Q_OBJECT
+public:
+ Q_DISABLE_COPY_MOVE(SignalManagerDestroyListener)
- SignalManagerPrivate() : m_globalReceivers(new GlobalReceiverV2Map{})
- {
- }
+ using QObject::QObject;
- ~SignalManagerPrivate()
- {
- if (!m_globalReceivers.isNull()) {
- // Delete receivers by always retrieving the current first element, because deleting a
- // receiver can indirectly delete another one, and if we use qDeleteAll, that could
- // cause either a double delete, or iterator invalidation, and thus undefined behavior.
- while (!m_globalReceivers->isEmpty())
- delete *m_globalReceivers->cbegin();
- Q_ASSERT(m_globalReceivers->isEmpty());
- }
+public Q_SLOTS:
+ void destroyNotify(const QObject *);
+
+protected:
+ void timerEvent(QTimerEvent *event) override;
+
+private:
+ int m_timerId = -1;
+};
+
+void SignalManagerDestroyListener::destroyNotify(const QObject *)
+{
+ if (qAppRunning && m_timerId == -1)
+ m_timerId = startTimer(0);
+}
+
+void SignalManagerDestroyListener::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == m_timerId) {
+ killTimer(std::exchange(m_timerId, -1));
+ SignalManager::instance().purgeEmptyGlobalReceivers();
}
+}
+
+struct SignalManager::SignalManagerPrivate
+{
+ Q_DISABLE_COPY_MOVE(SignalManagerPrivate)
+
+ SignalManagerPrivate() noexcept = default;
+ ~SignalManagerPrivate() { clear(); }
+
+ void deleteGlobalReceiver(const QObject *gr);
+ void clear();
+ void purgeEmptyGlobalReceivers();
+
+ GlobalReceiverV2Map m_globalReceivers;
+ static SignalManager::QmlMetaCallErrorHandler m_qmlMetaCallErrorHandler;
static void handleMetaCallError(QObject *object, int *result);
static int qtPropertyMetacall(QObject *object, QMetaObject::Call call,
int id, void **args);
static int qtMethodMetacall(QObject *object, int id, void **args);
+
+ QPointer<SignalManagerDestroyListener> m_listener;
};
SignalManager::QmlMetaCallErrorHandler
@@ -247,6 +314,8 @@ SignalManager::SignalManager() : m_d(new SignalManagerPrivate)
// Register PyObject type to use in queued signal and slot connections
qRegisterMetaType<PyObjectWrapper>("PyObject");
+ // Register QVariant(enum) conversion to QVariant(int)
+ QMetaType::registerConverter<PyObjectWrapper, int>(&PyObjectWrapper::toInt);
SbkConverter *converter = Shiboken::Conversions::createConverter(&PyBaseObject_Type, nullptr);
Shiboken::Conversions::setCppPointerToPythonFunction(converter, PyObject_PTR_CppToPython_PyObject);
@@ -264,8 +333,7 @@ SignalManager::SignalManager() : m_d(new SignalManagerPrivate)
void SignalManager::clear()
{
- delete m_d;
- m_d = new SignalManagerPrivate();
+ m_d->clear();
}
SignalManager::~SignalManager()
@@ -284,52 +352,118 @@ void SignalManager::setQmlMetaCallErrorHandler(QmlMetaCallErrorHandler handler)
SignalManagerPrivate::m_qmlMetaCallErrorHandler = handler;
}
-QObject *SignalManager::globalReceiver(QObject *sender, PyObject *callback)
+static void qAppAboutToQuit()
{
- GlobalReceiverV2MapPtr globalReceivers = m_d->m_globalReceivers;
- GlobalReceiverKey key = GlobalReceiverV2::key(callback);
- GlobalReceiverV2 *gr = nullptr;
- auto it = globalReceivers->find(key);
- if (it == globalReceivers->end()) {
- gr = new GlobalReceiverV2(callback, globalReceivers);
- globalReceivers->insert(key, gr);
- if (sender) {
- gr->incRef(sender); // create a link reference
- gr->decRef(); // remove extra reference
+ qAppRunning = false;
+ SignalManager::instance().purgeEmptyGlobalReceivers();
+}
+
+static bool isInMainThread(const QObject *o)
+{
+ if (o->isWidgetType() || o->isWindowType() || o->isQuickItemType())
+ return true;
+ auto *app = QCoreApplication::instance();
+ return app != nullptr && app->thread() == o->thread();
+}
+
+QObject *SignalManager::globalReceiver(QObject *sender, PyObject *callback, QObject *receiver)
+{
+ if (m_d->m_listener.isNull() && !QCoreApplication::closingDown()) {
+ if (auto *app = QCoreApplication::instance()) {
+ // The signal manager potentially outlives QCoreApplication, ensure deletion
+ m_d->m_listener = new SignalManagerDestroyListener(app);
+ m_d->m_listener->setObjectName("qt_pyside_signalmanagerdestroylistener");
+ QObject::connect(app, &QCoreApplication::aboutToQuit, qAppAboutToQuit);
+ qAppRunning = true;
+ }
+ }
+
+ auto &globalReceivers = m_d->m_globalReceivers;
+ const GlobalReceiverKey key = GlobalReceiverV2::key(callback);
+ auto it = globalReceivers.find(key);
+ if (it == globalReceivers.end()) {
+ auto gr = std::make_shared<GlobalReceiverV2>(callback, receiver);
+ it = globalReceivers.insert(key, gr);
+ }
+
+ if (sender != nullptr) {
+ it.value()->incRef(sender); // create a link reference
+
+ // For main thread-objects, add a notification for destroy (PYSIDE-2646, 2141)
+ if (qAppRunning && !m_d->m_listener.isNull() && isInMainThread(sender)) {
+ QObject::connect(sender, &QObject::destroyed,
+ m_d->m_listener, &SignalManagerDestroyListener::destroyNotify,
+ Qt::UniqueConnection);
}
- } else {
- gr = it.value();
- if (sender)
- gr->incRef(sender);
}
- return reinterpret_cast<QObject *>(gr);
+ return it.value().get();
}
-int SignalManager::countConnectionsWith(const QObject *object)
+void SignalManager::purgeEmptyGlobalReceivers()
{
- int count = 0;
- for (GlobalReceiverV2Map::const_iterator it = m_d->m_globalReceivers->cbegin(), end = m_d->m_globalReceivers->cend(); it != end; ++it) {
- if (it.value()->refCount(object))
- count++;
- }
- return count;
+ m_d->purgeEmptyGlobalReceivers();
}
void SignalManager::notifyGlobalReceiver(QObject *receiver)
{
reinterpret_cast<GlobalReceiverV2 *>(receiver)->notify();
+ purgeEmptyGlobalReceivers();
}
void SignalManager::releaseGlobalReceiver(const QObject *source, QObject *receiver)
{
- auto gr = reinterpret_cast<GlobalReceiverV2 *>(receiver);
+ auto gr = static_cast<GlobalReceiverV2 *>(receiver);
gr->decRef(source);
+ if (gr->isEmpty())
+ m_d->deleteGlobalReceiver(gr);
+}
+
+void SignalManager::deleteGlobalReceiver(const QObject *gr)
+{
+ SignalManager::instance().m_d->deleteGlobalReceiver(gr);
+}
+
+void SignalManager::SignalManagerPrivate::deleteGlobalReceiver(const QObject *gr)
+{
+ for (auto it = m_globalReceivers.begin(), end = m_globalReceivers.end(); it != end; ++it) {
+ if (it.value().get() == gr) {
+ m_globalReceivers.erase(it);
+ break;
+ }
+ }
+}
+
+void SignalManager::SignalManagerPrivate::clear()
+{
+ // Delete receivers by always retrieving the current first element,
+ // because deleting a receiver can indirectly delete another one
+ // via ~DynamicSlotDataV2(). Using ~QHash/clear() could cause an
+ // iterator invalidation, and thus undefined behavior.
+ while (!m_globalReceivers.isEmpty())
+ m_globalReceivers.erase(m_globalReceivers.cbegin());
+}
+
+static bool isEmptyGlobalReceiver(const GlobalReceiverV2Ptr &g)
+{
+ return g->isEmpty();
+}
+
+void SignalManager::SignalManagerPrivate::purgeEmptyGlobalReceivers()
+{
+ // Delete repetitively (see comment in clear()).
+ while (true) {
+ auto it = std::find_if(m_globalReceivers.cbegin(), m_globalReceivers.cend(),
+ isEmptyGlobalReceiver);
+ if (it == m_globalReceivers.cend())
+ break;
+ m_globalReceivers.erase(it);
+ }
}
int SignalManager::globalReceiverSlotIndex(QObject *receiver, const char *signature) const
{
- return reinterpret_cast<GlobalReceiverV2 *>(receiver)->addSlot(signature);
+ return static_cast<GlobalReceiverV2 *>(receiver)->addSlot(signature);
}
bool SignalManager::emitSignal(QObject *source, const char *signal, PyObject *args)
@@ -339,16 +473,7 @@ bool SignalManager::emitSignal(QObject *source, const char *signal, PyObject *ar
signal++;
int signalIndex = source->metaObject()->indexOfSignal(signal);
- if (signalIndex != -1) {
- // cryptic but works!
- // if the signature doesn't have a '(' it's a shor circuited signal, i.e. std::find
- // returned the string null terminator.
- bool isShortCircuit = !*std::find(signal, signal + std::strlen(signal), '(');
- return isShortCircuit
- ? emitShortCircuitSignal(source, signalIndex, args)
- : MetaFunction::call(source, signalIndex, args);
- }
- return false;
+ return signalIndex != -1 && MetaFunction::call(source, signalIndex, args);
}
// Handle errors from meta calls. Requires GIL and PyErr_Occurred()
@@ -392,7 +517,6 @@ int SignalManager::SignalManagerPrivate::qtPropertyMetacall(QObject *object,
auto *pySbkSelf = Shiboken::BindingManager::instance().retrieveWrapper(object);
Q_ASSERT(pySbkSelf);
auto *pySelf = reinterpret_cast<PyObject *>(pySbkSelf);
- Q_ASSERT(pySelf);
Shiboken::AutoDecRef pp_name(Shiboken::String::fromCString(mp.name()));
PySideProperty *pp = Property::getObject(pySelf, pp_name);
if (!pp) {
@@ -400,9 +524,25 @@ int SignalManager::SignalManagerPrivate::qtPropertyMetacall(QObject *object,
return false;
}
pp->d->metaCall(pySelf, call, args);
- Py_XDECREF(pp);
-
+ Py_DECREF(pp);
if (PyErr_Occurred()) {
+ // PYSIDE-2160: An unknown type was reported. Indicated by StopIteration.
+ if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
+ PyObject *excType, *excValue, *excTraceback;
+ PyErr_Fetch(&excType, &excValue, &excTraceback);
+ bool ign = call == QMetaObject::WriteProperty;
+ PyErr_WarnFormat(PyExc_RuntimeWarning, 0,
+ ign ? "Unknown property type '%s' of QObject '%s' used in fset"
+ : "Unknown property type '%s' of QObject '%s' used in fget with %R",
+ pp->d->typeName.constData(), metaObject->className(), excValue);
+ if (PyErr_Occurred())
+ Shiboken::Errors::storeErrorOrPrint();
+ Py_DECREF(excType);
+ Py_DECREF(excValue);
+ Py_XDECREF(excTraceback);
+ return result;
+ }
+
qWarning().noquote().nospace()
<< "An error occurred executing the property metacall " << call
<< " on property \"" << mp.name() << "\" of " << object;
@@ -439,7 +579,7 @@ int SignalManager::SignalManagerPrivate::qtMethodMetacall(QObject *object,
PyErr_Format(PyExc_AttributeError, "Slot '%s::%s' not found.",
metaObject->className(), method.methodSignature().constData());
} else {
- SignalManager::callPythonMetaMethod(method, args, pyMethod, false);
+ SignalManager::callPythonMetaMethod(method, args, pyMethod);
}
}
// WARNING Isn't safe to call any metaObject and/or object methods beyond this point
@@ -477,43 +617,38 @@ int SignalManager::qt_metacall(QObject *object, QMetaObject::Call call, int id,
<< metaCallName(call) << " #" << id << ' ' << object;
id -= object->metaObject()->methodCount();
break;
+#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
+ case QMetaObject::ConstructInPlace:
+ break;
+#endif
}
return id;
}
-int SignalManager::callPythonMetaMethod(const QMetaMethod &method, void **args, PyObject *pyMethod, bool isShortCuit)
+int SignalManager::callPythonMetaMethod(const QMetaMethod &method, void **args, PyObject *pyMethod)
{
Q_ASSERT(pyMethod);
Shiboken::GilState gil;
- PyObject *pyArguments = nullptr;
-
- if (isShortCuit){
- pyArguments = reinterpret_cast<PyObject *>(args[1]);
- } else {
- pyArguments = parseArguments(method.parameterTypes(), args);
- }
+ PyObject *pyArguments = parseArguments(method, args);
if (pyArguments) {
QScopedPointer<Shiboken::Conversions::SpecificConverter> retConverter;
const char *returnType = method.typeName();
- if (returnType && std::strcmp("", returnType) && std::strcmp("void", returnType)) {
+ if (returnType != nullptr && returnType[0] != 0 && std::strcmp("void", returnType) != 0) {
retConverter.reset(new Shiboken::Conversions::SpecificConverter(returnType));
if (!retConverter->isValid()) {
- PyErr_Format(PyExc_RuntimeError, "Can't find converter for '%s' to call Python meta method.", returnType);
+ PyErr_SetString(PyExc_RuntimeError, msgCannotConvertReturn(method).constData());
return -1;
}
}
Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, pyArguments));
- if (!isShortCuit && pyArguments){
- Py_DECREF(pyArguments);
- }
+ Py_DECREF(pyArguments);
- if (!retval.isNull() && retval != Py_None && !PyErr_Occurred() && retConverter) {
+ if (!retval.isNull() && retval != Py_None && !PyErr_Occurred() && retConverter)
retConverter->toCpp(retval, args[0]);
- }
}
return -1;
@@ -607,7 +742,9 @@ int SignalManager::registerMetaMethodGetIndex(QObject *source, const char *signa
if (methodIndex == -1) {
SbkObject *self = Shiboken::BindingManager::instance().retrieveWrapper(source);
if (!Shiboken::Object::hasCppWrapper(self)) {
- qWarning() << "Invalid Signal signature:" << signature;
+ qWarning().noquote().nospace() << __FUNCTION__
+ << ": Cannot add dynamic method \"" << signature << "\" (" << type
+ << ") to " << source << ": No Wrapper found.";
return -1;
}
auto *pySelf = reinterpret_cast<PyObject *>(self);
@@ -625,8 +762,8 @@ int SignalManager::registerMetaMethodGetIndex(QObject *source, const char *signa
if (type == QMetaMethod::Slot) {
qCWarning(lcPySide).noquote().nospace()
<< "Warning: Registering dynamic slot \""
- << signature << "\" on " << source->metaObject()->className()
- << ". Consider annotating with " << slotSignature(signature);
+ << signature << "\" on \"" << source->metaObject()->className()
+ << "\". Consider annotating with " << slotSignature(signature);
}
return type == QMetaMethod::Signal
@@ -655,33 +792,24 @@ const QMetaObject *SignalManager::retrieveMetaObject(PyObject *self)
return builder->update();
}
-namespace {
-
-static PyObject *parseArguments(const QList<QByteArray>& paramTypes, void **args)
+static PyObject *parseArguments(const QMetaMethod &method, void **args)
{
+ const auto &paramTypes = method.parameterTypes();
const qsizetype argsSize = paramTypes.size();
PyObject *preparedArgs = PyTuple_New(argsSize);
for (qsizetype i = 0; i < argsSize; ++i) {
void *data = args[i+1];
- const char *dataType = paramTypes[i].constData();
- Shiboken::Conversions::SpecificConverter converter(dataType);
- if (converter) {
- PyTuple_SET_ITEM(preparedArgs, i, converter.toPython(data));
- } else {
- PyErr_Format(PyExc_TypeError, "Can't call meta function because I have no idea how to handle %s", dataType);
+ auto param = paramTypes.at(i);
+ Shiboken::Conversions::SpecificConverter converter(param.constData());
+ if (!converter) {
+ PyErr_SetString(PyExc_TypeError, msgCannotConvertParameter(method, i).constData());
Py_DECREF(preparedArgs);
return nullptr;
}
+ PyTuple_SET_ITEM(preparedArgs, i, converter.toPython(data));
}
return preparedArgs;
}
-static bool emitShortCircuitSignal(QObject *source, int signalIndex, PyObject *args)
-{
- void *signalArgs[2] = {nullptr, args};
- source->qt_metacall(QMetaObject::InvokeMetaMethod, signalIndex, signalArgs);
- return true;
-}
-
-} //namespace
+#include "signalmanager.moc"
diff --git a/sources/pyside6/libpyside/signalmanager.h b/sources/pyside6/libpyside/signalmanager.h
index 60cf4e75e..397700df1 100644
--- a/sources/pyside6/libpyside/signalmanager.h
+++ b/sources/pyside6/libpyside/signalmanager.h
@@ -35,6 +35,14 @@ public:
~PyObjectWrapper();
operator PyObject*() const;
+ // FIXME: To be removed in Qt7
+ // This was done to make QAbstractItemModel::data() work without explicit conversion of
+ // QVariant(PyObjectWrapper) to QVariant(int). This works because QAbstractItemModel::data()
+ // inturn calls legacyEnumValueFromModelData(const QVariant &data). But this function will
+ // be removed in Qt7.
+ // The proper fix would be to associate PyObjectWrapper to the corresponding C++ Enum.
+ int toInt() const;
+
private:
PyObject* m_me;
};
@@ -44,15 +52,16 @@ PYSIDE_API QDataStream &operator>>(QDataStream& in, PyObjectWrapper& myObj);
class PYSIDE_API SignalManager
{
- Q_DISABLE_COPY(SignalManager)
public:
+ Q_DISABLE_COPY_MOVE(SignalManager)
+
using QmlMetaCallErrorHandler = std::optional<int>(*)(QObject *object);
static SignalManager& instance();
static void setQmlMetaCallErrorHandler(QmlMetaCallErrorHandler handler);
- QObject* globalReceiver(QObject* sender, PyObject* callback);
+ QObject* globalReceiver(QObject *sender, PyObject *callback, QObject *receiver = nullptr);
void releaseGlobalReceiver(const QObject* sender, QObject* receiver);
int globalReceiverSlotIndex(QObject* sender, const char* slotSignature) const;
void notifyGlobalReceiver(QObject* receiver);
@@ -67,14 +76,14 @@ public:
// used to discovery metaobject
static const QMetaObject* retrieveMetaObject(PyObject* self);
- // Used to discovery if SignalManager was connected with object "destroyed()" signal.
- int countConnectionsWith(const QObject *object);
-
// Disconnect all signals managed by Globalreceiver
void clear();
+ void purgeEmptyGlobalReceivers();
// Utility function to call a python method usign args received in qt_metacall
- static int callPythonMetaMethod(const QMetaMethod& method, void** args, PyObject* obj, bool isShortCuit);
+ static int callPythonMetaMethod(const QMetaMethod& method, void** args, PyObject* obj);
+
+ static void deleteGlobalReceiver(const QObject *globalReceiver);
private:
struct SignalManagerPrivate;