Skip to content

Commit 06860d4

Browse files
Fix reference/memory leaks
1 parent 87362b4 commit 06860d4

File tree

5 files changed

+107
-71
lines changed

5 files changed

+107
-71
lines changed

src/PythonQt.cpp

Lines changed: 84 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -379,18 +379,23 @@ PythonQt::PythonQt(int flags, const QByteArray& pythonQtModuleName)
379379
PythonQt::~PythonQt() {
380380
delete _p;
381381
_p = NULL;
382+
383+
Py_DECREF(&PythonQtSlotFunction_Type);
384+
Py_DECREF(&PythonQtSignalFunction_Type);
385+
Py_DECREF(&PythonQtSlotDecorator_Type);
386+
Py_DECREF(&PythonQtProperty_Type);
387+
Py_DECREF(&PythonQtBoolResult_Type);
388+
Py_DECREF(&PythonQtClassWrapper_Type);
389+
Py_DECREF(&PythonQtInstanceWrapper_Type);
390+
Py_DECREF(&PythonQtStdOutRedirectType);
391+
Py_DECREF(&PythonQtStdInRedirectType);
382392
}
383393

384394
PythonQtPrivate::~PythonQtPrivate() {
385395
delete _defaultImporter;
386396
_defaultImporter = NULL;
387397

388-
{
389-
QHashIterator<QByteArray, PythonQtClassInfo *> i(_knownClassInfos);
390-
while (i.hasNext()) {
391-
delete i.next().value();
392-
}
393-
}
398+
qDeleteAll(_knownClassInfos);
394399

395400
PythonQtMethodInfo::cleanupCachedMethodInfos();
396401
PythonQtArgumentFrame::cleanupFreeList();
@@ -409,7 +414,9 @@ void PythonQt::setRedirectStdInCallback(PythonQtInputChangedCB* callback, void *
409414

410415
// Backup original 'sys.stdin' if not yet done
411416
if( !PyObject_HasAttrString(sys.object(), "pythonqt_original_stdin") ) {
412-
PyObject_SetAttrString(sys.object(), "pythonqt_original_stdin", PyObject_GetAttrString(sys.object(), "stdin"));
417+
PyObject *stdin = PyObject_GetAttrString(sys.object(), "stdin");
418+
PyObject_SetAttrString(sys.object(), "pythonqt_original_stdin", stdin);
419+
Py_XDECREF(stdin);
413420
}
414421

415422
in = PythonQtStdInRedirectType.tp_new(&PythonQtStdInRedirectType, NULL, NULL);
@@ -428,15 +435,19 @@ void PythonQt::setRedirectStdInCallbackEnabled(bool enabled)
428435
PythonQtObjectPtr sys;
429436
sys.setNewRef(PyImport_ImportModule("sys"));
430437

438+
PythonQtObjectPtr stdin;
431439
if (enabled) {
432-
if( !PyObject_HasAttrString(sys.object(), "pythonqt_stdin") ) {
433-
PyObject_SetAttrString(sys.object(), "stdin", PyObject_GetAttrString(sys.object(), "pythonqt_stdin"));
440+
if( PyObject_HasAttrString(sys.object(), "pythonqt_stdin") ) {
441+
stdin.setNewRef(PyObject_GetAttrString(sys.object(), "pythonqt_stdin"));
434442
}
435443
} else {
436-
if( !PyObject_HasAttrString(sys.object(), "pythonqt_original_stdin") ) {
437-
PyObject_SetAttrString(sys.object(), "stdin", PyObject_GetAttrString(sys.object(), "pythonqt_original_stdin"));
444+
if( PyObject_HasAttrString(sys.object(), "pythonqt_original_stdin") ) {
445+
stdin.setNewRef(PyObject_GetAttrString(sys.object(), "pythonqt_original_stdin"));
438446
}
439447
}
448+
if (stdin) {
449+
PyObject_SetAttrString(sys.object(), "stdin", stdin);
450+
}
440451
}
441452

442453
PythonQtImportFileInterface* PythonQt::importInterface()
@@ -448,7 +459,7 @@ void PythonQt::qObjectNoLongerWrappedCB(QObject* o)
448459
{
449460
if (_self->_p->_noLongerWrappedCB) {
450461
(*_self->_p->_noLongerWrappedCB)(o);
451-
};
462+
}
452463
}
453464

454465
void PythonQt::setQObjectMissingAttributeCallback(PythonQtQObjectMissingAttributeCB* cb)
@@ -525,6 +536,7 @@ void PythonQtPrivate::createPythonQtClassWrapper(PythonQtClassInfo* info, const
525536
PythonQtClassInfo* outerClassInfo = lookupClassInfoAndCreateIfNotPresent(outerClass);
526537
outerClassInfo->addNestedClass(info);
527538
} else {
539+
Py_INCREF(pyobj);
528540
PyModule_AddObject(pack, info->className(), pyobj);
529541
}
530542
if (!module && package && strncmp(package, "Qt", 2) == 0) {
@@ -534,6 +546,7 @@ void PythonQtPrivate::createPythonQtClassWrapper(PythonQtClassInfo* info, const
534546
PyModule_AddObject(packageByName("Qt"), info->className(), pyobj);
535547
}
536548
info->setPythonQtClassWrapper(pyobj);
549+
Py_DECREF(pyobj);
537550
}
538551

539552
PyObject* PythonQtPrivate::wrapQObject(QObject* obj)
@@ -548,6 +561,7 @@ PyObject* PythonQtPrivate::wrapQObject(QObject* obj)
548561
// address, so probably that C++ wrapper has been deleted earlier and
549562
// now we see a QObject with the same address.
550563
// Do not use the old wrapper anymore.
564+
removeWrapperPointer(obj);
551565
wrap = NULL;
552566
}
553567
if (!wrap) {
@@ -733,6 +747,7 @@ PythonQtClassWrapper* PythonQtPrivate::createNewPythonQtClassWrapper(PythonQtCla
733747
PyObject* typeDict = PyDict_New();
734748
PyObject* moduleName = PyObject_GetAttrString(parentModule, "__name__");
735749
PyDict_SetItemString(typeDict, "__module__", moduleName);
750+
Py_DECREF(moduleName);
736751

737752
PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict);
738753

@@ -769,6 +784,7 @@ PyObject* PythonQtPrivate::createNewPythonQtEnumWrapper(const char* enumName, Py
769784
PyObject* module = PyObject_GetAttrString(parentObject, "__module__");
770785
PyObject* typeDict = PyDict_New();
771786
PyDict_SetItemString(typeDict, "__module__", module);
787+
Py_DECREF(module);
772788

773789
PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict);
774790

@@ -893,17 +909,21 @@ QVariant PythonQt::evalCode(PyObject* object, PyObject* pycode) {
893909
QVariant result;
894910
clearError();
895911
if (pycode) {
896-
PyObject* dict = NULL;
897-
PyObject* globals = NULL;
912+
PythonQtObjectPtr dict;
913+
PythonQtObjectPtr globals;
898914
if (PyModule_Check(object)) {
899915
dict = PyModule_GetDict(object);
900916
globals = dict;
901917
} else if (PyDict_Check(object)) {
902918
dict = object;
903919
globals = dict;
904920
} else {
905-
dict = PyObject_GetAttrString(object, "__dict__");
906-
globals = PyObject_GetAttrString(PyImport_ImportModule(PyString_AS_STRING(PyObject_GetAttrString(object, "__module__"))),"__dict__");
921+
PyObject *moduleName = PyObject_GetAttrString(object, "__module__");
922+
PyObject *module = PyImport_ImportModule(PyString_AS_STRING(moduleName));
923+
dict.setNewRef(PyObject_GetAttrString(object, "__dict__"));
924+
globals.setNewRef(PyObject_GetAttrString(module, "__dict__"));
925+
Py_XDECREF(moduleName);
926+
Py_XDECREF(module);
907927
}
908928
PyObject* r = NULL;
909929
if (dict) {
@@ -1048,24 +1068,30 @@ PythonQtObjectPtr PythonQt::createUniqueModule()
10481068

10491069
void PythonQt::addObject(PyObject* object, const QString& name, QObject* qObject)
10501070
{
1071+
PyObject *wrappedObject = _p->wrapQObject(qObject);
10511072
if (PyModule_Check(object)) {
1052-
PyModule_AddObject(object, QStringToPythonCharPointer(name), _p->wrapQObject(qObject));
1073+
Py_XINCREF(wrappedObject);
1074+
PyModule_AddObject(object, QStringToPythonCharPointer(name), wrappedObject);
10531075
} else if (PyDict_Check(object)) {
1054-
PyDict_SetItemString(object, QStringToPythonCharPointer(name), _p->wrapQObject(qObject));
1076+
PyDict_SetItemString(object, QStringToPythonCharPointer(name), wrappedObject);
10551077
} else {
1056-
PyObject_SetAttrString(object, QStringToPythonCharPointer(name), _p->wrapQObject(qObject));
1078+
PyObject_SetAttrString(object, QStringToPythonCharPointer(name), wrappedObject);
10571079
}
1080+
Py_XDECREF(wrappedObject);
10581081
}
10591082

10601083
void PythonQt::addVariable(PyObject* object, const QString& name, const QVariant& v)
10611084
{
1085+
PyObject *value = PythonQtConv::QVariantToPyObject(v);
10621086
if (PyModule_Check(object)) {
1063-
PyModule_AddObject(object, QStringToPythonCharPointer(name), PythonQtConv::QVariantToPyObject(v));
1087+
Py_XINCREF(value);
1088+
PyModule_AddObject(object, QStringToPythonCharPointer(name), value);
10641089
} else if (PyDict_Check(object)) {
1065-
PyDict_SetItemString(object, QStringToPythonCharPointer(name), PythonQtConv::QVariantToPyObject(v));
1090+
PyDict_SetItemString(object, QStringToPythonCharPointer(name), value);
10661091
} else {
1067-
PyObject_SetAttrString(object, QStringToPythonCharPointer(name), PythonQtConv::QVariantToPyObject(v));
1092+
PyObject_SetAttrString(object, QStringToPythonCharPointer(name), value);
10681093
}
1094+
Py_XDECREF(value);
10691095
}
10701096

10711097
void PythonQt::removeVariable(PyObject* object, const QString& name)
@@ -1107,7 +1133,7 @@ QStringList PythonQt::introspection(PyObject* module, const QString& objectname,
11071133
} else {
11081134
object = lookupObject(module, objectname);
11091135
if (!object && type == CallOverloads) {
1110-
PyObject* dict = lookupObject(module, "__builtins__");
1136+
PythonQtObjectPtr dict = lookupObject(module, "__builtins__");
11111137
if (dict) {
11121138
object = PyDict_GetItemString(dict, QStringToPythonCharPointer(objectname));
11131139
}
@@ -1159,36 +1185,33 @@ QStringList PythonQt::introspectObject(PyObject* object, ObjectType type)
11591185
}
11601186
}
11611187
} else {
1162-
PyObject* keys = NULL;
1188+
PythonQtObjectPtr keys;
11631189
bool isDict = false;
11641190
if (PyDict_Check(object)) {
1165-
keys = PyDict_Keys(object);
1191+
keys.setNewRef(PyDict_Keys(object));
11661192
isDict = true;
11671193
} else {
11681194
#if defined(MEVISLAB) && !defined(PY3K)
11691195
int oldPy3kWarningFlag = Py_Py3kWarningFlag;
11701196
Py_Py3kWarningFlag = 0; // temporarily disable Python 3 warnings
1171-
keys = PyObject_Dir(object);
1197+
keys.setNewRef(PyObject_Dir(object));
11721198
Py_Py3kWarningFlag = oldPy3kWarningFlag;
11731199
#else
1174-
keys = PyObject_Dir(object);
1200+
keys.setNewRef(PyObject_Dir(object));
11751201
#endif
11761202
}
11771203
if (keys) {
11781204
int count = PyList_Size(keys);
1179-
PyObject* key;
1180-
PyObject* value;
1181-
QString keystr;
11821205
for (int i = 0;i<count;i++) {
1183-
key = PyList_GetItem(keys,i);
1206+
PythonQtObjectPtr key = PyList_GetItem(keys,i);
1207+
PythonQtObjectPtr value;
11841208
if (isDict) {
11851209
value = PyDict_GetItem(object, key);
1186-
Py_INCREF(value);
11871210
} else {
1188-
value = PyObject_GetAttr(object, key);
1211+
value.setNewRef(PyObject_GetAttr(object, key));
11891212
}
11901213
if (!value) continue;
1191-
keystr = PyString_AsString(key);
1214+
QString keystr = PyString_AsString(key);
11921215
static const QString underscoreStr("__tmp");
11931216
if (!keystr.startsWith(underscoreStr)) {
11941217
switch (type) {
@@ -1233,9 +1256,7 @@ QStringList PythonQt::introspectObject(PyObject* object, ObjectType type)
12331256
std::cerr << "PythonQt: introspection: unknown case" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
12341257
}
12351258
}
1236-
Py_DECREF(value);
12371259
}
1238-
Py_DECREF(keys);
12391260
}
12401261
}
12411262
PyErr_Clear();
@@ -1288,6 +1309,7 @@ QStringList PythonQt::introspectType(const QString& typeName, ObjectType type)
12881309
PyObject* typeObject = getObjectByType(typeName);
12891310
if (typeObject) {
12901311
object = PyObject_GetAttrString(typeObject, QStringToPythonCharPointer(memberName));
1312+
Py_DECREF(typeObject);
12911313
}
12921314
}
12931315
if (object) {
@@ -1359,6 +1381,7 @@ PyObject* PythonQt::callAndReturnPyObject(PyObject* callable, const QVariantList
13591381
PyObject* arg = PythonQtConv::QVariantToPyObject(it.value());
13601382
if (arg) {
13611383
PyDict_SetItemString(pkwargs, QStringToPythonCharPointer(it.key()), arg);
1384+
Py_DECREF(arg);
13621385
} else {
13631386
err = true;
13641387
break;
@@ -1750,7 +1773,7 @@ void PythonQt::initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQ
17501773
}
17511774
#ifdef PY3K
17521775
PythonQtModuleDef.m_name = name.constData();
1753-
_p->_pythonQtModule = PyModule_Create(&PythonQtModuleDef);
1776+
_p->_pythonQtModule.setNewRef(PyModule_Create(&PythonQtModuleDef));
17541777
#else
17551778
_p->_pythonQtModule = Py_InitModule(name.constData(), PythonQtMethods);
17561779
#endif
@@ -1790,7 +1813,11 @@ void PythonQt::initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQ
17901813
Py_XDECREF(old_module_names);
17911814

17921815
#ifdef PY3K
1793-
PyDict_SetItem(PyObject_GetAttrString(sys.object(), "modules"), PyUnicode_FromString(name.constData()), _p->_pythonQtModule.object());
1816+
PyObject *modules = PyObject_GetAttrString(sys.object(), "modules");
1817+
PyObject *nameObj = PyUnicode_FromString(name.constData());
1818+
PyDict_SetItem(modules, nameObj, _p->_pythonQtModule.object());
1819+
Py_XDECREF(modules);
1820+
Py_XDECREF(nameObj);
17941821
#endif
17951822
}
17961823

@@ -1810,7 +1837,8 @@ QString PythonQt::getReturnTypeOfWrappedMethod(PyObject* module, const QString&
18101837

18111838
QString PythonQt::getReturnTypeOfWrappedMethod(const QString& typeName, const QString& methodName)
18121839
{
1813-
PythonQtObjectPtr typeObject = getObjectByType(typeName);
1840+
PythonQtObjectPtr typeObject;
1841+
typeObject.setNewRef(getObjectByType(typeName));
18141842
if (typeObject.isNull()) {
18151843
return "";
18161844
}
@@ -2105,10 +2133,12 @@ const QMetaObject* PythonQtPrivate::buildDynamicMetaObject(PythonQtClassWrapper*
21052133
builder.setClassName(((PyTypeObject*)type)->tp_name);
21062134

21072135
PyObject* dict = ((PyTypeObject*)type)->tp_dict;
2108-
Py_ssize_t pos = NULL;
2136+
Py_ssize_t pos = 0;
21092137
PyObject* value = NULL;
21102138
PyObject* key = NULL;
2111-
static PyObject* qtSlots = PyString_FromString("_qtSlots");
2139+
static PyObject* qtSlots = NULL;
2140+
if (!qtSlots)
2141+
qtSlots = PyString_FromString("_qtSlots");
21122142

21132143
bool needsMetaObject = false;
21142144
// Iterate over all members and check if they affect the QMetaObject:
@@ -2126,7 +2156,7 @@ const QMetaObject* PythonQtPrivate::buildDynamicMetaObject(PythonQtClassWrapper*
21262156
}
21272157
}
21282158
}
2129-
pos = NULL;
2159+
pos = 0;
21302160
value = NULL;
21312161
key = NULL;
21322162
// Now look for slots: (this is a bug in QMetaObjectBuilder, all signals need to be added first)
@@ -2160,10 +2190,11 @@ const QMetaObject* PythonQtPrivate::buildDynamicMetaObject(PythonQtClassWrapper*
21602190
}
21612191
if (PyFunction_Check(value) && PyObject_HasAttr(value, qtSlots)) {
21622192
// A function which has a "_qtSlots" signature list, add the slots to the meta object
2163-
PyObject* signatures = PyObject_GetAttr(value, qtSlots);
2193+
PythonQtObjectPtr signatures;
2194+
signatures.setNewRef(PyObject_GetAttr(value, qtSlots));
21642195
Py_ssize_t count = PyList_Size(signatures);
21652196
for (Py_ssize_t i = 0; i < count; i++) {
2166-
PyObject* signature = PyList_GET_ITEM(signatures, i);
2197+
PyObject* signature = PyList_GET_ITEM(signatures.object(), i);
21672198
QByteArray sig = PyString_AsString(signature);
21682199
// Split the return type and the rest of the signature,
21692200
// no spaces should be in the rest of the signature...
@@ -2209,9 +2240,11 @@ int PythonQtPrivate::handleMetaCall(QObject* object, PythonQtInstanceWrapper* wr
22092240
}
22102241
PythonQtProperty* prop = NULL;
22112242
// Get directly from the Python class, since we don't want to get the value of the property
2212-
PyObject* maybeProp = PyBaseObject_Type.tp_getattro((PyObject*)wrapper, PyString_FromString(metaProp.name()));
2243+
PythonQtObjectPtr name, maybeProp;
2244+
name.setNewRef(PyString_FromString(metaProp.name()));
2245+
maybeProp.setNewRef(PyBaseObject_Type.tp_getattro((PyObject*)wrapper, name));
22132246
if (maybeProp && PythonQtProperty_Check(maybeProp)) {
2214-
prop = (PythonQtProperty*)maybeProp;
2247+
prop = (PythonQtProperty*)maybeProp.object();
22152248
} else {
22162249
return id - methodCount;
22172250
}
@@ -2228,7 +2261,7 @@ int PythonQtPrivate::handleMetaCall(QObject* object, PythonQtInstanceWrapper* wr
22282261

22292262
PyObject* value = prop->data->callGetter((PyObject*)wrapper);
22302263
if (value) {
2231-
void* result = PythonQtConv::ConvertPythonToQt(info, value, false, NULL, args[0]);
2264+
void* result = PythonQtConv::ConvertPythonToQt(info, value, false, NULL, args[0]); // FIXME: what happens with result? free?
22322265
Py_DECREF(value);
22332266
return (result == NULL ? -1 : 0);
22342267
} else {
@@ -2267,17 +2300,15 @@ QString PythonQtPrivate::getSignature(PyObject* object)
22672300
PyMethodObject* method = NULL;
22682301
PyFunctionObject* func = NULL;
22692302

2270-
bool decrefMethod = false;
2271-
22722303
if (PythonQtUtils::isPythonClassType(object)) {
22732304
method = (PyMethodObject*)PyObject_GetAttrString(object, "__init__");
2274-
decrefMethod = true;
22752305
} else if (object->ob_type == &PyFunction_Type) {
22762306
func = (PyFunctionObject*)object;
22772307
} else if (object->ob_type == &PyMethod_Type) {
22782308
method = (PyMethodObject*)object;
2309+
Py_XINCREF(method);
22792310
}
2280-
if (method) {
2311+
if (method) {
22812312
if (PyFunction_Check(method->im_func)) {
22822313
func = (PyFunctionObject*)method->im_func;
22832314
} else if (isMethodDescriptor((PyObject*)method)) {
@@ -2384,9 +2415,7 @@ QString PythonQtPrivate::getSignature(PyObject* object)
23842415
signature = funcName + "(" + signature + ")";
23852416
}
23862417

2387-
if (method && decrefMethod) {
2388-
Py_DECREF(method);
2389-
}
2418+
Py_XDECREF(method);
23902419
}
23912420

23922421
return signature;
@@ -2451,7 +2480,7 @@ PythonQtClassInfo* PythonQtPrivate::getClassInfo( const QByteArray& className )
24512480
if (_knownLazyClasses.contains(className)) {
24522481
QByteArray module = _knownLazyClasses.value(className);
24532482
recursion = true;
2454-
PyImport_ImportModule(module);
2483+
PyImport_ImportModule(module); // FIXME: reference leaked
24552484
recursion = false;
24562485
result = _knownClassInfos.value(className);
24572486
if (!result) {

src/PythonQt.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject {
813813
PythonQtInstanceWrapper* findWrapperAndRemoveUnused(void* obj);
814814

815815
//! stores pointer to PyObject mapping of wrapped QObjects AND C++ objects
816-
QHash<void* , PythonQtInstanceWrapper *> _wrappedObjects;
816+
QHash<void* , PythonQtInstanceWrapper *> _wrappedObjects; // FIXME: remove unused entries in cleanup()
817817

818818
//! stores the meta info of known Qt classes
819819
QHash<QByteArray, PythonQtClassInfo *> _knownClassInfos;

0 commit comments

Comments
 (0)