aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/libpysideremoteobjects/pysidedynamicpod.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside6/libpysideremoteobjects/pysidedynamicpod.cpp')
-rw-r--r--sources/pyside6/libpysideremoteobjects/pysidedynamicpod.cpp260
1 files changed, 260 insertions, 0 deletions
diff --git a/sources/pyside6/libpysideremoteobjects/pysidedynamicpod.cpp b/sources/pyside6/libpysideremoteobjects/pysidedynamicpod.cpp
new file mode 100644
index 000000000..abfeaa037
--- /dev/null
+++ b/sources/pyside6/libpysideremoteobjects/pysidedynamicpod.cpp
@@ -0,0 +1,260 @@
+// Copyright (C) 2025 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "pysidedynamicpod_p.h"
+#include "pysidecapsulemethod_p.h"
+#include "pysidedynamiccommon_p.h"
+
+#include <autodecref.h>
+#include <helper.h>
+#include <pep384ext.h>
+#include <sbkconverter.h>
+#include <sbkstaticstrings.h>
+#include <sbkstring.h>
+
+#include <pysidestaticstrings.h>
+
+#include <QtCore/qmetaobject.h>
+
+using namespace Shiboken;
+
+extern "C"
+{
+
+struct PodDefs
+{
+ static PyObject *tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+ {
+ SBK_UNUSED(kwds);
+ AutoDecRef param_types(PyObject_GetAttrString(reinterpret_cast<PyObject *>(type),
+ "__param_types__"));
+ if (!param_types) {
+ PyErr_Format(PyExc_RuntimeError, "Failed to get POD attributes for type %s",
+ type->tp_name);
+ return nullptr;
+ }
+
+ // param_types is a tuple of PyTypeObject pointers
+ Py_ssize_t size = PyTuple_Size(param_types);
+ if (size != PyTuple_Size(args)) {
+ PyErr_Format(PyExc_TypeError,
+ "Incorrect number of arguments for type %s. Expected %zd.",
+ type->tp_name, size);
+ return nullptr;
+ }
+
+ PyObject *self = PepExt_Type_GetAllocSlot(type)(type, size);
+
+ if (!self)
+ return nullptr;
+
+ for (Py_ssize_t i = 0; i < size; ++i) {
+ PyObject *expected_type = PyTuple_GetItem(param_types, i);
+ PyObject *item = PyTuple_GetItem(args, i);
+ // Check if the item is an instance of the expected type
+ if (PyObject_IsInstance(item, expected_type)) {
+ Py_INCREF(item);
+ PyTuple_SetItem(self, i, item);
+ } else {
+ // Try to convert the item to the expected type
+ PyObject *converted_item = PyObject_CallFunctionObjArgs(expected_type, item, nullptr);
+ if (!converted_item) {
+ Py_DECREF(self);
+ PyErr_Format(PyExc_TypeError, "Argument %zd must be convertible to type %s", i,
+ reinterpret_cast<PyTypeObject *>(expected_type)->tp_name);
+ return nullptr;
+ }
+ PyTuple_SetItem(self, i, converted_item);
+ }
+ }
+
+ return self;
+ }
+
+ static PyObject *tp_repr(PyObject *self)
+ {
+ auto *type = Py_TYPE(self);
+ std::string repr(type->tp_name);
+ repr += "(";
+ for (Py_ssize_t i = 0; i < PyTuple_Size(self); ++i) {
+ if (i > 0)
+ repr += ", ";
+
+ PyObject *item_repr = PyObject_Repr(PyTuple_GetItem(self, i));
+ repr += String::toCString(item_repr);
+ }
+ repr += ")";
+ return PyUnicode_FromString(repr.c_str());
+ }
+
+ static PyObject *CapsuleMethod_handler(PyObject *payload, PyObject * /* args */)
+ {
+ auto *methodData = reinterpret_cast<CapsuleDescriptorData *>(
+ PyCapsule_GetPointer(payload, "Payload"));
+ if (!methodData) {
+ PyErr_SetString(PyExc_RuntimeError, "Invalid call to dynamic method. Missing payload.");
+ return nullptr;
+ }
+ PyObject *self = methodData->self;
+ if (PyCapsule_IsValid(methodData->payload, "PropertyCapsule")) {
+ // Handle property getter/setter against our hidden properties attribute
+ auto *capsule = PyCapsule_GetPointer(methodData->payload, "PropertyCapsule");
+ if (capsule) {
+ auto *callData = reinterpret_cast<PropertyCapsule *>(capsule);
+ if (callData->indexInObject < 0 || callData->indexInObject >= PyTuple_Size(self)) {
+ PyErr_Format(PyExc_RuntimeError, "Unknown property method: %s",
+ callData->name.constData());
+ return nullptr;
+ }
+ auto *val = PyTuple_GetItem(self, callData->indexInObject);
+ Py_INCREF(val);
+ return val;
+ }
+ }
+
+ PyErr_SetString(PyExc_RuntimeError, "Unknown capsule type");
+ return nullptr;
+ }
+};
+
+static PyMethodDef DynamicPod_tp_methods[] = {
+ {"get_enum", reinterpret_cast<PyCFunction>(DynamicType_get_enum), METH_O | METH_CLASS,
+ "Get enum type by name"},
+ {nullptr, nullptr, 0, nullptr}
+};
+
+static PyType_Slot DynamicPod_slots[] = {
+ {Py_tp_base, reinterpret_cast<void *>(&PyTuple_Type)},
+ {Py_tp_new, reinterpret_cast<void *>(PodDefs::tp_new)},
+ {Py_tp_repr, reinterpret_cast<void *>(PodDefs::tp_repr)},
+ {Py_tp_methods, reinterpret_cast<void *>(DynamicPod_tp_methods)},
+ {0, nullptr}
+};
+
+// C++ to Python conversion for POD types.
+static PyObject *cppToPython_POD_Tuple(const void *cppIn)
+{
+ const auto &cppInRef = *reinterpret_cast<const QVariantList *>(cppIn);
+ PyObject *pyOut = PyTuple_New(Py_ssize_t(cppInRef.size()));
+ Py_ssize_t idx = 0;
+ for (auto it = std::cbegin(cppInRef), end = std::cend(cppInRef); it != end; ++it, ++idx) {
+ static const Conversions::SpecificConverter argConverter("QVariant");
+ const auto &cppItem = *it;
+ PyTuple_SetItem(pyOut, idx, Shiboken::Conversions::copyToPython(argConverter, &cppItem));
+ }
+ return pyOut;
+}
+static void pythonToCpp_Tuple_POD(PyObject *pyIn, void *cppOut)
+{
+ auto &cppOutRef = *reinterpret_cast<QVariantList *>(cppOut);
+
+ Py_ssize_t tupleSize = PyTuple_Size(pyIn);
+ if (tupleSize != cppOutRef.size()) {
+ PyErr_Format(PyExc_ValueError,
+ "Size mismatch: tuple has %zd elements, but POD expects %d elements",
+ tupleSize, cppOutRef.size());
+ return;
+ }
+
+ for (Py_ssize_t i = 0; i < tupleSize; ++i) {
+ static const Conversions::SpecificConverter argConverter("QVariant");
+ PyObject *item = PyTuple_GetItem(pyIn, i);
+ QVariant &variant = cppOutRef[i];
+ Conversions::SpecificConverter converter(variant.metaType().name());
+ Shiboken::Conversions::pythonToCppCopy(converter, item, variant.data());
+ }
+}
+static PythonToCppFunc is_Tuple_PythonToCpp_POD_Convertible(PyObject *pyIn)
+{
+ if (PyTuple_Check(pyIn))
+ return pythonToCpp_Tuple_POD;
+
+ return {};
+}
+
+} // extern "C"
+
+PyTypeObject *createPodType(QMetaObject *meta)
+{
+ auto qualname = QByteArrayLiteral("DynamicPod.") + meta->className();
+ PyType_Spec spec = {
+ qualname.constData(),
+ 0,
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_TYPE_SUBCLASS,
+ DynamicPod_slots
+ };
+
+ PyObject *obType = PyType_FromSpec(&spec);
+ if (!obType)
+ return nullptr;
+
+ if (create_managed_py_enums(obType, meta) < 0)
+ return nullptr;
+
+ Py_ssize_t size = meta->propertyCount() - meta->propertyOffset();
+ AutoDecRef pyParamTypes(PyTuple_New(size));
+ for (int i = 0; i < size; ++i) {
+ auto metaProperty = meta->property(i + meta->propertyOffset());
+ auto metaType = metaProperty.metaType();
+ if (!metaType.isValid()) {
+ PyErr_Format(PyExc_RuntimeError, "Failed to get meta type for property %s",
+ metaProperty.name());
+ return nullptr;
+ }
+ auto *pyType = Conversions::getPythonTypeObject(metaType.name());
+ Py_INCREF(pyType);
+ PyTuple_SetItem(pyParamTypes, i, reinterpret_cast<PyObject *>(pyType));
+ }
+
+ auto *type = reinterpret_cast<PyTypeObject *>(obType);
+ PyMethodDef method = {
+ nullptr,
+ reinterpret_cast<PyCFunction>(PodDefs::CapsuleMethod_handler),
+ METH_VARARGS,
+ nullptr
+ };
+ for (int i = meta->propertyOffset(); i < meta->propertyCount(); ++i) {
+ // Create a PropertyCapsule for each property to store the info needed
+ // for the handler.
+ auto metaProperty = meta->property(i);
+
+ method.ml_name = metaProperty.name();
+ auto *capsule = PyCapsule_New(new PropertyCapsule{metaProperty.name(),
+ i,
+ i - meta->propertyOffset()},
+ "PropertyCapsule",
+ [](PyObject *capsule) {
+ delete static_cast<PropertyCapsule *>(
+ PyCapsule_GetPointer(capsule, "PropertyCapsule"));
+ });
+ auto *capsulePropObject = make_capsule_property(&method, capsule);
+ if (PyObject_SetAttrString(reinterpret_cast<PyObject *>(type), metaProperty.name(),
+ capsulePropObject) < 0) {
+ return nullptr;
+ }
+
+ Py_DECREF(capsulePropObject);
+ }
+
+ // createConverter increases the ref count of type, but that will create
+ // a circular reference. When we add the capsule with the converter's pointer
+ // to the type's attributes. So we need to decrease the ref count on the type
+ // after calling createConverter.
+ auto *converter = Shiboken::Conversions::createConverter(type, cppToPython_POD_Tuple);
+ Py_DECREF(type);
+ if (set_cleanup_capsule_attr_for_pointer(type, "_converter_capsule", converter) < 0)
+ return nullptr;
+ Shiboken::Conversions::registerConverterName(converter, meta->className());
+ Shiboken::Conversions::registerConverterName(converter, type->tp_name);
+ Shiboken::Conversions::addPythonToCppValueConversion(converter, pythonToCpp_Tuple_POD,
+ is_Tuple_PythonToCpp_POD_Convertible);
+
+ static PyObject *const module = String::createStaticString("PySide6.QtRemoteObjects");
+ AutoDecRef pyQualname(String::fromCString(qualname.constData()));
+ PyObject_SetAttr(obType, PyMagicName::qualname(), pyQualname);
+ PyObject_SetAttr(obType, PyMagicName::module(), module);
+ PyObject_SetAttrString(obType, "__param_types__", pyParamTypes);
+
+ return type;
+}