Shiboken2: need help converting args from default type to "custom"
-
By default Shiboken2 converts
const char *toPyUnicode/PyString, but I need it to be converted toPyBytes. Software being wrapped, Scintilla/ScintillaEdit/ScintillaEditPy, stores strings as bytes, so to avoid any potential issues I'd like it to reflect this and be explicit about this by usingb"string".There are two ways to do this:
- add
<primitive-type>to typesystem with conversion rule fromconst char *toPyBytes; - add
<modify-argument>along with<conversion-rule>to<modify-function>.
First won't work because there can be only one conversion native-to-target, and there already seems to be a conversion rule from
const char *toPyUnicode/PyString. As for second, I can't seem to get needed result. Here's an example of typesystem modification that should do this, based on<modify-function signature="loadFromData(const uchar*,uint,const char*,QFlags<Qt::ImageConversionFlag>)">from PySide2typesystem_gui_common.xml:<modify-function signature="describeProperty(const char *)" allow-thread="no"> <modify-argument index="1"> <replace-type modified-type="PyBytes" /> <conversion-rule class="native"> const char* %out = reinterpret_cast<const char*>(PyBytes_AS_STRING(%PYARG_1)); </conversion-rule> </modify-argument> <!-- <inject-code class="target"> --> <!-- const char* cppArg0 = reinterpret_cast<const char*>(PyBytes_AS_STRING(%PYARG_1)); --> <!-- </inject-code> --> </modify-function>First, it seems that
<conversion-rule>within<modify-argument>is handled differently then when in<primitive-type>:%incan't be used because it gets replaced withname, so instead argument SID-dependent%PYARG_1must be used, which is error-prone, also%outgets replaced withname_out, which limits use of multiple unnamed arguments (compare this to nice, automatically generatedcppArg0)... Because it's single type conversion, shouldn't%inand%outbe enough, to avoid messing with argument SID, like in<primitive-type>? Obviously, because it's how Qt/PySide2 itself does it, I doubt there's a solution to this other than to modify source code of Shiboken2...
Second (see code examples at the bottom), by default argument conversion is added at the beginning ofCall function/methodblock, beforePyErr_Occurred(), which is used to check for potential conversion errors, but it seems that<modify-argument>is treated as<inject-code>and both are being added afterPyErr_Occurred()is checked, forcing us to duplicatePyErr_Occurred()block to return in case of failure. Shouldn't<modify-argument>be treated as argument modification and processed beforePyErr_Occurred()?I have even tried using
%FUNCTION_NAME(), which switches method calling to fully manual mode, butPyErr_Occurred()block still remains, even though in this case it clearly serves no purpose at all.Analysis of Shiboken2 source code (
cppgenerator.cpp::CppGenerator::writeMethodWrapper()==>writeFunctionCalls()) shows that indeed both<conversion-rule>and<inject-code>are treated more or less equally: no matter what they are written bywriteMethodCall()afterPyErr_Occurred()block.writeArgumentConversion()(called fromwriteSingleFunctionCall()), which looks and sounds like the most appropriate place to interpret<conversion-rule>for an argument, contains statementif (argType->typeEntry()->isCustom() || argType->typeEntry()->isVarargs()) return;which likely prevents such conversion/snippet to be written in "needed" place.The more I look at all this, the more it becomes obvious that there's no solution to my problem and the code related to this is most likely an ad-hoc/hastily implemented feature...
Here's some code to hopefully make things clearer. Code generated by default:
static PyObject* Sbk_ScintillaEditFunc_describeProperty(PyObject* self, PyObject* pyArg) { ScintillaEditWrapper* cppSelf = nullptr; SBK_UNUSED(cppSelf) if (!Shiboken::Object::isValid(self)) return {}; cppSelf = static_cast<ScintillaEditWrapper *>(reinterpret_cast< ::ScintillaEdit *>(Shiboken::Conversions::cppPointer(SbkScintillaEditPyTypes[SBK_SCINTILLAEDIT_IDX], reinterpret_cast<SbkObject *>(self)))); PyObject* pyResult{}; int overloadId = -1; PythonToCppFunc pythonToCpp{}; SBK_UNUSED(pythonToCpp) // Overloaded function decisor // 0: ScintillaEdit::describeProperty(const char*) if (Shiboken::String::check(pyArg) && (pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(Shiboken::Conversions::PrimitiveTypeConverter<const char*>(), (pyArg)))) { // DEFAULT #1 overloadId = 0; // describeProperty(const char*) } // Function signature not found. if (overloadId == -1) goto Sbk_ScintillaEditFunc_describeProperty_TypeError; // Call function/method { const char* cppArg0; // DEFAULT #2 pythonToCpp(pyArg, &cppArg0); // DEFAULT #2 if (!PyErr_Occurred()) { QByteArray cppResult = cppSelf->describeProperty(cppArg0); pyResult = Shiboken::Conversions::copyToPython(reinterpret_cast<SbkObjectType *>(SbkPySide2_QtCoreTypes[SBK_QBYTEARRAY_IDX]), &cppResult); } } if (PyErr_Occurred() || !pyResult) { Py_XDECREF(pyResult); return {}; } return pyResult; Sbk_ScintillaEditFunc_describeProperty_TypeError: Shiboken::setErrorAboutWrongArguments(pyArg, "ScintillaEditPy.ScintillaEdit.describeProperty"); return {}; }Code I get with
<modify-argument>:static PyObject* Sbk_ScintillaEditFunc_describeProperty(PyObject* self, PyObject* pyArg) { ScintillaEditWrapper* cppSelf = nullptr; SBK_UNUSED(cppSelf) if (!Shiboken::Object::isValid(self)) return {}; cppSelf = static_cast<ScintillaEditWrapper *>(reinterpret_cast< ::ScintillaEdit *>(Shiboken::Conversions::cppPointer(SbkScintillaEditPyTypes[SBK_SCINTILLAEDIT_IDX], reinterpret_cast<SbkObject *>(self)))); PyObject* pyResult{}; int overloadId = -1; PythonToCppFunc pythonToCpp{}; SBK_UNUSED(pythonToCpp) // Overloaded function decisor // 0: ScintillaEdit::describeProperty(const char*) if (PyBytes_Check(pyArg)) { // CHANGE #1 overloadId = 0; // describeProperty(const char*) } // Function signature not found. if (overloadId == -1) goto Sbk_ScintillaEditFunc_describeProperty_TypeError; // Call function/method { if (!PyErr_Occurred()) { // describeProperty(const char*) // Begin code injection const char* name_out = reinterpret_cast<const char*>(PyBytes_AS_STRING(name)); // CHANGE #2. `name` is not defined. Must be `pyArg`. // End of code injection QByteArray cppResult = cppSelf->describeProperty(name_out); // CHANGE #3 pyResult = Shiboken::Conversions::copyToPython(reinterpret_cast<SbkObjectType *>(SbkPySide2_QtCoreTypes[SBK_QBYTEARRAY_IDX]), &cppResult); } } if (PyErr_Occurred() || !pyResult) { Py_XDECREF(pyResult); return {}; } return pyResult; Sbk_ScintillaEditFunc_describeProperty_TypeError: Shiboken::setErrorAboutWrongArguments(pyArg, "ScintillaEditPy.ScintillaEdit.describeProperty"); return {}; }Code I get with
<inject-code>:static PyObject* Sbk_ScintillaEditFunc_describeProperty(PyObject* self, PyObject* pyArg) { ScintillaEditWrapper* cppSelf = nullptr; SBK_UNUSED(cppSelf) if (!Shiboken::Object::isValid(self)) return {}; cppSelf = static_cast<ScintillaEditWrapper *>(reinterpret_cast< ::ScintillaEdit *>(Shiboken::Conversions::cppPointer(SbkScintillaEditPyTypes[SBK_SCINTILLAEDIT_IDX], reinterpret_cast<SbkObject *>(self)))); PyObject* pyResult{}; int overloadId = -1; PythonToCppFunc pythonToCpp{}; SBK_UNUSED(pythonToCpp) // Overloaded function decisor // 0: ScintillaEdit::describeProperty(const char*) if (PyBytes_Check(pyArg)) { // CHANGE #1 overloadId = 0; // describeProperty(const char*) } // Function signature not found. if (overloadId == -1) goto Sbk_ScintillaEditFunc_describeProperty_TypeError; // Call function/method { if (!PyErr_Occurred()) { // describeProperty(const char*) // Begin code injection const char* cppArg0 = reinterpret_cast<const char*>(PyBytes_AS_STRING(pyArg)); // CHANGE #2 // End of code injection QByteArray cppResult = cppSelf->describeProperty(cppArg0); // NO CHANGE. For some reason references undefined `cppArg0` (fully manual code injection is not definition). pyResult = Shiboken::Conversions::copyToPython(reinterpret_cast<SbkObjectType *>(SbkPySide2_QtCoreTypes[SBK_QBYTEARRAY_IDX]), &cppResult); } } if (PyErr_Occurred() || !pyResult) { Py_XDECREF(pyResult); return {}; } return pyResult; Sbk_ScintillaEditFunc_describeProperty_TypeError: Shiboken::setErrorAboutWrongArguments(pyArg, "ScintillaEditPy.ScintillaEdit.describeProperty"); return {}; }Code I want:
static PyObject* Sbk_ScintillaEditFunc_describeProperty(PyObject* self, PyObject* pyArg) { ScintillaEditWrapper* cppSelf = nullptr; SBK_UNUSED(cppSelf) if (!Shiboken::Object::isValid(self)) return {}; cppSelf = static_cast<ScintillaEditWrapper *>(reinterpret_cast< ::ScintillaEdit *>(Shiboken::Conversions::cppPointer(SbkScintillaEditPyTypes[SBK_SCINTILLAEDIT_IDX], reinterpret_cast<SbkObject *>(self)))); PyObject* pyResult{}; int overloadId = -1; PythonToCppFunc pythonToCpp{}; SBK_UNUSED(pythonToCpp) // Overloaded function decisor // 0: ScintillaEdit::describeProperty(const char*) if (PyBytes_Check(pyArg)) { // CHANGE #1 overloadId = 0; // describeProperty(const char*) } // Function signature not found. if (overloadId == -1) goto Sbk_ScintillaEditFunc_describeProperty_TypeError; // Call function/method { const char* cppArg0 = reinterpret_cast<const char*>(PyBytes_AS_STRING(pyArg)); // CHANGE #2 if (!PyErr_Occurred()) { QByteArray cppResult = cppSelf->describeProperty(cppArg0); pyResult = Shiboken::Conversions::copyToPython(reinterpret_cast<SbkObjectType *>(SbkPySide2_QtCoreTypes[SBK_QBYTEARRAY_IDX]), &cppResult); } } if (PyErr_Occurred() || !pyResult) { Py_XDECREF(pyResult); return {}; } return pyResult; Sbk_ScintillaEditFunc_describeProperty_TypeError: Shiboken::setErrorAboutWrongArguments(pyArg, "ScintillaEditPy.ScintillaEdit.describeProperty"); return {}; } - add