GUI: Work around Shiboken 6 bug for CallTips

Shiboken 6.4.0 to 6.8.0 and newer crash when using getAttr on their
objects. On these versions and newer, use a wrapper function to
call getattr.

Fixes #14101
This commit is contained in:
Jookia
2024-07-07 00:30:26 +10:00
parent 561e521817
commit 8e1476699f
2 changed files with 40 additions and 12 deletions

View File

@@ -340,24 +340,51 @@ QMap<QString, CallTip> CallTipsList::extractTips(const QString& context) const
return tips;
}
bool shibokenMayCrash(void)
{
// Shiboken 6.4.0 to 6.8.0 crash if we try to read their object
// atttributes without a current stack frame.
// FreeCAD issue: https://github.com/FreeCAD/FreeCAD/issues/14101
// Qt issue: https://bugreports.qt.io/browse/PYSIDE-2796
Py::Module shiboken("shiboken6");
Py::Tuple version(shiboken.getAttr("__version_info__"));
int major = Py::Long(version.getItem(0));
int minor = Py::Long(version.getItem(1));
int patch = Py::Long(version.getItem(2));
bool brokenVersion = (major == 6 && minor >= 4 && minor < 8);
bool fixedVersion = (major == 6 && minor == 7 && patch > 2);
return brokenVersion && !fixedVersion;
}
Py::Object CallTipsList::getAttrWorkaround(Py::Object& obj, Py::String& name) const
{
QString typestr(QLatin1String(Py_TYPE(obj.ptr())->tp_name));
bool hasWorkingGetAttr =
!(typestr == QLatin1String("Shiboken.ObjectType") && shibokenMayCrash());
if (hasWorkingGetAttr) {
return obj.getAttr(name.as_string());
}
Py::Dict evalDict;
evalDict.setItem("obj", obj);
evalDict.setItem("attr", name);
Py::Object bouncer(Py_CompileString("getattr(obj, attr)", "<CallTipsList>", Py_eval_input));
Py::Object attr(PyEval_EvalCode(bouncer.ptr(), evalDict.ptr(), evalDict.ptr()));
return attr;
}
void CallTipsList::extractTipsFromObject(Py::Object& obj, Py::List& list, QMap<QString, CallTip>& tips) const
{
for (Py::List::iterator it = list.begin(); it != list.end(); ++it) {
try {
Py::String attrname(*it);
std::string name = attrname.as_string();
std::string name(attrname.as_string());
// If 'name' is an invalid attribute then PyCXX raises an exception
// for Py2 but silently accepts it for Py3.
//
// FIXME: Add methods of extension to the current instance and not its type object
// https://forum.freecad.org/viewtopic.php?f=22&t=18105
// https://forum.freecad.org/viewtopic.php?f=3&t=20009&p=154447#p154447
// https://forum.freecad.org/viewtopic.php?f=10&t=12534&p=155290#p155290
//
// https://forum.freecad.org/viewtopic.php?f=39&t=33874&p=286759#p286759
// https://forum.freecad.org/viewtopic.php?f=39&t=33874&start=30#p286772
Py::Object attr = obj.getAttr(name);
Py::Object attr = getAttrWorkaround(obj, attrname);
if (!attr.ptr()) {
Base::Console().Log("Python attribute '%s' returns null!\n", name.c_str());
continue;