aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/libpyside
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside6/libpyside')
-rw-r--r--sources/pyside6/libpyside/CMakeLists.txt194
-rw-r--r--sources/pyside6/libpyside/PySide6Config-spec.cmake.in16
-rw-r--r--sources/pyside6/libpyside/PySide6Config.cmake.in5
-rw-r--r--sources/pyside6/libpyside/PySide6ConfigVersion.cmake.in10
-rw-r--r--sources/pyside6/libpyside/class_property.cpp178
-rw-r--r--sources/pyside6/libpyside/class_property.h43
-rw-r--r--sources/pyside6/libpyside/dynamicqmetaobject.cpp675
-rw-r--r--sources/pyside6/libpyside/dynamicqmetaobject.h54
-rw-r--r--sources/pyside6/libpyside/feature_select.cpp798
-rw-r--r--sources/pyside6/libpyside/feature_select.h19
-rw-r--r--sources/pyside6/libpyside/globalreceiverv2.cpp322
-rw-r--r--sources/pyside6/libpyside/globalreceiverv2.h115
-rw-r--r--sources/pyside6/libpyside/libpyside.qrc5
-rw-r--r--sources/pyside6/libpyside/pyside.cpp1215
-rw-r--r--sources/pyside6/libpyside/pyside.h16
-rw-r--r--sources/pyside6/libpyside/pyside6.pc.in15
-rw-r--r--sources/pyside6/libpyside/pyside_numpy.cpp98
-rw-r--r--sources/pyside6/libpyside/pyside_numpy.h37
-rw-r--r--sources/pyside6/libpyside/pyside_p.h32
-rw-r--r--sources/pyside6/libpyside/pysideclassdecorator.cpp103
-rw-r--r--sources/pyside6/libpyside/pysideclassdecorator_p.h164
-rw-r--r--sources/pyside6/libpyside/pysideclassinfo.cpp158
-rw-r--r--sources/pyside6/libpyside/pysideclassinfo.h33
-rw-r--r--sources/pyside6/libpyside/pysideclassinfo_p.h43
-rw-r--r--sources/pyside6/libpyside/pysidecleanup.h20
-rw-r--r--sources/pyside6/libpyside/pysideinit.h27
-rw-r--r--sources/pyside6/libpyside/pysidelogging_p.h11
-rw-r--r--sources/pyside6/libpyside/pysidemacros.h18
-rw-r--r--sources/pyside6/libpyside/pysidemetafunction.cpp197
-rw-r--r--sources/pyside6/libpyside/pysidemetafunction.h38
-rw-r--r--sources/pyside6/libpyside/pysidemetafunction_p.h25
-rw-r--r--sources/pyside6/libpyside/pysidemetatype.h26
-rw-r--r--sources/pyside6/libpyside/pysideproperty.cpp600
-rw-r--r--sources/pyside6/libpyside/pysideproperty.h73
-rw-r--r--sources/pyside6/libpyside/pysideproperty_p.h169
-rw-r--r--sources/pyside6/libpyside/pysideqapp.h19
-rw-r--r--sources/pyside6/libpyside/pysideqenum.cpp197
-rw-r--r--sources/pyside6/libpyside/pysideqenum.h20
-rw-r--r--sources/pyside6/libpyside/pysideqhash.h24
-rw-r--r--sources/pyside6/libpyside/pysideqmetatype.h35
-rw-r--r--sources/pyside6/libpyside/pysideqobject.h70
-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.cpp1332
-rw-r--r--sources/pyside6/libpyside/pysidesignal.h152
-rw-r--r--sources/pyside6/libpyside/pysidesignal_p.h60
-rw-r--r--sources/pyside6/libpyside/pysideslot.cpp183
-rw-r--r--sources/pyside6/libpyside/pysideslot_p.h28
-rw-r--r--sources/pyside6/libpyside/pysidestaticstrings.cpp40
-rw-r--r--sources/pyside6/libpyside/pysidestaticstrings.h38
-rw-r--r--sources/pyside6/libpyside/pysideutils.h69
-rw-r--r--sources/pyside6/libpyside/pysideweakref.cpp82
-rw-r--r--sources/pyside6/libpyside/pysideweakref.h18
-rw-r--r--sources/pyside6/libpyside/qobjectconnect.cpp336
-rw-r--r--sources/pyside6/libpyside/qobjectconnect.h47
-rw-r--r--sources/pyside6/libpyside/signalmanager.cpp815
-rw-r--r--sources/pyside6/libpyside/signalmanager.h100
57 files changed, 9292 insertions, 0 deletions
diff --git a/sources/pyside6/libpyside/CMakeLists.txt b/sources/pyside6/libpyside/CMakeLists.txt
new file mode 100644
index 000000000..ebfe123dd
--- /dev/null
+++ b/sources/pyside6/libpyside/CMakeLists.txt
@@ -0,0 +1,194 @@
+# 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
+ feature_select.cpp
+ signalmanager.cpp
+ globalreceiverv2.cpp
+ pysideclassdecorator.cpp
+ pysideclassinfo.cpp
+ pysideqenum.cpp
+ pysideqslotobject_p.cpp
+ pysidemetafunction.cpp
+ pysidesignal.cpp
+ pysideslot.cpp
+ pysideproperty.cpp
+ pysideweakref.cpp
+ pyside.cpp
+ pyside_numpy.cpp
+ pysidestaticstrings.cpp
+ qobjectconnect.cpp
+ ${libpyside_HEADERS}
+)
+
+qt6_add_resources(libpyside_SRC libpyside.qrc)
+
+# Add python files to project explorer in Qt Creator, when opening the CMakeLists.txt as a project,
+# so you can look up python files with the Locator.
+macro(add_other_files)
+ foreach(_it ${ARGN})
+ if(NOT IS_DIRECTORY ${_it})
+ get_filename_component(name ${_it} NAME)
+ if(NOT ${_it} MATCHES "^/\\\\..*$;~$")
+ set_source_files_properties(${_it} PROPERTIES HEADER_FILE_ONLY TRUE)
+ endif()
+ endif()
+ endforeach()
+endmacro()
+
+# Test files.
+file(GLOB_RECURSE pyside_folder_py_files "../*.py")
+
+# Mostly for setup.py.
+file(GLOB setup_folder_py_files "../../../*.py")
+
+set(other_files ${pyside_folder_py_files} ${setup_folder_py_files})
+add_other_files(${other_files})
+
+add_library(pyside6 SHARED ${libpyside_SRC} ${other_files})
+add_library(PySide6::pyside6 ALIAS pyside6)
+
+#does nothing if QFP_NO_OVERRIDE_OPTIMIZATION_FLAGS (no-size-optimization) flag is not set
+append_size_optimization_flags(pyside6)
+
+target_include_directories(pyside6 PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+ $<INSTALL_INTERFACE:include/PySide6>
+)
+
+target_compile_definitions(pyside6 PRIVATE -DQT_LEAN_HEADERS=1 -DQT_NO_KEYWORDS=1)
+
+target_link_libraries(pyside6
+ PRIVATE Shiboken6::libshiboken ${libpyside_libraries})
+
+set_target_properties(pyside6 PROPERTIES
+ VERSION ${BINDING_API_VERSION}
+ SOVERSION "${PYSIDE_SO_VERSION}"
+ OUTPUT_NAME "pyside6${pyside6_SUFFIX}${SHIBOKEN_PYTHON_SHARED_LIBRARY_SUFFIX}"
+ DEFINE_SYMBOL BUILD_LIBPYSIDE)
+
+set_property(TARGET pyside6 PROPERTY CXX_STANDARD 17)
+
+if(PYSIDE_QT_CONF_PREFIX)
+ set_property(SOURCE pyside.cpp
+ APPEND
+ PROPERTY COMPILE_DEFINITIONS
+ PYSIDE_QT_CONF_PREFIX=${PYSIDE_QT_CONF_PREFIX})
+endif()
+
+#
+# install stuff
+#
+
+if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+ set(LIBRARY_OUTPUT_SUFFIX ${CMAKE_DEBUG_POSTFIX})
+else()
+ set(LIBRARY_OUTPUT_SUFFIX ${CMAKE_RELEASE_POSTFIX})
+endif()
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D QT_NO_CAST_FROM_ASCII -D QT_NO_CAST_TO_ASCII")
+
+qfp_strip_library("pyside6")
+
+# create pkg-config file
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/pyside6.pc.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/pyside6${pyside6_SUFFIX}.pc" @ONLY)
+
+# for creating cmake-config files
+include(CMakePackageConfigHelpers)
+
+# Build-tree / super project package config file.
+set(PYSIDE_PYTHONPATH "${pysidebindings_BINARY_DIR}/PySide6")
+set(PYSIDE_TYPESYSTEMS "${pysidebindings_SOURCE_DIR}/PySide6/templates/")
+set(PYSIDE_GLUE "${pysidebindings_SOURCE_DIR}/PySide6/glue")
+
+configure_package_config_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/PySide6Config-spec.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/PySide6Config${SHIBOKEN_PYTHON_CONFIG_SUFFIX}.cmake"
+ INSTALL_DESTINATION "${CMAKE_CURRENT_BINARY_DIR}"
+ PATH_VARS PYSIDE_PYTHONPATH PYSIDE_TYPESYSTEMS PYSIDE_GLUE
+ INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}"
+)
+
+set(PYSIDE_PYTHONPATH "${PYTHON_SITE_PACKAGES}/PySide6")
+set(PYSIDE_TYPESYSTEMS "${CMAKE_INSTALL_PREFIX}/share/PySide6${pyside6_SUFFIX}/typesystems")
+set(PYSIDE_GLUE "${CMAKE_INSTALL_PREFIX}/share/PySide6${pyside6_SUFFIX}/glue")
+
+# Install-tree / relocatable package config file.
+configure_package_config_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/PySide6Config-spec.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/install/PySide6Config${SHIBOKEN_PYTHON_CONFIG_SUFFIX}.cmake"
+ INSTALL_DESTINATION "${LIB_INSTALL_DIR}/cmake/PySide6"
+ PATH_VARS PYSIDE_PYTHONPATH PYSIDE_TYPESYSTEMS PYSIDE_GLUE
+)
+
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/PySide6Config.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/PySide6Config.cmake" @ONLY)
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/PySide6ConfigVersion.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/PySide6ConfigVersion.cmake" @ONLY)
+
+install(FILES ${libpyside_HEADERS}
+ DESTINATION include/${BINDING_NAME}${pyside6_SUFFIX})
+
+install(TARGETS pyside6 EXPORT PySide6Targets
+ LIBRARY DESTINATION "${LIB_INSTALL_DIR}"
+ ARCHIVE DESTINATION "${LIB_INSTALL_DIR}"
+ RUNTIME DESTINATION bin)
+install(EXPORT PySide6Targets NAMESPACE PySide6::
+ DESTINATION "${LIB_INSTALL_DIR}/cmake/PySide6")
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pyside6${pyside6_SUFFIX}.pc"
+ DESTINATION "${LIB_INSTALL_DIR}/pkgconfig")
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/PySide6Config.cmake"
+ DESTINATION "${LIB_INSTALL_DIR}/cmake/PySide6")
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/PySide6Config${SHIBOKEN_PYTHON_CONFIG_SUFFIX}.cmake"
+ DESTINATION "${LIB_INSTALL_DIR}/cmake/PySide6")
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/PySide6ConfigVersion.cmake"
+ DESTINATION "${LIB_INSTALL_DIR}/cmake/PySide6")
diff --git a/sources/pyside6/libpyside/PySide6Config-spec.cmake.in b/sources/pyside6/libpyside/PySide6Config-spec.cmake.in
new file mode 100644
index 000000000..fd3de1e75
--- /dev/null
+++ b/sources/pyside6/libpyside/PySide6Config-spec.cmake.in
@@ -0,0 +1,16 @@
+# PYSIDE_PYTHONPATH - Path to where the PySide6 Python module files could be found
+# PYSIDE_TYPESYSTEMS - Type system files that should be used by other bindings extending PySide6
+# PYSIDE_GLUE - Path to module glue files.
+
+@PACKAGE_INIT@
+
+# Import targets only when using an installed PySide6 config file (so not during a regular
+# PySide6 build, or during a super project build).
+if (NOT TARGET PySide6::pyside6)
+ include("${CMAKE_CURRENT_LIST_DIR}/PySide6Targets.cmake")
+endif()
+
+# Set relocatable variables.
+set_and_check(PYSIDE_PYTHONPATH "@PACKAGE_PYSIDE_PYTHONPATH@")
+set_and_check(PYSIDE_TYPESYSTEMS "@PACKAGE_PYSIDE_TYPESYSTEMS@")
+set_and_check(PYSIDE_GLUE "@PACKAGE_PYSIDE_GLUE@")
diff --git a/sources/pyside6/libpyside/PySide6Config.cmake.in b/sources/pyside6/libpyside/PySide6Config.cmake.in
new file mode 100644
index 000000000..037a5c44f
--- /dev/null
+++ b/sources/pyside6/libpyside/PySide6Config.cmake.in
@@ -0,0 +1,5 @@
+if (NOT PYTHON_CONFIG_SUFFIX)
+ message(STATUS "PySide6Config: Using default python: @SHIBOKEN_PYTHON_CONFIG_SUFFIX@")
+ SET(PYTHON_CONFIG_SUFFIX @SHIBOKEN_PYTHON_CONFIG_SUFFIX@)
+endif()
+include(${CMAKE_CURRENT_LIST_DIR}/PySide6Config${PYTHON_CONFIG_SUFFIX}.cmake)
diff --git a/sources/pyside6/libpyside/PySide6ConfigVersion.cmake.in b/sources/pyside6/libpyside/PySide6ConfigVersion.cmake.in
new file mode 100644
index 000000000..f5073ce08
--- /dev/null
+++ b/sources/pyside6/libpyside/PySide6ConfigVersion.cmake.in
@@ -0,0 +1,10 @@
+set(PACKAGE_VERSION @BINDING_API_VERSION@)
+
+if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" )
+ set(PACKAGE_VERSION_COMPATIBLE FALSE)
+else("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" )
+ set(PACKAGE_VERSION_COMPATIBLE TRUE)
+ if( "${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}")
+ set(PACKAGE_VERSION_EXACT TRUE)
+ endif( "${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}")
+endif("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" )
diff --git a/sources/pyside6/libpyside/class_property.cpp b/sources/pyside6/libpyside/class_property.cpp
new file mode 100644
index 000000000..2bed97ef5
--- /dev/null
+++ b/sources/pyside6/libpyside/class_property.cpp
@@ -0,0 +1,178 @@
+// Copyright (C) 2020 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 "class_property.h"
+#include "pysidestaticstrings.h"
+#include "feature_select.h"
+
+#include <pep384ext.h>
+#include <shiboken.h>
+#include <sbkstaticstrings.h>
+
+extern "C" {
+
+/*
+ * A `classproperty` is the same as a `property` but the `__get__()` and `__set__()`
+ * methods are modified to always use the object class instead of a concrete instance.
+ *
+ * Note: A "static property" as it is often called does not exist per se.
+ * Static methods do not receive anything when created. Static methods which
+ * should participate in a property must be turned into class methods, before.
+ * See function `createProperty` in `feature_select.cpp`.
+ */
+
+// `class_property.__get__()`: Always pass the class instead of the instance.
+static PyObject *PyClassProperty_descr_get(PyObject *self, PyObject * /*ob*/, PyObject *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 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
+// because PyProperty_Type.tp_init thinks this is a subclass which needs PyObject_SetAttr.
+// We call `__init__` while pretending to be a PyProperty_Type instance.
+static int PyClassProperty_tp_init(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ auto hold = Py_TYPE(self);
+ self->ob_type = &PyProperty_Type;
+ auto ret = PepExt_Type_GetInitSlot(&PyProperty_Type)(self, args, kwargs);
+ self->ob_type = hold;
+ return ret;
+}
+
+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 auto *type = createPyClassPropertyType();
+ return type;
+}
+
+/*
+ * Types with class properties need to handle `Type.class_prop = x` in a specific way.
+ * By default, Python replaces the `class_property` itself, but for wrapped C++ types
+ * we need to call `class_property.__set__()` in order to propagate the new value to
+ * the underlying C++ data structure.
+ */
+static int SbkObjectType_meta_setattro(PyObject *obj, PyObject *name, PyObject *value)
+{
+ // Use `_PepType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw
+ // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`).
+ auto type = reinterpret_cast<PyTypeObject *>(obj);
+ PySide::Feature::Select(type);
+ PyObject *descr = _PepType_Lookup(type, name);
+
+ // The following assignment combinations are possible:
+ // 1. `Type.class_prop = value` --> descr_set: `Type.class_prop.__set__(value)`
+ // 2. `Type.class_prop = other_class_prop` --> setattro: replace existing `class_prop`
+ // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment
+ const auto class_prop = reinterpret_cast<PyObject *>(PyClassProperty_TypeF());
+ const auto call_descr_set = descr && PyObject_IsInstance(descr, class_prop)
+ && !PyObject_IsInstance(value, class_prop);
+ if (call_descr_set) {
+ // Call `class_property.__set__()` instead of replacing the `class_property`.
+ return PepExt_Type_GetDescrSetSlot(Py_TYPE(descr))(descr, obj, value);
+ } // Replace existing attribute.
+ return PepExt_Type_GetSetAttroSlot(&PyType_Type)(obj, name, value);
+}
+
+} // extern "C"
+
+/*
+ * These functions are added to the SbkObjectType_TypeF() dynamically.
+ */
+namespace PySide::ClassProperty {
+
+static const char *PyClassProperty_SignatureStrings[] = {
+ "PySide6.QtCore.PyClassProperty(cls,"
+ "fget:typing.Optional[typing.Callable[[typing.Any],typing.Any]]=None,"
+ "fset:typing.Optional[typing.Callable[[typing.Any,typing.Any],None]]=None,"
+ "fdel:typing.Optional[typing.Callable[[typing.Any],None]]=None,"
+ "doc:typing.Optional[str]=None)",
+ "PySide6.QtCore.PyClassProperty.getter(cls,fget:typing.Callable[[typing.Any],typing.Any])->PySide6.QtCore.PyClassProperty",
+ "PySide6.QtCore.PyClassProperty.setter(cls,fset:typing.Callable[[typing.Any,typing.Any],None])->PySide6.QtCore.PyClassProperty",
+ "PySide6.QtCore.PyClassProperty.deleter(cls,fdel:typing.Callable[[typing.Any],None])->PySide6.QtCore.PyClassProperty",
+ nullptr}; // Sentinel
+
+void init(PyObject *module)
+{
+ PyTypeObject *type = SbkObjectType_TypeF();
+ type->tp_setattro = SbkObjectType_meta_setattro;
+
+ if (InitSignatureStrings(PyClassProperty_TypeF(), PyClassProperty_SignatureStrings) < 0)
+ return;
+
+ Py_INCREF(PyClassProperty_TypeF());
+ auto classproptype = reinterpret_cast<PyObject *>(PyClassProperty_TypeF());
+ PyModule_AddObject(module, "PyClassProperty", classproptype);
+}
+
+} // namespace PySide::ClassProperty
diff --git a/sources/pyside6/libpyside/class_property.h b/sources/pyside6/libpyside/class_property.h
new file mode 100644
index 000000000..f2ed29f1f
--- /dev/null
+++ b/sources/pyside6/libpyside/class_property.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2020 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 CLASS_PROPERTY_H
+#define CLASS_PROPERTY_H
+
+#include "pysidemacros.h"
+#include <sbkpython.h>
+
+extern "C" {
+
+struct propertyobject {
+ PyObject_HEAD
+ PyObject *prop_get;
+ PyObject *prop_set;
+ PyObject *prop_del;
+ PyObject *prop_doc;
+ int getter_doc;
+};
+
+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::ClassProperty {
+
+PYSIDE_API void init(PyObject *module);
+
+} // namespace PySide::ClassProperty
+
+#endif // CLASS_PROPERTY_H
diff --git a/sources/pyside6/libpyside/dynamicqmetaobject.cpp b/sources/pyside6/libpyside/dynamicqmetaobject.cpp
new file mode 100644
index 000000000..048001f81
--- /dev/null
+++ b/sources/pyside6/libpyside/dynamicqmetaobject.cpp
@@ -0,0 +1,675 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "dynamicqmetaobject.h"
+#include "pysideqobject.h"
+#include "pysidesignal.h"
+#include "pysidesignal_p.h"
+#include "pysideproperty.h"
+#include "pysideproperty_p.h"
+#include "pysideslot_p.h"
+#include "pysideqenum.h"
+#include "pyside_p.h"
+#include "pysidestaticstrings.h"
+
+#include <shiboken.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QObject>
+#include <QtCore/QStringList>
+#include <QtCore/QTextStream>
+#include <QtCore/QList>
+#include <private/qmetaobjectbuilder_p.h>
+
+#include <cstring>
+#include <vector>
+
+using namespace Qt::StringLiterals;
+
+using namespace PySide;
+
+// MetaObjectBuilder: Provides the QMetaObject's returned by
+// QObject::metaObject() for PySide6 objects. There are several
+// scenarios to consider:
+// 1) A plain Qt class (say QTimer) is instantiated. In that case,
+// return the base meta object until a modification is made by
+// adding methods, properties or class info (cf qmetaobject_test.py).
+// In that case, instantiate a QMetaObjectBuilder inheriting the
+// base meta meta object, add the method and return the result
+// of QMetaObjectBuilder::toMetaObject() (with dirty handling should
+// further modifications be made).
+// 2) A Python class inheriting a Qt class is instantiated. For this,
+// instantiate a QMetaObjectBuilder and add the methods/properties
+// found by inspecting the Python class.
+
+class MetaObjectBuilderPrivate
+{
+public:
+ using MetaObjects = std::vector<const QMetaObject *>;
+
+ QMetaObjectBuilder *ensureBuilder();
+ void parsePythonType(PyTypeObject *type);
+ int indexOfMethod(QMetaMethod::MethodType mtype,
+ const QByteArray &signature) const;
+ int indexOfProperty(const QByteArray &name) const;
+ int addSlot(const QByteArray &signature);
+ int addSlot(const QByteArray &signature, const QByteArray &type,
+ const QByteArray &tag = {});
+ int addSignal(const QByteArray &signature);
+ void removeMethod(QMetaMethod::MethodType mtype, int index);
+ int getPropertyNotifyId(PySideProperty *property) const;
+ int addProperty(const QByteArray &property, PyObject *data);
+ void addInfo(const QByteArray &key, const QByteArray &value);
+ void addInfo(const QMap<QByteArray, QByteArray> &info);
+ void addEnumerator(const char *name, bool flag, bool scoped,
+ const MetaObjectBuilder::EnumValues &entries);
+ void removeProperty(int index);
+ const QMetaObject *update();
+
+ QMetaObjectBuilder *m_builder = nullptr;
+
+ const QMetaObject *m_baseObject = nullptr;
+ MetaObjects m_cachedMetaObjects;
+ bool m_dirty = true;
+
+private:
+ QMetaPropertyBuilder
+ createProperty(PySideProperty *property, const QByteArray &propertyName);
+};
+
+QMetaObjectBuilder *MetaObjectBuilderPrivate::ensureBuilder()
+{
+ if (!m_builder) {
+ m_builder = new QMetaObjectBuilder();
+ m_builder->setClassName(m_baseObject->className());
+ m_builder->setSuperClass(m_baseObject);
+ }
+ return m_builder;
+}
+
+MetaObjectBuilder::MetaObjectBuilder(const char *className, const QMetaObject *metaObject) :
+ m_d(new MetaObjectBuilderPrivate)
+{
+ m_d->m_baseObject = metaObject;
+ m_d->m_builder = new QMetaObjectBuilder();
+ m_d->m_builder->setClassName(className);
+ m_d->m_builder->setSuperClass(metaObject);
+ m_d->m_builder->setClassName(className);
+}
+
+MetaObjectBuilder::MetaObjectBuilder(PyTypeObject *type, const QMetaObject *metaObject)
+ : m_d(new MetaObjectBuilderPrivate)
+{
+ m_d->m_baseObject = metaObject;
+ const char *className = type->tp_name;
+ if (const char *lastDot = strrchr(type->tp_name, '.'))
+ className = lastDot + 1;
+ // Different names indicate a Python class inheriting a Qt class.
+ // Parse the type.
+ if (strcmp(className, metaObject->className()) != 0) {
+ m_d->m_builder = new QMetaObjectBuilder();
+ m_d->m_builder->setClassName(className);
+ m_d->m_builder->setSuperClass(metaObject);
+ m_d->parsePythonType(type);
+ }
+}
+
+MetaObjectBuilder::~MetaObjectBuilder()
+{
+ for (auto *metaObject : m_d->m_cachedMetaObjects)
+ free(const_cast<QMetaObject*>(metaObject));
+ delete m_d->m_builder;
+ delete m_d;
+}
+
+int MetaObjectBuilderPrivate::indexOfMethod(QMetaMethod::MethodType mtype,
+ const QByteArray &signature) const
+{
+ int result = -1;
+ if (m_builder) {
+ switch (mtype) {
+ case QMetaMethod::Signal:
+ result = m_builder->indexOfSignal(signature);
+ break;
+ case QMetaMethod::Slot:
+ result = m_builder->indexOfSlot(signature);
+ break;
+ case QMetaMethod::Constructor:
+ result = m_builder->indexOfConstructor(signature);
+ break;
+ case QMetaMethod::Method:
+ result = m_builder->indexOfMethod(signature);
+ break;
+ }
+ if (result >= 0)
+ return result + m_baseObject->methodCount();
+ }
+ switch (mtype) {
+ case QMetaMethod::Signal:
+ result = m_baseObject->indexOfSignal(signature);
+ break;
+ case QMetaMethod::Slot:
+ result = m_baseObject->indexOfSlot(signature);
+ break;
+ case QMetaMethod::Constructor:
+ result = m_baseObject->indexOfConstructor(signature);
+ break;
+ case QMetaMethod::Method:
+ result = m_baseObject->indexOfMethod(signature);
+ break;
+ }
+ return result;
+}
+
+int MetaObjectBuilder::indexOfMethod(QMetaMethod::MethodType mtype,
+ const QByteArray &signature) const
+{
+ return m_d->indexOfMethod(mtype, signature);
+}
+
+int MetaObjectBuilderPrivate::indexOfProperty(const QByteArray &name) const
+{
+ if (m_builder) {
+ const int result = m_builder->indexOfProperty(name);
+ if (result >= 0)
+ return m_baseObject->propertyCount() + result;
+ }
+ return m_baseObject->indexOfProperty(name);
+}
+
+int MetaObjectBuilder::indexOfProperty(const QByteArray &name) const
+{
+ return m_d->indexOfProperty(name);
+}
+
+static bool checkMethodSignature(const QByteArray &signature)
+{
+ // Common mistake not to add parentheses to the signature.
+ const auto openParen = signature.indexOf('(');
+ const auto closingParen = signature.lastIndexOf(')');
+ const bool ok = openParen != -1 && closingParen != -1 && openParen < closingParen;
+ if (!ok) {
+ const QByteArray message =
+ "MetaObjectBuilder::addMethod: Invalid method signature provided for \""
+ + signature + '"';
+ PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0);
+ }
+ return ok;
+}
+
+int MetaObjectBuilderPrivate::addSlot(const QByteArray &signature)
+{
+ if (!checkMethodSignature(signature))
+ return -1;
+ m_dirty = true;
+ return m_baseObject->methodCount()
+ + ensureBuilder()->addSlot(signature).index();
+}
+
+int MetaObjectBuilder::addSlot(const char *signature)
+{
+ return m_d->addSlot(signature);
+}
+
+int MetaObjectBuilderPrivate::addSlot(const QByteArray &signature,
+ const QByteArray &type,
+ const QByteArray &tag)
+{
+ if (!checkMethodSignature(signature))
+ return -1;
+ m_dirty = true;
+ QMetaMethodBuilder methodBuilder = ensureBuilder()->addSlot(signature);
+ if (!type.isEmpty() && type != "void"_ba)
+ methodBuilder.setReturnType(type);
+ if (!tag.isEmpty())
+ methodBuilder.setTag(tag);
+ return m_baseObject->methodCount() + methodBuilder.index();
+}
+
+int MetaObjectBuilder::addSlot(const char *signature, const char *type)
+{
+ return m_d->addSlot(signature, type);
+}
+
+int MetaObjectBuilderPrivate::addSignal(const QByteArray &signature)
+{
+ if (!checkMethodSignature(signature))
+ return -1;
+ m_dirty = true;
+ return m_baseObject->methodCount()
+ + ensureBuilder()->addSignal(signature).index();
+}
+
+int MetaObjectBuilder::addSignal(const char *signature)
+{
+ return m_d->addSignal(signature);
+}
+
+void MetaObjectBuilderPrivate::removeMethod(QMetaMethod::MethodType mtype,
+ int index)
+{
+ index -= m_baseObject->methodCount();
+ auto builder = ensureBuilder();
+ Q_ASSERT(index >= 0 && index < builder->methodCount());
+ switch (mtype) {
+ case QMetaMethod::Constructor:
+ builder->removeConstructor(index);
+ break;
+ default:
+ builder->removeMethod(index);
+ break;
+ }
+ m_dirty = true;
+}
+
+void MetaObjectBuilder::removeMethod(QMetaMethod::MethodType mtype, int index)
+{
+ m_d->removeMethod(mtype, index);
+}
+
+int MetaObjectBuilderPrivate::getPropertyNotifyId(PySideProperty *property) const
+{
+ int notifyId = -1;
+ if (property->d->notify) {
+ if (const char *signalNotify = PySide::Property::getNotifyName(property))
+ notifyId = indexOfMethod(QMetaMethod::Signal, signalNotify);
+ }
+ return notifyId;
+}
+
+QMetaPropertyBuilder
+ MetaObjectBuilderPrivate::createProperty(PySideProperty *property,
+ const QByteArray &propertyName)
+{
+ int propertyNotifyId = getPropertyNotifyId(property);
+ if (propertyNotifyId >= 0)
+ propertyNotifyId -= m_baseObject->methodCount();
+
+ // For QObject-derived Python types, retrieve the meta type registered
+ // by name from the qmlRegisterType, if there is one. This is required for
+ // grouped QML properties to work.
+ auto *builder = ensureBuilder();
+ auto *typeObject = Property::getTypeObject(property);
+ if (typeObject != nullptr && PyType_Check(typeObject)) {
+ auto *pyTypeObject = reinterpret_cast<PyTypeObject *>(typeObject);
+ if (qstrncmp(pyTypeObject->tp_name, "PySide", 6) != 0
+ && PySide::isQObjectDerived(pyTypeObject, false)) {
+ const QByteArray pyType(pyTypeObject->tp_name);
+ const auto metaType = QMetaType::fromName(pyType + '*');
+ if (metaType.isValid()) {
+ return builder->addProperty(propertyName, pyType,
+ metaType, propertyNotifyId);
+ }
+ }
+ }
+ return builder->addProperty(propertyName, property->d->typeName,
+ propertyNotifyId);
+}
+
+int MetaObjectBuilderPrivate::addProperty(const QByteArray &propertyName,
+ PyObject *data)
+{
+ int index = indexOfProperty(propertyName);
+ if (index != -1)
+ return index;
+
+ auto *property = reinterpret_cast<PySideProperty *>(data);
+ auto newProperty = createProperty(property, propertyName);
+
+ // Adding property attributes
+ newProperty.setReadable(PySide::Property::isReadable(property));
+ newProperty.setWritable(PySide::Property::isWritable(property));
+ newProperty.setResettable(PySide::Property::hasReset(property));
+ newProperty.setDesignable(PySide::Property::isDesignable(property));
+ newProperty.setScriptable(PySide::Property::isScriptable(property));
+ newProperty.setStored(PySide::Property::isStored(property));
+ newProperty.setUser(PySide::Property::isUser(property));
+ newProperty.setConstant(PySide::Property::isConstant(property));
+ newProperty.setFinal(PySide::Property::isFinal(property));
+
+ index = newProperty.index() + m_baseObject->propertyCount();
+ m_dirty = true;
+ return index;
+}
+
+int MetaObjectBuilder::addProperty(const char *property, PyObject *data)
+{
+ return m_d->addProperty(property, data);
+}
+
+void MetaObjectBuilderPrivate::addInfo(const QByteArray &key,
+ const QByteArray &value)
+{
+ ensureBuilder()->addClassInfo(key, value);
+ m_dirty = true;
+}
+
+void MetaObjectBuilder::addInfo(const char *key, const char *value)
+{
+ m_d->addInfo(key, value);
+}
+
+void MetaObjectBuilderPrivate::addInfo(const QMap<QByteArray, QByteArray> &info)
+{
+ auto builder = ensureBuilder();
+ for (auto i = info.constBegin(), end = info.constEnd(); i != end; ++i)
+ builder->addClassInfo(i.key(), i.value());
+ m_dirty = true;
+}
+
+void MetaObjectBuilder::addInfo(const QMap<QByteArray, QByteArray> &info)
+{
+ m_d->addInfo(info);
+}
+
+void MetaObjectBuilder::addEnumerator(const char *name, bool flag, bool scoped,
+ const EnumValues &entries)
+{
+ m_d->addEnumerator(name, flag, scoped, entries);
+}
+
+void MetaObjectBuilderPrivate::addEnumerator(const char *name, bool flag, bool scoped,
+ const MetaObjectBuilder::EnumValues &entries)
+{
+ auto builder = ensureBuilder();
+ int have_already = builder->indexOfEnumerator(name);
+ if (have_already >= 0)
+ builder->removeEnumerator(have_already);
+ auto enumbuilder = builder->addEnumerator(name);
+ enumbuilder.setIsFlag(flag);
+ enumbuilder.setIsScoped(scoped);
+
+ for (const auto &item : entries)
+ enumbuilder.addKey(item.first, item.second);
+ m_dirty = true;
+}
+
+void MetaObjectBuilderPrivate::removeProperty(int index)
+{
+ index -= m_baseObject->propertyCount();
+ auto builder = ensureBuilder();
+ Q_ASSERT(index >= 0 && index < builder->propertyCount());
+ builder->removeProperty(index);
+ m_dirty = true;
+}
+
+void MetaObjectBuilder::removeProperty(int index)
+{
+ m_d->removeProperty(index);
+}
+
+// PYSIDE-315: Instead of sorting the items and maybe breaking indices, we
+// ensure that the signals and slots are sorted by the improved
+// parsePythonType() (signals must go before slots). The order can only
+// become distorted if the class is modified after creation. In that
+// case, we give a warning.
+
+static QString msgMethodSortOrder(const QMetaObject *mo, int offendingIndex)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "\n\n*** Sort Warning ***\nSignals and slots in QMetaObject '"
+ << mo->className()
+ << "' are not ordered correctly, this may lead to issues.\n";
+ const int methodOffset = mo->methodOffset();
+ for (int m = methodOffset, methodCount = mo->methodCount(); m < methodCount; ++m) {
+ const auto method = mo->method(m);
+ str << (m - methodOffset + 1) << (m > offendingIndex ? '!' : ' ')
+ << (method.methodType() == QMetaMethod::Signal ? " Signal " : " Slot ")
+ << method.methodSignature() << '\n';
+ }
+ return result;
+}
+
+static void checkMethodOrder(const QMetaObject *metaObject)
+{
+ const int lastMethod = metaObject->methodCount() - 1;
+ for (int m = metaObject->methodOffset(); m < lastMethod; ++m) {
+ if (metaObject->method(m).methodType() == QMetaMethod::Slot
+ && metaObject->method(m + 1).methodType() == QMetaMethod::Signal) {
+ const auto message = msgMethodSortOrder(metaObject, m);
+ PyErr_WarnEx(PyExc_RuntimeWarning, qPrintable(message), 0);
+ // Prevent a warning from being turned into an error. We cannot easily unwind.
+ PyErr_Clear();
+ break;
+ }
+ }
+}
+
+const QMetaObject *MetaObjectBuilderPrivate::update()
+{
+ if (!m_builder)
+ return m_baseObject;
+ if (m_cachedMetaObjects.empty() || m_dirty) {
+ // PYSIDE-803: The dirty branch needs to be protected by the GIL.
+ // This was moved from SignalManager::retrieveMetaObject to here,
+ // which is only the update in "return builder->update()".
+ Shiboken::GilState gil;
+ m_cachedMetaObjects.push_back(m_builder->toMetaObject());
+ checkMethodOrder(m_cachedMetaObjects.back());
+ m_dirty = false;
+ }
+ return m_cachedMetaObjects.back();
+}
+
+const QMetaObject *MetaObjectBuilder::update()
+{
+ return m_d->update();
+}
+
+static void formatEnum(QTextStream &str, const QMetaEnum &e)
+{
+ str << '"' << e.name() << "\" {";
+ for (int k = 0, cnt = e.keyCount(); k < cnt; ++k) {
+ if (k)
+ str << ", ";
+ str << e.key(k);
+ }
+ str << "}";
+}
+
+static void formatProperty(QTextStream &str, const QMetaProperty &p)
+{
+ str << '"' << p.name() << "\", " << p.typeName();
+ if (p.isWritable())
+ str << " [writeable]";
+ if (p.isResettable())
+ str << " [resettable]";
+ if (p.isConstant())
+ str << " [constant]";
+ if (p.isFinal())
+ str << " [final]";
+ if (p.isDesignable())
+ str << " [designable]";
+ auto sig = p.notifySignal();
+ if (sig.isValid())
+ str << ", notify=" << sig.name();
+}
+
+static void formatMethod(QTextStream &str, const QMetaMethod &m)
+{
+ str << "type=";
+ switch (m.methodType()) {
+ case QMetaMethod::Method:
+ str << "Method";
+ break;
+ case QMetaMethod::Signal:
+ str << "Signal";
+ break;
+ case QMetaMethod::Slot:
+ str << "Slot";
+ break;
+ case QMetaMethod::Constructor:
+ str << "Constructor";
+ break;
+ }
+
+ str << ", signature="
+ << m.methodSignature();
+ const QByteArrayList parms = m.parameterTypes();
+ if (!parms.isEmpty())
+ str << ", parameters=" << parms.join(", ");
+}
+
+QString MetaObjectBuilder::formatMetaObject(const QMetaObject *metaObject)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "PySide" << QT_VERSION_MAJOR << ".QtCore.QMetaObject(\""
+ << metaObject->className() << '"';
+ if (auto *s = metaObject->superClass())
+ str << " inherits \"" << s->className() << '"';
+ str << ":\n";
+
+ int offset = metaObject->enumeratorOffset();
+ int count = metaObject->enumeratorCount();
+ if (offset < count) {
+ str << "Enumerators:\n";
+ for (int e = offset; e < count; ++e) {
+ str << " #" << e << ' ';
+ formatEnum(str, metaObject->enumerator(e));
+ str << '\n';
+ }
+ }
+
+ offset = metaObject->propertyOffset();
+ count = metaObject->propertyCount();
+ if (offset < count) {
+ str << "Properties:\n";
+ for (int p = offset; p < count; ++p) {
+ str << " #" << p << ' ';
+ formatProperty(str, metaObject->property(p));
+ str << '\n';
+ }
+ }
+
+ offset = metaObject->methodOffset();
+ count = metaObject->methodCount();
+ if (offset < count) {
+ str << "Methods:\n";
+ for (int m = offset; m < count; ++m) {
+ str << " #" << m << ' ';
+ formatMethod(str, metaObject->method(m));
+ str << '\n';
+ }
+ }
+
+ str << ')';
+ return result;
+}
+
+using namespace Shiboken;
+
+void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
+{
+ // Get all non-QObject-derived base types in method resolution order, filtering out the types
+ // that can't have signals, slots or properties.
+ // This enforces registering of all signals and slots at type parsing time, and not later at
+ // signal connection time, thus making sure no method indices change which would break
+ // existing connections.
+ const PyObject *mro = type->tp_mro;
+ const Py_ssize_t basesCount = PyTuple_GET_SIZE(mro);
+
+ std::vector<PyTypeObject *> basesToCheck;
+ // Prepend the actual type that we are parsing.
+ basesToCheck.reserve(1u + basesCount);
+ basesToCheck.push_back(type);
+
+ auto sbkObjTypeF = SbkObject_TypeF();
+ auto baseObjType = reinterpret_cast<PyTypeObject *>(&PyBaseObject_Type);
+ for (Py_ssize_t i = 0; i < basesCount; ++i) {
+ auto baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, i));
+ if (baseType != sbkObjTypeF && baseType != baseObjType
+ && !PySide::isQObjectDerived(baseType, false)) {
+ basesToCheck.push_back(baseType);
+ }
+ }
+
+ // PYSIDE-315: Handle all signals first, in all involved types.
+ // Leave the properties to be registered after signals because they may depend on
+ // notify signals.
+ for (PyTypeObject *baseType : basesToCheck) {
+ AutoDecRef tpDict(PepType_GetDict(baseType));
+ PyObject *attrs = tpDict.object();
+ PyObject *key = nullptr;
+ PyObject *value = nullptr;
+ Py_ssize_t pos = 0;
+
+ while (PyDict_Next(attrs, &pos, &key, &value)) {
+ if (Signal::checkType(value)) {
+ // Register signals.
+ auto *data = reinterpret_cast<PySideSignal *>(value)->data;
+ if (data->signalName.isEmpty())
+ data->signalName = String::toCString(key);
+ for (const auto &s : data->signatures) {
+ const auto sig = data->signalName + '(' + s.signature + ')';
+ if (m_baseObject->indexOfSignal(sig) == -1) {
+ // Registering the parameterNames to the QMetaObject (PYSIDE-634)
+ // from:
+ // Signal(..., arguments=['...', ...]
+ // the arguments are now on data-data->signalArguments
+ auto builder = m_builder->addSignal(sig);
+ if (!data->signalArguments.isEmpty())
+ builder.setParameterNames(data->signalArguments);
+ }
+ }
+ }
+ }
+ }
+
+ PyObject *slotAttrName = PySide::PySideMagicName::slot_list_attr();
+ // PYSIDE-315: Now take care of the rest.
+ // Signals and slots should be separated, unless the types are modified, later.
+ // We check for this using "is_sorted()". Sorting no longer happens at all.
+ for (PyTypeObject *baseType : basesToCheck) {
+ AutoDecRef tpDict(PepType_GetDict(baseType));
+ PyObject *attrs = tpDict.object();
+ PyObject *key = nullptr;
+ PyObject *value = nullptr;
+ Py_ssize_t pos = 0;
+
+ while (PyDict_Next(attrs, &pos, &key, &value)) {
+ if (Property::checkType(value)) {
+ const QByteArray name = String::toCString(key);
+ const int index = m_baseObject->indexOfProperty(name);
+ if (index == -1)
+ addProperty(name, value);
+ } else if (PepType_GetSlot(Py_TYPE(value), Py_tp_call) != nullptr) {
+ // PYSIDE-198: PyFunction_Check does not work with Nuitka.
+ // Register slots.
+ if (PyObject_HasAttr(value, slotAttrName)) {
+ auto *capsule = PyObject_GetAttr(value, slotAttrName);
+ const auto *entryList = PySide::Slot::dataListFromCapsule(capsule);
+ for (const auto &e : *entryList) {
+ if (m_baseObject->indexOfSlot(e.signature) == -1)
+ addSlot(e.signature, e.resultType, e.tag);
+ }
+ }
+ }
+ }
+ }
+ // PYSIDE-957: Collect the delayed QEnums
+ auto collectedEnums = PySide::QEnum::resolveDelayedQEnums(type);
+ for (PyObject *obEnumType : collectedEnums) {
+ bool isFlag = PySide::QEnum::isFlag(obEnumType);
+ AutoDecRef obName(PyObject_GetAttr(obEnumType, PyMagicName::name()));
+ // Everything has been checked already in resolveDelayedQEnums.
+ // Therefore, we don't need to error-check here again.
+ auto name = String::toCString(obName);
+ AutoDecRef members(PyObject_GetAttr(obEnumType, PyMagicName::members()));
+ AutoDecRef items(PyMapping_Items(members));
+ Py_ssize_t nr_items = PySequence_Length(items);
+
+ QList<std::pair<QByteArray, int> > entries;
+ for (Py_ssize_t idx = 0; idx < nr_items; ++idx) {
+ AutoDecRef item(PySequence_GetItem(items, idx));
+ AutoDecRef key(PySequence_GetItem(item, 0));
+ AutoDecRef member(PySequence_GetItem(item, 1));
+ AutoDecRef value(PyObject_GetAttr(member, Shiboken::PyName::value()));
+ auto ckey = String::toCString(key);
+ auto ivalue = PyLong_AsSsize_t(value);
+ entries.push_back(std::make_pair(ckey, int(ivalue)));
+ }
+ addEnumerator(name, isFlag, true, entries);
+ }
+}
diff --git a/sources/pyside6/libpyside/dynamicqmetaobject.h b/sources/pyside6/libpyside/dynamicqmetaobject.h
new file mode 100644
index 000000000..dd33f65f7
--- /dev/null
+++ b/sources/pyside6/libpyside/dynamicqmetaobject.h
@@ -0,0 +1,54 @@
+// 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 DYNAMICQMETAOBJECT_H
+#define DYNAMICQMETAOBJECT_H
+
+#include <sbkpython.h>
+#include <pysidemacros.h>
+
+#include <QtCore/QMetaObject>
+#include <QtCore/QMetaMethod>
+
+#include <utility>
+
+class MetaObjectBuilderPrivate;
+
+namespace PySide
+{
+
+class MetaObjectBuilder
+{
+ Q_DISABLE_COPY_MOVE(MetaObjectBuilder)
+public:
+ using EnumValue = std::pair<QByteArray, int>;
+ using EnumValues = QList<EnumValue>;
+
+ MetaObjectBuilder(const char *className, const QMetaObject *metaObject);
+
+ MetaObjectBuilder(PyTypeObject *type, const QMetaObject *metaObject);
+ ~MetaObjectBuilder();
+
+ int indexOfMethod(QMetaMethod::MethodType mtype, const QByteArray &signature) const;
+ int indexOfProperty(const QByteArray &name) const;
+ int addSlot(const char *signature);
+ int addSlot(const char *signature, const char *type);
+ int addSignal(const char *signature);
+ void removeMethod(QMetaMethod::MethodType mtype, int index);
+ int addProperty(const char *property, PyObject *data);
+ void addInfo(const char *key, const char *value);
+ void addInfo(const QMap<QByteArray, QByteArray> &info);
+ void addEnumerator(const char *name, bool flag,
+ bool scoped, const EnumValues &entries);
+ void removeProperty(int index);
+
+ const QMetaObject *update();
+
+ PYSIDE_API static QString formatMetaObject(const QMetaObject *metaObject);
+
+private:
+ MetaObjectBuilderPrivate *m_d;
+};
+
+}
+#endif
diff --git a/sources/pyside6/libpyside/feature_select.cpp b/sources/pyside6/libpyside/feature_select.cpp
new file mode 100644
index 000000000..cfd465267
--- /dev/null
+++ b/sources/pyside6/libpyside/feature_select.cpp
@@ -0,0 +1,798 @@
+// Copyright (C) 2020 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 "feature_select.h"
+#include "pysidecleanup.h"
+#include "pysideqobject.h"
+#include "pysidestaticstrings.h"
+#include "class_property.h"
+
+#include <shiboken.h>
+#include <sbkfeature_base.h>
+#include <signature_p.h>
+
+#include <QtCore/QStringList>
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// PYSIDE-1019: Support switchable extensions
+//
+// This functionality is no longer implemented in the signature module, since
+// the PyCFunction getsets do not have to be modified any longer.
+// Instead, we simply exchange the complete class dicts. This is done in the
+// basewrapper.cpp file and in every generated `tp_(get|set)attro`.
+//
+// This is the general framework of the switchable extensions.
+// A maximum of eight features is planned so far. This seems to be enough.
+// More features are possible, but then we must somehow register the
+// extra `select_id`s above 255.
+//
+
+/*****************************************************************************
+
+ How Does This Feature Selection Work?
+ -------------------------------------
+
+The basic idea is to replace the `tp_dict` of a QObject derived type.
+This way, we can replace the methods of the class in no time.
+
+The crucial point to understand is how the `tp_dict` is actually accessed:
+When you type "QObject.__dict__", the descriptor of `SbkObjectType_Type`
+is called. This descriptor is per default unassigned, so the base class
+PyType_Type provides the tp_getset method `type_dict`:
+
+ static PyObject *
+ type_dict(PyTypeObject *type, void *context)
+ {
+ if (type->tp_dict == NULL) {
+ Py_RETURN_NONE;
+ }
+ return PyDictProxy_New(type->tp_dict);
+ }
+
+In order to change that, we need to insert our own version into SbkObjectType:
+
+ static PyObject *Sbk_TypeGet___dict__(PyTypeObject *type, void *context)
+ {
+ auto dict = type->tp_dict;
+ if (dict == NULL)
+ Py_RETURN_NONE;
+ if (SelectFeatureSet != nullptr)
+ dict = SelectFeatureSet(type);
+ return PyDictProxy_New(dict);
+ }
+
+This way, the Python function `type_ready()` does not fill in the default,
+but uses our modified version. It a similar way, we overwrite type_getattro
+with our own version, again in SbkObjectType, replacing the default of
+PyType_Type.
+
+Now we can exchange the dict with a customized version.
+We have our own derived type `ChameleonDict` with additional attributes.
+These allow us to create a ring of dicts which can be rotated to the actual
+needed dict version:
+
+Every dict has a field `select_id` which is selected by the `from __feature__`
+import. The dicts are cyclic connected by the `dict_ring` field.
+
+When a class dict is required, now always `SelectFeatureSet` is called, which
+looks into the `__name__` attribute of the active module and decides which
+version of `tp_dict` is needed. Then the right dict is searched in the ring
+and created if not already there.
+
+Furthermore, we need to overwrite every `tp_(get|set)attro` with a version
+that switches dicts right before looking up methods.
+The dict changing must walk the whole `tp_mro` in order to change all names.
+
+This is everything that the following code does.
+
+*****************************************************************************/
+
+
+namespace PySide::Feature {
+
+using namespace Shiboken;
+
+using FeatureProc = bool(*)(PyTypeObject *type, PyObject *prev_dict, int id);
+
+static FeatureProc *featurePointer = nullptr;
+
+// Create a derived dict class
+static PyTypeObject *
+createDerivedDictType()
+{
+ // It is not easy to create a compatible dict object with the
+ // limited API. Easier is to use Python to create a derived
+ // type and to modify that a bit from the C code.
+ PyObject *ChameleonDict = PepRun_GetResult(R"CPP(if True:
+
+ class ChameleonDict(dict):
+ __slots__ = ("dict_ring", "select_id", "orig_dict")
+
+ result = ChameleonDict
+
+ )CPP");
+ return reinterpret_cast<PyTypeObject *>(ChameleonDict);
+}
+
+static PyTypeObject *new_dict_type = nullptr;
+
+static void ensureNewDictType()
+{
+ if (new_dict_type == nullptr) {
+ new_dict_type = createDerivedDictType();
+ if (new_dict_type == nullptr)
+ Py_FatalError("PySide6: Problem creating ChameleonDict");
+ }
+}
+
+static inline PyObject *nextInCircle(PyObject *dict)
+{
+ // returns a borrowed ref
+ AutoDecRef next_dict(PyObject_GetAttr(dict, PySideName::dict_ring()));
+ return next_dict;
+}
+
+static inline void setNextDict(PyObject *dict, PyObject *next_dict)
+{
+ PyObject_SetAttr(dict, PySideName::dict_ring(), next_dict);
+}
+
+static inline void setSelectId(PyObject *dict, int select_id)
+{
+ PyObject_SetAttr(dict, PySideName::select_id(), PyLong_FromLong(select_id));
+}
+
+static inline int getSelectId(PyObject *dict)
+{
+ auto *py_select_id = PyObject_GetAttr(dict, PyName::select_id());
+ if (py_select_id == nullptr) {
+ PyErr_Clear();
+ return 0;
+ }
+ int ret = PyLong_AsLong(py_select_id);
+ Py_DECREF(py_select_id);
+ return ret;
+}
+
+static bool replaceClassDict(PyTypeObject *type)
+{
+ /*
+ * Replace the type dict by the derived ChameleonDict.
+ * This is mandatory for all type dicts when they are touched.
+ */
+ ensureNewDictType();
+ 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)
+ return false;
+ // Insert the default id. Cannot fail for small numbers.
+ setSelectId(new_dict, 0);
+ // 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.
+ 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;
+}
+
+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.
+ */
+ 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)
+ return false;
+ setSelectId(new_dict, select_id);
+ // insert the dict into the ring
+ auto next_dict = nextInCircle(dict);
+ setNextDict(dict, new_dict);
+ setNextDict(new_dict, next_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;
+}
+
+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`.
+ */
+ 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) {
+ PepType_SetDict(type, dict);
+ return true;
+ }
+ dict = nextInCircle(dict);
+ } while (dict != initial_dict);
+ PepType_SetDict(type, initial_dict);
+ return false;
+}
+
+static bool createNewFeatureSet(PyTypeObject *type, int select_id)
+{
+ /*
+ * Create a new feature set.
+ * A `false` return value is a fatal error.
+ *
+ * A FeatureProc sees an empty `type->tp_dict` and the previous dict
+ * content in `prev_dict`. It is responsible of filling `type->tp_dict`
+ * with modified content.
+ */
+
+ bool ok = moveToFeatureSet(type, 0);
+ Q_UNUSED(ok);
+ assert(ok);
+
+ AutoDecRef prev_dict(PepType_GetDict(type));
+ if (!addNewDict(type, select_id))
+ return false;
+ int id = select_id;
+ if (id == -1)
+ return false;
+ FeatureProc *proc = featurePointer;
+ for (int idx = id; *proc != nullptr; ++proc, idx >>= 1) {
+ if (idx & 1) {
+ // clear the tp_dict that will get new content
+ 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(tpDict.object()));
+ if (prev_dict.isNull())
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+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.
+ */
+ 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)) {
+ Py_FatalError("failed to replace class dict!");
+ return;
+ }
+ }
+ if (!moveToFeatureSet(type, select_id)) {
+ if (!createNewFeatureSet(type, select_id)) {
+ Py_FatalError("failed to create a new feature set!");
+ return;
+ }
+ }
+ }
+
+static PyObject *cached_globals{};
+static int last_select_id{};
+
+static inline int getFeatureSelectId()
+{
+ static auto *undef = PyLong_FromLong(-1);
+ static auto *feature_dict = GetFeatureDict();
+ // these things are all borrowed
+ auto *globals = PyEval_GetGlobals();
+ if (globals == nullptr
+ || globals == cached_globals)
+ return last_select_id;
+
+ auto *modname = PyDict_GetItem(globals, PyMagicName::name());
+ if (modname == nullptr)
+ return last_select_id;
+
+ auto *py_select_id = PyDict_GetItem(feature_dict, modname);
+ if (py_select_id == nullptr
+ || !PyLong_Check(py_select_id)
+ || py_select_id == undef)
+ return last_select_id;
+
+ cached_globals = globals;
+ last_select_id = PyLong_AsLong(py_select_id) & 0xff;
+ return last_select_id;
+}
+
+static inline void SelectFeatureSet(PyTypeObject *type)
+{
+ /*
+ * This is the main function of the module.
+ * The purpose of this function is to switch the dict of a class right
+ * before a (get|set)attro call is performed.
+ *
+ * Generated functions call this directly.
+ * Shiboken will assign it via a public hook of `basewrapper.cpp`.
+ */
+ 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!");
+ return;
+ }
+ }
+
+ int select_id = getFeatureSelectId();
+ static int last_select_id{};
+ static PyTypeObject *last_type{};
+
+ // PYSIDE-2029: Implement a very simple but effective cache that cannot fail.
+ if (type == last_type && select_id == last_select_id)
+ return;
+ last_type = type;
+ last_select_id = select_id;
+
+ auto *mro = type->tp_mro;
+ Py_ssize_t idx, n = PyTuple_GET_SIZE(mro);
+ // We leave 'Shiboken.Object' and 'object' alone, therefore "n - 2".
+ for (idx = 0; idx < n - 2; idx++) {
+ auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+ SelectFeatureSetSubtype(sub_type, select_id);
+ }
+ // PYSIDE-1436: Clear all caches for the type and subtypes.
+ PyType_Modified(type);
+}
+
+// For cppgenerator:
+void Select(PyObject *obj)
+{
+ if (featurePointer == nullptr)
+ return;
+ auto *type = Py_TYPE(obj);
+ SelectFeatureSet(type);
+}
+
+void Select(PyTypeObject *type)
+{
+ if (featurePointer != nullptr)
+ SelectFeatureSet(type);
+}
+
+static bool feature_01_addLowerNames(PyTypeObject *type, PyObject *prev_dict, int id);
+static bool feature_02_true_property(PyTypeObject *type, PyObject *prev_dict, int id);
+static bool feature_04_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id);
+static bool feature_08_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id);
+static bool feature_10_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id);
+static bool feature_20_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id);
+static bool feature_40_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id);
+static bool feature_80_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id);
+
+static FeatureProc featureProcArray[] = {
+ feature_01_addLowerNames,
+ feature_02_true_property,
+ feature_04_addDummyNames,
+ feature_08_addDummyNames,
+ feature_10_addDummyNames,
+ feature_20_addDummyNames,
+ feature_40_addDummyNames,
+ feature_80_addDummyNames,
+ nullptr
+};
+
+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;
+ }
+ last_select_id = 0;
+ // Reset the cache. This is called at any "from __feature__ import".
+ cached_globals = nullptr;
+}
+
+void Enable(bool enable)
+{
+ if (!is_initialized)
+ return;
+ featurePointer = enable ? featureProcArray : nullptr;
+ initSelectableFeature(enable ? SelectFeatureSet : nullptr);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// PYSIDE-1019: Support switchable extensions
+//
+// Feature 0x01: Allow snake_case instead of camelCase
+//
+// This functionality is no longer implemented in the signature module, since
+// the PyCFunction getsets do not have to be modified any longer.
+// Instead, we simply exchange the complete class dicts. This is done in the
+// basewrapper.cpp file.
+//
+
+static PyObject *methodWithNewName(PyTypeObject *type,
+ PyMethodDef *meth,
+ const char *new_name)
+{
+ /*
+ * Create a method with a lower case name.
+ */
+ auto *obtype = reinterpret_cast<PyObject *>(type);
+ int len = strlen(new_name);
+ auto name = new char[len + 1];
+ strcpy(name, new_name);
+ auto new_meth = new PyMethodDef;
+ new_meth->ml_name = name;
+ new_meth->ml_meth = meth->ml_meth;
+ new_meth->ml_flags = meth->ml_flags;
+ new_meth->ml_doc = meth->ml_doc;
+ PyObject *descr = nullptr;
+ if (new_meth->ml_flags & METH_STATIC) {
+ AutoDecRef cfunc(PyCFunction_NewEx(new_meth, obtype, nullptr));
+ if (cfunc.isNull())
+ return nullptr;
+ descr = PyStaticMethod_New(cfunc);
+ }
+ else {
+ descr = PyDescr_NewMethod(type, new_meth);
+ }
+ return descr;
+}
+
+static bool feature_01_addLowerNames(PyTypeObject *type, PyObject *prev_dict, int /* id */)
+{
+ PyMethodDef *meth = type->tp_methods;
+ 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.
+ if (!meth)
+ return PyDict_Update(lower_dict, prev_dict) >= 0;
+
+ /*
+ * Add objects with lower names to `type->tp_dict` from 'prev_dict`.
+ */
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+
+ // We first copy the things over which will not be changed:
+ while (PyDict_Next(prev_dict, &pos, &key, &value)) {
+ if (Py_TYPE(value) != PepMethodDescr_TypePtr
+ && Py_TYPE(value) != PepStaticMethod_TypePtr) {
+ if (PyDict_SetItem(lower_dict, key, value))
+ return false;
+ continue;
+ }
+ }
+
+ // Then we walk over the tp_methods to get all methods and insert
+ // them with changed names.
+
+ for (; meth != nullptr && meth->ml_name != nullptr; ++meth) {
+ const char *name = String::toCString(String::getSnakeCaseName(meth->ml_name, true));
+ AutoDecRef new_method(methodWithNewName(type, meth, name));
+ if (new_method.isNull())
+ return false;
+ if (PyDict_SetItemString(lower_dict, name, new_method) < 0)
+ return false;
+ }
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// PYSIDE-1019: Support switchable extensions
+//
+// Feature 0x02: Use true properties instead of getters and setters
+//
+
+// This is the Python 2 version for inspection of m_ml, only.
+// The actual Python 3 version is larget.
+
+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 */
+};
+
+static PyObject *modifyStaticToClassMethod(PyTypeObject *type, PyObject *sm)
+{
+ AutoDecRef func_ob(PyObject_GetAttr(sm, PyMagicName::func()));
+ if (func_ob.isNull())
+ return nullptr;
+ auto *func = reinterpret_cast<PyCFunctionObject *>(func_ob.object());
+ auto *new_func = new PyMethodDef;
+ new_func->ml_name = func->m_ml->ml_name;
+ new_func->ml_meth = func->m_ml->ml_meth;
+ new_func->ml_flags = (func->m_ml->ml_flags & ~METH_STATIC) | METH_CLASS;
+ new_func->ml_doc = func->m_ml->ml_doc;
+ PyCFunction_NewEx(new_func, nullptr, nullptr);
+ return PyDescr_NewClassMethod(type, new_func);
+}
+
+static PyObject *createProperty(PyTypeObject *type, PyObject *getter, PyObject *setter)
+{
+ assert(getter != nullptr);
+ if (setter == nullptr)
+ setter = Py_None;
+ auto *ptype = &PyProperty_Type;
+ if (Py_TYPE(getter) == PepStaticMethod_TypePtr) {
+ ptype = PyClassProperty_TypeF();
+ getter = modifyStaticToClassMethod(type, getter);
+ if (setter != Py_None)
+ setter = modifyStaticToClassMethod(type, setter);
+ }
+ auto *obtype = reinterpret_cast<PyObject *>(ptype);
+ PyObject *prop = PyObject_CallFunctionObjArgs(obtype, getter, setter, nullptr);
+ return prop;
+}
+
+static const QByteArrayList parseFields(const char *propStr, bool *stdWrite)
+{
+ /*
+ * Break the string into subfields at ':' and add defaults.
+ */
+ if (stdWrite)
+ *stdWrite = true;
+ QByteArray s = QByteArray(propStr);
+ auto list = s.split(u':');
+ assert(list.size() == 2 || list.size() == 3);
+ auto name = list[0];
+ auto read = list[1];
+ if (read.isEmpty())
+ list[1] = name;
+ if (list.size() == 2)
+ return list;
+ auto write = list[2];
+ if (stdWrite)
+ *stdWrite = write.isEmpty();
+ if (write.isEmpty()) {
+ list[2] = "set" + name;
+ list[2][3] = std::toupper(list[2][3]);
+ }
+ return list;
+}
+
+static PyObject *make_snake_case(const QByteArray &s, bool lower)
+{
+ if (s.isNull())
+ return nullptr;
+ return String::getSnakeCaseName(s.constData(), lower);
+}
+
+PyObject *adjustPropertyName(PyObject *dict, PyObject *name)
+{
+ // PYSIDE-1670: If this is a function with multiple arity or with
+ // parameters, we use a mangled name for the property.
+ PyObject *existing = PyDict_GetItem(dict, name); // borrowed
+ if (existing) {
+ Shiboken::AutoDecRef sig(get_signature_intern(existing, nullptr));
+ if (sig.object()) {
+ bool name_clash = false;
+ if (PyList_CheckExact(sig)) {
+ name_clash = true;
+ } else {
+ Shiboken::AutoDecRef params(PyObject_GetAttr(sig, PySideName::parameters()));
+ // Are there parameters except self or cls?
+ if (PyObject_Size(params.object()) > 1)
+ name_clash = true;
+ }
+ if (name_clash) {
+ // PyPy has no PyUnicode_AppendAndDel function, yet
+ Shiboken::AutoDecRef hold(name);
+ Shiboken::AutoDecRef under(Py_BuildValue("s", "_"));
+ name = PyUnicode_Concat(hold, under);
+ }
+ }
+ }
+ return name;
+}
+
+static QByteArrayList GetPropertyStringsMro(PyTypeObject *type)
+{
+ /*
+ * PYSIDE-2042: There are possibly more methods which should become properties,
+ * because the wrapping process does not obey inheritance.
+ * Therefore, we need to walk the mro to find property strings.
+ */
+ auto res = QByteArrayList();
+
+ PyObject *mro = type->tp_mro;
+ const Py_ssize_t n = PyTuple_GET_SIZE(mro);
+ // We leave 'Shiboken.Object' and 'object' alone, therefore "n - 2".
+ 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)
+ for (; *props != nullptr; ++props)
+ res << QByteArray(*props);
+ }
+ return res;
+}
+
+static bool feature_02_true_property(PyTypeObject *type, PyObject *prev_dict, int id)
+{
+ /*
+ * Use the property info to create true Python property objects.
+ */
+
+ PyMethodDef *meth = type->tp_methods;
+ 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)
+ return false;
+
+ // PYSIDE-1702: A user-defined class in Python has no internal method list.
+ // We are not going to change anything.
+ if (!meth)
+ return true;
+
+ // For speed, we establish a helper dict that maps the removed property
+ // method names to property name.
+ PyObject *prop_methods = PyDict_GetItem(prop_dict, PyMagicName::property_methods());
+ if (prop_methods == nullptr) {
+ prop_methods = PyDict_New();
+ if (prop_methods == nullptr
+ || PyDict_SetItem(prop_dict, PyMagicName::property_methods(), prop_methods))
+ return false;
+ }
+ // We then replace methods by properties.
+ bool lower = (id & 0x01) != 0;
+ auto props = GetPropertyStringsMro(type);
+ if (props.isEmpty())
+ return true;
+
+ for (const auto &propStr : std::as_const(props)) {
+ bool isStdWrite;
+ auto fields = parseFields(propStr, &isStdWrite);
+ bool haveWrite = fields.size() == 3;
+ PyObject *name = make_snake_case(fields[0], lower);
+ PyObject *read = make_snake_case(fields[1], lower);
+ PyObject *write = haveWrite ? make_snake_case(fields[2], lower) : nullptr;
+ PyObject *getter = PyDict_GetItem(prev_dict, read);
+ if (getter == nullptr || !(Py_TYPE(getter) == PepMethodDescr_TypePtr ||
+ Py_TYPE(getter) == PepStaticMethod_TypePtr))
+ continue;
+ PyObject *setter = haveWrite ? PyDict_GetItem(prev_dict, write) : nullptr;
+
+ // PYSIDE-1670: If multiple arities exist as a property name, rename it.
+ name = adjustPropertyName(prop_dict, name);
+
+ AutoDecRef PyProperty(createProperty(type, getter, setter));
+ if (PyProperty.isNull())
+ return false;
+ if (PyDict_SetItem(prop_dict, name, PyProperty) < 0
+ || PyDict_SetItem(prop_methods, read, name) < 0
+ || (setter != nullptr && PyDict_SetItem(prop_methods, write, name) < 0))
+ return false;
+ if (fields[0] != fields[1] && PyDict_GetItem(prop_dict, read))
+ if (PyDict_DelItem(prop_dict, read) < 0)
+ return false;
+ // Theoretically, we need to check for multiple signatures to be exact.
+ // But we don't do so intentionally because it would be confusing.
+ if (haveWrite && PyDict_GetItem(prop_dict, write) && isStdWrite) {
+ if (PyDict_DelItem(prop_dict, write) < 0)
+ return false;
+ }
+ }
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// These are a number of patches to make Python's property object better
+// suitable for us.
+// We turn `__doc__` into a lazy attribute saving signature initialization.
+//
+// There is now also a class property implementation which inherits
+// from this one.
+//
+
+static PyObject *property_doc_get(PyObject *self, void *)
+{
+ auto *po = reinterpret_cast<propertyobject *>(self);
+
+ if (po->prop_doc != nullptr && po->prop_doc != Py_None) {
+ Py_INCREF(po->prop_doc);
+ return po->prop_doc;
+ }
+ if (po->prop_get) {
+ // PYSIDE-1019: Fetch the default `__doc__` from fget. We do it late.
+ auto *txt = PyObject_GetAttr(po->prop_get, PyMagicName::doc());
+ if (txt != nullptr) {
+ Py_INCREF(txt);
+ po->prop_doc = txt;
+ Py_INCREF(txt);
+ return txt;
+ }
+ PyErr_Clear();
+ }
+ Py_RETURN_NONE;
+}
+
+static int property_doc_set(PyObject *self, PyObject *value, void *)
+{
+ auto *po = reinterpret_cast<propertyobject *>(self);
+
+ Py_INCREF(value);
+ po->prop_doc = value;
+ return 0;
+}
+
+static PyGetSetDef property_getset[] = {
+ // This gets added to the existing getsets
+ {const_cast<char *>("__doc__"), property_doc_get, property_doc_set, nullptr, nullptr},
+ {nullptr, nullptr, nullptr, nullptr, nullptr}
+};
+
+static bool patch_property_impl()
+{
+ // Turn `__doc__` into a computed attribute without changing writability.
+ auto gsp = property_getset;
+ auto *type = &PyProperty_Type;
+ AutoDecRef dict(PepType_GetDict(type));
+ AutoDecRef descr(PyDescr_NewGetSet(type, gsp));
+ if (descr.isNull())
+ return false;
+ if (PyDict_SetItemString(dict.object(), gsp->name, descr) < 0)
+ return false;
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// PYSIDE-1019: Support switchable extensions
+//
+// Feature 0x04..0x80: A fake switchable option for testing
+//
+
+#define SIMILAR_FEATURE(xx) \
+static bool feature_##xx##_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int /* id */) \
+{ \
+ 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) \
+ return false; \
+ return true; \
+}
+
+SIMILAR_FEATURE(04)
+SIMILAR_FEATURE(08)
+SIMILAR_FEATURE(10)
+SIMILAR_FEATURE(20)
+SIMILAR_FEATURE(40)
+SIMILAR_FEATURE(80)
+
+} // namespace PySide::Feature
diff --git a/sources/pyside6/libpyside/feature_select.h b/sources/pyside6/libpyside/feature_select.h
new file mode 100644
index 000000000..bf5a1b56b
--- /dev/null
+++ b/sources/pyside6/libpyside/feature_select.h
@@ -0,0 +1,19 @@
+// Copyright (C) 2020 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 FEATURE_SELECT_H
+#define FEATURE_SELECT_H
+
+#include "pysidemacros.h"
+#include <sbkpython.h>
+
+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 PySide::Feature
+
+#endif // FEATURE_SELECT_H
diff --git a/sources/pyside6/libpyside/globalreceiverv2.cpp b/sources/pyside6/libpyside/globalreceiverv2.cpp
new file mode 100644
index 000000000..2c75e39e1
--- /dev/null
+++ b/sources/pyside6/libpyside/globalreceiverv2.cpp
@@ -0,0 +1,322 @@
+// 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 "globalreceiverv2.h"
+#include "pysideweakref.h"
+#include "pysidestaticstrings.h"
+#include "pysideutils.h"
+#include "signalmanager.h"
+
+#include <autodecref.h>
+#include <gilstate.h>
+#include <pep384ext.h>
+
+#include <QtCore/QMetaMethod>
+#include <QtCore/QSet>
+#include <QtCore/QDebug>
+
+#include <cstring>
+
+#define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)"
+
+
+namespace PySide
+{
+
+class DynamicSlotDataV2
+{
+ Q_DISABLE_COPY_MOVE(DynamicSlotDataV2)
+ public:
+ DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *parent);
+ ~DynamicSlotDataV2();
+
+ int addSlot(const char *signature);
+ int id(const char *signature) const;
+ PyObject *callback();
+ GlobalReceiverKey key() const { return {m_pythonSelf, m_callback}; }
+ void notify();
+
+ static void onCallbackDestroyed(void *data);
+ static GlobalReceiverKey key(PyObject *callback);
+
+ void formatDebug(QDebug &debug) const;
+
+ private:
+ bool m_isMethod;
+ PyObject *m_callback;
+ PyObject *m_pythonSelf = nullptr;
+ PyObject *m_pyClass = nullptr;
+ PyObject *m_weakRef = nullptr;
+ QMap<QByteArray, int> m_signatures;
+ 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;
+
+DynamicSlotDataV2::DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *parent) :
+ m_parent(parent)
+{
+ Shiboken::GilState gil;
+
+ if (PyMethod_Check(callback)) {
+ m_isMethod = true;
+ // To avoid increment instance reference keep the callback information
+ m_callback = PyMethod_GET_FUNCTION(callback);
+ Py_INCREF(m_callback);
+ m_pythonSelf = PyMethod_GET_SELF(callback);
+
+ //monitor class from method lifetime
+ m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotDataV2::onCallbackDestroyed, this);
+ } else if (PySide::isCompiledMethod(callback)) {
+ // PYSIDE-1523: PyMethod_Check is not accepting compiled form, we just go by attributes.
+ m_isMethod = true;
+
+ m_callback = PyObject_GetAttr(callback, PySide::PySideName::im_func());
+ Py_DECREF(m_callback);
+
+ m_pythonSelf = PyObject_GetAttr(callback, PySide::PySideName::im_self());
+ Py_DECREF(m_pythonSelf);
+
+ //monitor class from method lifetime
+ m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotDataV2::onCallbackDestroyed, this);
+ } else {
+ m_isMethod = false;
+
+ m_callback = callback;
+ Py_INCREF(m_callback);
+ }
+}
+
+GlobalReceiverKey DynamicSlotDataV2::key(PyObject *callback)
+{
+ Shiboken::GilState gil;
+ if (PyMethod_Check(callback)) {
+ // PYSIDE-1422: Avoid hash on self which might be unhashable.
+ 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::PySideName::im_self()));
+ Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PySideName::im_func()));
+ return {self, func};
+ }
+ return {nullptr, callback};
+}
+
+PyObject *DynamicSlotDataV2::callback()
+{
+ PyObject *callback = m_callback;
+
+ //create a callback based on method data
+ if (m_isMethod)
+ callback = PepExt_Type_CallDescrGet(m_callback, m_pythonSelf, nullptr);
+ else
+ Py_INCREF(callback);
+
+ return callback;
+}
+
+int DynamicSlotDataV2::id(const char *signature) const
+{
+ const auto it = m_signatures.constFind(signature);
+ return it != m_signatures.cend() ? it.value() : -1;
+}
+
+int DynamicSlotDataV2::addSlot(const char *signature)
+{
+ int index = id(signature);
+ if (index == -1)
+ index = m_signatures[signature] = m_parent->metaObjectBuilder().addSlot(signature);
+ return index;
+}
+
+void DynamicSlotDataV2::onCallbackDestroyed(void *data)
+{
+ auto self = reinterpret_cast<DynamicSlotDataV2 *>(data);
+ self->m_weakRef = nullptr;
+ Py_BEGIN_ALLOW_THREADS
+ SignalManager::instance().deleteGlobalReceiver(self->m_parent);
+ Py_END_ALLOW_THREADS
+}
+
+DynamicSlotDataV2::~DynamicSlotDataV2()
+{
+ Shiboken::GilState gil;
+
+ Py_XDECREF(m_weakRef);
+ m_weakRef = nullptr;
+
+ Py_DECREF(m_callback);
+}
+
+const char *GlobalReceiverV2::senderDynamicProperty = "_q_pyside_sender";
+
+GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, QObject *receiver) :
+ QObject(nullptr),
+ m_metaObject("__GlobalReceiver__", &QObject::staticMetaObject),
+ m_receiver(receiver)
+{
+ m_data = new DynamicSlotDataV2(callback, this);
+}
+
+GlobalReceiverV2::~GlobalReceiverV2()
+{
+ m_refs.clear();
+ // Remove itself from map.
+ // 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.
+ // The signal has to be suppressed because it would lead to the following situation:
+ // Callback is deleted, hence the last reference is decremented,
+ // 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 = nullptr;
+ delete data;
+}
+
+int GlobalReceiverV2::addSlot(const char *signature)
+{
+ return m_data->addSlot(signature);
+}
+
+void GlobalReceiverV2::incRef(const QObject *link)
+{
+ Q_ASSERT(link);
+ m_refs.append(link);
+}
+
+void GlobalReceiverV2::decRef(const QObject *link)
+{
+ Q_ASSERT(link);
+ m_refs.removeOne(link);
+}
+
+void GlobalReceiverV2::notify()
+{
+ purgeDeletedSenders();
+}
+
+static bool isNull(const QPointer<const QObject> &p)
+{
+ return p.isNull();
+}
+
+void GlobalReceiverV2::purgeDeletedSenders()
+{
+ m_refs.erase(std::remove_if(m_refs.begin(), m_refs.end(), isNull), m_refs.end());
+}
+
+bool GlobalReceiverV2::isEmpty() const
+{
+ return std::all_of(m_refs.cbegin(), m_refs.cend(), isNull);
+}
+
+GlobalReceiverKey GlobalReceiverV2::key() const
+{
+ return m_data->key();
+}
+
+GlobalReceiverKey GlobalReceiverV2::key(PyObject *callback)
+{
+ return DynamicSlotDataV2::key(callback);
+}
+
+const QMetaObject *GlobalReceiverV2::metaObject() const
+{
+ return const_cast<GlobalReceiverV2 *>(this)->m_metaObject.update();
+}
+
+int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args)
+{
+ Shiboken::GilState gil;
+ Q_ASSERT(call == QMetaObject::InvokeMetaMethod);
+ Q_ASSERT(id >= QObject::staticMetaObject.methodCount());
+
+ QMetaMethod slot = metaObject()->method(id);
+ Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
+
+ if (!m_data) {
+ 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;
+ }
+
+ 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".
+ if (PyErr_Occurred()) {
+ int reclimit = Py_GetRecursionLimit();
+ // Inspired by Python's errors.c: PyErr_GivenExceptionMatches() function.
+ // Temporarily bump the recursion limit, so that PyErr_Print will not raise a recursion
+ // error again. Don't do it when the limit is already insanely high, to avoid overflow.
+ if (reclimit < (1 << 30))
+ Py_SetRecursionLimit(reclimit + 5);
+ PyErr_Print();
+ Py_SetRecursionLimit(reclimit);
+ }
+
+ 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
new file mode 100644
index 000000000..0e3bc562a
--- /dev/null
+++ b/sources/pyside6/libpyside/globalreceiverv2.h
@@ -0,0 +1,115 @@
+// 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 GLOBALRECEIVER_V2_H
+#define GLOBALRECEIVER_V2_H
+
+#include <sbkpython.h>
+
+#include "dynamicqmetaobject.h"
+
+#include <QtCore/QtCompare>
+#include <QtCore/QByteArray>
+#include <QtCore/QHashFunctions>
+#include <QtCore/QObject>
+#include <QtCore/QPointer>
+#include <QtCore/QMap>
+
+#include <memory>
+
+QT_FORWARD_DECLARE_CLASS(QDebug);
+
+namespace PySide
+{
+
+class DynamicSlotDataV2;
+class GlobalReceiverV2;
+
+struct GlobalReceiverKey
+{
+ const PyObject *object;
+ const PyObject *method;
+
+ 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:
+ 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
+ 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
+ int addSlot(const char *signature);
+
+ /// 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 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 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;
+ 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/libpyside.qrc b/sources/pyside6/libpyside/libpyside.qrc
new file mode 100644
index 000000000..1e652eb4c
--- /dev/null
+++ b/sources/pyside6/libpyside/libpyside.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/qt-project.org/logos">
+ <file alias="pysidelogo.png">../doc/_static/qtforpython.png</file>
+ </qresource>
+</RCC>
diff --git a/sources/pyside6/libpyside/pyside.cpp b/sources/pyside6/libpyside/pyside.cpp
new file mode 100644
index 000000000..9e12e3cd7
--- /dev/null
+++ b/sources/pyside6/libpyside/pyside.cpp
@@ -0,0 +1,1215 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "pyside.h"
+#include "pysideinit.h"
+#include "pysidecleanup.h"
+#include "pysidemetatype.h"
+#include "pysideqapp.h"
+#include "pysideqobject.h"
+#include "pysideutils.h"
+#include "pyside_p.h"
+#include "signalmanager.h"
+#include "pysideclassinfo_p.h"
+#include "pysideproperty_p.h"
+#include "class_property.h"
+#include "pysideproperty.h"
+#include "pysidesignal.h"
+#include "pysidesignal_p.h"
+#include "pysidestaticstrings.h"
+#include "pysideslot_p.h"
+#include "pysidemetafunction_p.h"
+#include "pysidemetafunction.h"
+#include "dynamicqmetaobject.h"
+#include "feature_select.h"
+#include "pysidelogging_p.h"
+
+#include <autodecref.h>
+#include <basewrapper.h>
+#include <bindingmanager.h>
+#include <gilstate.h>
+#include <sbkconverter.h>
+#include <sbkstring.h>
+#include <sbkstaticstrings.h>
+#include <sbkfeature_base.h>
+#include <sbkmodule.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QMetaMethod>
+#include <QtCore/QMutex>
+#include <QtCore/QStack>
+#include <QtCore/QThread>
+#include <QtCore/private/qobject_p.h>
+
+#include <algorithm>
+#include <cstring>
+#include <cctype>
+#include <memory>
+#include <optional>
+#include <typeinfo>
+
+using namespace Qt::StringLiterals;
+
+static QStack<PySide::CleanupFunction> cleanupFunctionList;
+static void *qobjectNextAddr;
+
+QT_BEGIN_NAMESPACE
+extern bool qRegisterResourceData(int, const unsigned char *, const unsigned char *,
+ const unsigned char *);
+QT_END_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcPySide, "qt.pyside.libpyside", QtCriticalMsg)
+
+static QObjectData *qt_object_private(const QObject *o)
+{
+ class FriendlyQObject : public QObject {
+ public:
+ using QObject::d_ptr;
+ };
+ return static_cast<const FriendlyQObject *>(o)->d_ptr.data();
+}
+
+static bool hasDynamicMetaObject(const QObject *o)
+{
+ return qt_object_private(o)->metaObject != nullptr;
+}
+
+namespace PySide
+{
+
+void init(PyObject *module)
+{
+ qobjectNextAddr = nullptr;
+ ClassInfo::init(module);
+ Signal::init(module);
+ Slot::init(module);
+ Property::init(module);
+ ClassProperty::init(module);
+ MetaFunction::init(module);
+ // Init signal manager, so it will register some meta types used by QVariant.
+ SignalManager::instance();
+ initQApp();
+}
+
+static const QByteArray _sigWithMangledName(const QByteArray &signature, bool mangle)
+{
+ if (!mangle)
+ return signature;
+ auto bracePos = signature.indexOf('(');
+ auto limit = bracePos >= 0 ? bracePos : signature.size();
+ if (limit < 3)
+ return signature;
+ QByteArray result;
+ result.reserve(signature.size() + 4);
+ for (auto i = 0; i < limit; ++i) {
+ const char c = signature.at(i);
+ if (std::isupper(c)) {
+ if (i > 0) {
+ if (std::isupper(signature.at(i - 1)))
+ return signature; // Give up at consecutive upper chars
+ result.append('_');
+ }
+ result.append(std::tolower(c));
+ } else {
+ result.append(c);
+ }
+ }
+ // Copy the rest after the opening brace (if any)
+ result.append(signature.mid(limit));
+ return result;
+}
+
+static const QByteArray _sigWithOrigName(const QByteArray &signature, bool mangle)
+{
+ if (!mangle)
+ return signature;
+ auto bracePos = signature.indexOf('(');
+ auto limit = bracePos >= 0 ? bracePos : signature.size();
+ QByteArray result;
+ result.reserve(signature.size());
+ for (auto i = 0; i < limit; ++i) {
+ const char c = signature.at(i);
+ if (std::isupper(c)) {
+ if (i > 0) {
+ if (std::isupper(signature.at(i - 1)))
+ return signature; // Give up at consecutive upper chars
+ return QByteArray{}; // Error, this was not converted!
+ }
+ }
+ if (std::islower(c) && i > 0 && signature.at(i - 1) == '_') {
+ result.chop(1);
+ result.append(std::toupper(c));
+ } else {
+ result.append(c);
+ }
+ }
+ // Copy the rest after the opening brace (if any)
+ result.append(signature.mid(limit));
+ return result;
+}
+
+/*****************************************************************************
+ *
+ * How do we find a property?
+ * --------------------------
+ *
+ * There are methods which are truly parts of properties, and there are
+ * other property-like methods which are not. True properties can be
+ * found by inspecting `SbkObjectType_GetPropertyStrings(type)`.
+ *
+ * Pseudo-properties have only a getter and a setter, and we must assume that
+ * the name of the getter is the property name, and the name of the setter
+ * is the uppercase of the getter with "set" prepended.
+ *
+ * We first walk the mro and search the property name and get the setter
+ * name. If that doesn't work, we use the heuristics for the setter.
+ * We then do the final mro lookup.
+ *
+ * Note that the true property lists have the original names, while the
+ * dict entries in the mro are already mangled.
+ */
+
+static const QByteArrayList parseFields(const char *propStr, int flags, bool *stdWrite)
+{
+ /*
+ * Break the string into subfields at ':' and add defaults.
+ */
+ if (stdWrite)
+ *stdWrite = true;
+ QByteArray s = QByteArray(propStr);
+ auto list = s.split(':');
+ assert(list.size() == 2 || list.size() == 3);
+ auto name = list[0];
+ auto read = list[1];
+ if (read.isEmpty())
+ list[1] = name;
+ if (list.size() == 2)
+ return list;
+ auto write = list[2];
+ if (stdWrite)
+ *stdWrite = write.isEmpty();
+ if (write.isEmpty()) {
+ auto snake_flag = flags & 0x01;
+ if (snake_flag) {
+ list[2] = ("set_") + name;
+ } else {
+ list[2] = QByteArray("set") + name;
+ list[2][3] = std::toupper(list[2][3]);
+ }
+ }
+ return list;
+}
+
+static QByteArrayList _SbkType_LookupProperty(PyTypeObject *type,
+ const QByteArray &name, int flags)
+{
+ /*
+ * Looks up a property and returns all fields.
+ */
+ int snake_flag = flags & 0x01;
+ QByteArray origName(_sigWithOrigName(name, snake_flag));
+ if (origName.isEmpty())
+ return QByteArrayList{};
+ PyObject *mro = type->tp_mro;
+ auto n = PyTuple_GET_SIZE(mro);
+ auto len = std::strlen(origName);
+ for (Py_ssize_t idx = 0; idx < n; idx++) {
+ PyTypeObject *base = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+ if (!SbkObjectType_Check(base))
+ continue;
+ auto props = SbkObjectType_GetPropertyStrings(base);
+ if (props == nullptr || *props == nullptr)
+ continue;
+ for (; *props != nullptr; ++props) {
+ QByteArray propStr(*props);
+ if (std::strncmp(propStr, origName, len) == 0) {
+ if (propStr[len] != ':')
+ continue;
+ // We found the property. Return the parsed fields.
+ propStr = _sigWithMangledName(propStr, snake_flag);
+ return parseFields(propStr, flags, nullptr);
+ }
+ }
+ }
+ return QByteArrayList{};
+}
+
+static QByteArrayList _SbkType_FakeProperty(const QByteArray &name, int flags)
+{
+ /*
+ * Handle a pseudo.property and return all fields.
+ */
+ int snake_flag = flags & 0x01;
+ QByteArray propStr(name);
+ propStr += "::";
+ propStr = _sigWithMangledName(propStr, snake_flag);
+ return parseFields(propStr, snake_flag, nullptr);
+}
+
+static bool _setProperty(PyObject *qObj, PyObject *name, PyObject *value, bool *accept)
+{
+ using Shiboken::AutoDecRef;
+
+ QByteArray propName(Shiboken::String::toCString(name));
+ auto type = Py_TYPE(qObj);
+ int flags = currentSelectId(type);
+ int prop_flag = flags & 0x02;
+ auto found = false;
+ QByteArray getterName{}, setterName{};
+
+ auto fields = _SbkType_LookupProperty(type, propName, flags);
+ if (!fields.isEmpty()) {
+ found = true;
+ bool haveWrite = fields.size() == 3;
+ if (!haveWrite)
+ return false;
+ } else {
+ fields = _SbkType_FakeProperty(propName, flags);
+ }
+
+ propName = fields[0];
+ getterName = fields[1];
+ setterName = fields[2];
+
+ // PYSIDE-1702: We do not use getattr, since that could trigger an action
+ // if we have a true property. Better to look inside the mro.
+ // That should return a descriptor or a property.
+ PyObject *look{};
+
+ if (found && prop_flag) {
+ // We have a property, and true_property is active.
+ // There must be a property object and we use it's fset.
+ AutoDecRef pyPropName(Shiboken::String::fromCString(propName.constData()));
+ look = _PepType_Lookup(Py_TYPE(qObj), pyPropName);
+ } else {
+ // We have a pseudo property or true_property is off, looking for a setter.
+ AutoDecRef pySetterName(Shiboken::String::fromCString(setterName.constData()));
+ look = _PepType_Lookup(Py_TYPE(qObj), pySetterName);
+ }
+
+ if (look) {
+ AutoDecRef propSetter{};
+ static PyObject *magicGet = Shiboken::PyMagicName::get();
+ if (found && prop_flag) {
+ // the indirection of the setter descriptor in a true property
+ AutoDecRef descr(PyObject_GetAttr(look, PySideName::fset()));
+ propSetter.reset(PyObject_CallMethodObjArgs(descr, magicGet, qObj, nullptr));
+ } else {
+ // look is already the descriptor
+ propSetter.reset(PyObject_CallMethodObjArgs(look, magicGet, qObj, nullptr));
+ }
+ *accept = true;
+ AutoDecRef args(PyTuple_Pack(1, value));
+ AutoDecRef retval(PyObject_CallObject(propSetter, args));
+ if (retval.isNull())
+ return false;
+ } else {
+ PyErr_Clear();
+ AutoDecRef attr(PyObject_GenericGetAttr(qObj, name));
+ if (PySide::Property::checkType(attr)) {
+ *accept = true;
+ if (PySide::Property::setValue(reinterpret_cast<PySideProperty *>(
+ attr.object()), qObj, value) < 0)
+ return false;
+ }
+ }
+ return true;
+}
+
+// PYSIDE-2329: Search a signal by name (Note: QMetaObject::indexOfSignal()
+// searches by signature).
+static std::optional<QMetaMethod> findSignal(const QMetaObject *mo,
+ const QByteArray &name)
+{
+ const auto count = mo->methodCount();
+ for (int i = mo->methodOffset(); i < count; ++i) {
+ const auto method = mo->method(i);
+ if (method.methodType() == QMetaMethod::Signal && method.name() == name)
+ return method;
+ }
+ auto *base = mo->superClass();
+ return base != nullptr ? findSignal(base, name) : std::nullopt;
+}
+
+bool fillQtProperties(PyObject *qObj, const QMetaObject *metaObj,
+ PyObject *kwds, bool allowErrors)
+{
+
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ int flags = currentSelectId(Py_TYPE(qObj));
+ int snake_flag = flags & 0x01;
+
+ while (PyDict_Next(kwds, &pos, &key, &value)) {
+ const QByteArray propName = Shiboken::String::toCString(key);
+ QByteArray unmangledName = _sigWithOrigName(propName, snake_flag);
+ bool accept = false;
+ // PYSIDE-1705: Make sure that un-mangled names are not recognized in snake_case mode.
+ if (!unmangledName.isEmpty()) {
+ if (metaObj->indexOfProperty(unmangledName) != -1) {
+ if (!_setProperty(qObj, key, value, &accept))
+ return false;
+ } else {
+ const auto methodO = findSignal(metaObj, propName);
+ if (methodO.has_value()) {
+ const auto signature = "2"_ba + methodO->methodSignature();
+ accept = true;
+ if (!PySide::Signal::connect(qObj, signature, value))
+ return false;
+ }
+ }
+ if (!accept) {
+ // PYSIDE-1019: Allow any existing attribute in the constructor.
+ if (!_setProperty(qObj, key, value, &accept))
+ return false;
+ }
+ }
+ if (allowErrors) {
+ PyErr_Clear();
+ continue;
+ }
+ if (!accept) {
+ PyErr_Format(PyExc_AttributeError, "'%s' is not a Qt property or a signal",
+ propName.constData());
+ return false;
+ }
+ }
+ return true;
+}
+
+void registerCleanupFunction(CleanupFunction func)
+{
+ cleanupFunctionList.push(func);
+}
+
+void runCleanupFunctions()
+{
+ while (!cleanupFunctionList.isEmpty()) {
+ CleanupFunction f = cleanupFunctionList.pop();
+ f();
+ }
+}
+
+static void destructionVisitor(SbkObject *pyObj, void *data)
+{
+ auto realData = reinterpret_cast<void **>(data);
+ auto pyQApp = reinterpret_cast<SbkObject *>(realData[0]);
+ auto pyQObjectType = reinterpret_cast<PyTypeObject *>(realData[1]);
+
+ if (pyObj != pyQApp && PyObject_TypeCheck(pyObj, pyQObjectType)) {
+ if (Shiboken::Object::hasOwnership(pyObj) && Shiboken::Object::isValid(pyObj, false)) {
+ Shiboken::Object::setValidCpp(pyObj, false);
+
+ Py_BEGIN_ALLOW_THREADS
+ Shiboken::callCppDestructor<QObject>(Shiboken::Object::cppPointer(pyObj, pyQObjectType));
+ Py_END_ALLOW_THREADS
+ }
+ }
+
+};
+
+void destroyQCoreApplication()
+{
+ QCoreApplication *app = QCoreApplication::instance();
+ if (!app)
+ return;
+ SignalManager::instance().clear();
+
+ Shiboken::BindingManager &bm = Shiboken::BindingManager::instance();
+ SbkObject *pyQApp = bm.retrieveWrapper(app);
+ PyTypeObject *pyQObjectType = Shiboken::Conversions::getPythonTypeObject("QObject*");
+ assert(pyQObjectType);
+
+ void *data[2] = {pyQApp, pyQObjectType};
+ bm.visitAllPyObjects(&destructionVisitor, &data);
+
+ // in the end destroy app
+ // Allow threads because the destructor calls
+ // QThreadPool::globalInstance().waitForDone() which may deadlock on the GIL
+ // if there is a worker working with python objects.
+ Py_BEGIN_ALLOW_THREADS
+ delete app;
+ Py_END_ALLOW_THREADS
+ // PYSIDE-571: make sure to create a singleton deleted qApp.
+ Py_DECREF(MakeQAppWrapper(nullptr));
+}
+
+std::size_t getSizeOfQObject(PyTypeObject *type)
+{
+ return retrieveTypeUserData(type)->cppObjSize;
+}
+
+void initDynamicMetaObject(PyTypeObject *type, const QMetaObject *base, std::size_t cppObjSize)
+{
+ //create DynamicMetaObject based on python type
+ auto userData = new TypeUserData(reinterpret_cast<PyTypeObject *>(type), base, cppObjSize);
+ userData->mo.update();
+ Shiboken::ObjectType::setTypeUserData(type, userData, Shiboken::callCppDestructor<TypeUserData>);
+
+ //initialize staticQMetaObject property
+ void *metaObjectPtr = const_cast<QMetaObject *>(userData->mo.update());
+ static SbkConverter *converter = Shiboken::Conversions::getConverter("QMetaObject");
+ if (!converter)
+ return;
+ Shiboken::AutoDecRef pyMetaObject(Shiboken::Conversions::pointerToPython(converter, metaObjectPtr));
+ PyObject_SetAttr(reinterpret_cast<PyObject *>(type),
+ Shiboken::PyName::qtStaticMetaObject(), pyMetaObject);
+}
+
+TypeUserData *retrieveTypeUserData(PyTypeObject *pyTypeObj)
+{
+ if (!SbkObjectType_Check(pyTypeObj))
+ return nullptr;
+ return reinterpret_cast<TypeUserData *>(Shiboken::ObjectType::getTypeUserData(pyTypeObj));
+}
+
+TypeUserData *retrieveTypeUserData(PyObject *pyObj)
+{
+ auto pyTypeObj = PyType_Check(pyObj)
+ ? reinterpret_cast<PyTypeObject *>(pyObj) : Py_TYPE(pyObj);
+ return retrieveTypeUserData(pyTypeObj);
+}
+
+const QMetaObject *retrieveMetaObject(PyTypeObject *pyTypeObj)
+{
+ TypeUserData *userData = retrieveTypeUserData(pyTypeObj);
+ return userData ? userData->mo.update() : nullptr;
+}
+
+const QMetaObject *retrieveMetaObject(PyObject *pyObj)
+{
+ auto pyTypeObj = PyType_Check(pyObj)
+ ? reinterpret_cast<PyTypeObject *>(pyObj) : Py_TYPE(pyObj);
+ return retrieveMetaObject(pyTypeObj);
+}
+
+void initQObjectSubType(PyTypeObject *type, PyObject *args, PyObject * /* kwds */)
+{
+ PyTypeObject *qObjType = Shiboken::Conversions::getPythonTypeObject("QObject*");
+
+ PyObject *bases = PyTuple_GET_ITEM(args, 1);
+ int numBases = PyTuple_GET_SIZE(bases);
+
+ TypeUserData *userData = nullptr;
+
+ for (int i = 0; i < numBases; ++i) {
+ auto base = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, i));
+ if (PyType_IsSubtype(base, qObjType)) {
+ userData = retrieveTypeUserData(base);
+ break;
+ }
+ }
+ if (!userData) {
+ const char *className = Shiboken::String::toCString(PyTuple_GET_ITEM(args, 0));
+ qWarning("Sub class of QObject not inheriting QObject!? Crash will happen when using %s.",
+ className);
+ return;
+ }
+ // PYSIDE-1463: Don't change feature selection durin subtype initialization.
+ // This behavior is observed with PySide 6.
+ PySide::Feature::Enable(false);
+ initDynamicMetaObject(type, userData->mo.update(), userData->cppObjSize);
+ PySide::Feature::Enable(true);
+}
+
+void initQApp()
+{
+ /*
+ * qApp will not be initialized when embedding is active.
+ * That means that qApp exists already when PySide is initialized.
+ * We could solve that by creating a qApp variable, but in embedded
+ * mode, we also have the effect that the first assignment to qApp
+ * is persistent! Therefore, we can never be sure to have created
+ * qApp late enough to get the right type for the instance.
+ *
+ * I would appreciate very much if someone could explain or even fix
+ * this issue. It exists only when a pre-existing application exists.
+ */
+ if (!qApp)
+ Py_DECREF(MakeQAppWrapper(nullptr));
+
+ // PYSIDE-1470: Register a function to destroy an application from shiboken.
+ setDestroyQApplication(destroyQCoreApplication);
+}
+
+PyObject *getHiddenDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *name)
+{
+ using Shiboken::AutoDecRef;
+
+ // PYSIDE-68-bis: This getattr finds signals early by `signalDescrGet`.
+ PyObject *attr = PyObject_GenericGetAttr(self, name);
+ if (!Shiboken::Object::isValid(reinterpret_cast<SbkObject *>(self), false))
+ return attr;
+
+ if (attr && Property::checkType(attr)) {
+ PyObject *value = Property::getValue(reinterpret_cast<PySideProperty *>(attr), self);
+ Py_DECREF(attr);
+ if (!value)
+ return nullptr;
+ attr = value;
+ }
+
+ // Search on metaobject (avoid internal attributes started with '__')
+ if (!attr) {
+ PyObject *type, *value, *traceback;
+ PyErr_Fetch(&type, &value, &traceback); // This was omitted for a loong time.
+
+ int flags = currentSelectId(Py_TYPE(self));
+ int snake_flag = flags & 0x01;
+ int propFlag = flags & 0x02;
+
+ if (propFlag) {
+ // PYSIDE-1889: If we have actually a Python property, return f(get|set|del).
+ // Do not store this attribute in the instance dict, because this
+ // would create confusion with overload.
+ // Note: before implementing this property handling, the meta function code
+ // below created meta functions which was quite wrong.
+ auto *subdict = _PepType_Lookup(Py_TYPE(self), PySideMagicName::property_methods());
+ PyObject *propName = PyDict_GetItem(subdict, name);
+ if (propName) {
+ // We really have a property name and need to fetch the fget or fset function.
+ static PyObject *const _fget = Shiboken::String::createStaticString("fget");
+ static PyObject *const _fset = Shiboken::String::createStaticString("fset");
+ static PyObject *const _fdel = Shiboken::String::createStaticString("fdel");
+ static PyObject *const arr[3] = {_fget, _fset, _fdel};
+ auto prop = _PepType_Lookup(Py_TYPE(self), propName);
+ for (int idx = 0; idx < 3; ++idx) {
+ auto *trial = arr[idx];
+ auto *res = PyObject_GetAttr(prop, trial);
+ if (res) {
+ AutoDecRef elemName(PyObject_GetAttr(res, PySideMagicName::name()));
+ // Note: This comparison works because of interned strings.
+ if (elemName == name)
+ return res;
+ Py_DECREF(res);
+ }
+ PyErr_Clear();
+ }
+ }
+ }
+
+ const char *cname = Shiboken::String::toCString(name);
+ uint cnameLen = qstrlen(cname);
+ if (std::strncmp("__", cname, 2) != 0) {
+ const QMetaObject *metaObject = cppSelf->metaObject();
+ QList<QMetaMethod> signalList;
+ // Caution: This inserts a meta function or a signal into the instance dict.
+ for (int i=0, imax = metaObject->methodCount(); i < imax; i++) {
+ QMetaMethod method = metaObject->method(i);
+ // PYSIDE-1753: Snake case names must be renamed here too, or they will be
+ // found unexpectedly when forgetting to rename them.
+ auto origSignature = method.methodSignature();
+ // Currently, we rename only methods but no signals. This might change.
+ bool use_lower = snake_flag and method.methodType() != QMetaMethod::Signal;
+ const QByteArray methSig_ = _sigWithMangledName(origSignature, use_lower);
+ const char *methSig = methSig_.constData();
+ bool methMatch = std::strncmp(cname, methSig, cnameLen) == 0
+ && methSig[cnameLen] == '(';
+ if (methMatch) {
+ if (method.methodType() == QMetaMethod::Signal) {
+ signalList.append(method);
+ } else {
+ PySideMetaFunction *func = MetaFunction::newObject(cppSelf, i);
+ if (func) {
+ PyObject *result = reinterpret_cast<PyObject *>(func);
+ PyObject_SetAttr(self, name, result);
+ return result;
+ }
+ }
+ }
+ }
+ if (!signalList.isEmpty()) {
+ PyObject *pySignal = reinterpret_cast<PyObject *>(
+ Signal::newObjectFromMethod(self, signalList));
+ PyObject_SetAttr(self, name, pySignal);
+ return pySignal;
+ }
+ }
+ PyErr_Restore(type, value, traceback);
+ }
+ return attr;
+}
+
+bool inherits(PyTypeObject *objType, const char *class_name)
+{
+ if (strcmp(objType->tp_name, class_name) == 0)
+ return true;
+
+ PyTypeObject *base = objType->tp_base;
+ if (base == nullptr)
+ return false;
+
+ return inherits(base, class_name);
+}
+
+QMutex &nextQObjectMemoryAddrMutex()
+{
+ static QMutex mutex;
+ return mutex;
+}
+
+void *nextQObjectMemoryAddr()
+{
+ return qobjectNextAddr;
+}
+
+void setNextQObjectMemoryAddr(void *addr)
+{
+ qobjectNextAddr = addr;
+}
+
+} // namespace PySide
+
+// A std::shared_ptr is used with a deletion function to invalidate a pointer
+// when the property value is cleared. This should be a QSharedPointer with
+// a void *pointer, but that isn't allowed
+using any_t = char;
+Q_DECLARE_METATYPE(std::shared_ptr<any_t>);
+
+
+namespace PySide
+{
+
+static void invalidatePtr(any_t *object)
+{
+ // PYSIDE-2254: Guard against QObjects outliving Python, for example the
+ // adopted main thread as returned by QObjects::thread().
+ if (Py_IsInitialized() == 0)
+ return;
+
+ Shiboken::GilState state;
+
+ SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(object);
+ if (wrapper != nullptr)
+ Shiboken::BindingManager::instance().releaseWrapper(wrapper);
+}
+
+static const char invalidatePropertyName[] = "_PySideInvalidatePtr";
+
+// PYSIDE-2749: Skip over internal QML classes and classes
+// with dynamic meta objects when looking for the best matching
+// type to avoid unnessarily triggering the lazy load mechanism
+// for classes that do not have a binding from things like eventFilter().
+static inline bool isInternalObject(const char *name)
+{
+ return std::strstr(name, "QMLTYPE") != nullptr || std::strstr(name, "QQmlPrivate") != nullptr;
+}
+
+static const QMetaObject *metaObjectCandidate(const QObject *o)
+{
+ auto *metaObject = o->metaObject();
+ // Skip QML helper types and Python objects
+ if (hasDynamicMetaObject(o)) {
+ if (auto *super = metaObject->superClass())
+ metaObject = super;
+ }
+ for (auto *candidate = metaObject; candidate != nullptr; candidate = candidate->superClass()) {
+ if (!isInternalObject(candidate->className())) {
+ metaObject = candidate;
+ break;
+ }
+ }
+ return metaObject;
+}
+
+// PYSIDE-1214, when creating new wrappers for classes inheriting QObject but
+// not exposed to Python, try to find the best-matching (most-derived) Qt
+// class by walking up the meta objects.
+static const char *typeName(const QObject *cppSelf)
+{
+ const char *typeName = typeid(*cppSelf).name();
+ if (!Shiboken::Conversions::getConverter(typeName)) {
+ auto *metaObject = metaObjectCandidate(cppSelf);
+ for (; metaObject != nullptr; metaObject = metaObject->superClass()) {
+ const char *name = metaObject->className();
+ if (Shiboken::Conversions::getConverter(name)) {
+ typeName = name;
+ break;
+ }
+ }
+ }
+ return typeName;
+}
+
+PyTypeObject *getTypeForQObject(const QObject *cppSelf)
+{
+ // First check if there are any instances of Python implementations
+ // inheriting a PySide class.
+ auto *existing = Shiboken::BindingManager::instance().retrieveWrapper(cppSelf);
+ if (existing != nullptr)
+ return reinterpret_cast<PyObject *>(existing)->ob_type;
+ // Find the best match (will return a PySide type)
+ auto *sbkObjectType = Shiboken::ObjectType::typeForTypeName(typeName(cppSelf));
+ if (sbkObjectType != nullptr)
+ return reinterpret_cast<PyTypeObject *>(sbkObjectType);
+ return nullptr;
+}
+
+PyObject *getWrapperForQObject(QObject *cppSelf, PyTypeObject *sbk_type)
+{
+ PyObject *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppSelf));
+ if (pyOut) {
+ Py_INCREF(pyOut);
+ return pyOut;
+ }
+
+ // Setting the property will trigger an QEvent notification, which may call into
+ // code that creates the wrapper so only set the property if it isn't already
+ // set and check if it's created after the set call
+ QVariant existing = cppSelf->property(invalidatePropertyName);
+ if (!existing.isValid()) {
+ if (cppSelf->thread() == QThread::currentThread()) {
+ std::shared_ptr<any_t> shared_with_del(reinterpret_cast<any_t *>(cppSelf), invalidatePtr);
+ cppSelf->setProperty(invalidatePropertyName, QVariant::fromValue(shared_with_del));
+ }
+ pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppSelf));
+ if (pyOut) {
+ Py_INCREF(pyOut);
+ return pyOut;
+ }
+ }
+
+ pyOut = Shiboken::Object::newObjectWithHeuristics(sbk_type, cppSelf, false, typeName(cppSelf));
+
+ return pyOut;
+}
+
+QString pyUnicodeToQString(PyObject *str)
+{
+ Q_ASSERT(PyUnicode_Check(str) != 0);
+
+ const void *data = _PepUnicode_DATA(str);
+ const Py_ssize_t len = PyUnicode_GetLength(str);
+ switch (_PepUnicode_KIND(str)) {
+ case PepUnicode_1BYTE_KIND:
+ return QString::fromLatin1(reinterpret_cast<const char *>(data), len);
+ case PepUnicode_2BYTE_KIND:
+ return QString::fromUtf16(reinterpret_cast<const char16_t *>(data), len);
+ case PepUnicode_4BYTE_KIND:
+ break;
+ }
+ return QString::fromUcs4(reinterpret_cast<const char32_t *>(data), len);
+}
+
+PyObject *qStringToPyUnicode(QStringView s)
+{
+ const QByteArray ba = s.toUtf8();
+ return PyUnicode_FromStringAndSize(ba.constData(), ba.size());
+}
+
+// Inspired by Shiboken::String::toCString;
+QString pyStringToQString(PyObject *str)
+{
+ if (str == Py_None)
+ return QString();
+
+ if (PyUnicode_Check(str) != 0)
+ return pyUnicodeToQString(str);
+
+ if (PyBytes_Check(str)) {
+ const char *asciiBuffer = PyBytes_AS_STRING(str);
+ if (asciiBuffer)
+ return QString::fromLatin1(asciiBuffer);
+ }
+ return QString();
+}
+
+// PySide-1499: Provide an efficient, correct PathLike interface
+QString pyPathToQString(PyObject *path)
+{
+ // For empty constructors path can be nullptr
+ // fallback to an empty QString in that case.
+ if (!path)
+ return QString();
+
+ // str or bytes pass through
+ if (PyUnicode_Check(path) || PyBytes_Check(path))
+ return pyStringToQString(path);
+
+ // Let PyOS_FSPath do its work and then fix the result for Windows.
+ Shiboken::AutoDecRef strPath(PyOS_FSPath(path));
+ if (strPath.isNull())
+ return QString();
+ return QDir::fromNativeSeparators(pyStringToQString(strPath));
+}
+
+bool isCompiledMethod(PyObject *callback)
+{
+ return PyObject_HasAttr(callback, PySide::PySideName::im_func())
+ && PyObject_HasAttr(callback, PySide::PySideName::im_self())
+ && PyObject_HasAttr(callback, PySide::PySideMagicName::code());
+}
+
+static const unsigned char qt_resource_name[] = {
+ // qt
+ 0x0,0x2,
+ 0x0,0x0,0x7,0x84,
+ 0x0,0x71,
+ 0x0,0x74,
+ // etc
+ 0x0,0x3,
+ 0x0,0x0,0x6c,0xa3,
+ 0x0,0x65,
+ 0x0,0x74,0x0,0x63,
+ // qt.conf
+ 0x0,0x7,
+ 0x8,0x74,0xa6,0xa6,
+ 0x0,0x71,
+ 0x0,0x74,0x0,0x2e,0x0,0x63,0x0,0x6f,0x0,0x6e,0x0,0x66
+};
+
+static const unsigned char qt_resource_struct[] = {
+ // :
+ 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+ // :/qt
+ 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,
+ // :/qt/etc
+ 0x0,0x0,0x0,0xa,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,
+ // :/qt/etc/qt.conf
+ 0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0
+};
+
+bool registerInternalQtConf()
+{
+ // Guard to ensure single registration.
+#ifdef PYSIDE_QT_CONF_PREFIX
+ static bool registrationAttempted = false;
+#else
+ static bool registrationAttempted = true;
+#endif
+ static bool isRegistered = false;
+ if (registrationAttempted)
+ return isRegistered;
+ registrationAttempted = true;
+
+ // Support PyInstaller case when a qt.conf file might be provided next to the generated
+ // PyInstaller executable.
+ // This will disable the internal qt.conf which points to the PySide6 subdirectory (due to the
+ // subdirectory not existing anymore).
+#ifndef PYPY_VERSION
+ QString executablePath = QString::fromWCharArray(Py_GetProgramFullPath());
+#else
+ // PYSIDE-535: FIXME: Add this function when available.
+ QString executablePath = QLatin1StringView("missing Py_GetProgramFullPath");
+#endif // PYPY_VERSION
+
+ QString appDirPath = QFileInfo(executablePath).absolutePath();
+
+ QString maybeQtConfPath = QDir(appDirPath).filePath(u"qt.conf"_s);
+ maybeQtConfPath = QDir::toNativeSeparators(maybeQtConfPath);
+ bool executableQtConfAvailable = QFileInfo::exists(maybeQtConfPath);
+
+ QString maybeQt6ConfPath = QDir(appDirPath).filePath(u"qt6.conf"_s);
+ maybeQt6ConfPath = QDir::toNativeSeparators(maybeQt6ConfPath);
+ bool executableQt6ConfAvailable = QFileInfo::exists(maybeQt6ConfPath);
+
+ // Allow disabling the usage of the internal qt.conf. This is necessary for tests to work,
+ // because tests are executed before the package is installed, and thus the Prefix specified
+ // in qt.conf would point to a not yet existing location.
+ bool disableInternalQtConf =
+ qEnvironmentVariableIntValue("PYSIDE_DISABLE_INTERNAL_QT_CONF") > 0;
+ bool runsInConda =
+ qEnvironmentVariableIsSet("CONDA_DEFAULT_ENV") || qEnvironmentVariableIsSet("CONDA_PREFIX");
+
+ if ((!runsInConda && (disableInternalQtConf || executableQtConfAvailable))
+ || (runsInConda && executableQt6ConfAvailable)) {
+ registrationAttempted = true;
+ return false;
+ }
+
+ PyObject *pysideModule = PyImport_ImportModule("PySide6");
+ if (!pysideModule)
+ return false;
+
+ // Querying __file__ should be done only for modules that have finished their initialization.
+ // Thus querying for the top-level PySide6 package works for us whenever any Qt-wrapped module
+ // is loaded.
+ PyObject *pysideInitFilePath = PyObject_GetAttr(pysideModule, Shiboken::PyMagicName::file());
+ Py_DECREF(pysideModule);
+ if (!pysideInitFilePath)
+ return false;
+
+ QString initPath = pyStringToQString(pysideInitFilePath);
+ Py_DECREF(pysideInitFilePath);
+ if (initPath.isEmpty())
+ return false;
+
+ // pysideDir - absolute path to the directory containing the init file, which also contains
+ // the rest of the PySide6 modules.
+ // prefixPath - absolute path to the directory containing the installed Qt (prefix).
+ QDir pysideDir = QFileInfo(QDir::fromNativeSeparators(initPath)).absoluteDir();
+ QString setupPrefix;
+#ifdef PYSIDE_QT_CONF_PREFIX
+ setupPrefix = QStringLiteral(PYSIDE_QT_CONF_PREFIX);
+#endif
+ const QByteArray prefixPath = pysideDir.absoluteFilePath(setupPrefix).toUtf8();
+
+ // rccData needs to be static, otherwise when it goes out of scope, the Qt resource system
+ // will point to invalid memory.
+ static QByteArray rccData = QByteArrayLiteral("[Paths]\nPrefix = ") + prefixPath + "\n";
+#ifdef Q_OS_WIN
+ // LibraryExecutables needs to point to Prefix instead of ./bin because we don't
+ // currently conform to the Qt default directory layout on Windows. This is necessary
+ // for QtWebEngineCore to find the location of QtWebEngineProcess.exe.
+ rccData += QByteArrayLiteral("LibraryExecutables = ") + prefixPath + "\n";
+#endif
+
+ // The RCC data structure expects a 4-byte size value representing the actual data.
+ qsizetype size = rccData.size();
+
+ for (int i = 0; i < 4; ++i) {
+ rccData.prepend((size & 0xff));
+ size >>= 8;
+ }
+
+ const int version = 0x01;
+ isRegistered = qRegisterResourceData(version, qt_resource_struct, qt_resource_name,
+ reinterpret_cast<const unsigned char *>(
+ rccData.constData()));
+
+ return isRegistered;
+}
+
+static PyTypeObject *qobjectType()
+{
+ static PyTypeObject * const result = Shiboken::Conversions::getPythonTypeObject("QObject*");
+ return result;
+}
+
+bool isQObjectDerived(PyTypeObject *pyType, bool raiseError)
+{
+ const bool result = PyType_IsSubtype(pyType, qobjectType());
+ if (!result && raiseError) {
+ PyErr_Format(PyExc_TypeError, "A type inherited from %s expected, got %s.",
+ qobjectType()->tp_name, pyType->tp_name);
+ }
+ return result;
+}
+
+QObject *convertToQObject(PyObject *object, bool raiseError)
+{
+ if (object == nullptr) {
+ if (raiseError)
+ PyErr_Format(PyExc_TypeError, "None passed for QObject");
+ return nullptr;
+ }
+
+ if (!isQObjectDerived(Py_TYPE(object), raiseError))
+ return nullptr;
+
+ auto *sbkObject = reinterpret_cast<SbkObject*>(object);
+ auto *ptr = Shiboken::Object::cppPointer(sbkObject, qobjectType());
+ if (ptr == nullptr) {
+ if (raiseError) {
+ PyErr_Format(PyExc_TypeError, "Conversion of %s to QObject failed.",
+ Py_TYPE(object)->tp_name);
+ }
+ return nullptr;
+ }
+ return reinterpret_cast<QObject*>(ptr);
+}
+
+QMetaType qMetaTypeFromPyType(PyTypeObject *pyType)
+{
+ if (Shiboken::String::checkType(pyType))
+ return QMetaType(QMetaType::QString);
+ if (pyType == &PyFloat_Type)
+ return QMetaType(QMetaType::Double);
+ if (pyType == &PyLong_Type)
+ return QMetaType(QMetaType::Int);
+ if (Shiboken::ObjectType::checkType(pyType))
+ return QMetaType::fromName(Shiboken::ObjectType::getOriginalName(pyType));
+ return QMetaType::fromName(pyType->tp_name);
+}
+
+debugPyTypeObject::debugPyTypeObject(const PyTypeObject *o) noexcept
+ : m_object(o)
+{
+}
+
+QDebug operator<<(QDebug debug, const debugPyTypeObject &o)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "PyTypeObject(";
+ if (o.m_object)
+ debug << '"' << o.m_object->tp_name << '"';
+ else
+ debug << '0';
+ debug << ')';
+ return debug;
+}
+
+static void formatPyObject(PyObject *obj, QDebug &debug);
+
+static void formatPySequence(PyObject *obj, QDebug &debug)
+{
+ const Py_ssize_t size = PySequence_Size(obj);
+ debug << size << " [";
+ for (Py_ssize_t i = 0; i < size; ++i) {
+ if (i)
+ debug << ", ";
+ Shiboken::AutoDecRef item(PySequence_GetItem(obj, i));
+ formatPyObject(item.object(), debug);
+ }
+ debug << ']';
+}
+
+static void formatPyDict(PyObject *obj, QDebug &debug)
+{
+ PyObject *key;
+ PyObject *value;
+ Py_ssize_t pos = 0;
+ bool first = true;
+ debug << '{';
+ while (PyDict_Next(obj, &pos, &key, &value) != 0) {
+ if (first)
+ first = false;
+ else
+ debug << ", ";
+ formatPyObject(key, debug);
+ debug << '=';
+ formatPyObject(value, debug);
+ }
+ debug << '}';
+}
+
+static inline const char *pyTypeName(PyObject *obj)
+{
+ return Py_TYPE(obj)->tp_name;
+}
+
+static QString getQualName(PyObject *obj)
+{
+ Shiboken::AutoDecRef result(PyObject_GetAttr(obj, Shiboken::PyMagicName::qualname()));
+ return result.object() != nullptr
+ ? pyStringToQString(result.object()) : QString{};
+}
+
+static void formatPyFunction(PyObject *obj, QDebug &debug)
+{
+ debug << '"' << getQualName(obj) << "()\"";
+}
+
+static void formatPyMethod(PyObject *obj, QDebug &debug)
+{
+ if (auto *func = PyMethod_Function(obj))
+ formatPyFunction(func, debug);
+ debug << ", instance=" << PyMethod_Self(obj);
+}
+
+static void formatPyObjectValue(PyObject *obj, QDebug &debug)
+{
+ if (PyType_Check(obj) != 0)
+ debug << "type: \"" << pyTypeName(obj) << '"';
+ else if (PyLong_Check(obj) != 0) {
+ const auto llv = PyLong_AsLongLong(obj);
+ if (PyErr_Occurred() != PyExc_OverflowError) {
+ debug << llv;
+ } else {
+ PyErr_Clear();
+ debug << "0x" << Qt::hex << PyLong_AsUnsignedLongLong(obj) << Qt::dec;
+ }
+ } else if (PyFloat_Check(obj) != 0)
+ debug << PyFloat_AsDouble(obj);
+ else if (PyUnicode_Check(obj) != 0)
+ debug << '"' << pyStringToQString(obj) << '"';
+ else if (PyFunction_Check(obj) != 0)
+ formatPyFunction(obj, debug);
+ else if (PyMethod_Check(obj) != 0)
+ formatPyMethod(obj, debug);
+ else if (PySequence_Check(obj) != 0)
+ formatPySequence(obj, debug);
+ else if (PyDict_Check(obj) != 0)
+ formatPyDict(obj, debug);
+ else
+ debug << obj;
+}
+
+static void formatPyObject(PyObject *obj, QDebug &debug)
+{
+ if (obj == nullptr) {
+ debug << '0';
+ return;
+ }
+ if (obj == Py_None) {
+ debug << "None";
+ return;
+ }
+ if (obj == Py_True) {
+ debug << "True";
+ return;
+ }
+ if (obj == Py_False) {
+ debug << "False";
+ return;
+ }
+ if (PyType_Check(obj) == 0)
+ debug << pyTypeName(obj) << ": ";
+ formatPyObjectValue(obj, debug);
+}
+
+debugPyObject::debugPyObject(PyObject *o) noexcept : m_object(o)
+{
+}
+
+QDebug operator<<(QDebug debug, const debugPyObject &o)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "PyObject(";
+ formatPyObject(o.m_object, debug);
+ debug << ')';
+ return debug;
+}
+
+debugPyBuffer::debugPyBuffer(Py_buffer *b) noexcept : m_buffer(b)
+{
+}
+
+static void formatPy_ssizeArray(QDebug &debug, const char *name, const Py_ssize_t *array, int len)
+{
+ debug << ", " << name << '=';
+ if (array != nullptr) {
+ debug << '[';
+ for (int i = 0; i < len; ++i)
+ debug << array[i] << ' ';
+ debug << ']';
+ } else {
+ debug << '0';
+ }
+}
+
+PYSIDE_API QDebug operator<<(QDebug debug, const debugPyBuffer &b)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "Py_buffer(";
+ if (b.m_buffer != nullptr) {
+ debug << "obj=" << b.m_buffer->obj
+ << ", buf=" << b.m_buffer->buf << ", len=" << b.m_buffer->len
+ << ", readonly=" << b.m_buffer->readonly
+ << ", itemsize=" << b.m_buffer->itemsize << ", format=";
+ if (b.m_buffer->format != nullptr)
+ debug << '"' << b.m_buffer->format << '"';
+ else
+ debug << '0';
+ debug << ", ndim=" << b.m_buffer->ndim;
+ formatPy_ssizeArray(debug, "shape", b.m_buffer->shape, b.m_buffer->ndim);
+ formatPy_ssizeArray(debug, "strides", b.m_buffer->strides, b.m_buffer->ndim);
+ formatPy_ssizeArray(debug, "suboffsets", b.m_buffer->suboffsets, b.m_buffer->ndim);
+ } else {
+ debug << '0';
+ }
+ debug << ')';
+ return debug;
+}
+
+} // namespace PySide
diff --git a/sources/pyside6/libpyside/pyside.h b/sources/pyside6/libpyside/pyside.h
new file mode 100644
index 000000000..c8793cbf0
--- /dev/null
+++ b/sources/pyside6/libpyside/pyside.h
@@ -0,0 +1,16 @@
+// 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_H
+#define PYSIDE_H
+
+#include <sbkpython.h>
+
+#include <pysidemacros.h>
+
+#include "pysideinit.h"
+#include "pysideqapp.h"
+#include "pysideqobject.h"
+#include "pysideutils.h"
+
+#endif // PYSIDE_H
diff --git a/sources/pyside6/libpyside/pyside6.pc.in b/sources/pyside6/libpyside/pyside6.pc.in
new file mode 100644
index 000000000..394372539
--- /dev/null
+++ b/sources/pyside6/libpyside/pyside6.pc.in
@@ -0,0 +1,15 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@LIB_INSTALL_DIR@
+includedir=@CMAKE_INSTALL_PREFIX@/include/PySide6@pyside6_SUFFIX@
+typesystemdir=@CMAKE_INSTALL_PREFIX@/share/PySide6@pyside6_SUFFIX@/typesystems
+gluedir=@CMAKE_INSTALL_PREFIX@/share/PySide6@pyside6_SUFFIX@/glue
+pythonpath=@PYTHON_SITE_PACKAGES@
+
+Name: PySide6@pyside6_SUFFIX@
+Description: Support library for Python bindings of Qt5-based libraries.
+Version: @BINDING_API_VERSION_FULL@
+Libs: -L${libdir} -lpyside6@pyside6_SUFFIX@@SHIBOKEN_PYTHON_CONFIG_SUFFIX@@LIBRARY_OUTPUT_SUFFIX@
+Cflags: -I${includedir}
+Requires: shiboken6
+
diff --git a/sources/pyside6/libpyside/pyside_numpy.cpp b/sources/pyside6/libpyside/pyside_numpy.cpp
new file mode 100644
index 000000000..5f43cc5ce
--- /dev/null
+++ b/sources/pyside6/libpyside/pyside_numpy.cpp
@@ -0,0 +1,98 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "pyside_numpy.h"
+#include <sbknumpyview.h>
+
+// Convert X,Y of type T data to a list of points (QPoint, PointF)
+template <class T, class Point>
+static QList<Point>
+ xyDataToQPointHelper(const void *xData, const void *yData, qsizetype size)
+{
+ auto *x = reinterpret_cast<const T *>(xData);
+ auto *y = reinterpret_cast<const T *>(yData);
+ QList<Point> result;
+ result.reserve(size);
+ for (auto xEnd = x + size; x < xEnd; ++x, ++y)
+ result.append(Point(*x, *y));
+ return result;
+}
+
+// Convert X,Y of double/float type data to a list of QPoint (rounding)
+template <class T>
+static QList<QPoint>
+ xyFloatDataToQPointHelper(const void *xData, const void *yData, qsizetype size)
+{
+ auto *x = reinterpret_cast<const T *>(xData);
+ auto *y = reinterpret_cast<const T *>(yData);
+ QList<QPoint> result;
+ result.reserve(size);
+ for (auto xEnd = x + size; x < xEnd; ++x, ++y)
+ result.append(QPoint(qRound(*x), qRound(*y)));
+ return result;
+}
+
+namespace PySide::Numpy
+{
+
+QList<QPointF> xyDataToQPointFList(PyObject *pyXIn, PyObject *pyYIn)
+{
+ auto xv = Shiboken::Numpy::View::fromPyObject(pyXIn);
+ auto yv = Shiboken::Numpy::View::fromPyObject(pyYIn);
+ if (!xv.sameLayout(yv))
+ return {};
+ const qsizetype size = qMin(xv.dimensions[0], yv.dimensions[0]);
+ 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:
+ break;
+ }
+ return xyDataToQPointHelper<double, QPointF>(xv.data, yv.data, size);
+}
+
+QList<QPoint> xyDataToQPointList(PyObject *pyXIn, PyObject *pyYIn)
+{
+ auto xv = Shiboken::Numpy::View::fromPyObject(pyXIn);
+ auto yv = Shiboken::Numpy::View::fromPyObject(pyYIn);
+ if (!xv.sameLayout(yv))
+ return {};
+ const qsizetype size = qMin(xv.dimensions[0], yv.dimensions[0]);
+ 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:
+ break;
+ }
+ return xyFloatDataToQPointHelper<double>(xv.data, yv.data, size);
+}
+
+} //namespace PySide::Numpy
diff --git a/sources/pyside6/libpyside/pyside_numpy.h b/sources/pyside6/libpyside/pyside_numpy.h
new file mode 100644
index 000000000..0925a6bd6
--- /dev/null
+++ b/sources/pyside6/libpyside/pyside_numpy.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 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_NUMPY_H
+#define PYSIDE_NUMPY_H
+
+#include <sbkpython.h>
+#include <sbknumpycheck.h>
+
+#include <pysidemacros.h>
+
+#include <QtCore/QList>
+#include <QtCore/QPoint>
+#include <QtCore/QPointF>
+
+namespace PySide::Numpy
+{
+
+/// Create a list of QPointF from 2 equally sized numpy array of x and y data
+/// (float,double).
+/// \param pyXIn X data array
+/// \param pyYIn Y data array
+/// \return List of QPointF
+
+PYSIDE_API QList<QPointF> xyDataToQPointFList(PyObject *pyXIn, PyObject *pyYIn);
+
+/// Create a list of QPoint from 2 equally sized numpy array of x and y data
+/// (int).
+/// \param pyXIn X data array
+/// \param pyYIn Y data array
+/// \return List of QPoint
+
+PYSIDE_API QList<QPoint> xyDataToQPointList(PyObject *pyXIn, PyObject *pyYIn);
+
+} //namespace PySide::Numpy
+
+#endif // PYSIDE_NUMPY_H
diff --git a/sources/pyside6/libpyside/pyside_p.h b/sources/pyside6/libpyside/pyside_p.h
new file mode 100644
index 000000000..b13c1829a
--- /dev/null
+++ b/sources/pyside6/libpyside/pyside_p.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef PYSIDE_P_H
+#define PYSIDE_P_H
+
+#include <pysidemacros.h>
+
+#include <dynamicqmetaobject.h>
+
+namespace PySide
+{
+
+// Struct associated with QObject's via Shiboken::Object::getTypeUserData()
+struct TypeUserData
+{
+ explicit TypeUserData(PyTypeObject* type, const QMetaObject* metaobject, std::size_t size) :
+ mo(type, metaobject), cppObjSize(size) {}
+
+ MetaObjectBuilder mo;
+ std::size_t cppObjSize;
+};
+
+TypeUserData *retrieveTypeUserData(PyTypeObject *pyTypeObj);
+TypeUserData *retrieveTypeUserData(PyObject *pyObj);
+// For QML
+PYSIDE_API const QMetaObject *retrieveMetaObject(PyTypeObject *pyTypeObj);
+PYSIDE_API const QMetaObject *retrieveMetaObject(PyObject *pyObj);
+
+} //namespace PySide
+
+#endif // PYSIDE_P_H
diff --git a/sources/pyside6/libpyside/pysideclassdecorator.cpp b/sources/pyside6/libpyside/pysideclassdecorator.cpp
new file mode 100644
index 000000000..ec69c5fe7
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideclassdecorator.cpp
@@ -0,0 +1,103 @@
+// Copyright (C) 2022 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 <sbkpython.h>
+
+#include "pysideclassdecorator_p.h"
+#include "pysideqobject.h"
+
+#include <basewrapper.h>
+#include <sbkstring.h>
+
+namespace PySide::ClassDecorator {
+
+DecoratorPrivate::DecoratorPrivate() noexcept = default;
+DecoratorPrivate::~DecoratorPrivate() = default;
+
+DecoratorPrivate *DecoratorPrivate::getPrivate(PyObject *o)
+{
+ auto *decorator = reinterpret_cast<PySideClassDecorator *>(o);
+ return decorator->d;
+}
+
+PyObject *DecoratorPrivate::tp_call_check(PyObject *args, CheckMode checkMode) const
+{
+ if (!PyTuple_Check(args) || PyTuple_Size(args) != 1) {
+ PyErr_Format(PyExc_TypeError,
+ "The %s decorator takes exactly 1 positional argument (%zd given)",
+ name(), PyTuple_Size(args));
+ return nullptr;
+ }
+
+ PyObject *arg = PyTuple_GetItem(args, 0);
+
+ // This will sometimes segfault if you mistakenly use it on a function declaration
+ if (!PyType_Check(arg)) {
+ PyErr_Format(PyExc_TypeError,
+ "The %s decorator can only be used on class declarations", name());
+ return nullptr;
+ }
+
+ auto type = reinterpret_cast<PyTypeObject *>(arg);
+
+ if (checkMode != CheckMode::None && !Shiboken::ObjectType::checkType(type)) {
+ PyErr_Format(PyExc_TypeError,
+ "The %s decorator can only be used on wrapped types.", name());
+ return nullptr;
+ }
+
+ if (checkMode == CheckMode::QObjectType && !isQObjectDerived(type, false)) {
+ PyErr_Format(PyExc_TypeError,
+ "The %s decorator can only be used on QObject-derived types.", name());
+ return nullptr;
+ }
+
+ return arg;
+}
+
+int StringDecoratorPrivate::convertToString(PyObject *self, PyObject *args)
+{
+ int result = -1;
+ if (PyTuple_Size(args) == 1) {
+ PyObject *arg = PyTuple_GET_ITEM(args, 0);
+ if (PyUnicode_Check(arg)) {
+ auto *pData = DecoratorPrivate::get<StringDecoratorPrivate>(self);
+ result = 0;
+ pData->m_string.assign(Shiboken::String::toCString(arg));
+ }
+ }
+ return result;
+}
+
+int StringDecoratorPrivate::tp_init(PyObject *self, PyObject *args, PyObject *)
+{
+ const int result = convertToString(self, args);
+ if (result != 0)
+ PyErr_Format(PyExc_TypeError, "%s takes a single string argument.", name());
+ return result;
+}
+
+int TypeDecoratorPrivate::tp_init(PyObject *self, PyObject *args, PyObject *)
+{
+ const int result = convertToType(self, args);
+ if (result != 0)
+ PyErr_Format(PyExc_TypeError, "%s takes a single type argument.", name());
+ return result;
+}
+
+int TypeDecoratorPrivate::convertToType(PyObject *self, PyObject *args)
+{
+ int result = -1;
+ const auto argsCount = PyTuple_Size(args);
+ if (argsCount == 1) {
+ PyObject *arg = PyTuple_GET_ITEM(args, 0);
+ if (PyType_Check(arg)) {
+ result = 0;
+ auto *pData = DecoratorPrivate::get<TypeDecoratorPrivate>(self);
+ pData->m_type = reinterpret_cast<PyTypeObject *>(arg);
+ }
+ }
+ return result;
+}
+
+} // namespace PySide::ClassDecorator
diff --git a/sources/pyside6/libpyside/pysideclassdecorator_p.h b/sources/pyside6/libpyside/pysideclassdecorator_p.h
new file mode 100644
index 000000000..6068f6a2e
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideclassdecorator_p.h
@@ -0,0 +1,164 @@
+// Copyright (C) 2022 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 CLASSDECORATOR_P_H
+#define CLASSDECORATOR_P_H
+
+#include <pysidemacros.h>
+
+#include <sbkpython.h>
+#include <pep384ext.h>
+
+#include <QtCore/QByteArray>
+
+#include <array>
+#include <string>
+
+/// Helpers for class decorators with parameters
+namespace PySide::ClassDecorator {
+
+/// Base class for private objects of class decorators with parameters
+class PYSIDE_API DecoratorPrivate
+{
+public:
+ Q_DISABLE_COPY_MOVE(DecoratorPrivate)
+
+ virtual ~DecoratorPrivate();
+
+ /// Virtual function which is passed the decorated class type
+ /// \param args Decorated class type argument
+ /// \return class with reference count increased if the call was successful,
+ /// else nullptr
+ virtual PyObject *tp_call(PyObject *self, PyObject *args, PyObject * /* kw */) = 0;
+
+ /// Virtual function which is passed the decorator parameters
+ /// \param args Decorator arguments
+ /// \return 0 if the parameters are correct
+ virtual int tp_init(PyObject *self, PyObject *args, PyObject *kwds) = 0;
+ virtual const char *name() const = 0;
+
+ /// Helper that returns DecoratorPrivate instance from a PyObject
+ template <class DerivedPrivate>
+ static DerivedPrivate *get(PyObject *o)
+ { return static_cast<DerivedPrivate *>(DecoratorPrivate::getPrivate(o)); }
+
+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
+ /// \param args Arguments
+ /// \param checkMode Type check mode
+ /// \return The type object extracted from args tuple (borrowed reference)
+ /// if the argument is a matching type
+ PyObject *tp_call_check(PyObject *args,
+ CheckMode checkMode = CheckMode::QObjectType) const;
+};
+
+/// Base class for private objects of class decorator with a string parameter
+class PYSIDE_API StringDecoratorPrivate : public DecoratorPrivate
+{
+public:
+ /// Init function that retrieves the string parameter using convertToString()
+ int tp_init(PyObject *self, PyObject *args, PyObject *kwds) override;
+
+ QByteArray string() const { return m_string; }
+
+protected:
+ /// Helper function that retrieves the string parameter
+ /// \param self self
+ /// \param args Arguments
+ /// \return 0 if the parameter is correct, else -1 (for tp_init())
+ int convertToString(PyObject *self, PyObject *args);
+
+private:
+ QByteArray m_string;
+};
+
+/// Base class for private objects of class decorator with a type parameter
+class PYSIDE_API TypeDecoratorPrivate : public DecoratorPrivate
+{
+public:
+ /// Init function that retrieves the type parameter using convertToType()
+ int tp_init(PyObject *self, PyObject *args, PyObject *kwds) override;
+
+ PyTypeObject *type() const { return m_type; }
+
+protected:
+ /// Helper function that retrieves the type parameter
+ /// \param self self
+ /// \param args Arguments
+ /// \return 0 if the parameter is correct, else -1 (for tp_init())
+ int convertToType(PyObject *self, PyObject *args);
+
+private:
+ PyTypeObject *m_type = nullptr;
+};
+
+} // namespace PySide::ClassDecorator
+
+extern "C"
+{
+LIBSHIBOKEN_API void Sbk_object_dealloc(PyObject *self);
+
+/// Python type for class decorators with DecoratorPrivate
+struct PYSIDE_API PySideClassDecorator
+{
+ PyObject_HEAD
+ PySide::ClassDecorator::DecoratorPrivate *d;
+};
+};
+
+namespace PySide::ClassDecorator {
+
+/// Helper template providing the methods (slots) for class decorators
+template <class DecoratorPrivate>
+struct Methods
+{
+ static PyObject *tp_new(PyTypeObject *subtype)
+ {
+ auto *result = PepExt_TypeCallAlloc<PySideClassDecorator>(subtype, 0);
+ result->d = new DecoratorPrivate;
+ return reinterpret_cast<PyObject *>(result);
+ }
+
+ static void tp_free(void *self)
+ {
+ auto pySelf = reinterpret_cast<PyObject *>(self);
+ auto decorator = reinterpret_cast<PySideClassDecorator *>(self);
+ delete decorator->d;
+ PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, self);
+ }
+
+ static PyObject *tp_call(PyObject *self, PyObject *args, PyObject *kwds)
+ {
+ auto *decorator = reinterpret_cast<PySideClassDecorator *>(self);
+ return decorator->d->tp_call(self, args, kwds);
+ }
+
+ static int tp_init(PyObject *self, PyObject *args, PyObject *kwds)
+ {
+ auto *decorator = reinterpret_cast<PySideClassDecorator *>(self);
+ return decorator->d->tp_init(self, args, kwds);
+ }
+
+ using TypeSlots = std::array<PyType_Slot, 6>;
+
+ static TypeSlots typeSlots()
+ {
+ return { {{Py_tp_call, reinterpret_cast<void *>(tp_call)},
+ {Py_tp_init, reinterpret_cast<void *>(tp_init)},
+ {Py_tp_new, reinterpret_cast<void *>(tp_new)},
+ {Py_tp_free, reinterpret_cast<void *>(tp_free)},
+ {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
+ {0, nullptr}}
+ };
+ }
+};
+
+} // namespace PySide::ClassDecorator
+
+#endif // CLASSDECORATOR_P_H
diff --git a/sources/pyside6/libpyside/pysideclassinfo.cpp b/sources/pyside6/libpyside/pysideclassinfo.cpp
new file mode 100644
index 000000000..698cb1c76
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideclassinfo.cpp
@@ -0,0 +1,158 @@
+// 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 <sbkpython.h>
+
+#include "pysideclassinfo.h"
+#include "pyside_p.h"
+#include "pysideclassinfo_p.h"
+#include "dynamicqmetaobject.h"
+
+#include <shiboken.h>
+#include <signature.h>
+
+extern "C"
+{
+
+static PyTypeObject *createClassInfoType()
+{
+ auto typeSlots =
+ PySide::ClassDecorator::Methods<PySide::ClassInfo::ClassInfoPrivate>::typeSlots();
+
+ PyType_Spec PySideClassInfoType_spec = {
+ "2:PySide6.QtCore.ClassInfo",
+ sizeof(PySideClassDecorator),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ typeSlots.data()};
+ return SbkType_FromSpec(&PySideClassInfoType_spec);
+};
+
+PyTypeObject *PySideClassInfo_TypeF(void)
+{
+ static auto *type = createClassInfoType();
+ return type;
+}
+
+} // extern "C"
+
+namespace PySide::ClassInfo {
+
+const char *ClassInfoPrivate::name() const
+{
+ return "ClassInfo";
+}
+
+PyObject *ClassInfoPrivate::tp_call(PyObject *self, PyObject *args, PyObject * /* kw */)
+{
+ PyObject *klass = tp_call_check(args, CheckMode::QObjectType);
+ if (klass == nullptr)
+ return nullptr;
+
+ auto *pData = DecoratorPrivate::get<ClassInfoPrivate>(self);
+
+ 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 (!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");
+
+ pData->m_alreadyWrapped = true;
+
+ Py_INCREF(klass);
+ return klass;
+}
+
+int ClassInfoPrivate::tp_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *infoDict = nullptr;
+ auto size = PyTuple_Size(args);
+ if (size == 1 && kwds == nullptr) {
+ PyObject *tmp = PyTuple_GET_ITEM(args, 0);
+ if (PyDict_Check(tmp))
+ infoDict = tmp;
+ } else if (size == 0 && kwds && PyDict_Check(kwds)) {
+ infoDict = kwds;
+ }
+
+ if (infoDict == nullptr) {
+ PyErr_Format(PyExc_TypeError, "ClassInfo() takes either keyword argument(s) or "
+ "a single dictionary argument");
+ return -1;
+ }
+
+ auto *pData = DecoratorPrivate::get<ClassInfoPrivate>(self);
+
+ 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)) {
+ 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");
+ return -1;
+ }
+ }
+ }
+
+ return PyErr_Occurred() != nullptr ? -1 : 0;
+}
+
+static const char *ClassInfo_SignatureStrings[] = {
+ "PySide6.QtCore.ClassInfo(self,**info:typing.Dict[str,str])",
+ nullptr}; // Sentinel
+
+void init(PyObject *module)
+{
+ if (InitSignatureStrings(PySideClassInfo_TypeF(), ClassInfo_SignatureStrings) < 0)
+ return;
+
+ Py_INCREF(PySideClassInfo_TypeF());
+ PyModule_AddObject(module, "ClassInfo", reinterpret_cast<PyObject *>(PySideClassInfo_TypeF()));
+}
+
+bool checkType(PyObject *pyObj)
+{
+ return pyObj != nullptr
+ && PyType_IsSubtype(Py_TYPE(pyObj), PySideClassInfo_TypeF()) != 0;
+}
+
+ClassInfoList getClassInfoList(PyObject *decorator)
+{
+ auto *pData = PySide::ClassDecorator::DecoratorPrivate::get<ClassInfoPrivate>(decorator);
+ return pData->m_data;
+}
+
+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
new file mode 100644
index 000000000..e04865829
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideclassinfo.h
@@ -0,0 +1,33 @@
+// 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_CLASSINFO_H
+#define PYSIDE_CLASSINFO_H
+
+#include <pysidemacros.h>
+
+#include <sbkpython.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+
+namespace PySide::ClassInfo {
+
+struct ClassInfo
+{
+ QByteArray key;
+ QByteArray value;
+};
+
+using ClassInfoList = QList<ClassInfo>;
+
+PYSIDE_API bool checkType(PyObject* pyObj);
+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 PySide::ClassInfo
+
+#endif
diff --git a/sources/pyside6/libpyside/pysideclassinfo_p.h b/sources/pyside6/libpyside/pysideclassinfo_p.h
new file mode 100644
index 000000000..4ef456f76
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideclassinfo_p.h
@@ -0,0 +1,43 @@
+// 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_CLASSINFO_P_H
+#define PYSIDE_CLASSINFO_P_H
+
+#include <sbkpython.h>
+
+#include "pysideclassdecorator_p.h"
+#include "pysideclassinfo.h"
+
+#include <QtCore/QMetaObject>
+
+struct PySideClassInfo;
+
+extern "C"
+{
+extern PYSIDE_API PyTypeObject *PySideClassInfo_TypeF(void);
+
+} // extern "C"
+
+namespace PySide::ClassInfo {
+
+class ClassInfoPrivate : public PySide::ClassDecorator::DecoratorPrivate
+{
+public:
+ PyObject *tp_call(PyObject *self, PyObject *args, PyObject * /* kw */) override;
+ int tp_init(PyObject *self, PyObject *args, PyObject *kwds) override;
+ const char *name() const override;
+
+ ClassInfoList m_data;
+ bool m_alreadyWrapped = false;
+};
+
+/**
+ * Init PySide QProperty support system
+ */
+void init(PyObject* module);
+
+
+} // namespace PySide::ClassInfo
+
+#endif
diff --git a/sources/pyside6/libpyside/pysidecleanup.h b/sources/pyside6/libpyside/pysidecleanup.h
new file mode 100644
index 000000000..cc5bbb0e6
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidecleanup.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 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 PYSIDECLEANUP_H
+#define PYSIDECLEANUP_H
+
+#include <pysidemacros.h>
+
+namespace PySide
+{
+
+using CleanupFunction = void(*)();
+
+/// Register a function to be called before python dies
+PYSIDE_API void registerCleanupFunction(CleanupFunction func);
+PYSIDE_API void runCleanupFunctions();
+
+} //namespace PySide
+
+#endif // PYSIDECLEANUP_H
diff --git a/sources/pyside6/libpyside/pysideinit.h b/sources/pyside6/libpyside/pysideinit.h
new file mode 100644
index 000000000..c623a0d27
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideinit.h
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 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 PYSIDEINIT_H
+#define PYSIDEINIT_H
+
+#include <sbkpython.h>
+
+#include <pysidemacros.h>
+
+namespace PySide
+{
+
+PYSIDE_API void init(PyObject *module);
+
+/// Registers a dynamic "qt.conf" file with the Qt resource system.
+///
+/// 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
+
+#endif // PYSIDEINIT_H
diff --git a/sources/pyside6/libpyside/pysidelogging_p.h b/sources/pyside6/libpyside/pysidelogging_p.h
new file mode 100644
index 000000000..bf167264d
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidelogging_p.h
@@ -0,0 +1,11 @@
+// Copyright (C) 2022 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_LOGGING_P_H
+#define PYSIDE_LOGGING_P_H
+
+#include <QtCore/QLoggingCategory>
+
+Q_DECLARE_LOGGING_CATEGORY(lcPySide)
+
+#endif // PYSIDE_LOGGING_P_H
diff --git a/sources/pyside6/libpyside/pysidemacros.h b/sources/pyside6/libpyside/pysidemacros.h
new file mode 100644
index 000000000..46f64b4ed
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidemacros.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2020 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 PYSIDEMACROS_H
+#define PYSIDEMACROS_H
+
+#include <shibokenmacros.h>
+
+#define PYSIDE_EXPORT LIBSHIBOKEN_EXPORT
+#define PYSIDE_IMPORT LIBSHIBOKEN_IMPORT
+
+#ifdef BUILD_LIBPYSIDE
+# define PYSIDE_API PYSIDE_EXPORT
+#else
+# define PYSIDE_API PYSIDE_IMPORT
+#endif
+
+#endif // PYSIDEMACROS_H
diff --git a/sources/pyside6/libpyside/pysidemetafunction.cpp b/sources/pyside6/libpyside/pysidemetafunction.cpp
new file mode 100644
index 000000000..e8173b97d
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidemetafunction.cpp
@@ -0,0 +1,197 @@
+// 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 "pysidemetafunction.h"
+#include "pysidemetafunction_p.h"
+
+#include <shiboken.h>
+#include <signature.h>
+
+#include <QtCore/QMetaMethod>
+
+extern "C"
+{
+
+struct PySideMetaFunctionPrivate
+{
+ QObject *qobject;
+ int methodIndex;
+};
+
+//methods
+static void functionFree(void *);
+static PyObject *functionCall(PyObject *, PyObject *, PyObject *);
+
+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 = createMetaFunctionType();
+ return type;
+}
+
+void functionFree(void *self)
+{
+ PySideMetaFunction *function = reinterpret_cast<PySideMetaFunction *>(self);
+ delete function->d;
+}
+
+PyObject *functionCall(PyObject *self, PyObject *args, PyObject * /* kw */)
+{
+ PySideMetaFunction *function = reinterpret_cast<PySideMetaFunction *>(self);
+
+ PyObject *retVal;
+ if (!PySide::MetaFunction::call(function->d->qobject, function->d->methodIndex, args, &retVal))
+ return nullptr;
+ return retVal;
+}
+
+} // extern "C"
+
+namespace PySide::MetaFunction {
+
+static const char *MetaFunction_SignatureStrings[] = {
+ "PySide6.QtCore.MetaFunction.__call__(self,*args:typing.Any)->typing.Any",
+ nullptr}; // Sentinel
+
+void init(PyObject *module)
+{
+ if (InitSignatureStrings(PySideMetaFunction_TypeF(), MetaFunction_SignatureStrings) < 0)
+ return;
+
+ Py_INCREF(PySideMetaFunction_TypeF());
+ PyModule_AddObject(module, "MetaFunction", reinterpret_cast<PyObject *>(PySideMetaFunction_TypeF()));
+}
+
+PySideMetaFunction *newObject(QObject *source, int methodIndex)
+{
+ if (methodIndex >= source->metaObject()->methodCount())
+ return nullptr;
+
+ QMetaMethod method = source->metaObject()->method(methodIndex);
+ if ((method.methodType() == QMetaMethod::Slot) ||
+ (method.methodType() == QMetaMethod::Method)) {
+ PySideMetaFunction *function = PyObject_New(PySideMetaFunction, PySideMetaFunction_TypeF());
+ function->d = new PySideMetaFunctionPrivate();
+ function->d->qobject = source;
+ function->d->methodIndex = methodIndex;
+ return function;
+ }
+ return nullptr;
+}
+
+bool call(QObject *self, int methodIndex, PyObject *args, PyObject **retVal)
+{
+
+ QMetaMethod method = self->metaObject()->method(methodIndex);
+ QList<QByteArray> argTypes = method.parameterTypes();
+
+ // args given plus return type
+ Shiboken::AutoDecRef sequence(PySequence_Fast(args, nullptr));
+ qsizetype numArgs = PySequence_Fast_GET_SIZE(sequence.object()) + 1;
+
+ if (numArgs - 1 > argTypes.size()) {
+ PyErr_Format(PyExc_TypeError, "%s only accepts %d argument(s), %d given!",
+ method.methodSignature().constData(),
+ argTypes.size(), numArgs - 1);
+ return false;
+ }
+
+ if (numArgs - 1 < argTypes.size()) {
+ PyErr_Format(PyExc_TypeError, "%s needs %d argument(s), %d given!",
+ method.methodSignature().constData(),
+ argTypes.size(), numArgs - 1);
+ return false;
+ }
+
+ QVariant *methValues = new QVariant[numArgs];
+ void **methArgs = new void *[numArgs];
+
+ // Prepare room for return type
+ const char *returnType = method.typeName();
+ if (returnType && std::strcmp("void", returnType))
+ argTypes.prepend(returnType);
+ else
+ argTypes.prepend(QByteArray());
+
+ int i = 0;
+ for (; i < numArgs; ++i) {
+ const QByteArray &typeName = argTypes.at(i);
+ // This must happen only when the method hasn't return type.
+ if (typeName.isEmpty()) {
+ methArgs[i] = nullptr;
+ continue;
+ }
+
+ Shiboken::Conversions::SpecificConverter converter(typeName);
+ if (converter) {
+ QMetaType metaType = QMetaType::fromName(typeName);
+ if (!Shiboken::Conversions::pythonTypeIsObjectType(converter)) {
+ if (!metaType.isValid()) {
+ PyErr_Format(PyExc_TypeError, "Value types used on meta functions (including signals) need to be "
+ "registered on meta type: %s", typeName.data());
+ break;
+ }
+ methValues[i] = QVariant(metaType);
+ }
+ methArgs[i] = methValues[i].data();
+ if (i == 0) // Don't do this for return type
+ continue;
+ if (metaType.id() == QMetaType::QString) {
+ QString tmp;
+ converter.toCpp(PySequence_Fast_GET_ITEM(sequence.object(), i - 1), &tmp);
+ methValues[i] = tmp;
+ } else {
+ converter.toCpp(PySequence_Fast_GET_ITEM(sequence.object(), i - 1), methArgs[i]);
+ }
+ } else {
+ PyErr_Format(PyExc_TypeError, "Unknown type used to call meta function (that may be a signal): %s", argTypes[i].constData());
+ break;
+ }
+ }
+
+ bool ok = i == numArgs;
+ if (ok) {
+ Py_BEGIN_ALLOW_THREADS
+ QMetaObject::metacall(self, QMetaObject::InvokeMetaMethod, method.methodIndex(), methArgs);
+ Py_END_ALLOW_THREADS
+
+ if (retVal) {
+ if (methArgs[0]) {
+ static SbkConverter *qVariantTypeConverter = Shiboken::Conversions::getConverter("QVariant");
+ Q_ASSERT(qVariantTypeConverter);
+ *retVal = Shiboken::Conversions::copyToPython(qVariantTypeConverter, &methValues[0]);
+ } else {
+ *retVal = Py_None;
+ Py_INCREF(*retVal);
+ }
+ }
+ }
+
+ delete[] methArgs;
+ delete[] methValues;
+
+ return ok;
+}
+
+} //namespace PySide::MetaFunction
+
diff --git a/sources/pyside6/libpyside/pysidemetafunction.h b/sources/pyside6/libpyside/pysidemetafunction.h
new file mode 100644
index 000000000..26a2cfd68
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidemetafunction.h
@@ -0,0 +1,38 @@
+// 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_METAFUNCTION_H
+#define PYSIDE_METAFUNCTION_H
+
+#include <pysidemacros.h>
+
+#include <sbkpython.h>
+
+#include <QtCore/QObject>
+
+extern "C"
+{
+ extern PYSIDE_API PyTypeObject *PySideMetaFunction_TypeF(void);
+
+ struct PySideMetaFunctionPrivate;
+ struct PYSIDE_API PySideMetaFunction
+ {
+ PyObject_HEAD
+ PySideMetaFunctionPrivate *d;
+ };
+}; //extern "C"
+
+namespace PySide::MetaFunction {
+
+/**
+ * This function creates a MetaFunction object
+ *
+ * @param obj the QObject witch this fuction is part of
+ * @param methodIndex The index of this function on MetaObject
+ * @return Return a new reference of PySideMetaFunction
+ **/
+PYSIDE_API PySideMetaFunction *newObject(QObject *obj, int methodIndex);
+
+} //namespace PySide::MetaFunction
+
+#endif
diff --git a/sources/pyside6/libpyside/pysidemetafunction_p.h b/sources/pyside6/libpyside/pysidemetafunction_p.h
new file mode 100644
index 000000000..0207ec3a2
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidemetafunction_p.h
@@ -0,0 +1,25 @@
+// 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_METAFUNCTION_P_H
+#define PYSIDE_METAFUNCTION_P_H
+
+#include <sbkpython.h>
+
+#include <QtCore/qtconfigmacros.h>
+
+QT_BEGIN_NAMESPACE
+class QObject;
+QT_END_NAMESPACE
+
+namespace PySide::MetaFunction {
+
+ void init(PyObject *module);
+ /**
+ * Does a Qt metacall on a QObject
+ */
+ bool call(QObject *self, int methodIndex, PyObject *args, PyObject **retVal = nullptr);
+
+} //namespace PySide::MetaFunction
+
+#endif
diff --git a/sources/pyside6/libpyside/pysidemetatype.h b/sources/pyside6/libpyside/pysidemetatype.h
new file mode 100644
index 000000000..85e70f7c9
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidemetatype.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2022 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 PYSIDEMETATYPE_H
+#define PYSIDEMETATYPE_H
+
+#include <sbkpython.h>
+
+#include <pysidemacros.h>
+
+#include <QtCore/qtconfigmacros.h>
+
+QT_FORWARD_DECLARE_CLASS(QMetaType)
+
+namespace PySide
+{
+
+/// Returns the QMetaType matching a PyTypeObject
+/// \param
+/// \param type TypeObject
+/// \return QMetaType
+PYSIDE_API QMetaType qMetaTypeFromPyType(PyTypeObject *type);
+
+} //namespace PySide
+
+#endif // PYSIDEMETATYPE_H
diff --git a/sources/pyside6/libpyside/pysideproperty.cpp b/sources/pyside6/libpyside/pysideproperty.cpp
new file mode 100644
index 000000000..3720815db
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideproperty.cpp
@@ -0,0 +1,600 @@
+// Copyright (C) 2020 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 <sbkpython.h>
+#include "pysideproperty.h"
+#include "pysideproperty_p.h"
+#include "pysidesignal.h"
+#include "pysidesignal_p.h"
+
+#include <shiboken.h>
+#include <pep384ext.h>
+#include <signature.h>
+
+using namespace Shiboken;
+
+extern "C"
+{
+
+static PyObject *qpropertyTpNew(PyTypeObject *subtype, PyObject *args, PyObject *kwds);
+static int qpropertyTpInit(PyObject *, PyObject *, PyObject *);
+static void qpropertyDeAlloc(PyObject *self);
+
+//methods
+static PyObject *qPropertyGetter(PyObject *, PyObject *);
+static PyObject *qPropertySetter(PyObject *, PyObject *);
+static PyObject *qPropertyResetter(PyObject *, PyObject *);
+static PyObject *qPropertyDeleter(PyObject *, PyObject *);
+static PyObject *qPropertyCall(PyObject *, PyObject *, PyObject *);
+static int qpropertyTraverse(PyObject *self, visitproc visit, void *arg);
+static int qpropertyClear(PyObject *self);
+
+// Attributes
+static PyObject *qPropertyDocGet(PyObject *, void *);
+static int qPropertyDocSet(PyObject *, PyObject *, void *);
+static PyObject *qProperty_fget(PyObject *, void *);
+static PyObject *qProperty_fset(PyObject *, void *);
+static PyObject *qProperty_freset(PyObject *, void *);
+static PyObject *qProperty_fdel(PyObject *, void *);
+
+static PyMethodDef PySidePropertyMethods[] = {
+ {"getter", reinterpret_cast<PyCFunction>(qPropertyGetter), METH_O, nullptr},
+ {"setter", reinterpret_cast<PyCFunction>(qPropertySetter), METH_O, nullptr},
+ {"resetter", reinterpret_cast<PyCFunction>(qPropertyResetter), METH_O, nullptr},
+ {"deleter", reinterpret_cast<PyCFunction>(qPropertyDeleter), METH_O, nullptr},
+ // Synonyms from Qt
+ {"read", reinterpret_cast<PyCFunction>(qPropertyGetter), METH_O, nullptr},
+ {"write", reinterpret_cast<PyCFunction>(qPropertySetter), METH_O, nullptr},
+ {nullptr, nullptr, 0, nullptr}
+};
+
+static PyGetSetDef PySidePropertyType_getset[] = {
+ // Note: we could not use `PyMemberDef` like Python's properties,
+ // because of the indirection of PySidePropertyPrivate.
+ {const_cast<char *>("fget"), qProperty_fget, nullptr, nullptr, nullptr},
+ {const_cast<char *>("fset"), qProperty_fset, nullptr, nullptr, nullptr},
+ {const_cast<char *>("freset"), qProperty_freset, nullptr, nullptr, nullptr},
+ {const_cast<char *>("fdel"), qProperty_fdel, nullptr, nullptr, nullptr},
+ {const_cast<char *>("__doc__"), qPropertyDocGet, qPropertyDocSet, nullptr, nullptr},
+ {nullptr, nullptr, nullptr, nullptr, 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}
+ };
+
+ 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 = createPropertyType();
+ return type;
+}
+
+PySidePropertyPrivate::PySidePropertyPrivate() noexcept = default;
+PySidePropertyPrivate::~PySidePropertyPrivate() = default;
+
+PyObject *PySidePropertyPrivate::getValue(PyObject *source)
+{
+ if (fget) {
+ Shiboken::AutoDecRef args(PyTuple_New(1));
+ Py_INCREF(source);
+ PyTuple_SET_ITEM(args, 0, source);
+ return PyObject_CallObject(fget, args);
+ }
+ return nullptr;
+}
+
+int PySidePropertyPrivate::setValue(PyObject *source, PyObject *value)
+{
+ if (fset && value) {
+ Shiboken::AutoDecRef args(PyTuple_New(2));
+ PyTuple_SET_ITEM(args, 0, source);
+ PyTuple_SET_ITEM(args, 1, value);
+ Py_INCREF(source);
+ Py_INCREF(value);
+ Shiboken::AutoDecRef result(PyObject_CallObject(fset, args));
+ return (result.isNull() ? -1 : 0);
+ }
+ if (fdel) {
+ Shiboken::AutoDecRef args(PyTuple_New(1));
+ PyTuple_SET_ITEM(args, 0, source);
+ Py_INCREF(source);
+ Shiboken::AutoDecRef result(PyObject_CallObject(fdel, args));
+ return (result.isNull() ? -1 : 0);
+ }
+ PyErr_SetString(PyExc_AttributeError, "Attribute is read only");
+ return -1;
+}
+
+int PySidePropertyPrivate::reset(PyObject *source)
+{
+ if (freset) {
+ Shiboken::AutoDecRef args(PyTuple_New(1));
+ Py_INCREF(source);
+ PyTuple_SET_ITEM(args, 0, source);
+ Shiboken::AutoDecRef result(PyObject_CallObject(freset, args));
+ return (result.isNull() ? -1 : 0);
+ }
+ return -1;
+}
+
+void PySidePropertyPrivate::metaCall(PyObject *source, QMetaObject::Call call, void **args)
+{
+ switch (call) {
+ case QMetaObject::ReadProperty: {
+ 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: {
+ 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;
+
+ case QMetaObject::ResetProperty:
+ reset(source);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static PyObject *qpropertyTpNew(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */)
+{
+ 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{};
+ auto data = reinterpret_cast<PySideProperty *>(self);
+ PySidePropertyPrivate *pData = data->d;
+
+ static const char *kwlist[] = {"type", "fget", "fset", "freset", "fdel", "doc", "notify",
+ "designable", "scriptable", "stored",
+ "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),
+ /*OO*/ &type, &(pData->fget),
+ /*OOO*/ &(pData->fset), &(pData->freset), &(pData->fdel),
+ /*s*/ &doc,
+ /*O*/ &(pData->notify),
+ /*bbb*/ &(pData->designable), &(pData->scriptable), &(pData->stored),
+ /*bbb*/ &(pData->user), &(pData->constant), &(pData->final))) {
+ return -1;
+ }
+
+ // PYSIDE-1019: Fetching the default `__doc__` from fget would fail for inherited functions
+ // because we don't initialize the mro with signatures (and we will not!).
+ // But it is efficient and in-time to do that on demand in qPropertyDocGet.
+ pData->getter_doc = false;
+ if (doc)
+ pData->doc = doc;
+ else
+ pData->doc.clear();
+
+ pData->pyTypeObject = type;
+ Py_XINCREF(pData->pyTypeObject);
+ pData->typeName = PySide::Signal::getTypeName(type);
+
+ if (pData->typeName.isEmpty())
+ PyErr_SetString(PyExc_TypeError, "Invalid property type or type name.");
+ else if (pData->constant && ((pData->fset && pData->fset != Py_None)
+ || (pData->notify && pData->notify != Py_None)))
+ PyErr_SetString(PyExc_TypeError, "A constant property cannot have a WRITE method or a "
+ "NOTIFY signal.");
+ if (!PyErr_Occurred()) {
+ Py_XINCREF(pData->fget);
+ Py_XINCREF(pData->fset);
+ Py_XINCREF(pData->freset);
+ Py_XINCREF(pData->fdel);
+ Py_XINCREF(pData->notify);
+ return 0;
+ }
+ pData->fget = nullptr;
+ pData->fset = nullptr;
+ pData->freset = nullptr;
+ pData->fdel = nullptr;
+ pData->notify = nullptr;
+ return -1;
+}
+
+static void qpropertyDeAlloc(PyObject *self)
+{
+ qpropertyClear(self);
+ if (PepRuntime_38_flag) {
+ // PYSIDE-939: Handling references correctly.
+ // This was not needed before Python 3.8 (Python issue 35810)
+ Py_DECREF(Py_TYPE(self));
+ }
+ PyObject_GC_UnTrack(self);
+ PepExt_TypeCallFree(self);
+}
+
+// Create a copy of the property to prevent the @property.setter from modifying
+// the property in place and avoid strange side effects in derived classes
+// (cf https://bugs.python.org/issue1620).
+static PyObject *
+_property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *reset, PyObject *del)
+{
+ auto *pold = reinterpret_cast<PySideProperty *>(old);
+ PySidePropertyPrivate *pData = pold->d;
+
+ AutoDecRef type(PyObject_Type(old));
+ QByteArray doc{};
+ if (type.isNull())
+ return nullptr;
+
+ if (get == nullptr || get == Py_None) {
+ Py_XDECREF(get);
+ get = pData->fget ? pData->fget : Py_None;
+ }
+ if (set == nullptr || set == Py_None) {
+ Py_XDECREF(set);
+ set = pData->fset ? pData->fset : Py_None;
+ }
+ if (reset == nullptr || reset == Py_None) {
+ Py_XDECREF(reset);
+ reset = pData->freset ? pData->freset : Py_None;
+ }
+ if (del == nullptr || del == Py_None) {
+ Py_XDECREF(del);
+ del = pData->fdel ? pData->fdel : Py_None;
+ }
+ // make _init use __doc__ from getter
+ if ((pData->getter_doc && get != Py_None) || pData->doc.isEmpty())
+ doc.clear();
+ else
+ doc = pData->doc;
+
+ auto notify = pData->notify ? pData->notify : Py_None;
+
+ PyObject *obNew = PyObject_CallFunction(type, const_cast<char *>("OOOOOsO" "bbb" "bbb"),
+ pData->pyTypeObject, get, set, reset, del, doc.data(), notify,
+ pData->designable, pData->scriptable, pData->stored,
+ pData->user, pData->constant, pData->final);
+
+ return obNew;
+}
+
+static PyObject *qPropertyGetter(PyObject *self, PyObject *getter)
+{
+ return _property_copy(self, getter, nullptr, nullptr, nullptr);
+}
+
+static PyObject *qPropertySetter(PyObject *self, PyObject *setter)
+{
+ return _property_copy(self, nullptr, setter, nullptr, nullptr);
+}
+
+static PyObject *qPropertyResetter(PyObject *self, PyObject *resetter)
+{
+ return _property_copy(self, nullptr, nullptr, resetter, nullptr);
+}
+
+static PyObject *qPropertyDeleter(PyObject *self, PyObject *deleter)
+{
+ return _property_copy(self, nullptr, nullptr, nullptr, deleter);
+}
+
+static PyObject *qPropertyCall(PyObject *self, PyObject *args, PyObject * /* kw */)
+{
+ PyObject *getter = PyTuple_GetItem(args, 0);
+ return _property_copy(self, getter, nullptr, nullptr, nullptr);
+}
+
+// PYSIDE-1019: Provide the same getters as Pythons `PyProperty`.
+
+static PyObject *qProperty_fget(PyObject *self, void *)
+{
+ auto func = reinterpret_cast<PySideProperty *>(self)->d->fget;
+ auto ret = func != nullptr ? func : Py_None;
+ Py_INCREF(ret);
+ return ret;
+}
+
+static PyObject *qProperty_fset(PyObject *self, void *)
+{
+ auto func = reinterpret_cast<PySideProperty *>(self)->d->fset;
+ auto ret = func != nullptr ? func : Py_None;
+ Py_INCREF(ret);
+ return ret;
+}
+
+static PyObject *qProperty_freset(PyObject *self, void *)
+{
+ auto func = reinterpret_cast<PySideProperty *>(self)->d->freset;
+ auto ret = func != nullptr ? func : Py_None;
+ Py_INCREF(ret);
+ return ret;
+}
+
+static PyObject *qProperty_fdel(PyObject *self, void *)
+{
+ auto func = reinterpret_cast<PySideProperty *>(self)->d->fdel;
+ auto ret = func != nullptr ? func : Py_None;
+ Py_INCREF(ret);
+ return ret;
+}
+
+static PyObject *qPropertyDocGet(PyObject *self, void *)
+{
+ auto data = reinterpret_cast<PySideProperty *>(self);
+ PySidePropertyPrivate *pData = data->d;
+
+ QByteArray doc(pData->doc);
+ if (!doc.isEmpty())
+ return PyUnicode_FromString(doc);
+ if (pData->fget != nullptr) {
+ // PYSIDE-1019: Fetch the default `__doc__` from fget. We do it late.
+ AutoDecRef get_doc(PyObject_GetAttr(pData->fget, PyMagicName::doc()));
+ if (!get_doc.isNull() && get_doc.object() != Py_None) {
+ pData->doc = String::toCString(get_doc);
+ pData->getter_doc = true;
+ if (Py_TYPE(self) == PySideProperty_TypeF())
+ return qPropertyDocGet(self, nullptr);
+ /*
+ * If this is a property subclass, put __doc__ in dict of the
+ * subclass instance instead, otherwise it gets shadowed by
+ * __doc__ in the class's dict.
+ */
+ auto get_doc_obj = get_doc.object();
+ int err = PyObject_SetAttr(self, PyMagicName::doc(), get_doc);
+ return err < 0 ? nullptr : (Py_INCREF(get_doc_obj), get_doc_obj);
+ }
+ PyErr_Clear();
+ }
+ Py_RETURN_NONE;
+}
+
+static int qPropertyDocSet(PyObject *self, PyObject *value, void *)
+{
+ auto data = reinterpret_cast<PySideProperty *>(self);
+ PySidePropertyPrivate *pData = data->d;
+
+ if (String::check(value)) {
+ pData->doc = String::toCString(value);
+ return 0;
+ }
+ PyErr_SetString(PyExc_TypeError, "String argument expected.");
+ return -1;
+}
+
+static int qpropertyTraverse(PyObject *self, visitproc visit, void *arg)
+{
+ PySidePropertyPrivate *data = reinterpret_cast<PySideProperty *>(self)->d;
+ if (!data)
+ return 0;
+
+ Py_VISIT(data->fget);
+ Py_VISIT(data->fset);
+ Py_VISIT(data->freset);
+ Py_VISIT(data->fdel);
+ Py_VISIT(data->notify);
+ Py_VISIT(data->pyTypeObject);
+ return 0;
+}
+
+static int qpropertyClear(PyObject *self)
+{
+ PySidePropertyPrivate *data = reinterpret_cast<PySideProperty *>(self)->d;
+ if (!data)
+ return 0;
+
+ Py_CLEAR(data->fget);
+ Py_CLEAR(data->fset);
+ Py_CLEAR(data->freset);
+ Py_CLEAR(data->fdel);
+ Py_CLEAR(data->notify);
+ Py_CLEAR(data->pyTypeObject);
+
+ delete data;
+ reinterpret_cast<PySideProperty *>(self)->d = nullptr;
+ return 0;
+}
+
+} // extern "C"
+
+static PyObject *getFromType(PyTypeObject *type, PyObject *name)
+{
+ AutoDecRef tpDict(PepType_GetDict(type));
+ auto *attr = PyDict_GetItem(tpDict.object(), name);
+ if (!attr) {
+ PyObject *bases = type->tp_bases;
+ 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)
+ return attr;
+ }
+ }
+ return attr;
+}
+
+namespace PySide::Property {
+
+static const char *Property_SignatureStrings[] = {
+ "PySide6.QtCore.Property(self,type:type,fget:typing.Callable=None,fset:typing.Callable=None,"
+ "freset:typing.Callable=None,fdel:typing.Callable=None,doc:str=None,"
+ "notify:typing.Callable=None,designable:bool=True,scriptable:bool=True,"
+ "stored:bool=True,user:bool=False,constant:bool=False,final:bool=False)",
+ "PySide6.QtCore.Property.deleter(self,fdel:typing.Callable)->PySide6.QtCore.Property",
+ "PySide6.QtCore.Property.getter(self,fget:typing.Callable)->PySide6.QtCore.Property",
+ "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)
+{
+ if (InitSignatureStrings(PySideProperty_TypeF(), Property_SignatureStrings) < 0)
+ return;
+
+ Py_INCREF(PySideProperty_TypeF());
+ PyModule_AddObject(module, "Property", reinterpret_cast<PyObject *>(PySideProperty_TypeF()));
+}
+
+bool checkType(PyObject *pyObj)
+{
+ if (pyObj) {
+ return PyType_IsSubtype(Py_TYPE(pyObj), PySideProperty_TypeF());
+ }
+ return false;
+}
+
+PyObject *getValue(PySideProperty *self, PyObject *source)
+{
+ return self->d->getValue(source);
+}
+
+int setValue(PySideProperty *self, PyObject *source, PyObject *value)
+{
+ return self->d->setValue(source, value);
+}
+
+int reset(PySideProperty *self, PyObject *source)
+{
+ return self->d->reset(source);
+}
+
+const char *getTypeName(const PySideProperty *self)
+{
+ return self->d->typeName;
+}
+
+PySideProperty *getObject(PyObject *source, PyObject *name)
+{
+ PyObject *attr = nullptr;
+
+ attr = getFromType(Py_TYPE(source), name);
+ if (attr && checkType(attr)) {
+ Py_INCREF(attr);
+ return reinterpret_cast<PySideProperty *>(attr);
+ }
+
+ if (!attr)
+ PyErr_Clear(); //Clear possible error caused by PyObject_GenericGetAttr
+
+ return nullptr;
+}
+
+bool isReadable(const PySideProperty * /* self */)
+{
+ return true;
+}
+
+bool isWritable(const PySideProperty *self)
+{
+ return self->d->fset != nullptr;
+}
+
+bool hasReset(const PySideProperty *self)
+{
+ return self->d->freset != nullptr;
+}
+
+bool isDesignable(const PySideProperty *self)
+{
+ return self->d->designable;
+}
+
+bool isScriptable(const PySideProperty *self)
+{
+ return self->d->scriptable;
+}
+
+bool isStored(const PySideProperty *self)
+{
+ return self->d->stored;
+}
+
+bool isUser(const PySideProperty *self)
+{
+ return self->d->user;
+}
+
+bool isConstant(const PySideProperty *self)
+{
+ return self->d->constant;
+}
+
+bool isFinal(const PySideProperty *self)
+{
+ return self->d->final;
+}
+
+const char *getNotifyName(PySideProperty *self)
+{
+ if (self->d->notifySignature.isEmpty()) {
+ AutoDecRef str(PyObject_Str(self->d->notify));
+ self->d->notifySignature = Shiboken::String::toCString(str);
+ }
+
+ return self->d->notifySignature.isEmpty()
+ ? nullptr : self->d->notifySignature.constData();
+}
+
+void setTypeName(PySideProperty *self, const char *typeName)
+{
+ self->d->typeName = typeName;
+}
+
+PyObject *getTypeObject(const PySideProperty *self)
+{
+ return self->d->pyTypeObject;
+}
+
+} //namespace PySide::Property
diff --git a/sources/pyside6/libpyside/pysideproperty.h b/sources/pyside6/libpyside/pysideproperty.h
new file mode 100644
index 000000000..a572efe45
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideproperty.h
@@ -0,0 +1,73 @@
+// 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_PROPERTY_H
+#define PYSIDE_PROPERTY_H
+
+#include <pysidemacros.h>
+
+#include <sbkpython.h>
+
+#include <QtCore/QMetaObject>
+
+class PySidePropertyPrivate;
+
+extern "C"
+{
+ extern PYSIDE_API PyTypeObject *PySideProperty_TypeF(void);
+
+ struct PYSIDE_API PySideProperty
+ {
+ PyObject_HEAD
+ PySidePropertyPrivate* d;
+ };
+};
+
+namespace PySide::Property {
+
+PYSIDE_API bool checkType(PyObject *pyObj);
+
+/**
+ * This function call set property function and pass value as arg
+ * This function does not check the property object type
+ *
+ * @param self The property object
+ * @param source The QObject witch has the property
+ * @param value The value to set in property
+ * @return Return 0 if ok or -1 if this function fail
+ **/
+PYSIDE_API int setValue(PySideProperty *self, PyObject *source, PyObject *value);
+
+/**
+ * This function call get property function
+ * This function does not check the property object type
+ *
+ * @param self The property object
+ * @param source The QObject witch has the property
+ * @return Return the result of property get function or 0 if this fail
+ **/
+PYSIDE_API PyObject *getValue(PySideProperty *self, PyObject *source);
+
+/**
+ * This function return the notify name used on this property
+ *
+ * @param self The property object
+ * @return Return a const char with the notify name used
+ **/
+PYSIDE_API const char *getNotifyName(PySideProperty *self);
+
+
+/**
+ * This function search in the source object for desired property
+ *
+ * @param source The QObject object
+ * @param name The property name
+ * @return Return a new reference to property object
+ **/
+PYSIDE_API PySideProperty *getObject(PyObject *source, PyObject *name);
+
+PYSIDE_API void setTypeName(PySideProperty *self, const char *typeName);
+
+} //namespace PySide::Property
+
+#endif
diff --git a/sources/pyside6/libpyside/pysideproperty_p.h b/sources/pyside6/libpyside/pysideproperty_p.h
new file mode 100644
index 000000000..10cb3ce87
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideproperty_p.h
@@ -0,0 +1,169 @@
+// 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_QPROPERTY_P_H
+#define PYSIDE_QPROPERTY_P_H
+
+#include <sbkpython.h>
+
+#include "pysideproperty.h"
+#include <pysidemacros.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/qtclasshelpermacros.h>
+#include <QtCore/QMetaObject>
+
+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);
+
+ PyObject *getValue(PyObject *source);
+ int setValue(PyObject *source, PyObject *value);
+ int reset(PyObject *source);
+
+ QByteArray typeName;
+ // Type object: A real PyTypeObject ("@Property(int)") or a string
+ // "@Property('QVariant')".
+ PyObject *pyTypeObject = nullptr;
+ PyObject *fget = nullptr;
+ PyObject *fset = nullptr;
+ PyObject *freset = nullptr;
+ PyObject *fdel = nullptr;
+ PyObject *notify = nullptr;
+ bool getter_doc = false;
+ QByteArray notifySignature;
+ QByteArray doc;
+ bool designable = true;
+ bool scriptable = true;
+ bool stored = true;
+ bool user = false;
+ bool constant = false;
+ bool final = false;
+};
+
+namespace PySide::Property {
+
+/**
+ * Init PySide QProperty support system
+ */
+void init(PyObject* module);
+
+/**
+ * This function call reset property function
+ * This function does not check the property object type
+ *
+ * @param self The property object
+ * @param source The QObject witch has the property
+ * @return Return 0 if ok or -1 if this function fail
+ **/
+int reset(PySideProperty* self, PyObject* source);
+
+
+/**
+ * This function return the property type
+ * This function does not check the property object type
+ *
+ * @param self The property object
+ * @return Return the property type name
+ **/
+const char* getTypeName(const PySideProperty* self);
+
+/**
+ * This function check if property has read function
+ * This function does not check the property object type
+ *
+ * @param self The property object
+ * @return Return a boolean value
+ **/
+bool isReadable(const PySideProperty* self);
+
+/**
+ * This function check if property has write function
+ * This function does not check the property object type
+ *
+ * @param self The property object
+ * @return Return a boolean value
+ **/
+bool isWritable(const PySideProperty* self);
+
+/**
+ * This function check if property has reset function
+ * This function does not check the property object type
+ *
+ * @param self The property object
+ * @return Return a boolean value
+ **/
+bool hasReset(const PySideProperty* self);
+
+/**
+ * This function check if property has the flag DESIGNABLE setted
+ * This function does not check the property object type
+ *
+ * @param self The property object
+ * @return Return a boolean value
+ **/
+bool isDesignable(const PySideProperty* self);
+
+/**
+ * This function check if property has the flag SCRIPTABLE setted
+ * This function does not check the property object type
+ *
+ * @param self The property object
+ * @return Return a boolean value
+ **/
+bool isScriptable(const PySideProperty* self);
+
+/**
+ * This function check if property has the flag STORED setted
+ * This function does not check the property object type
+ *
+ * @param self The property object
+ * @return Return a boolean value
+ **/
+bool isStored(const PySideProperty* self);
+
+/**
+ * This function check if property has the flag USER setted
+ * This function does not check the property object type
+ *
+ * @param self The property object
+ * @return Return a boolean value
+ **/
+bool isUser(const PySideProperty* self);
+
+/**
+ * This function check if property has the flag CONSTANT setted
+ * This function does not check the property object type
+ *
+ * @param self The property object
+ * @return Return a boolean value
+ **/
+bool isConstant(const PySideProperty* self);
+
+/**
+ * This function check if property has the flag FINAL setted
+ * This function does not check the property object type
+ *
+ * @param self The property object
+ * @return Return a boolean value
+ **/
+bool isFinal(const PySideProperty* self);
+
+/// This function returns the type object of the property. It is either a real
+/// PyTypeObject ("@Property(int)") or a string "@Property('QVariant')".
+/// @param self The property object
+/// @return type object
+PyObject *getTypeObject(const PySideProperty* self);
+
+} // namespace PySide::Property
+
+#endif
diff --git a/sources/pyside6/libpyside/pysideqapp.h b/sources/pyside6/libpyside/pysideqapp.h
new file mode 100644
index 000000000..5543d83d8
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideqapp.h
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 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 PYSIDEQAPP_H
+#define PYSIDEQAPP_H
+
+#include <pysidemacros.h>
+
+namespace PySide
+{
+
+PYSIDE_API void initQApp();
+
+/// Destroy a QCoreApplication taking care of destroy all instances of QObject first.
+PYSIDE_API void destroyQCoreApplication();
+
+} //namespace PySide
+
+#endif // PYSIDEQPP_H
diff --git a/sources/pyside6/libpyside/pysideqenum.cpp b/sources/pyside6/libpyside/pysideqenum.cpp
new file mode 100644
index 000000000..c0479160f
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideqenum.cpp
@@ -0,0 +1,197 @@
+// Copyright (C) 2020 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 <shiboken.h>
+
+#include "pysideqenum.h"
+#include "dynamicqmetaobject.h"
+#include "pyside_p.h"
+
+
+///////////////////////////////////////////////////////////////
+//
+// PYSIDE-957: Create QEnum dynamically from Python Enum
+//
+//
+extern "C" {
+
+using namespace Shiboken;
+
+static PyObject *analyzePyEnum(PyObject *pyenum)
+{
+ /*
+ * This is the straight-forward implementation of QEnum/QFlag. It does no
+ * longer create an equivalent Qt enum but takes the Python enum as-is.
+ *
+ * It parses an Enum/Flag derived Python enum completely so that
+ * registering can be done without error checks. This would be impossible
+ * in MetaObjectBuilderPrivate::parsePythonType.
+ */
+ AutoDecRef members(PyObject_GetAttr(pyenum, Shiboken::PyMagicName::members()));
+ if (members.isNull())
+ return nullptr;
+ AutoDecRef items(PyMapping_Items(members));
+ if (items.isNull())
+ return nullptr;
+ int iflag = PySide::QEnum::isFlag(pyenum);
+ if (iflag < 0)
+ return nullptr;
+ Py_ssize_t nr_items = PySequence_Length(items);
+ if (nr_items < 0)
+ return nullptr;
+
+ for (Py_ssize_t idx = 0; idx < nr_items; ++idx) {
+ AutoDecRef item(PySequence_GetItem(items, idx));
+ if (item.isNull())
+ return nullptr;
+
+ // The item should be a 2-element sequence of the key name and an
+ // object containing the value.
+ AutoDecRef key(PySequence_GetItem(item, 0));
+ AutoDecRef member(PySequence_GetItem(item, 1));
+ if (key.isNull() || member.isNull())
+ return nullptr;
+ if (!Shiboken::String::check(key)) {
+ // '%.200s' is the safety stringbuffer size of most CPython functions.
+ PyErr_Format(PyExc_TypeError,
+ "QEnum expected a string mapping as __members__, got '%.200s'",
+ Py_TYPE(key)->tp_name);
+ return nullptr;
+ }
+
+ // Get the value.
+ AutoDecRef value(PyObject_GetAttr(member, Shiboken::PyName::value()));
+ if (value.isNull())
+ return nullptr;
+ if (!PyLong_Check(value)) {
+ PyErr_Format(PyExc_TypeError,
+ "QEnum expected an int value as '%.200s', got '%.200s'",
+ Shiboken::String::toCString(key), Py_TYPE(value)->tp_name);
+ return nullptr;
+ }
+ }
+ Py_RETURN_NONE;
+}
+
+static Py_ssize_t get_lineno()
+{
+ PyObject *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame()); // borrowed ref
+ AutoDecRef ob_lineno(PyObject_GetAttr(frame, Shiboken::PyName::f_lineno()));
+ if (ob_lineno.isNull() || !PyLong_Check(ob_lineno))
+ return -1;
+ return PyLong_AsSsize_t(ob_lineno);
+}
+
+static bool is_module_code()
+{
+ PyObject *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame()); // borrowed ref
+ AutoDecRef ob_code(PyObject_GetAttr(frame, Shiboken::PyName::f_code()));
+ if (ob_code.isNull())
+ return false;
+ AutoDecRef ob_name(PyObject_GetAttr(ob_code, Shiboken::PyName::co_name()));
+ if (ob_name.isNull())
+ return false;
+ const char *codename = Shiboken::String::toCString(ob_name);
+ return strcmp(codename, "<module>") == 0;
+}
+
+} // extern "C"
+
+namespace PySide::QEnum {
+
+static std::map<int, PyObject *> enumCollector;
+
+int isFlag(PyObject *obType)
+{
+ /*
+ * Find out if this is an Enum or a Flag derived class.
+ * It checks also if things come from the enum module and if it is
+ * an Enum or Flag class at all.
+ *
+ * The function is called in MetaObjectBuilderPrivate::parsePythonType
+ * again to obtain the flag value.
+ */
+ int res = enumIsFlag(obType);
+ if (res < 0) {
+ auto *type = reinterpret_cast<PyTypeObject *>(obType);
+ PyErr_Format(PyExc_TypeError, "type %.200s does not inherit from 'Enum' or 'Flag'",
+ type->tp_name);
+ return -1;
+ }
+ return bool(res);
+}
+
+PyObject *QEnumMacro(PyObject *pyenum, bool flag)
+{
+ /*
+ * This is the official interface of 'QEnum'. It first calls 'analyzePyEnum'.
+ * When called as toplevel enum, it simply returns after some checks.
+ * Otherwise, 'pyenum' is stored for later use by the meta class registation.
+ */
+ int computedFlag = isFlag(pyenum);
+ if (computedFlag < 0)
+ return nullptr;
+ if (bool(computedFlag) != flag) {
+ AutoDecRef name(PyObject_GetAttr(pyenum, PyMagicName::qualname()));
+ auto cname = String::toCString(name);
+ const char *e = "Enum";
+ const char *f = "Flag";
+ PyErr_Format(PyExc_TypeError, "expected '%s' but got '%s' (%.200s)",
+ flag ? f : e, flag ? e : f, cname);
+ return nullptr;
+ }
+ auto ok = analyzePyEnum(pyenum);
+ if (ok == nullptr)
+ return nullptr;
+ if (is_module_code()) {
+ // This is a toplevel enum which we resolve immediately.
+ Py_INCREF(pyenum);
+ return pyenum;
+ }
+
+ Py_ssize_t lineno = get_lineno();
+ if (lineno < 0)
+ return nullptr;
+ // Handle the rest via line number and the meta class.
+ Py_INCREF(pyenum);
+ Py_XDECREF(enumCollector[lineno]);
+ enumCollector[lineno] = pyenum;
+ Py_RETURN_NONE;
+}
+
+std::vector<PyObject *> resolveDelayedQEnums(PyTypeObject *containerType)
+{
+ /*
+ * This is the internal interface of 'QEnum'.
+ * It is called at the end of the meta class call 'SbkObjectType_tp_new' via
+ * MetaObjectBuilderPrivate::parsePythonType and resolves the collected
+ * Python Enum arguments. The result is then registered.
+ */
+ if (enumCollector.empty())
+ return {};
+ PyObject *obContainerType = reinterpret_cast<PyObject *>(containerType);
+ Py_ssize_t lineno = get_lineno();
+
+ std::vector<PyObject *> result;
+
+ auto it = enumCollector.begin();
+ while (it != enumCollector.end()) {
+ int nr = it->first;
+ PyObject *pyenum = it->second;
+ if (nr >= lineno) {
+ AutoDecRef name(PyObject_GetAttr(pyenum, PyMagicName::name()));
+ if (name.isNull() || PyObject_SetAttr(obContainerType, name, pyenum) < 0)
+ return {};
+ result.push_back(pyenum);
+ it = enumCollector.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ return result;
+}
+
+} // namespace Shiboken::Enum
+
+//
+///////////////////////////////////////////////////////////////
diff --git a/sources/pyside6/libpyside/pysideqenum.h b/sources/pyside6/libpyside/pysideqenum.h
new file mode 100644
index 000000000..c3483e63e
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideqenum.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2020 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_QENUM_H
+#define PYSIDE_QENUM_H
+
+#include <pysidemacros.h>
+#include <vector>
+
+namespace PySide::QEnum {
+
+// PYSIDE-957: Support the QEnum macro
+PYSIDE_API PyObject *QEnumMacro(PyObject *, bool);
+PYSIDE_API int isFlag(PyObject *);
+PYSIDE_API std::vector<PyObject *> resolveDelayedQEnums(PyTypeObject *);
+PYSIDE_API void init();
+
+} // namespace PySide::QEnum
+
+#endif
diff --git a/sources/pyside6/libpyside/pysideqhash.h b/sources/pyside6/libpyside/pysideqhash.h
new file mode 100644
index 000000000..ae2d295f6
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideqhash.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 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 PYSIDEQHASH_H
+#define PYSIDEQHASH_H
+
+#include <sbkpython.h>
+
+#include <QtCore/QHash>
+
+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>
+[[deprecated]] inline Py_ssize_t hash(const T& value)
+{
+ return qHash(value.toString());
+}
+
+} //namespace PySide
+
+#endif // PYSIDEQHASH_H
diff --git a/sources/pyside6/libpyside/pysideqmetatype.h b/sources/pyside6/libpyside/pysideqmetatype.h
new file mode 100644
index 000000000..3782dc3bb
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideqmetatype.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 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 PYSIDEQMETATYPE_H
+#define PYSIDEQMETATYPE_H
+
+#include <QtCore/QMetaType>
+
+namespace PySide
+{
+
+/// If the type \p T was registered on Qt meta type system with Q_DECLARE_METATYPE macro,
+/// this class will initialize the meta type.
+///
+/// Initialize a meta type means register it on Qt meta type system, Qt itself only do this
+/// on the first call of qMetaTypeId, and this is exactly what we do to init it. If we don't
+/// do that, calls to QMetaType::type("QMatrix2x2") could return zero, causing QVariant to
+/// not recognize some C++ types, like QMatrix2x2.
+
+template<typename T, bool OK = QMetaTypeId<T>::Defined >
+struct initQtMetaType {
+ initQtMetaType()
+ {
+ qMetaTypeId<T>();
+ }
+};
+
+// Template specialization to do nothing when the type wasn't registered on Qt meta type system.
+template<typename T>
+struct initQtMetaType<T, false> {
+};
+
+} //namespace PySide
+
+#endif // PYSIDEQMETATYPE_H
diff --git a/sources/pyside6/libpyside/pysideqobject.h b/sources/pyside6/libpyside/pysideqobject.h
new file mode 100644
index 000000000..f81c50399
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideqobject.h
@@ -0,0 +1,70 @@
+// Copyright (C) 2021 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 PYSIDEQOBJECT_H
+#define PYSIDEQOBJECT_H
+
+#include <sbkpython.h>
+
+#include <pysidemacros.h>
+
+#include <QtCore/qtclasshelpermacros.h>
+
+#include <cstddef>
+
+QT_FORWARD_DECLARE_CLASS(QObject)
+QT_FORWARD_DECLARE_STRUCT(QMetaObject)
+QT_FORWARD_DECLARE_CLASS(QMutex)
+
+namespace PySide
+{
+
+/// Fill QObject properties and do signal connections using the values found in \p kwds dictionary.
+/// \param qObj PyObject fot the QObject.
+/// \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, bool allowErrors);
+
+PYSIDE_API void initDynamicMetaObject(PyTypeObject *type, const QMetaObject *base,
+ std::size_t cppObjSize);
+PYSIDE_API void initQObjectSubType(PyTypeObject *type, PyObject *args, PyObject *kwds);
+
+/// Return the size in bytes of a type that inherits QObject.
+PYSIDE_API std::size_t getSizeOfQObject(PyTypeObject *type);
+
+/// Check if a PyTypeObject or its bases contains a QObject
+/// \param pyType is the PyTypeObject to check
+/// \param raiseError controls if a TypeError is raised when an object does not
+/// inherit QObject
+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.
+/// 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
+/// 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();
+PYSIDE_API void *nextQObjectMemoryAddr();
+/// Set the address where to allocate the next QObject (for QML)
+PYSIDE_API void setNextQObjectMemoryAddr(void *addr);
+
+PYSIDE_API PyObject *getWrapperForQObject(QObject *cppSelf, PyTypeObject *sbk_type);
+
+/// Return the best-matching type for a QObject (Helper for QObject.findType())
+/// \param cppSelf QObject instance
+/// \return type object
+PYSIDE_API PyTypeObject *getTypeForQObject(const QObject *cppSelf);
+
+} //namespace PySide
+
+#endif // PYSIDEQOBJECT_H
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
new file mode 100644
index 000000000..35a6e7ef9
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidesignal.cpp
@@ -0,0 +1,1332 @@
+// Copyright (C) 2020 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 <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>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+#include <QtCore/QHash>
+#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);
+ debug.noquote();
+ debug.nospace();
+ debug << "Signature(\"" << s.signature << '"';
+ if (s.attributes)
+ debug << ", attributes=" << s.attributes;
+ debug << ')';
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const PySideSignalData &d)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "PySideSignalData(\"" << d.signalName << "\", "
+ << d.signatures;
+ if (!d.signalArguments.isEmpty())
+ debug << ", signalArguments=" << d.signalArguments;
+ debug << ')';
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const PySideSignalInstancePrivate &d)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "PySideSignalInstancePrivate(\"" << d.signalName
+ << "\", \"" << d.signature << '"';
+ if (d.attributes)
+ debug << ", attributes=" << d.attributes;
+ if (d.homonymousMethod)
+ debug << ", homonymousMethod=" << d.homonymousMethod;
+ debug << ')';
+ return debug;
+}
+
+static bool connection_Check(PyObject *o)
+{
+ if (o == nullptr || o == Py_None)
+ return false;
+ static QByteArray typeName = QByteArrayLiteral("PySide")
+ + QByteArray::number(QT_VERSION_MAJOR)
+ + QByteArrayLiteral(".QtCore.QMetaObject.Connection");
+ return std::strcmp(o->ob_type->tp_name, typeName.constData()) == 0;
+}
+
+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 instanceInitialize(PySideSignalInstance *, PyObject *, PySideSignal *, PyObject *, int);
+ static PySideSignalData::Signature parseSignature(PyObject *);
+ static PyObject *buildQtCompatible(const QByteArray &);
+} // PySide::Signal
+
+extern "C"
+{
+
+// Signal methods
+static int signalTpInit(PyObject *, PyObject *, PyObject *);
+static void signalFree(void *);
+static void signalInstanceFree(void *);
+static PyObject *signalGetItem(PyObject *self, PyObject *key);
+static PyObject *signalGetAttr(PyObject *self, PyObject *name);
+static PyObject *signalToString(PyObject *self);
+static PyObject *signalDescrGet(PyObject *self, PyObject *obj, PyObject *type);
+
+// Signal Instance methods
+static PyObject *signalInstanceConnect(PyObject *, PyObject *, PyObject *);
+static PyObject *signalInstanceDisconnect(PyObject *, PyObject *);
+static PyObject *signalInstanceEmit(PyObject *, PyObject *);
+static PyObject *signalInstanceGetItem(PyObject *, PyObject *);
+
+static PyObject *signalInstanceCall(PyObject *self, PyObject *args, PyObject *kw);
+static PyObject *signalCall(PyObject *, PyObject *, PyObject *);
+
+static PyObject *metaSignalCheck(PyObject *, PyObject *);
+
+
+static PyMethodDef MetaSignal_tp_methods[] = {
+ {"__instancecheck__", reinterpret_cast<PyCFunction>(metaSignalCheck),
+ METH_O|METH_STATIC, nullptr},
+ {nullptr, nullptr, 0, nullptr}
+};
+
+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 = createMetaSignalType();
+ return type;
+}
+
+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 = createSignalType();
+ return type;
+}
+
+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 ? self->d->signature.constData()
+ : "(no signature)", obSelf);
+}
+
+static PyMethodDef SignalInstance_methods[] = {
+ {"connect", reinterpret_cast<PyCFunction>(signalInstanceConnect),
+ METH_VARARGS|METH_KEYWORDS, nullptr},
+ {"disconnect", signalInstanceDisconnect, METH_VARARGS, nullptr},
+ {"emit", signalInstanceEmit, METH_VARARGS, nullptr},
+ {nullptr, nullptr, 0, nullptr} /* Sentinel */
+};
+
+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 = createSignalInstanceType();
+ return type;
+}
+
+static int signalTpInit(PyObject *obSelf, PyObject *args, PyObject *kwds)
+{
+ static PyObject * const emptyTuple = PyTuple_New(0);
+ static const char *kwlist[] = {"name", "arguments", nullptr};
+ char *argName = nullptr;
+ PyObject *argArguments = nullptr;
+
+ if (!PyArg_ParseTupleAndKeywords(emptyTuple, kwds,
+ "|sO:QtCore.Signal{name, arguments}",
+ const_cast<char **>(kwlist), &argName, &argArguments))
+ return -1;
+
+ bool tupledArgs = false;
+ PySideSignal *self = reinterpret_cast<PySideSignal *>(obSelf);
+ if (!self->data)
+ self->data = new PySideSignalData;
+ if (argName)
+ self->data->signalName = argName;
+
+ 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;
+ self->data->signatures.append(PySide::Signal::parseSignature(arg));
+ }
+ }
+
+ if (!tupledArgs)
+ self->data->signatures.append(PySide::Signal::parseSignature(args));
+
+ return 0;
+}
+
+static void signalFree(void *vself)
+{
+ auto pySelf = reinterpret_cast<PyObject *>(vself);
+ auto self = reinterpret_cast<PySideSignal *>(vself);
+ if (self->data) {
+ delete self->data;
+ self->data = nullptr;
+ }
+ Py_XDECREF(self->homonymousMethod);
+ self->homonymousMethod = nullptr;
+
+ PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, self);
+}
+
+static PyObject *signalGetItem(PyObject *obSelf, PyObject *key)
+{
+ auto self = reinterpret_cast<PySideSignal *>(obSelf);
+ QByteArray sigKey;
+ if (key) {
+ sigKey = PySide::Signal::parseSignature(key).signature;
+ } else {
+ sigKey = self->data == nullptr || self->data->signatures.isEmpty()
+ ? PySide::Signal::voidType() : self->data->signatures.constFirst().signature;
+ }
+ auto sig = PySide::Signal::buildSignature(self->data->signalName, sigKey);
+ return Shiboken::String::fromCString(sig.constData());
+}
+
+static PyObject *signalToString(PyObject *obSelf)
+{
+ 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)
+{
+ auto self = reinterpret_cast<PySideSignal *>(obSelf);
+
+ if (PyUnicode_CompareWithASCIIString(name, "signatures") != 0)
+ return PyObject_GenericGetAttr(obSelf, name);
+
+ auto nelems = self->data->signatures.count();
+ PyObject *tuple = PyTuple_New(nelems);
+
+ for (Py_ssize_t idx = 0; idx < nelems; ++idx) {
+ QByteArray sigKey = self->data->signatures.at(idx).signature;
+ auto sig = PySide::Signal::buildSignature(self->data->signalName, sigKey);
+ PyObject *entry = Shiboken::String::fromCString(sig.constData());
+ PyTuple_SetItem(tuple, idx, entry);
+ }
+ return tuple;
+}
+
+static void signalInstanceFree(void *vself)
+{
+ auto pySelf = reinterpret_cast<PyObject *>(vself);
+ auto self = reinterpret_cast<PySideSignalInstance *>(vself);
+
+ PySideSignalInstancePrivate *dataPvt = self->d;
+ if (dataPvt) {
+ Py_XDECREF(dataPvt->homonymousMethod);
+
+ if (dataPvt->next) {
+ Py_DECREF(dataPvt->next);
+ dataPvt->next = nullptr;
+ }
+ delete dataPvt;
+ self->d = nullptr;
+ }
+ 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.
+
+struct FunctionArgumentsResult
+{
+ PyObject *function = nullptr;
+ PepCodeObject *objCode = nullptr;
+ PyObject *functionName = nullptr;
+ bool isMethod = false;
+};
+
+static FunctionArgumentsResult extractFunctionArgumentsFromSlot(PyObject *slot)
+{
+ FunctionArgumentsResult ret;
+ ret.isMethod = PyMethod_Check(slot);
+ const bool isFunction = PyFunction_Check(slot);
+
+ 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);
+
+ } else if (PySide::isCompiledMethod(slot)) {
+ // PYSIDE-1523: PyFunction_Check and PyMethod_Check are not accepting compiled forms, we
+ // just go by attributes.
+ 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(ret.function);
+
+ 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(ret.objCode);
+
+ // 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) {
+ ret.isMethod = false;
+ ret.function = slot;
+
+ 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(ret.objCode);
+
+ // 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)
+{
+ PyObject *slot = nullptr;
+ PyObject *type = nullptr;
+ static const char *kwlist[] = {"slot", "type", nullptr};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds,
+ "O|O:SignalInstance", const_cast<char **>(kwlist), &slot, &type))
+ 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;
+ if (Py_TYPE(slot) == PySideSignalInstance_TypeF()) {
+ PySideSignalInstance *sourceWalk = source;
+
+ //find best match
+ while (sourceWalk && !match) {
+ auto targetWalk = reinterpret_cast<PySideSignalInstance *>(slot);
+ while (targetWalk && !match) {
+ if (QMetaObject::checkConnectArgs(sourceWalk->d->signature,
+ targetWalk->d->signature)) {
+ PyList_Append(pyArgs, sourceWalk->d->source);
+ Shiboken::AutoDecRef sourceSignature(PySide::Signal::buildQtCompatible(sourceWalk->d->signature));
+ PyList_Append(pyArgs, sourceSignature);
+
+ PyList_Append(pyArgs, targetWalk->d->source);
+ Shiboken::AutoDecRef targetSignature(PySide::Signal::buildQtCompatible(targetWalk->d->signature));
+ PyList_Append(pyArgs, targetSignature);
+
+ match = true;
+ }
+ targetWalk = reinterpret_cast<PySideSignalInstance *>(targetWalk->d->next);
+ }
+ sourceWalk = reinterpret_cast<PySideSignalInstance *>(sourceWalk->d->next);
+ }
+ } else {
+ // Check signature of the slot (method or function) to match signal
+ 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
+ // Iterate the possible types of connection for this signal and compare
+ // it with slot arguments
+ 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 != nullptr) {
+ // If a slot matching the same number of arguments was found,
+ // include signature to the pyArgs
+ 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
+ Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(source->d->signature));
+ PyList_Append(pyArgs, signature);
+ }
+ PyList_Append(pyArgs, slot);
+ match = true;
+ }
+
+ if (type)
+ PyList_Append(pyArgs, type);
+
+ if (match) {
+ Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
+ Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
+ 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;
+ Py_XDECREF(result);
+ }
+ if (!PyErr_Occurred()) // PYSIDE-79: inverse the logic. A Null return needs an error.
+ PyErr_Format(PyExc_RuntimeError, "Failed to connect signal %s.",
+ source->d->signature.constData());
+ return nullptr;
+}
+
+static int argCountInSignature(const char *signature)
+{
+ return QByteArray(signature).count(",") + 1;
+}
+
+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);
+ int numArgsInSignature = argCountInSignature(source->d->signature);
+
+ // If number of arguments given to emit is smaller than the first source signature expects,
+ // it is possible it's a case of emitting a signal with default parameters.
+ // Search through all the overloaded signals with the same name, and try to find a signature
+ // with the same number of arguments as given to emit, and is also marked as a cloned method
+ // (which in metaobject parlance means a signal with default parameters).
+ // @TODO: This should be improved to take into account argument types as well. The current
+ // assumption is there are no signals which are both overloaded on argument types and happen to
+ // have signatures with default parameters.
+ if (numArgsGiven < numArgsInSignature) {
+ PySideSignalInstance *possibleDefaultInstance = source;
+ while ((possibleDefaultInstance = possibleDefaultInstance->d->next)) {
+ if (possibleDefaultInstance->d->attributes & QMetaMethod::Cloned
+ && argCountInSignature(possibleDefaultInstance->d->signature) == numArgsGiven) {
+ source = possibleDefaultInstance;
+ break;
+ }
+ }
+ }
+ Shiboken::AutoDecRef sourceSignature(PySide::Signal::buildQtCompatible(source->d->signature));
+
+ PyList_Append(pyArgs, sourceSignature);
+ for (Py_ssize_t i = 0, max = PyTuple_Size(args); i < max; i++)
+ PyList_Append(pyArgs, PyTuple_GetItem(args, i));
+
+ Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
+ PySide::PySideName::qtEmit()));
+
+ Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
+ return PyObject_CallObject(pyMethod.object(), tupleArgs);
+}
+
+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).signature;
+ const auto sig = PySide::Signal::buildSignature(sigName, sigKey);
+ for (auto *data = firstSignal; data != nullptr; data = data->d->next) {
+ if (data->d->signature == sig) {
+ PyObject *result = reinterpret_cast<PyObject *>(data);
+ Py_INCREF(result);
+ return result;
+ }
+ }
+
+ // Build error message with candidates
+ QByteArray message = "Signature \"" + sig + "\" not found for signal: \""
+ + sigName + "\". Available candidates: ";
+ for (auto *data = firstSignal; data != nullptr; data = data->d->next) {
+ if (data != firstSignal)
+ message += ", ";
+ message += '"' + data->d->signature + '"';
+ }
+
+ 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;
+ if (PyTuple_Check(args) && PyTuple_GET_SIZE(args))
+ slot = PyTuple_GET_ITEM(args, 0);
+
+ bool match = false;
+ if (Py_TYPE(slot) == PySideSignalInstance_TypeF()) {
+ PySideSignalInstance *target = reinterpret_cast<PySideSignalInstance *>(slot);
+ if (QMetaObject::checkConnectArgs(source->d->signature, target->d->signature)) {
+ PyList_Append(pyArgs, source->d->source);
+ Shiboken::AutoDecRef source_signature(PySide::Signal::buildQtCompatible(source->d->signature));
+ PyList_Append(pyArgs, source_signature);
+
+ PyList_Append(pyArgs, target->d->source);
+ Shiboken::AutoDecRef target_signature(PySide::Signal::buildQtCompatible(target->d->signature));
+ PyList_Append(pyArgs, target_signature);
+ match = true;
+ }
+ } else if (connection_Check(slot)) {
+ PyList_Append(pyArgs, slot);
+ match = true;
+ } else {
+ //try the first signature
+ PyList_Append(pyArgs, source->d->source);
+ Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(source->d->signature));
+ PyList_Append(pyArgs, signature);
+
+ // disconnect all, so we need to use the c++ signature disconnect(qobj, signal, 0, 0)
+ if (slot == Py_None)
+ PyList_Append(pyArgs, slot);
+ PyList_Append(pyArgs, slot);
+ match = true;
+ }
+
+ if (match) {
+ Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
+ Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
+ PySide::PySideName::qtDisconnect()));
+ PyObject *result = PyObject_CallObject(pyMethod, tupleArgs);
+ if (result != Py_True)
+ warnDisconnectFailed(slot, source->d->signature);
+ return result;
+ }
+
+ warnDisconnectFailed(slot, source->d->signature);
+ Py_RETURN_FALSE;
+}
+
+// PYSIDE-68: Supply the missing __get__ function
+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
+ || !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()));
+ 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)
+{
+ auto signal = reinterpret_cast<PySideSignal *>(self);
+
+ // Native C++ signals can't be called like functions, thus we throw an exception.
+ // 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)
+ 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(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 = PepExt_Type_GetCallSlot(Py_TYPE(signal->homonymousMethod));
+ return callFunc(homonymousMethod, args, kw);
+}
+
+// This function returns a borrowed reference.
+static inline PyObject *_getRealCallable(PyObject *func)
+{
+ static const auto *SignalType = PySideSignal_TypeF();
+ static const auto *SignalInstanceType = PySideSignalInstance_TypeF();
+
+ // If it is a signal, use the (maybe empty) homonymous method.
+ if (Py_TYPE(func) == SignalType) {
+ auto *signal = reinterpret_cast<PySideSignal *>(func);
+ return signal->homonymousMethod;
+ }
+ // If it is a signal instance, use the (maybe empty) homonymous method.
+ if (Py_TYPE(func) == SignalInstanceType) {
+ auto *signalInstance = reinterpret_cast<PySideSignalInstance *>(func);
+ return signalInstance->d->homonymousMethod;
+ }
+ return func;
+}
+
+// This function returns a borrowed reference.
+static PyObject *_getHomonymousMethod(PySideSignalInstance *inst)
+{
+ if (inst->d->homonymousMethod)
+ return inst->d->homonymousMethod;
+
+ // PYSIDE-1730: We are searching methods with the same name not only at the same place,
+ // but walk through the whole mro to find a hidden method with the same name.
+ auto signalName = inst->d->signalName;
+ Shiboken::AutoDecRef name(Shiboken::String::fromCString(signalName));
+ auto *mro = Py_TYPE(inst->d->source)->tp_mro;
+ const Py_ssize_t n = PyTuple_GET_SIZE(mro);
+
+ for (Py_ssize_t idx = 0; idx < n; idx++) {
+ auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+ 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;
+ }
+ return nullptr;
+}
+
+static PyObject *signalInstanceCall(PyObject *self, PyObject *args, PyObject *kw)
+{
+ auto *PySideSignal = reinterpret_cast<PySideSignalInstance *>(self);
+ auto *hom = _getHomonymousMethod(PySideSignal);
+ if (!hom) {
+ PyErr_Format(PyExc_TypeError, "native Qt signal instance '%s' is not callable",
+ PySideSignal->d->signalName.constData());
+ return nullptr;
+ }
+
+ Shiboken::AutoDecRef homonymousMethod(PepExt_Type_CallDescrGet(hom, PySideSignal->d->source,
+ nullptr));
+ return PyObject_Call(homonymousMethod, args, kw);
+}
+
+static PyObject *metaSignalCheck(PyObject * /* klass */, PyObject *arg)
+{
+ if (PyType_IsSubtype(Py_TYPE(arg), PySideSignalInstance_TypeF()))
+ Py_RETURN_TRUE;
+ Py_RETURN_FALSE;
+}
+
+} // extern "C"
+
+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: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,"
+ "owner:Optional[typing.Any])->PySide6.QtCore.SignalInstance",
+ nullptr}; // Sentinel
+
+static const char *SignalInstance_SignatureStrings[] = {
+ "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
+
+void init(PyObject *module)
+{
+ if (InitSignatureStrings(PySideMetaSignal_TypeF(), MetaSignal_SignatureStrings) < 0)
+ return;
+ Py_INCREF(PySideMetaSignal_TypeF());
+ auto *obMetaSignal_Type = reinterpret_cast<PyObject *>(PySideMetaSignal_TypeF());
+ PyModule_AddObject(module, "MetaSignal", obMetaSignal_Type);
+
+ if (InitSignatureStrings(PySideSignal_TypeF(), Signal_SignatureStrings) < 0)
+ return;
+ Py_INCREF(PySideSignal_TypeF());
+ auto *obSignal_Type = reinterpret_cast<PyObject *>(PySideSignal_TypeF());
+ PyModule_AddObject(module, "Signal", obSignal_Type);
+
+ if (InitSignatureStrings(PySideSignalInstance_TypeF(), SignalInstance_SignatureStrings) < 0)
+ return;
+ Py_INCREF(PySideSignalInstance_TypeF());
+ auto *obSignalInstance_Type = reinterpret_cast<PyObject *>(PySideSignalInstance_TypeF());
+ PyModule_AddObject(module, "SignalInstance", obSignalInstance_Type);
+}
+
+bool checkType(PyObject *pyObj)
+{
+ if (pyObj)
+ return PyType_IsSubtype(Py_TYPE(pyObj), PySideSignal_TypeF());
+ return false;
+}
+
+bool checkInstanceType(PyObject *pyObj)
+{
+ return pyObj != nullptr
+ && PyType_IsSubtype(Py_TYPE(pyObj), PySideSignalInstance_TypeF()) != 0;
+}
+
+void updateSourceObject(PyObject *source)
+{
+ // TODO: Provide for actual upstream exception handling.
+ // For now we'll just return early to avoid further issues.
+
+ if (source == nullptr) // Bad input
+ return;
+
+ Shiboken::AutoDecRef mroIterator(PyObject_GetIter(source->ob_type->tp_mro));
+
+ if (mroIterator.isNull()) // Not iterable
+ return;
+
+ Shiboken::AutoDecRef mroItem{};
+ auto *dict = SbkObject_GetDict_NoRef(source);
+
+ // PYSIDE-1431: Walk the mro and update. But see PYSIDE-1751 below.
+ while ((mroItem.reset(PyIter_Next(mroIterator))), mroItem.object()) {
+ Py_ssize_t pos = 0;
+ PyObject *key, *value;
+ auto *type = reinterpret_cast<PyTypeObject *>(mroItem.object());
+ 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
+ // as PyObject_SetAttr, but filtered by existing signal names.
+ if (!PyDict_GetItem(dict, key)) {
+ auto *inst = PyObject_New(PySideSignalInstance, PySideSignalInstance_TypeF());
+ Shiboken::AutoDecRef signalInstance(reinterpret_cast<PyObject *>(inst));
+ 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
+ }
+ }
+ }
+ }
+
+ if (PyErr_Occurred()) // An iteration error occurred
+ return;
+}
+
+QByteArray getTypeName(PyObject *obType)
+{
+ if (PyType_Check(obType)) {
+ auto *type = reinterpret_cast<PyTypeObject *>(obType);
+ if (PyType_IsSubtype(type, SbkObject_TypeF()))
+ return Shiboken::ObjectType::getOriginalName(type);
+ // Translate Python types to Qt names
+ if (Shiboken::String::checkType(type))
+ return QByteArrayLiteral("QString");
+ if (type == &PyLong_Type)
+ return QByteArrayLiteral("int");
+ if (type == &PyFloat_Type)
+ return QByteArrayLiteral("double");
+ if (type == &PyBool_Type)
+ return QByteArrayLiteral("bool");
+ if (type == &PyList_Type)
+ return QByteArrayLiteral("QVariantList");
+ if (type == &PyDict_Type)
+ return QByteArrayLiteral("QVariantMap");
+ return QByteArrayLiteral("PyObject");
+ }
+ if (obType == Py_None) // Must be checked before as Shiboken::String::check accepts Py_None
+ return voidType();
+ if (Shiboken::String::check(obType)) {
+ QByteArray result = Shiboken::String::toCString(obType);
+ if (result == "qreal")
+ result = sizeof(qreal) == sizeof(double) ? "double" : "float";
+ return result;
+ }
+ return QByteArray();
+}
+
+static QByteArray buildSignature(const QByteArray &name, const QByteArray &signature)
+{
+ return QMetaObject::normalizedSignature(name + '(' + signature + ')');
+}
+
+static PySideSignalData::Signature parseSignature(PyObject *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;
+ }
+
+ 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 (!result.signature.isEmpty())
+ result.signature += ',';
+ result.signature += typeName;
+ ++result.argCount;
+ }
+ }
+ return result;
+}
+
+static void sourceGone(void *data)
+{
+ 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())
+ signal->data->signalName = Shiboken::String::toCString(name);
+ selfPvt->signalName = signal->data->signalName;
+
+ 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()) {
+ selfPvt->next = PyObject_New(PySideSignalInstance, PySideSignalInstance_TypeF());
+ instanceInitialize(selfPvt->next, name, signal, source, index);
+ }
+}
+
+PySideSignalInstance *initialize(PySideSignal *self, PyObject *name, PyObject *object)
+{
+ static PyTypeObject *pyQObjectType = Shiboken::Conversions::getPythonTypeObject("QObject*");
+ assert(pyQObjectType);
+
+ if (!PyObject_TypeCheck(object, pyQObjectType)) {
+ PyErr_Format(PyExc_TypeError, "%s cannot be converted to %s",
+ Py_TYPE(object)->tp_name, pyQObjectType->tp_name);
+ return nullptr;
+ }
+
+ PySideSignalInstance *instance = PyObject_New(PySideSignalInstance,
+ PySideSignalInstance_TypeF());
+ instanceInitialize(instance, name, self, object, 0);
+ auto sbkObj = reinterpret_cast<SbkObject *>(object);
+ if (!Shiboken::Object::wasCreatedByPython(sbkObj))
+ Py_INCREF(object); // PYSIDE-79: this flag was crucial for a wrapper call.
+ return instance;
+}
+
+bool connect(PyObject *source, const char *signal, PyObject *callback)
+{
+ Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source,
+ PySide::PySideName::qtConnect()));
+ if (pyMethod.isNull())
+ return false;
+
+ Shiboken::AutoDecRef pySignature(Shiboken::String::fromCString(signal));
+ Shiboken::AutoDecRef pyArgs(PyTuple_Pack(3, source, pySignature.object(), callback));
+ PyObject *result = PyObject_CallObject(pyMethod, pyArgs);
+ if (result == Py_False) {
+ PyErr_Format(PyExc_RuntimeError, "Failed to connect signal %s, to python callable object.", signal);
+ Py_DECREF(result);
+ result = nullptr;
+ }
+ return result;
+}
+
+PySideSignalInstance *newObjectFromMethod(PyObject *source, const QList<QMetaMethod>& methodList)
+{
+ PySideSignalInstance *root = nullptr;
+ PySideSignalInstance *previous = nullptr;
+ for (const QMetaMethod &m : methodList) {
+ PySideSignalInstance *item = PyObject_New(PySideSignalInstance, PySideSignalInstance_TypeF());
+ if (!root)
+ root = item;
+
+ if (previous)
+ previous->d->next = item;
+
+ item->d = new PySideSignalInstancePrivate;
+ item->deleted = false;
+ PySideSignalInstancePrivate *selfPvt = item->d;
+ selfPvt->source = source;
+ QByteArray cppName(m.methodSignature());
+ cppName.truncate(cppName.indexOf('('));
+ // separate SignalName
+ selfPvt->signalName = cppName;
+ selfPvt->signature = m.methodSignature();
+ selfPvt->argCount = int(m.parameterCount());
+ selfPvt->attributes = m.attributes();
+ selfPvt->homonymousMethod = nullptr;
+ selfPvt->next = nullptr;
+ }
+ return root;
+}
+
+static void _addSignalToWrapper(PyTypeObject *wrapperType, const char *signalName, PySideSignal *signal)
+{
+ Shiboken::AutoDecRef tpDict(PepType_GetDict(wrapperType));
+ auto typeDict = tpDict.object();
+ PyObject *homonymousMethod;
+ if ((homonymousMethod = PyDict_GetItemString(typeDict, signalName))) {
+ Py_INCREF(homonymousMethod);
+ signal->homonymousMethod = homonymousMethod;
+ }
+ PyDict_SetItemString(typeDict, signalName, reinterpret_cast<PyObject *>(signal));
+}
+
+// This function is used by qStableSort to promote empty signatures
+static bool compareSignals(const PySideSignalData::Signature &sig1,
+ const PySideSignalData::Signature &sig2)
+{
+ return sig1.signature.isEmpty() && !sig2.signature.isEmpty();
+}
+
+static PyObject *buildQtCompatible(const QByteArray &signature)
+{
+ const auto ba = QT_SIGNAL_SENTINEL + signature;
+ return Shiboken::String::fromStringAndSize(ba, ba.size());
+}
+
+void registerSignals(PyTypeObject *pyObj, const QMetaObject *metaObject)
+{
+ 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.truncate(methodName.indexOf('('));
+ Signature signature{method.parameterTypes().join(','), {},
+ short(method.parameterCount())};
+ if (method.attributes() & QMetaMethod::Cloned)
+ 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}});
+ }
+ }
+
+ for (const auto &metaSignal : std::as_const(signalsFound)) {
+ PySideSignal *self = PyObject_New(PySideSignal, PySideSignal_TypeF());
+ self->data = new PySideSignalData;
+ self->data->signalName = metaSignal.methodName;
+ self->homonymousMethod = nullptr;
+
+ // Empty signatures comes first! So they will be the default signal signature
+ self->data->signatures = metaSignal.signatures;
+ std::stable_sort(self->data->signatures.begin(),
+ self->data->signatures.end(), &compareSignals);
+
+ _addSignalToWrapper(pyObj, metaSignal.methodName, self);
+ Py_DECREF(reinterpret_cast<PyObject *>(self));
+ }
+}
+
+PyObject *getObject(PySideSignalInstance *signal)
+{
+ return signal->d->source;
+}
+
+const char *getSignature(PySideSignalInstance *signal)
+{
+ return signal->d->signature;
+}
+
+EmitterData getEmitterData(PySideSignalInstance *signal)
+{
+ 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;
+}
+
+QByteArrayList getArgsFromSignature(const char *signature)
+{
+ QByteArray qsignature = QByteArray(signature).trimmed();
+ QByteArrayList result;
+
+ if (qsignature.contains("()") || qsignature.contains("(void)"))
+ return result;
+ if (qsignature.endsWith(')')) {
+ const auto paren = qsignature.indexOf('(');
+ if (paren >= 0) {
+ qsignature.chop(1);
+ qsignature.remove(0, paren + 1);
+ result = qsignature.split(u',');
+ for (auto &type : result)
+ type = type.trimmed();
+ }
+ }
+ return result;
+}
+
+QByteArray getCallbackSignature(const char *signal, QObject *receiver,
+ PyObject *callback, bool encodeName)
+{
+ QByteArray functionName;
+ qsizetype numArgs = -1;
+
+ const auto slotArgs = extractFunctionArgumentsFromSlot(callback);
+ qsizetype useSelf = slotArgs.isMethod ? 1 : 0;
+
+ 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.
+ Shiboken::AutoDecRef temp(PyObject_GetAttr(callback, Shiboken::PyMagicName::name()));
+ functionName = Shiboken::String::toCString(temp);
+ useSelf = true;
+
+ if (receiver) {
+ // Search for signature on metaobject
+ const QMetaObject *mo = receiver->metaObject();
+ QByteArray prefix(functionName);
+ prefix += '(';
+ for (int i = 0; i < mo->methodCount(); i++) {
+ QMetaMethod me = mo->method(i);
+ if ((strncmp(me.methodSignature(), prefix, prefix.size()) == 0) &&
+ QMetaObject::checkConnectArgs(signal, me.methodSignature())) {
+ numArgs = me.parameterTypes().size() + useSelf;
+ break;
+ }
+ }
+ }
+#endif
+ } else if (PyCFunction_Check(callback)) {
+ const PyCFunctionObject *funcObj = reinterpret_cast<const PyCFunctionObject *>(callback);
+ functionName = PepCFunction_GET_NAMESTR(funcObj);
+ useSelf = PyCFunction_GET_SELF(funcObj) != nullptr ? 1 : 0;
+ const int flags = PyCFunction_GET_FLAGS(funcObj);
+
+ if (receiver) {
+ // Search for signature on metaobject
+ const QMetaObject *mo = receiver->metaObject();
+ QByteArray prefix(functionName);
+ prefix += '(';
+ 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())) {
+ numArgs = me.parameterTypes().size() + useSelf;
+ break;
+ }
+ }
+ }
+
+ if (numArgs == -1) {
+ if (flags & METH_VARARGS)
+ numArgs = -1;
+ else if (flags & METH_NOARGS)
+ numArgs = 0;
+ }
+ } else if (PyCallable_Check(callback)) {
+ functionName = "__callback" + QByteArray::number(quintptr(callback));
+ }
+
+ if (functionName.isEmpty() && slotArgs.functionName != nullptr)
+ functionName = Shiboken::String::toCString(slotArgs.functionName);
+ Q_ASSERT(!functionName.isEmpty());
+
+ if (functionName.startsWith('<') && functionName.endsWith('>')) { // fix "<lambda>"
+ functionName[0] = '_';
+ functionName[functionName.size() - 1] = '_';
+ }
+ QByteArray signature = encodeName ? codeCallbackName(callback, functionName) : functionName;
+ QByteArrayList args = getArgsFromSignature(signal);
+
+ signature.append(u'(');
+ if (numArgs != -1) {
+ while (!args.isEmpty() && (args.size() > (numArgs - useSelf)))
+ args.removeLast();
+ }
+ signature.append(args.join(','));
+ signature.append(')');
+
+ return signature;
+}
+
+bool isQtSignal(const char *signal)
+{
+ return (signal && signal[0] == QT_SIGNAL_SENTINEL);
+}
+
+bool checkQtSignal(const char *signal)
+{
+ if (!isQtSignal(signal)) {
+ PyErr_SetString(PyExc_TypeError, "Use the function PySide6.QtCore.SIGNAL on signals");
+ return false;
+ }
+ return true;
+}
+
+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 + 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::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 + QByteArray::number(quint64(callback), 16);
+}
+
+QByteArray voidType()
+{
+ return QByteArrayLiteral("void");
+}
+
+} //namespace PySide::Signal
diff --git a/sources/pyside6/libpyside/pysidesignal.h b/sources/pyside6/libpyside/pysidesignal.h
new file mode 100644
index 000000000..7493f94b5
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidesignal.h
@@ -0,0 +1,152 @@
+// Copyright (C) 2020 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_SIGNAL_H
+#define PYSIDE_SIGNAL_H
+
+#include <pysidemacros.h>
+
+#include <sbkpython.h>
+#include <basewrapper.h>
+
+#include <QtCore/QList>
+#include <QtCore/QMetaMethod>
+
+QT_BEGIN_NAMESPACE
+struct QMetaObject;
+class QObject;
+QT_END_NAMESPACE
+
+extern "C"
+{
+ extern PYSIDE_API PyTypeObject *PySideSignal_TypeF(void);
+ extern PYSIDE_API PyTypeObject *PySideSignalInstance_TypeF(void);
+
+ // Internal object
+ struct PYSIDE_API PySideSignal;
+
+ struct PySideSignalInstancePrivate;
+ struct PYSIDE_API PySideSignalInstance
+ {
+ PyObject_HEAD
+ PySideSignalInstancePrivate *d;
+ bool deleted;
+ };
+}; // extern "C"
+
+namespace PySide::Signal {
+
+/**
+ * This function checks for the PySideSignal type.
+ *
+ * @param pyObj
+ * @return whether pyObj is a PySideSignal
+ **/
+PYSIDE_API bool checkType(PyObject *pyObj);
+
+/**
+ * This function checks for the PySideSignalInstanceType type.
+ *
+ * @param pyObj
+ * @return Whether pyObj is a PySideSignalInstance
+ **/
+PYSIDE_API bool checkInstanceType(PyObject *pyObj);
+
+/**
+ * Register all C++ signals of a QObject on Python type.
+ */
+PYSIDE_API void registerSignals(PyTypeObject *pyObj, const QMetaObject *metaObject);
+
+/**
+ * This function creates a Signal object which stays attached to QObject class based on a list of QMetaMethods
+ *
+ * @param source of the Signal to be registered on meta object
+ * @param methods a list of QMetaMethod wich contains the supported signature
+ * @return Return a new reference to PyObject* of type PySideSignal
+ **/
+PYSIDE_API PySideSignalInstance *newObjectFromMethod(PyObject *source, const QList<QMetaMethod> &methods);
+
+/**
+ * This function initializes the Signal object by creating a PySideSignalInstance
+ *
+ * @param self a Signal object used as base to PySideSignalInstance
+ * @param name the name to be used on PySideSignalInstance
+ * @param object the PyObject where the signal will be attached
+ * @return Return a new reference to PySideSignalInstance
+ **/
+PYSIDE_API PySideSignalInstance *initialize(PySideSignal *signal, PyObject *name, PyObject *object);
+
+/**
+ * This function is used to retrieve the object in which the signal is attached
+ *
+ * @param self The Signal object
+ * @return Return the internal reference to the parent object of the signal
+ **/
+PYSIDE_API PyObject *getObject(PySideSignalInstance *signal);
+
+/**
+ * This function is used to retrieve the signal signature
+ *
+ * @param self The Signal object
+ * @return Return the signal signature
+ **/
+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
+ *
+ * @param self The Signal object
+ * @return Return the signal signature
+ **/
+PYSIDE_API void updateSourceObject(PyObject *source);
+
+/**
+ * This function verifies if the signature is a QtSignal base on SIGNAL flag
+ * @param signature The signal signature
+ * @return Return true if this is a Qt Signal, otherwise return false
+ **/
+PYSIDE_API bool isQtSignal(const char *signature);
+
+/**
+ * This function is similar to isQtSignal, however if it fails, it'll raise a Python error instead.
+ *
+ * @param signature The signal signature
+ * @return Return true if this is a Qt Signal, otherwise return false
+ **/
+PYSIDE_API bool checkQtSignal(const char *signature);
+
+/**
+ * This function is used to retrieve the signature base on Signal and receiver callback
+ * @param signature The signal signature
+ * @param receiver The QObject which will receive the signal
+ * @param callback Callback function which will connect to the signal
+ * @param encodeName Used to specify if the returned signature will be encoded with Qt signal/slot style
+ * @return Return the callback signature
+ **/
+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
+ * @return Return true if this is a Qt Signal, otherwise return false
+ * @todo replace return type by QList<QByteArray>
+ **/
+QByteArrayList getArgsFromSignature(const char *signature);
+
+} // namespace PySide::Signal
+
+#endif
diff --git a/sources/pyside6/libpyside/pysidesignal_p.h b/sources/pyside6/libpyside/pysidesignal_p.h
new file mode 100644
index 000000000..55a9a7a70
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidesignal_p.h
@@ -0,0 +1,60 @@
+// 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_QSIGNAL_P_H
+#define PYSIDE_QSIGNAL_P_H
+
+#include <sbkpython.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+
+struct PySideSignalData
+{
+ struct Signature
+ {
+ QByteArray signature; // ','-separated list of parameter types
+ unsigned short attributes;
+ short argCount;
+ };
+
+ QByteArray signalName;
+ QList<Signature> signatures;
+ QByteArrayList signalArguments;
+};
+
+extern "C"
+{
+ extern PyTypeObject *PySideSignal_TypeF(void);
+
+ struct PySideSignal {
+ PyObject_HEAD
+ PySideSignalData *data;
+ PyObject *homonymousMethod;
+ };
+
+ struct PySideSignalInstance;
+}; //extern "C"
+
+struct PySideSignalInstancePrivate
+{
+ QByteArray signalName;
+ QByteArray signature;
+ PyObject *source = nullptr;
+ PyObject *homonymousMethod = nullptr;
+ PySideSignalInstance *next = nullptr;
+ unsigned short attributes = 0;
+ short argCount = 0;
+};
+
+namespace PySide::Signal {
+
+ void init(PyObject *module);
+ bool connect(PyObject *source, const char *signal, PyObject *callback);
+ QByteArray getTypeName(PyObject *);
+ QByteArray codeCallbackName(PyObject *callback, const QByteArray &funcName);
+ QByteArray voidType();
+
+} // namespace PySide::Signal
+
+#endif
diff --git a/sources/pyside6/libpyside/pysideslot.cpp b/sources/pyside6/libpyside/pysideslot.cpp
new file mode 100644
index 000000000..fa7e89f42
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideslot.cpp
@@ -0,0 +1,183 @@
+// 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 "pysidesignal_p.h"
+#include "pysideslot_p.h"
+#include "pysidestaticstrings.h"
+
+#include <shiboken.h>
+
+#include <QtCore/QMetaObject>
+#include <QtCore/QString>
+#include <signature.h>
+
+using namespace Shiboken;
+
+struct SlotData
+{
+ QByteArray name;
+ QByteArray args;
+ QByteArray resultType;
+ QByteArray tag; // QMetaMethod::tag()
+};
+
+struct PySideSlot
+{
+ PyObject_HEAD
+ SlotData *slotData;
+};
+
+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 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 = createSlotType();
+ return type;
+}
+
+int slotTpInit(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static PyObject *emptyTuple = 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, "|sOs:QtCore.Slot",
+ const_cast<char **>(kwlist),
+ &argName, &argResult, &tag)) {
+ return -1;
+ }
+
+ PySideSlot *data = reinterpret_cast<PySideSlot *>(self);
+ if (!data->slotData)
+ data->slotData = new SlotData;
+ for(Py_ssize_t i = 0, i_max = PyTuple_Size(args); i < i_max; i++) {
+ PyObject *argType = PyTuple_GET_ITEM(args, i);
+ const auto typeName = PySide::Signal::getTypeName(argType);
+ if (typeName.isEmpty()) {
+ PyErr_Format(PyExc_TypeError, "Unknown signal argument type: %s", Py_TYPE(argType)->tp_name);
+ return -1;
+ }
+ if (!data->slotData->args.isEmpty())
+ data->slotData->args += ',';
+ data->slotData->args += typeName;
+ }
+
+ if (argName)
+ data->slotData->name = argName;
+
+ if (tag)
+ data->slotData->tag = tag;
+
+ data->slotData->resultType = argResult
+ ? PySide::Signal::getTypeName(argResult) : PySide::Signal::voidType();
+
+ return 0;
+}
+
+PyObject *slotCall(PyObject *self, PyObject *args, PyObject * /* kw */)
+{
+ PyObject *callback = nullptr;
+
+ if (!PyArg_UnpackTuple(args, "Slot.__call__", 1, 1, &callback))
+ return nullptr;
+ Py_INCREF(callback);
+
+ if (PyCallable_Check(callback)) {
+ PySideSlot *data = reinterpret_cast<PySideSlot *>(self);
+
+ if (!data->slotData)
+ data->slotData = new SlotData;
+
+ if (data->slotData->name.isEmpty()) {
+ // PYSIDE-198: Use PyObject_GetAttr instead of PepFunction_GetName to support Nuitka.
+ AutoDecRef funcName(PyObject_GetAttr(callback, PyMagicName::name()));
+ data->slotData->name = funcName.isNull() ? "<no name>" : String::toCString(funcName);
+ }
+ const QByteArray returnType = QMetaObject::normalizedType(data->slotData->resultType);
+ const QByteArray signature = data->slotData->name + '(' + data->slotData->args + ')';
+
+ PyObject *pySlotName = PySide::PySideMagicName::slot_list_attr();
+ PySide::Slot::DataList *entryList = nullptr;
+ if (PyObject_HasAttr(callback, pySlotName)) {
+ auto *capsule = PyObject_GetAttr(callback, pySlotName);
+ entryList = PySide::Slot::dataListFromCapsule(capsule);
+ } else {
+ entryList = new PySide::Slot::DataList{};
+ auto *capsule = PyCapsule_New(entryList, nullptr /* name */, slotDataListDestructor);
+ Py_INCREF(capsule);
+ PyObject_SetAttr(callback, pySlotName, capsule);
+ }
+ entryList->append({signature, returnType, data->slotData->tag});
+
+ //clear data
+ delete data->slotData;
+ data->slotData = nullptr;
+ }
+ return callback;
+}
+
+} // extern "C"
+
+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:type=nullptr)",
+ "PySide6.QtCore.Slot.__call__(self,function:typing.Callable)->typing.Any",
+ nullptr}; // Sentinel
+
+void init(PyObject *module)
+{
+ if (InitSignatureStrings(PySideSlot_TypeF(), Slot_SignatureStrings) < 0)
+ return;
+
+ Py_INCREF(PySideSlot_TypeF());
+ PyModule_AddObject(module, "Slot", reinterpret_cast<PyObject *>(PySideSlot_TypeF()));
+}
+
+} // namespace PySide::Slot
diff --git a/sources/pyside6/libpyside/pysideslot_p.h b/sources/pyside6/libpyside/pysideslot_p.h
new file mode 100644
index 000000000..9852301ee
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideslot_p.h
@@ -0,0 +1,28 @@
+// 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_SLOT_P_H
+#define PYSIDE_SLOT_P_H
+
+#include <sbkpython.h>
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qlist.h>
+
+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
new file mode 100644
index 000000000..3bddc84c2
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidestaticstrings.cpp
@@ -0,0 +1,40 @@
+// Copyright (C) 2019 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 "pysidestaticstrings.h"
+#include <sbkstring.h>
+
+#define STATIC_STRING_IMPL(funcName, value) \
+PyObject *funcName() \
+{ \
+ static PyObject *const s = Shiboken::String::createStaticString(value); \
+ return s; \
+}
+
+namespace PySide
+{
+namespace PySideName
+{
+STATIC_STRING_IMPL(qtConnect, "connect")
+STATIC_STRING_IMPL(qtDisconnect, "disconnect")
+STATIC_STRING_IMPL(qtEmit, "emit")
+STATIC_STRING_IMPL(dict_ring, "dict_ring")
+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 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
new file mode 100644
index 000000000..b4bc61800
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidestaticstrings.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2019 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 PYSIDESTRINGS_H
+#define PYSIDESTRINGS_H
+
+#include <sbkpython.h>
+#include <pysidemacros.h>
+
+namespace PySide
+{
+namespace PySideName
+{
+PYSIDE_API PyObject *qtConnect();
+PYSIDE_API PyObject *qtDisconnect();
+PYSIDE_API PyObject *qtEmit();
+PYSIDE_API PyObject *dict_ring();
+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 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
+
+#endif // PYSIDESTRINGS_H
diff --git a/sources/pyside6/libpyside/pysideutils.h b/sources/pyside6/libpyside/pysideutils.h
new file mode 100644
index 000000000..47c2f2c1b
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideutils.h
@@ -0,0 +1,69 @@
+// Copyright (C) 2021 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 PYSIDEUTILS_H
+#define PYSIDEUTILS_H
+
+#include <sbkpython.h>
+
+#include <pysidemacros.h>
+
+#include <QtCore/qtclasshelpermacros.h>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+QT_FORWARD_DECLARE_CLASS(QString)
+QT_FORWARD_DECLARE_CLASS(QStringView)
+
+namespace PySide
+{
+
+/// Check if self inherits from class_name
+/// \param self Python object
+/// \param class_name strict with the class name
+/// \return Returns true if self object inherits from class_name, otherwise returns false
+PYSIDE_API bool inherits(PyTypeObject *self, const char *class_name);
+
+/// Given A PyObject representing Unicode data, returns an equivalent QString.
+PYSIDE_API QString pyUnicodeToQString(PyObject *str);
+
+/// Given a QString, return the PyObject repeesenting Unicode data.
+PYSIDE_API PyObject *qStringToPyUnicode(QStringView s);
+
+/// Given A PyObject representing ASCII or Unicode data, returns an equivalent QString.
+PYSIDE_API QString pyStringToQString(PyObject *str);
+
+/// Provide an efficient, correct PathLike interface.
+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
new file mode 100644
index 000000000..5f3ca59e4
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideweakref.cpp
@@ -0,0 +1,82 @@
+// 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 "pysideweakref.h"
+
+#include <sbkpython.h>
+#include <shiboken.h>
+
+struct PySideCallableObject {
+ PyObject_HEAD
+ /* Type-specific fields go here. */
+ PySideWeakRefFunction weakref_func;
+ void *user_data;
+};
+
+static PyObject *CallableObject_call(PyObject *callable_object, PyObject *args, PyObject *kw);
+
+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 = createCallableObjectType();
+ return type;
+}
+
+static PyObject *CallableObject_call(PyObject *callable_object, PyObject *args, PyObject * /* kw */)
+{
+ PySideCallableObject *obj = reinterpret_cast<PySideCallableObject *>(callable_object);
+ obj->weakref_func(obj->user_data);
+
+ Py_XDECREF(PyTuple_GET_ITEM(args, 0)); //kill weak ref object
+ Py_RETURN_NONE;
+}
+
+namespace PySide::WeakRef {
+
+PyObject *create(PyObject *obj, PySideWeakRefFunction func, void *userData)
+{
+ if (obj == Py_None)
+ return nullptr;
+
+ auto *callableObject_Type = PySideCallableObject_TypeF();
+ auto *callableObject_PyObject = reinterpret_cast<PyObject *>(callableObject_Type);
+ if (callableObject_PyObject->ob_type == nullptr) {
+ callableObject_PyObject->ob_type = &PyType_Type;
+ PyType_Ready(callableObject_Type);
+ }
+
+ PyTypeObject *type = PySideCallableObject_TypeF();
+ PySideCallableObject *callable = PyObject_New(PySideCallableObject, type);
+ if (!callable || PyErr_Occurred())
+ return nullptr;
+
+ PyObject *weak = PyWeakref_NewRef(obj, reinterpret_cast<PyObject *>(callable));
+ if (!weak || PyErr_Occurred())
+ return nullptr;
+
+ callable->weakref_func = func;
+ callable->user_data = userData;
+ Py_DECREF(callable); // PYSIDE-79: after decref the callable is undefined (theoretically)
+
+ return reinterpret_cast<PyObject *>(weak);
+}
+
+} // namespace PySide::WeakRef
diff --git a/sources/pyside6/libpyside/pysideweakref.h b/sources/pyside6/libpyside/pysideweakref.h
new file mode 100644
index 000000000..e29c73455
--- /dev/null
+++ b/sources/pyside6/libpyside/pysideweakref.h
@@ -0,0 +1,18 @@
+// 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 __PYSIDEWEAKREF__
+#define __PYSIDEWEAKREF__
+
+#include <pysidemacros.h>
+#include <sbkpython.h>
+
+using PySideWeakRefFunction = void (*)(void *userData);
+
+namespace PySide::WeakRef {
+
+PYSIDE_API PyObject* create(PyObject* ob, PySideWeakRefFunction func, void* userData);
+
+} // namespace PySide::WeakRef
+
+#endif
diff --git a/sources/pyside6/libpyside/qobjectconnect.cpp b/sources/pyside6/libpyside/qobjectconnect.cpp
new file mode 100644
index 000000000..3c5b75953
--- /dev/null
+++ b/sources/pyside6/libpyside/qobjectconnect.cpp
@@ -0,0 +1,336 @@
+// Copyright (C) 2021 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 "qobjectconnect.h"
+#include "pysideqobject.h"
+#include "pysideqslotobject_p.h"
+#include "pysidesignal.h"
+#include "pysideutils.h"
+#include "signalmanager.h"
+
+#include "shiboken.h"
+#include "basewrapper.h"
+#include "autodecref.h"
+
+#include <QtCore/QDebug>
+#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()));
+ if (!PyObject_HasAttr(self, methodName))
+ return true;
+ Shiboken::AutoDecRef otherMethod(PyObject_GetAttr(self, methodName));
+
+ // PYSIDE-1523: Each could be a compiled method or a normal method here, for the
+ // compiled ones we can use the attributes.
+ PyObject *function1;
+ if (PyMethod_Check(otherMethod.object())) {
+ 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.
+ }
+
+ PyObject *function2;
+ if (is_pymethod) {
+ function2 = PyMethod_GET_FUNCTION(method);
+ } else {
+ function2 = PyObject_GetAttr(method, Shiboken::PyName::im_func());
+ Py_DECREF(function2);
+ // Not retaining a reference in line with what PyMethod_GET_FUNCTION does.
+ }
+
+ return function1 != function2;
+}
+
+struct GetReceiverResult
+{
+ QObject *receiver = nullptr;
+ PyObject *self = nullptr;
+ QByteArray callbackSig;
+ bool usingGlobalReceiver = false;
+ int slotIndex = -1;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const GetReceiverResult &r)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "GetReceiverResult(receiver=" << r.receiver << ", self=" << r.self
+ << ", 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)
+{
+ GetReceiverResult result;
+
+ bool forceGlobalReceiver = false;
+ if (PyMethod_Check(callback)) {
+ result.self = PyMethod_GET_SELF(callback);
+ result.receiver = PySide::convertToQObject(result.self, false);
+ forceGlobalReceiver = isMethodDecorator(callback, true, result.self);
+#ifdef PYPY_VERSION
+ } else if (Py_TYPE(callback) == PepBuiltinMethod_TypePtr) {
+ result.self = PyObject_GetAttrString(callback, "__self__");
+ Py_DECREF(result.self);
+ result.receiver = PySide::convertToQObject(result.self, false);
+#endif
+ } else if (PyCFunction_Check(callback)) {
+ result.self = PyCFunction_GET_SELF(callback);
+ result.receiver = PySide::convertToQObject(result.self, false);
+ } else if (PySide::isCompiledMethod(callback)) {
+ result.self = PyObject_GetAttr(callback, Shiboken::PyName::im_self());
+ Py_DECREF(result.self);
+ result.receiver = PySide::convertToQObject(result.self, false);
+ forceGlobalReceiver = isMethodDecorator(callback, false, result.self);
+ } else if (PyCallable_Check(callback)) {
+ // Ok, just a callable object
+ result.receiver = nullptr;
+ result.self = nullptr;
+ }
+
+ result.usingGlobalReceiver = !result.receiver || forceGlobalReceiver;
+
+ // 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);
+ const QMetaObject *metaObject = result.receiver->metaObject();
+ result.slotIndex = metaObject->indexOfSlot(result.callbackSig.constData());
+ 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);
+ // 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);
+ const QMetaObject *metaObject = result.receiver->metaObject();
+ result.slotIndex = metaObject->indexOfSlot(result.callbackSig.constData());
+ }
+
+ return result;
+}
+
+namespace PySide
+{
+class FriendlyQObject : public QObject // Make protected connectNotify() accessible.
+{
+public:
+ using QObject::connectNotify;
+ using QObject::disconnectNotify;
+};
+
+QMetaObject::Connection qobjectConnect(QObject *source, const char *signal,
+ QObject *receiver, const char *slot,
+ Qt::ConnectionType type)
+{
+ if (!signal || !slot || !PySide::Signal::checkQtSignal(signal))
+ return {};
+
+ if (!PySide::SignalManager::registerMetaMethod(source, signal + 1, QMetaMethod::Signal))
+ return {};
+
+ const auto methodType = PySide::Signal::isQtSignal(slot)
+ ? QMetaMethod::Signal : QMetaMethod::Slot;
+ PySide::SignalManager::registerMetaMethod(receiver, slot + 1, methodType);
+ return QObject::connect(source, signal, receiver, slot, type);
+}
+
+QMetaObject::Connection qobjectConnect(QObject *source, QMetaMethod signal,
+ QObject *receiver, QMetaMethod slot,
+ Qt::ConnectionType type)
+{
+ return qobjectConnect(source, signal.methodSignature().constData(),
+ receiver, slot.methodSignature().constData(), type);
+}
+
+QMetaObject::Connection qobjectConnectCallback(QObject *source, const char *signal,
+ 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 {};
+
+ int slotIndex = receiver.slotIndex;
+
+ PySide::SignalManager &signalManager = PySide::SignalManager::instance();
+ if (slotIndex == -1) {
+ if (!receiver.usingGlobalReceiver && receiver.self
+ && !Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(receiver.self))) {
+ qWarning("You can't add dynamic slots on an object originated from C++.");
+ if (receiver.usingGlobalReceiver)
+ signalManager.releaseGlobalReceiver(source, receiver.receiver);
+
+ return {};
+ }
+
+ const char *slotSignature = receiver.callbackSig.constData();
+ slotIndex = receiver.usingGlobalReceiver
+ ? signalManager.globalReceiverSlotIndex(receiver.receiver, slotSignature)
+ : PySide::SignalManager::registerMetaMethodGetIndex(receiver.receiver, slotSignature,
+ QMetaMethod::Slot);
+
+ if (slotIndex == -1) {
+ if (receiver.usingGlobalReceiver)
+ signalManager.releaseGlobalReceiver(source, receiver.receiver);
+
+ return {};
+ }
+ }
+
+ 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);
+ 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;
+}
+
+bool qobjectDisconnectCallback(QObject *source, const char *signal, PyObject *callback)
+{
+ if (!PySide::Signal::checkQtSignal(signal))
+ return false;
+
+ // Extract receiver from callback
+ const GetReceiverResult receiver = getReceiver(nullptr, signal, callback);
+ if (receiver.receiver == nullptr && receiver.self == nullptr)
+ return false;
+
+ const int signalIndex = source->metaObject()->indexOfSignal(signal + 1);
+ const int slotIndex = 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);
+ const QMetaMethod slotMethod = receiver.receiver->metaObject()->method(slotIndex);
+ static_cast<FriendlyQObject *>(source)->disconnectNotify(slotMethod);
+
+ if (receiver.usingGlobalReceiver) { // might delete the receiver
+ PySide::SignalManager &signalManager = PySide::SignalManager::instance();
+ signalManager.releaseGlobalReceiver(source, receiver.receiver);
+ }
+ return true;
+}
+
+} // namespace PySide
diff --git a/sources/pyside6/libpyside/qobjectconnect.h b/sources/pyside6/libpyside/qobjectconnect.h
new file mode 100644
index 000000000..c99b8006e
--- /dev/null
+++ b/sources/pyside6/libpyside/qobjectconnect.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2021 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 QOBJECTCONNECT_H
+#define QOBJECTCONNECT_H
+
+#include "pysidemacros.h"
+
+#include <sbkpython.h>
+
+#include <QtCore/QMetaObject>
+
+QT_FORWARD_DECLARE_CLASS(QObject)
+QT_FORWARD_DECLARE_CLASS(QMetaMethod)
+
+namespace PySide
+{
+
+/// Helpers for QObject::connect(): Make a string-based connection
+PYSIDE_API QMetaObject::Connection
+ qobjectConnect(QObject *source, const char *signal,
+ QObject *receiver, const char *slot,
+ Qt::ConnectionType type);
+
+/// Helpers for QObject::connect(): Make a connection based on QMetaMethod
+PYSIDE_API QMetaObject::Connection
+ qobjectConnect(QObject *source, QMetaMethod signal,
+ QObject *receiver, QMetaMethod slot,
+ Qt::ConnectionType type);
+
+/// Helpers for QObject::connect(): Make a connection to a Python callback
+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);
+
+} // namespace PySide
+
+#endif // QOBJECTCONNECT_H
diff --git a/sources/pyside6/libpyside/signalmanager.cpp b/sources/pyside6/libpyside/signalmanager.cpp
new file mode 100644
index 000000000..f4c2bbf43
--- /dev/null
+++ b/sources/pyside6/libpyside/signalmanager.cpp
@@ -0,0 +1,815 @@
+// 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 "signalmanager.h"
+#include "pysidesignal.h"
+#include "pysidelogging_p.h"
+#include "pysideproperty.h"
+#include "pysideproperty_p.h"
+#include "pysidecleanup.h"
+#include "pyside_p.h"
+#include "dynamicqmetaobject.h"
+#include "pysidemetafunction_p.h"
+#include "pysidestaticstrings.h"
+
+#include <autodecref.h>
+#include <basewrapper.h>
+#include <bindingmanager.h>
+#include <gilstate.h>
+#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
+#define PYSIDE_SLOT '1'
+#define PYSIDE_SIGNAL '2'
+#include "globalreceiverv2.h"
+
+static PyObject *metaObjectAttr = nullptr;
+static PyObject *parseArguments(const QMetaMethod &method, void **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 const char *metaCallName(QMetaObject::Call call)
+{
+ static const QHash<QMetaObject::Call, const char *> mapping = {
+ {QMetaObject::InvokeMetaMethod, "InvokeMetaMethod"},
+ {QMetaObject::ReadProperty, "ReadProperty"},
+ {QMetaObject::WriteProperty, "WriteProperty"},
+ {QMetaObject::ResetProperty, "ResetProperty"},
+ {QMetaObject::CreateInstance, "CreateInstance"},
+ {QMetaObject::IndexOfMethod, "IndexOfMethod"},
+ {QMetaObject::RegisterPropertyMetaType, "RegisterPropertyMetaType"},
+ {QMetaObject::RegisterMethodArgumentMetaType, "RegisterMethodArgumentMetaType"},
+ {QMetaObject::BindableProperty, "BindableProperty"},
+ {QMetaObject::CustomCall, "CustomCall"}
+ };
+ auto it = mapping.constFind(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()
+ :m_me(Py_None)
+{
+ // PYSIDE-813: When PYSIDE-164 was solved by adding some thread allowance,
+ // this code was no longer protected. It was hard to find this connection.
+ // See the website https://bugreports.qt.io/browse/PYSIDE-813 for details.
+ Shiboken::GilState gil;
+ Py_XINCREF(m_me);
+}
+
+PyObjectWrapper::PyObjectWrapper(PyObject *me)
+ : m_me(me)
+{
+ Shiboken::GilState gil;
+ Py_XINCREF(m_me);
+}
+
+PyObjectWrapper::PyObjectWrapper(const PyObjectWrapper &other)
+ : m_me(other.m_me)
+{
+ Shiboken::GilState gil;
+ Py_XINCREF(m_me);
+}
+
+PyObjectWrapper::~PyObjectWrapper()
+{
+ // Check that Python is still initialized as sometimes this is called by a static destructor
+ // after Python interpeter is shutdown.
+ if (!Py_IsInitialized())
+ return;
+
+ Shiboken::GilState gil;
+ Py_XDECREF(m_me);
+}
+
+void PyObjectWrapper::reset(PyObject *o)
+{
+ Shiboken::GilState gil;
+ Py_XINCREF(o);
+ Py_XDECREF(m_me);
+ m_me = o;
+}
+
+PyObjectWrapper &PyObjectWrapper::operator=(const PySide::PyObjectWrapper &other)
+{
+ if (this != &other)
+ reset(other.m_me);
+ return *this;
+}
+
+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) {
+ qWarning() << "Stream operator for PyObject called without python interpreter.";
+ return out;
+ }
+
+ static PyObject *reduce_func = nullptr;
+
+ Shiboken::GilState gil;
+ if (!reduce_func) {
+ Shiboken::AutoDecRef pickleModule(PyImport_ImportModule("pickle"));
+ reduce_func = PyObject_GetAttr(pickleModule, Shiboken::PyName::dumps());
+ }
+ PyObject *pyObj = myObj;
+ Shiboken::AutoDecRef repr(PyObject_CallFunctionObjArgs(reduce_func, pyObj, nullptr));
+ if (repr.object()) {
+ const char *buff = nullptr;
+ Py_ssize_t size = 0;
+ if (PyBytes_Check(repr.object())) {
+ buff = PyBytes_AS_STRING(repr.object());
+ size = PyBytes_GET_SIZE(repr.object());
+ } else if (Shiboken::String::check(repr.object())) {
+ buff = Shiboken::String::toCString(repr.object());
+ size = Shiboken::String::len(repr.object());
+ }
+ QByteArray data(buff, size);
+ out << data;
+ }
+ return out;
+}
+
+QDataStream &operator>>(QDataStream &in, PyObjectWrapper &myObj)
+{
+ if (Py_IsInitialized() == 0) {
+ qWarning() << "Stream operator for PyObject called without python interpreter.";
+ return in;
+ }
+
+ static PyObject *eval_func = nullptr;
+
+ Shiboken::GilState gil;
+ if (!eval_func) {
+ Shiboken::AutoDecRef pickleModule(PyImport_ImportModule("pickle"));
+ eval_func = PyObject_GetAttr(pickleModule, Shiboken::PyName::loads());
+ }
+
+ QByteArray repr;
+ in >> repr;
+ Shiboken::AutoDecRef pyCode(PyBytes_FromStringAndSize(repr.data(), repr.size()));
+ Shiboken::AutoDecRef value(PyObject_CallFunctionObjArgs(eval_func, pyCode.object(), 0));
+ if (!value.object())
+ value.reset(Py_None);
+ myObj.reset(value);
+ return in;
+}
+
+};
+
+namespace PySide {
+using GlobalReceiverV2Ptr = std::shared_ptr<GlobalReceiverV2>;
+using GlobalReceiverV2Map = QHash<PySide::GlobalReceiverKey, GlobalReceiverV2Ptr>;
+}
+
+using namespace PySide;
+
+// Listen for destroy() of main thread objects and ensure cleanup
+class SignalManagerDestroyListener : public QObject
+{
+ Q_OBJECT
+public:
+ Q_DISABLE_COPY_MOVE(SignalManagerDestroyListener)
+
+ using QObject::QObject;
+
+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
+ SignalManager::SignalManagerPrivate::m_qmlMetaCallErrorHandler = nullptr;
+
+static void clearSignalManager()
+{
+ PySide::SignalManager::instance().clear();
+}
+
+static void PyObject_PythonToCpp_PyObject_PTR(PyObject *pyIn, void *cppOut)
+{
+ *reinterpret_cast<PyObject **>(cppOut) = pyIn;
+}
+static PythonToCppFunc is_PyObject_PythonToCpp_PyObject_PTR_Convertible(PyObject * /* pyIn */)
+{
+ return PyObject_PythonToCpp_PyObject_PTR;
+}
+static PyObject *PyObject_PTR_CppToPython_PyObject(const void *cppIn)
+{
+ auto pyOut = reinterpret_cast<PyObject *>(const_cast<void *>(cppIn));
+ if (pyOut)
+ Py_INCREF(pyOut);
+ return pyOut;
+}
+
+SignalManager::SignalManager() : m_d(new SignalManagerPrivate)
+{
+ // Register Qt primitive typedefs used on signals.
+ using namespace Shiboken;
+
+ // 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);
+ Shiboken::Conversions::setPythonToCppPointerFunctions(converter, PyObject_PythonToCpp_PyObject_PTR, is_PyObject_PythonToCpp_PyObject_PTR_Convertible);
+ Shiboken::Conversions::registerConverterName(converter, "PyObject");
+ Shiboken::Conversions::registerConverterName(converter, "object");
+ Shiboken::Conversions::registerConverterName(converter, "PyObjectWrapper");
+ Shiboken::Conversions::registerConverterName(converter, "PySide::PyObjectWrapper");
+
+ PySide::registerCleanupFunction(clearSignalManager);
+
+ if (!metaObjectAttr)
+ metaObjectAttr = Shiboken::String::fromCString("__METAOBJECT__");
+}
+
+void SignalManager::clear()
+{
+ m_d->clear();
+}
+
+SignalManager::~SignalManager()
+{
+ delete m_d;
+}
+
+SignalManager &SignalManager::instance()
+{
+ static SignalManager me;
+ return me;
+}
+
+void SignalManager::setQmlMetaCallErrorHandler(QmlMetaCallErrorHandler handler)
+{
+ SignalManagerPrivate::m_qmlMetaCallErrorHandler = handler;
+}
+
+static void qAppAboutToQuit()
+{
+ 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);
+ }
+ }
+
+ return it.value().get();
+}
+
+void SignalManager::purgeEmptyGlobalReceivers()
+{
+ m_d->purgeEmptyGlobalReceivers();
+}
+
+void SignalManager::notifyGlobalReceiver(QObject *receiver)
+{
+ reinterpret_cast<GlobalReceiverV2 *>(receiver)->notify();
+ purgeEmptyGlobalReceivers();
+}
+
+void SignalManager::releaseGlobalReceiver(const QObject *source, QObject *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 static_cast<GlobalReceiverV2 *>(receiver)->addSlot(signature);
+}
+
+bool SignalManager::emitSignal(QObject *source, const char *signal, PyObject *args)
+{
+ if (!Signal::checkQtSignal(signal))
+ return false;
+ signal++;
+
+ int signalIndex = source->metaObject()->indexOfSignal(signal);
+ return signalIndex != -1 && MetaFunction::call(source, signalIndex, args);
+}
+
+// Handle errors from meta calls. Requires GIL and PyErr_Occurred()
+void SignalManager::SignalManagerPrivate::handleMetaCallError(QObject *object, int *result)
+{
+ // Bubbles Python exceptions up to the Javascript engine, if called from one
+ if (m_qmlMetaCallErrorHandler) {
+ auto idOpt = m_qmlMetaCallErrorHandler(object);
+ if (idOpt.has_value())
+ *result = idOpt.value();
+ }
+
+ const int reclimit = Py_GetRecursionLimit();
+ // Inspired by Python's errors.c: PyErr_GivenExceptionMatches() function.
+ // Temporarily bump the recursion limit, so that PyErr_Print will not raise a recursion
+ // error again. Don't do it when the limit is already insanely high, to avoid overflow.
+ if (reclimit < (1 << 30))
+ Py_SetRecursionLimit(reclimit + 5);
+ PyErr_Print();
+ Py_SetRecursionLimit(reclimit);
+}
+
+// Handler for QMetaObject::ReadProperty/WriteProperty/ResetProperty:
+int SignalManager::SignalManagerPrivate::qtPropertyMetacall(QObject *object,
+ QMetaObject::Call call,
+ int id, void **args)
+{
+ const QMetaObject *metaObject = object->metaObject();
+ int result = id - metaObject->propertyCount();
+
+ const QMetaProperty mp = metaObject->property(id);
+
+ qCDebug(lcPySide).noquote().nospace() << __FUNCTION__
+ << ' ' << metaCallName(call) << " #" << id << ' ' << mp.typeName()
+ << "/\"" << mp.name() << "\" " << object;
+
+ if (!mp.isValid())
+ return result;
+
+ Shiboken::GilState gil;
+ auto *pySbkSelf = Shiboken::BindingManager::instance().retrieveWrapper(object);
+ Q_ASSERT(pySbkSelf);
+ auto *pySelf = reinterpret_cast<PyObject *>(pySbkSelf);
+ Shiboken::AutoDecRef pp_name(Shiboken::String::fromCString(mp.name()));
+ PySideProperty *pp = Property::getObject(pySelf, pp_name);
+ if (!pp) {
+ qWarning("Invalid property: %s.", mp.name());
+ return false;
+ }
+ pp->d->metaCall(pySelf, call, args);
+ 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;
+ handleMetaCallError(object, &result);
+ }
+ return result;
+}
+
+// Handler for QMetaObject::InvokeMetaMethod
+int SignalManager::SignalManagerPrivate::qtMethodMetacall(QObject *object,
+ int id, void **args)
+{
+ const QMetaObject *metaObject = object->metaObject();
+ const QMetaMethod method = metaObject->method(id);
+ int result = id - metaObject->methodCount();
+
+ std::unique_ptr<Shiboken::GilState> gil;
+
+ qCDebug(lcPySide).noquote().nospace() << __FUNCTION__ << " #" << id
+ << " \"" << method.methodSignature() << '"';
+
+ if (method.methodType() == QMetaMethod::Signal) {
+ // emit python signal
+ QMetaObject::activate(object, id, args);
+ } else {
+ gil.reset(new Shiboken::GilState);
+ auto *pySbkSelf = Shiboken::BindingManager::instance().retrieveWrapper(object);
+ Q_ASSERT(pySbkSelf);
+ auto *pySelf = reinterpret_cast<PyObject *>(pySbkSelf);
+ QByteArray methodName = method.methodSignature();
+ methodName.truncate(methodName.indexOf('('));
+ Shiboken::AutoDecRef pyMethod(PyObject_GetAttrString(pySelf, methodName));
+ if (pyMethod.isNull()) {
+ PyErr_Format(PyExc_AttributeError, "Slot '%s::%s' not found.",
+ metaObject->className(), method.methodSignature().constData());
+ } else {
+ SignalManager::callPythonMetaMethod(method, args, pyMethod);
+ }
+ }
+ // WARNING Isn't safe to call any metaObject and/or object methods beyond this point
+ // because the object can be deleted inside the called slot.
+
+ if (gil.get() == nullptr)
+ gil.reset(new Shiboken::GilState);
+
+ if (PyErr_Occurred())
+ handleMetaCallError(object, &result);
+
+ return result;
+}
+
+int SignalManager::qt_metacall(QObject *object, QMetaObject::Call call, int id, void **args)
+{
+ switch (call) {
+ case QMetaObject::ReadProperty:
+ case QMetaObject::WriteProperty:
+ case QMetaObject::ResetProperty:
+ id = SignalManagerPrivate::qtPropertyMetacall(object, call, id, args);
+ break;
+ case QMetaObject::RegisterPropertyMetaType:
+ case QMetaObject::BindableProperty:
+ id -= object->metaObject()->propertyCount();
+ break;
+ case QMetaObject::InvokeMetaMethod:
+ id = SignalManagerPrivate::qtMethodMetacall(object, id, args);
+ break;
+ case QMetaObject::CreateInstance:
+ case QMetaObject::IndexOfMethod:
+ case QMetaObject::RegisterMethodArgumentMetaType:
+ case QMetaObject::CustomCall:
+ qCDebug(lcPySide).noquote().nospace() << __FUNCTION__ << ' '
+ << 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)
+{
+ Q_ASSERT(pyMethod);
+
+ Shiboken::GilState gil;
+ PyObject *pyArguments = parseArguments(method, args);
+
+ if (pyArguments) {
+ QScopedPointer<Shiboken::Conversions::SpecificConverter> retConverter;
+ const char *returnType = method.typeName();
+ if (returnType != nullptr && returnType[0] != 0 && std::strcmp("void", returnType) != 0) {
+ retConverter.reset(new Shiboken::Conversions::SpecificConverter(returnType));
+ if (!retConverter->isValid()) {
+ PyErr_SetString(PyExc_RuntimeError, msgCannotConvertReturn(method).constData());
+ return -1;
+ }
+ }
+
+ Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, pyArguments));
+
+ Py_DECREF(pyArguments);
+
+ if (!retval.isNull() && retval != Py_None && !PyErr_Occurred() && retConverter)
+ retConverter->toCpp(retval, args[0]);
+ }
+
+ return -1;
+}
+
+bool SignalManager::registerMetaMethod(QObject *source, const char *signature, QMetaMethod::MethodType type)
+{
+ int ret = registerMetaMethodGetIndex(source, signature, type);
+ return (ret != -1);
+}
+
+static MetaObjectBuilder *metaBuilderFromDict(PyObject *dict)
+{
+ // PYSIDE-803: The dict in this function is the ob_dict of an SbkObject.
+ // The "metaObjectAttr" entry is only handled in this file. There is no
+ // way in this function to involve the interpreter. Therefore, we need
+ // no GIL.
+ // Note that "SignalManager::registerMetaMethodGetIndex" has write actions
+ // that might involve the interpreter, but in that context the GIL is held.
+ if (!dict || !PyDict_Contains(dict, metaObjectAttr))
+ return nullptr;
+
+ // PYSIDE-813: The above assumption is not true in debug mode:
+ // PyDict_GetItem would touch PyThreadState_GET and the global error state.
+ // PyDict_GetItemWithError instead can work without GIL.
+ PyObject *pyBuilder = PyDict_GetItemWithError(dict, metaObjectAttr);
+ return reinterpret_cast<MetaObjectBuilder *>(PyCapsule_GetPointer(pyBuilder, nullptr));
+}
+
+// Helper to format a method signature "foo(QString)" into
+// Slot decorator "@Slot(str)"
+
+struct slotSignature
+{
+ explicit slotSignature(const char *signature) : m_signature(signature) {}
+
+ const char *m_signature;
+};
+
+QDebug operator<<(QDebug debug, const slotSignature &sig)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "@Slot(";
+ QByteArrayView signature(sig.m_signature);
+ const auto len = signature.size();
+ auto pos = signature.indexOf('(');
+ if (pos != -1 && pos < len - 2) {
+ ++pos;
+ while (true) {
+ auto nextPos = signature.indexOf(',', pos);
+ if (nextPos == -1)
+ nextPos = len - 1;
+ const QByteArrayView parameter = signature.sliced(pos, nextPos - pos);
+ if (parameter == "QString") {
+ debug << "str";
+ } else if (parameter == "double") {
+ debug << "float";
+ } else {
+ const bool hasDelimiter = parameter.contains("::");
+ if (hasDelimiter)
+ debug << '"';
+ if (!hasDelimiter && parameter.endsWith('*'))
+ debug << parameter.first(parameter.size() - 1);
+ else
+ debug << parameter;
+ if (hasDelimiter)
+ debug << '"';
+ }
+ pos = nextPos + 1;
+ if (pos >= len)
+ break;
+ debug << ',';
+ }
+ }
+ debug << ')';
+ return debug;
+}
+
+int SignalManager::registerMetaMethodGetIndex(QObject *source, const char *signature, QMetaMethod::MethodType type)
+{
+ if (!source) {
+ qWarning("SignalManager::registerMetaMethodGetIndex(\"%s\") called with source=nullptr.",
+ signature);
+ return -1;
+ }
+ const QMetaObject *metaObject = source->metaObject();
+ int methodIndex = metaObject->indexOfMethod(signature);
+ // Create the dynamic signal is needed
+ if (methodIndex == -1) {
+ SbkObject *self = Shiboken::BindingManager::instance().retrieveWrapper(source);
+ if (!Shiboken::Object::hasCppWrapper(self)) {
+ qWarning().noquote().nospace() << __FUNCTION__
+ << ": Cannot add dynamic method \"" << signature << "\" (" << type
+ << ") to " << source << ": No Wrapper found.";
+ return -1;
+ }
+ auto *pySelf = reinterpret_cast<PyObject *>(self);
+ auto *dict = SbkObject_GetDict_NoRef(pySelf);
+ MetaObjectBuilder *dmo = metaBuilderFromDict(dict);
+
+ // Create a instance meta object
+ if (!dmo) {
+ dmo = new MetaObjectBuilder(Py_TYPE(pySelf), metaObject);
+ PyObject *pyDmo = PyCapsule_New(dmo, nullptr, destroyMetaObject);
+ PyObject_SetAttr(pySelf, metaObjectAttr, pyDmo);
+ Py_DECREF(pyDmo);
+ }
+
+ if (type == QMetaMethod::Slot) {
+ qCWarning(lcPySide).noquote().nospace()
+ << "Warning: Registering dynamic slot \""
+ << signature << "\" on \"" << source->metaObject()->className()
+ << "\". Consider annotating with " << slotSignature(signature);
+ }
+
+ return type == QMetaMethod::Signal
+ ? dmo->addSignal(signature) : dmo->addSlot(signature);
+ }
+ return methodIndex;
+}
+
+const QMetaObject *SignalManager::retrieveMetaObject(PyObject *self)
+{
+ // PYSIDE-803: Avoid the GIL in SignalManager::retrieveMetaObject
+ // This function had the GIL. We do not use the GIL unless we have to.
+ // metaBuilderFromDict accesses a Python dict, but in that context there
+ // is no way to reach the interpreter, see "metaBuilderFromDict".
+ //
+ // The update function is MetaObjectBuilderPrivate::update in
+ // dynamicmetaobject.c . That function now uses the GIL when the
+ // m_dirty flag is set.
+ Q_ASSERT(self);
+
+ auto *ob_dict = SbkObject_GetDict_NoRef(self);
+ MetaObjectBuilder *builder = metaBuilderFromDict(ob_dict);
+ if (!builder)
+ builder = &(retrieveTypeUserData(self)->mo);
+
+ return builder->update();
+}
+
+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];
+ 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;
+}
+
+#include "signalmanager.moc"
diff --git a/sources/pyside6/libpyside/signalmanager.h b/sources/pyside6/libpyside/signalmanager.h
new file mode 100644
index 000000000..397700df1
--- /dev/null
+++ b/sources/pyside6/libpyside/signalmanager.h
@@ -0,0 +1,100 @@
+// 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 SIGNALMANAGER_H
+#define SIGNALMANAGER_H
+
+#include "pysidemacros.h"
+
+#include <sbkpython.h>
+#include <shibokenmacros.h>
+
+#include <QtCore/QMetaMethod>
+
+#include <optional>
+
+QT_FORWARD_DECLARE_CLASS(QDataStream)
+
+namespace PySide
+{
+
+/// Thin wrapper for PyObject which increases the reference count at the constructor but *NOT* at destructor.
+class PYSIDE_API PyObjectWrapper
+{
+public:
+ PyObjectWrapper(PyObjectWrapper&&) = delete;
+ PyObjectWrapper& operator=(PyObjectWrapper &&) = delete;
+
+ PyObjectWrapper();
+ explicit PyObjectWrapper(PyObject* me);
+ PyObjectWrapper(const PyObjectWrapper &other);
+ PyObjectWrapper& operator=(const PyObjectWrapper &other);
+
+ void reset(PyObject *o);
+
+ ~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;
+};
+
+PYSIDE_API QDataStream &operator<<(QDataStream& out, const PyObjectWrapper& myObj);
+PYSIDE_API QDataStream &operator>>(QDataStream& in, PyObjectWrapper& myObj);
+
+class PYSIDE_API 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 *receiver = nullptr);
+ void releaseGlobalReceiver(const QObject* sender, QObject* receiver);
+ int globalReceiverSlotIndex(QObject* sender, const char* slotSignature) const;
+ void notifyGlobalReceiver(QObject* receiver);
+
+ bool emitSignal(QObject* source, const char* signal, PyObject* args);
+ static int qt_metacall(QObject* object, QMetaObject::Call call, int id, void** args);
+
+ // Used to register a new signal/slot on QMetaobject of source.
+ static bool registerMetaMethod(QObject* source, const char* signature, QMetaMethod::MethodType type);
+ static int registerMetaMethodGetIndex(QObject* source, const char* signature, QMetaMethod::MethodType type);
+
+ // used to discovery metaobject
+ static const QMetaObject* retrieveMetaObject(PyObject* self);
+
+ // 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);
+
+ static void deleteGlobalReceiver(const QObject *globalReceiver);
+
+private:
+ struct SignalManagerPrivate;
+ SignalManagerPrivate* m_d;
+
+ SignalManager();
+ ~SignalManager();
+};
+
+}
+
+Q_DECLARE_METATYPE(PySide::PyObjectWrapper)
+
+#endif