diff options
Diffstat (limited to 'sources/pyside6/libpysideremoteobjects/pysidecapsulemethod.cpp')
| -rw-r--r-- | sources/pyside6/libpysideremoteobjects/pysidecapsulemethod.cpp | 230 |
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" |
