aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/libpysideremoteobjects/pysidecapsulemethod.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside6/libpysideremoteobjects/pysidecapsulemethod.cpp')
-rw-r--r--sources/pyside6/libpysideremoteobjects/pysidecapsulemethod.cpp230
1 files changed, 230 insertions, 0 deletions
diff --git a/sources/pyside6/libpysideremoteobjects/pysidecapsulemethod.cpp b/sources/pyside6/libpysideremoteobjects/pysidecapsulemethod.cpp
new file mode 100644
index 000000000..d5a5454f0
--- /dev/null
+++ b/sources/pyside6/libpysideremoteobjects/pysidecapsulemethod.cpp
@@ -0,0 +1,230 @@
+// 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 "pysidecapsulemethod_p.h"
+
+extern "C"
+{
+
+// This struct is used for both CapsuleMethod and CapsuleProperty
+struct CapsuleDescriptor
+{
+ PyTypeObject base;
+ PyObject *capsule;
+ PyMethodDef methodDef;
+
+ void configure(PyObject *capsule, PyMethodDef *method)
+ {
+ this->capsule = capsule;
+ Py_INCREF(capsule);
+ // We make a copy of the input name and doc strings so they can be temporary on
+ // the input.
+ if (method->ml_name)
+ methodDef.ml_name = strdup(method->ml_name);
+ if (method->ml_doc)
+ methodDef.ml_doc = strdup(method->ml_doc);
+ methodDef.ml_meth = method->ml_meth;
+ methodDef.ml_flags = method->ml_flags;
+ }
+};
+
+static PyObject *CapsuleDescriptor_tp_new(PyTypeObject *type, PyObject * /* args */, PyObject * /* kwds */);
+static void CapsuleDescriptor_free(PyObject *self);
+static PyObject *CapsuleMethod_descr_get(PyObject *self, PyObject *instance, PyObject * /* owner */);
+static PyObject *CapsuleProperty_descr_get(PyObject *self, PyObject *instance, PyObject * /* owner */);
+static int CapsuleProperty_descr_set(PyObject *self, PyObject *instance, PyObject * /* owner */);
+
+/**
+ * We are creating two related types, CapsuleMethod and CapsuleProperty, that are
+ * used to enable lambda-like behavior. The difference is in usage, where
+ * CapsuleMethod's __get__ function returns a Callable (i.e., method-like usage:
+ * obj.capsuleMethodName(args)) and only supports the __get__ method.
+ * CapsuleProperty on the other hand is used for properties, and supports both
+ * __get__ and __set__ methods (i.e., obj.capsulePropertyName = value or val =
+ * obj.capsulePropertyName).
+ */
+static PyTypeObject *createCapsuleMethodType()
+{
+ PyType_Slot CapsuleMethodType_slots[] = {
+ {Py_tp_new, reinterpret_cast<void *>(CapsuleDescriptor_tp_new)},
+ {Py_tp_descr_get, reinterpret_cast<void *>(CapsuleMethod_descr_get)},
+ {Py_tp_free, reinterpret_cast<void *>(CapsuleDescriptor_free)},
+ {0, nullptr}
+ };
+
+ PyType_Spec CapsuleMethodType_spec = {
+ "2:PySide6.QtRemoteObjects.CapsuleMethod",
+ sizeof(CapsuleDescriptor),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ CapsuleMethodType_slots};
+
+ PyObject *type = PyType_FromSpec(&CapsuleMethodType_spec);
+ if (!type) {
+ PyErr_Print();
+ return nullptr;
+ }
+ return reinterpret_cast<PyTypeObject*>(type);
+}
+
+PyTypeObject *CapsuleMethod_TypeF(void)
+{
+ static auto *type = createCapsuleMethodType();
+ return type;
+}
+
+static PyTypeObject *createCapsulePropertyType(bool isWritable)
+{
+ PyType_Slot WritablePropertyType_slots[] = {
+ {Py_tp_new, reinterpret_cast<void *>(CapsuleDescriptor_tp_new)},
+ {Py_tp_descr_get, reinterpret_cast<void *>(CapsuleProperty_descr_get)},
+ {Py_tp_descr_set, reinterpret_cast<void *>(CapsuleProperty_descr_set)},
+ {Py_tp_free, reinterpret_cast<void *>(CapsuleDescriptor_free)},
+ {0, nullptr}
+ };
+
+ PyType_Slot ReadOnlyPropertyType_slots[] = {
+ {Py_tp_new, reinterpret_cast<void *>(CapsuleDescriptor_tp_new)},
+ {Py_tp_descr_get, reinterpret_cast<void *>(CapsuleProperty_descr_get)},
+ {Py_tp_free, reinterpret_cast<void *>(CapsuleDescriptor_free)},
+ {0, nullptr}
+ };
+
+ PyType_Spec CapsulePropertyType_spec = {
+ "2:PySide6.QtRemoteObjects.CapsuleProperty",
+ sizeof(CapsuleDescriptor),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ isWritable ? WritablePropertyType_slots : ReadOnlyPropertyType_slots};
+
+ PyObject *type = PyType_FromSpec(&CapsulePropertyType_spec);
+ if (!type) {
+ PyErr_Print();
+ return nullptr;
+ }
+ return reinterpret_cast<PyTypeObject*>(type);
+}
+
+PyTypeObject *CapsuleProperty_TypeF(bool isWritable=false)
+{
+ if (isWritable) {
+ static auto *type = createCapsulePropertyType(true);
+ return type;
+ }
+ static auto *type = createCapsulePropertyType(false);
+ return type;
+}
+
+static PyObject *CapsuleDescriptor_tp_new(PyTypeObject *type, PyObject * /* args */, PyObject * /* kwds */)
+{
+ auto *self = reinterpret_cast<CapsuleDescriptor *>(PyType_GenericAlloc(type, 0));
+ if (self != nullptr) {
+ self->capsule = nullptr;
+ self->methodDef = {nullptr, nullptr, METH_NOARGS, nullptr}; // Initialize methodDef
+ }
+ return reinterpret_cast<PyObject *>(self);
+}
+
+static void CapsuleDescriptor_free(PyObject *self)
+{
+ auto *d = reinterpret_cast<CapsuleDescriptor *>(self);
+ Py_XDECREF(d->capsule);
+ free(const_cast<char*>(d->methodDef.ml_name));
+ free(const_cast<char*>(d->methodDef.ml_doc));
+}
+
+static PyObject *CapsuleMethod_descr_get(PyObject *self, PyObject *instance, PyObject * /* owner */)
+{
+ if (instance == nullptr) {
+ // Return the descriptor object if accessed from the class
+ Py_INCREF(self);
+ return self;
+ }
+
+ auto *d = reinterpret_cast<CapsuleDescriptor *>(self);
+ CapsuleDescriptorData *data = new CapsuleDescriptorData{instance, d->capsule};
+ PyObject *payload = PyCapsule_New(data, "Payload", [](PyObject *capsule) {
+ delete reinterpret_cast<CapsuleDescriptorData *>(PyCapsule_GetPointer(capsule, "Payload"));
+ });
+ if (!payload)
+ return nullptr;
+
+ Py_INCREF(payload);
+ return PyCFunction_New(&d->methodDef, payload);
+}
+
+bool add_capsule_method_to_type(PyTypeObject *type, PyMethodDef *method, PyObject *capsule)
+{
+ if (PyType_Ready(type) < 0) {
+ PyErr_Print();
+ return false;
+ }
+ auto *descriptor = reinterpret_cast<CapsuleDescriptor *>(
+ PyObject_CallObject(reinterpret_cast<PyObject *>(CapsuleMethod_TypeF()), nullptr));
+ if (!descriptor) {
+ PyErr_Print();
+ return false;
+ }
+ descriptor->configure(capsule, method);
+
+ auto *descr = reinterpret_cast<PyObject *>(descriptor);
+ if (PyObject_SetAttrString(reinterpret_cast<PyObject *>(type), method->ml_name, descr) < 0) {
+ PyErr_Print();
+ return false;
+ }
+ return true;
+}
+
+static PyObject *CapsuleProperty_descr_get(PyObject *self, PyObject *instance, PyObject * /* owner */)
+{
+ if (instance == nullptr) {
+ // Return the descriptor object if accessed from the class
+ Py_INCREF(self);
+ return self;
+ }
+
+ auto *d = reinterpret_cast<CapsuleDescriptor *>(self);
+ CapsuleDescriptorData *data = new CapsuleDescriptorData{instance, d->capsule};
+ PyObject *payload = PyCapsule_New(data, "Payload", [](PyObject *capsule) {
+ delete reinterpret_cast<CapsuleDescriptorData *>(PyCapsule_GetPointer(capsule, "Payload"));
+ });
+ if (!payload)
+ return nullptr;
+
+ return PyObject_CallFunctionObjArgs(PyCFunction_New(&d->methodDef, payload), nullptr);
+}
+
+static int CapsuleProperty_descr_set(PyObject *self, PyObject *instance, PyObject *value)
+{
+ auto *d = reinterpret_cast<CapsuleDescriptor *>(self);
+ CapsuleDescriptorData *data = new CapsuleDescriptorData{instance, d->capsule};
+ PyObject *payload = PyCapsule_New(data, "Payload", [](PyObject *capsule) {
+ delete reinterpret_cast<CapsuleDescriptorData *>(PyCapsule_GetPointer(capsule, "Payload"));
+ });
+ if (!payload)
+ return -1;
+
+ Py_INCREF(payload);
+ PyObject *result = PyObject_CallFunctionObjArgs(PyCFunction_New(&d->methodDef, payload),
+ value, nullptr);
+ if (!result)
+ return -1;
+
+ Py_DECREF(result);
+ return 0;
+}
+
+// Returns a new CapsuleProperty descriptor object for use with PySideProperty
+PyObject *make_capsule_property(PyMethodDef *method, PyObject *capsule, bool isWritable)
+{
+ auto *type = CapsuleProperty_TypeF(isWritable);
+ auto *descriptor = PyObject_CallObject(reinterpret_cast<PyObject *>(type), nullptr);
+ if (!descriptor)
+ return nullptr;
+
+ reinterpret_cast<CapsuleDescriptor*>(descriptor)->configure(capsule, method);
+
+ return descriptor;
+}
+
+} // extern "C"