// 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 "pysidedynamiccommon_p.h" #include "pysidedynamicenum_p.h" #include #include int capsule_count = 0; using namespace Shiboken; PyObject *toPython(const QVariant &variant) { auto metaType = variant.metaType(); Conversions::SpecificConverter converter(metaType.name()); auto *value = converter.toPython(variant.data()); if (metaType.flags().testFlag(QMetaType::IsGadget)) { // A single converter is used for all POD types - it converts to a Python // tuple. We need an additional step to convert to our Python type for the POD. // Thankfully, the converter stores the specific type we created, so we can call // the constructor with the tuple. auto *podType = Conversions::getPythonTypeObject(converter); if (!podType) { Py_DECREF(value); PyErr_SetString(PyExc_RuntimeError, "Failed to get Python type for POD"); return nullptr; } PyObject *podValue = PyObject_CallObject(reinterpret_cast(podType), value); Py_DECREF(value); if (!podValue) { PyErr_SetString(PyExc_RuntimeError, "Failed to create POD instance"); return nullptr; } return podValue; } if (metaType.flags().testFlag(QMetaType::IsEnumeration)) { // Enums are converted to Python ints auto *enumType = Conversions::getPythonTypeObject(converter); if (!enumType) { Py_DECREF(value); PyErr_SetString(PyExc_RuntimeError, "Failed to get Python type for enum"); return nullptr; } PyObject *enumValue = PyObject_CallFunctionObjArgs(reinterpret_cast(enumType), value, nullptr); Py_DECREF(value); if (!enumValue) { PyErr_Print(); PyErr_SetString(PyExc_RuntimeError, "Failed to create enum instance"); return nullptr; } return enumValue; } return value; } /** * @brief Creates and manages memory for Python enum types for each QEnum in the * provided QMetaObject. * * This function iterates over the enumerators in the provided QMetaObject, * creates corresponding Python enum types, and stores them in a dictionary. * The dictionary is then set as an attribute ()"_enum_data") on the provided * Python object, to be accessed by the _get_enum that has been added to each * of our dynamic types. * * These are "managed" in the sense that the enums clean up their converters * using our PyCapsule method, and by adding the dictionary as a Python attribute, * the dictionary will be cleaned up when the containing type is garbage * collected. * * @param self A pointer to the Python object where the enum data will be stored. * @param meta A pointer to the QMetaObject containing the enumerators. * @return Returns 0 on success, or -1 on failure. */ int create_managed_py_enums(PyObject *self, QMetaObject *meta) { PyObject *enum_data = PyDict_New(); for (int i = meta->enumeratorOffset(); i < meta->enumeratorCount(); ++i) { auto metaEnum = meta->enumerator(i); auto *enumType = createEnumType(&metaEnum); if (!enumType) { PyErr_Print(); PyErr_Format(PyExc_RuntimeError, "Failed to create enum type for POD '%s'", meta->className()); return -1; } PyDict_SetItemString(enum_data, metaEnum.enumName(), reinterpret_cast(enumType)); Py_DECREF(enumType); } if (PyObject_SetAttrString(self, "_enum_data", enum_data) < 0) { PyErr_Print(); qWarning() << "Failed to set _enum_data attribute on type" << reinterpret_cast(self)->tp_name; return -1; } Py_DECREF(enum_data); return 0; } PyObject *DynamicType_get_enum(PyObject *self, PyObject *name) { // Our enum types are always stored in a dictionary attribute named "_enum_data" PyObject *enum_dict = PyObject_GetAttrString(self, "_enum_data"); if (!enum_dict) { PyErr_SetString(PyExc_RuntimeError, "Failed to get _enum_data attribute"); return nullptr; } PyObject *enum_type = PyDict_GetItem(enum_dict, name); Py_DECREF(enum_dict); if (!enum_type) { PyErr_Format(PyExc_KeyError, "Enum '%s' not found", String::toCString(name)); return nullptr; } Py_INCREF(enum_type); return enum_type; }