I’m looking for help, support on how to improve or eventually debug this particular code.
This is part of a large C++ application which allow to call python code.
I have tested on 3.9, 3.11 and 3.12 and when running valgrind , this report an important leak of memory in this CDevice_refresh().
I’m not the developper of this code, and not a specialist of the embedded python, so I’m looking for help and advice.
Is that the right way to do it ?
I feel that PyDict_Clear(self->Options) is cleaning the dictionnary ( self->Options), but in case anything has been allocated , it won’t be release. Am I wrong ? Is there a better way to write that ?
PyObject* CDevice_refresh(CDevice* self)
{
if ((self->pPlugin) && (self->HwdID != -1) && (self->Unit != -1))
{
// load associated devices to make them available to python
std::vector<std::vector<std::string> > result;
result = m_sql.safe_query("SELECT Unit, ID, Name, nValue, sValue, DeviceID, Type, SubType, SwitchType, LastLevel, CustomImage, SignalLevel, BatteryLevel, LastUpdate, Options, Description, Color, Used FROM DeviceStatus WHERE (HardwareID==%d) AND (Unit==%d) ORDER BY Unit ASC", self->HwdID, self->Unit);
if (!result.empty())
{
for (const auto &sd : result)
{
self->Unit = atoi(sd[0].c_str());
self->ID = atoi(sd[1].c_str());
Py_XDECREF(self->Name);
self->Name = PyUnicode_FromString(sd[2].c_str());
self->nValue = atoi(sd[3].c_str());
Py_XDECREF(self->sValue);
self->sValue = PyUnicode_FromString(sd[4].c_str());
Py_XDECREF(self->DeviceID);
self->DeviceID = PyUnicode_FromString(sd[5].c_str());
self->Type = atoi(sd[6].c_str());
self->SubType = atoi(sd[7].c_str());
self->SwitchType = atoi(sd[8].c_str());
self->LastLevel = atoi(sd[9].c_str());
self->Image = atoi(sd[10].c_str());
self->SignalLevel = atoi(sd[11].c_str());
self->BatteryLevel = atoi(sd[12].c_str());
Py_XDECREF(self->LastUpdate);
self->LastUpdate = PyUnicode_FromString(sd[13].c_str());
PyDict_Clear(self->Options);
if (!sd[14].empty())
{
if (self->SubType == sTypeCustom)
{
PyDict_SetItemString(self->Options, "Custom", PyUnicode_FromString(sd[14].c_str()));
}
else
{
std::map<std::string, std::string> mpOptions;
Py_BEGIN_ALLOW_THREADS
mpOptions = m_sql.BuildDeviceOptions(sd[14], true);
Py_END_ALLOW_THREADS
for (const auto &opt : mpOptions)
{
PyNewRef pKeyDict = PyUnicode_FromString(opt.first.c_str());
PyNewRef pValueDict = PyUnicode_FromString(opt.second.c_str());
if (PyDict_SetItem(self->Options, pKeyDict, pValueDict) == -1)
{
_log.Log(LOG_ERROR, "(%s) Failed to refresh Options dictionary for Hardware/Unit combination (%d:%d).", self->pPlugin->m_Name.c_str(), self->HwdID, self->Unit);
break;
}
}
}
}
Py_XDECREF(self->Description);
self->Description = PyUnicode_FromString(sd[15].c_str());
Py_XDECREF(self->Color);
self->Color = PyUnicode_FromString(_tColor(std::string(sd[16])).toJSONString().c_str()); //Parse the color to detect incorrectly formatted color data
self->Used = atoi(sd[17].c_str());
}
}
}
else
{
_log.Log(LOG_ERROR, "Device refresh failed, Device object is not associated with a plugin.");
}
Py_RETURN_NONE;
}