diff options
Diffstat (limited to 'sources/pyside6/libpysideremoteobjects/pysidedynamicpod.cpp')
| -rw-r--r-- | sources/pyside6/libpysideremoteobjects/pysidedynamicpod.cpp | 260 |
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; +} |
