From 06860d4974f26dca80a3309c9bc1b830f9ec0308 Mon Sep 17 00:00:00 2001 From: Gregor Anich Date: Fri, 9 Oct 2020 14:26:26 +0200 Subject: [PATCH 1/4] Fix reference/memory leaks --- src/PythonQt.cpp | 139 +++++++++++++++++++------------- src/PythonQt.h | 2 +- src/PythonQtImporter.cpp | 21 ++++- src/PythonQtInstanceWrapper.cpp | 2 +- src/PythonQtSignalReceiver.cpp | 14 +--- 5 files changed, 107 insertions(+), 71 deletions(-) diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index 3c091bd0b..d0f40d06c 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -379,18 +379,23 @@ PythonQt::PythonQt(int flags, const QByteArray& pythonQtModuleName) PythonQt::~PythonQt() { delete _p; _p = NULL; + + Py_DECREF(&PythonQtSlotFunction_Type); + Py_DECREF(&PythonQtSignalFunction_Type); + Py_DECREF(&PythonQtSlotDecorator_Type); + Py_DECREF(&PythonQtProperty_Type); + Py_DECREF(&PythonQtBoolResult_Type); + Py_DECREF(&PythonQtClassWrapper_Type); + Py_DECREF(&PythonQtInstanceWrapper_Type); + Py_DECREF(&PythonQtStdOutRedirectType); + Py_DECREF(&PythonQtStdInRedirectType); } PythonQtPrivate::~PythonQtPrivate() { delete _defaultImporter; _defaultImporter = NULL; - { - QHashIterator i(_knownClassInfos); - while (i.hasNext()) { - delete i.next().value(); - } - } + qDeleteAll(_knownClassInfos); PythonQtMethodInfo::cleanupCachedMethodInfos(); PythonQtArgumentFrame::cleanupFreeList(); @@ -409,7 +414,9 @@ void PythonQt::setRedirectStdInCallback(PythonQtInputChangedCB* callback, void * // Backup original 'sys.stdin' if not yet done if( !PyObject_HasAttrString(sys.object(), "pythonqt_original_stdin") ) { - PyObject_SetAttrString(sys.object(), "pythonqt_original_stdin", PyObject_GetAttrString(sys.object(), "stdin")); + PyObject *stdin = PyObject_GetAttrString(sys.object(), "stdin"); + PyObject_SetAttrString(sys.object(), "pythonqt_original_stdin", stdin); + Py_XDECREF(stdin); } in = PythonQtStdInRedirectType.tp_new(&PythonQtStdInRedirectType, NULL, NULL); @@ -428,15 +435,19 @@ void PythonQt::setRedirectStdInCallbackEnabled(bool enabled) PythonQtObjectPtr sys; sys.setNewRef(PyImport_ImportModule("sys")); + PythonQtObjectPtr stdin; if (enabled) { - if( !PyObject_HasAttrString(sys.object(), "pythonqt_stdin") ) { - PyObject_SetAttrString(sys.object(), "stdin", PyObject_GetAttrString(sys.object(), "pythonqt_stdin")); + if( PyObject_HasAttrString(sys.object(), "pythonqt_stdin") ) { + stdin.setNewRef(PyObject_GetAttrString(sys.object(), "pythonqt_stdin")); } } else { - if( !PyObject_HasAttrString(sys.object(), "pythonqt_original_stdin") ) { - PyObject_SetAttrString(sys.object(), "stdin", PyObject_GetAttrString(sys.object(), "pythonqt_original_stdin")); + if( PyObject_HasAttrString(sys.object(), "pythonqt_original_stdin") ) { + stdin.setNewRef(PyObject_GetAttrString(sys.object(), "pythonqt_original_stdin")); } } + if (stdin) { + PyObject_SetAttrString(sys.object(), "stdin", stdin); + } } PythonQtImportFileInterface* PythonQt::importInterface() @@ -448,7 +459,7 @@ void PythonQt::qObjectNoLongerWrappedCB(QObject* o) { if (_self->_p->_noLongerWrappedCB) { (*_self->_p->_noLongerWrappedCB)(o); - }; + } } void PythonQt::setQObjectMissingAttributeCallback(PythonQtQObjectMissingAttributeCB* cb) @@ -525,6 +536,7 @@ void PythonQtPrivate::createPythonQtClassWrapper(PythonQtClassInfo* info, const PythonQtClassInfo* outerClassInfo = lookupClassInfoAndCreateIfNotPresent(outerClass); outerClassInfo->addNestedClass(info); } else { + Py_INCREF(pyobj); PyModule_AddObject(pack, info->className(), pyobj); } if (!module && package && strncmp(package, "Qt", 2) == 0) { @@ -534,6 +546,7 @@ void PythonQtPrivate::createPythonQtClassWrapper(PythonQtClassInfo* info, const PyModule_AddObject(packageByName("Qt"), info->className(), pyobj); } info->setPythonQtClassWrapper(pyobj); + Py_DECREF(pyobj); } PyObject* PythonQtPrivate::wrapQObject(QObject* obj) @@ -548,6 +561,7 @@ PyObject* PythonQtPrivate::wrapQObject(QObject* obj) // address, so probably that C++ wrapper has been deleted earlier and // now we see a QObject with the same address. // Do not use the old wrapper anymore. + removeWrapperPointer(obj); wrap = NULL; } if (!wrap) { @@ -733,6 +747,7 @@ PythonQtClassWrapper* PythonQtPrivate::createNewPythonQtClassWrapper(PythonQtCla PyObject* typeDict = PyDict_New(); PyObject* moduleName = PyObject_GetAttrString(parentModule, "__name__"); PyDict_SetItemString(typeDict, "__module__", moduleName); + Py_DECREF(moduleName); PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict); @@ -769,6 +784,7 @@ PyObject* PythonQtPrivate::createNewPythonQtEnumWrapper(const char* enumName, Py PyObject* module = PyObject_GetAttrString(parentObject, "__module__"); PyObject* typeDict = PyDict_New(); PyDict_SetItemString(typeDict, "__module__", module); + Py_DECREF(module); PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict); @@ -893,8 +909,8 @@ QVariant PythonQt::evalCode(PyObject* object, PyObject* pycode) { QVariant result; clearError(); if (pycode) { - PyObject* dict = NULL; - PyObject* globals = NULL; + PythonQtObjectPtr dict; + PythonQtObjectPtr globals; if (PyModule_Check(object)) { dict = PyModule_GetDict(object); globals = dict; @@ -902,8 +918,12 @@ QVariant PythonQt::evalCode(PyObject* object, PyObject* pycode) { dict = object; globals = dict; } else { - dict = PyObject_GetAttrString(object, "__dict__"); - globals = PyObject_GetAttrString(PyImport_ImportModule(PyString_AS_STRING(PyObject_GetAttrString(object, "__module__"))),"__dict__"); + PyObject *moduleName = PyObject_GetAttrString(object, "__module__"); + PyObject *module = PyImport_ImportModule(PyString_AS_STRING(moduleName)); + dict.setNewRef(PyObject_GetAttrString(object, "__dict__")); + globals.setNewRef(PyObject_GetAttrString(module, "__dict__")); + Py_XDECREF(moduleName); + Py_XDECREF(module); } PyObject* r = NULL; if (dict) { @@ -1048,24 +1068,30 @@ PythonQtObjectPtr PythonQt::createUniqueModule() void PythonQt::addObject(PyObject* object, const QString& name, QObject* qObject) { + PyObject *wrappedObject = _p->wrapQObject(qObject); if (PyModule_Check(object)) { - PyModule_AddObject(object, QStringToPythonCharPointer(name), _p->wrapQObject(qObject)); + Py_XINCREF(wrappedObject); + PyModule_AddObject(object, QStringToPythonCharPointer(name), wrappedObject); } else if (PyDict_Check(object)) { - PyDict_SetItemString(object, QStringToPythonCharPointer(name), _p->wrapQObject(qObject)); + PyDict_SetItemString(object, QStringToPythonCharPointer(name), wrappedObject); } else { - PyObject_SetAttrString(object, QStringToPythonCharPointer(name), _p->wrapQObject(qObject)); + PyObject_SetAttrString(object, QStringToPythonCharPointer(name), wrappedObject); } + Py_XDECREF(wrappedObject); } void PythonQt::addVariable(PyObject* object, const QString& name, const QVariant& v) { + PyObject *value = PythonQtConv::QVariantToPyObject(v); if (PyModule_Check(object)) { - PyModule_AddObject(object, QStringToPythonCharPointer(name), PythonQtConv::QVariantToPyObject(v)); + Py_XINCREF(value); + PyModule_AddObject(object, QStringToPythonCharPointer(name), value); } else if (PyDict_Check(object)) { - PyDict_SetItemString(object, QStringToPythonCharPointer(name), PythonQtConv::QVariantToPyObject(v)); + PyDict_SetItemString(object, QStringToPythonCharPointer(name), value); } else { - PyObject_SetAttrString(object, QStringToPythonCharPointer(name), PythonQtConv::QVariantToPyObject(v)); + PyObject_SetAttrString(object, QStringToPythonCharPointer(name), value); } + Py_XDECREF(value); } void PythonQt::removeVariable(PyObject* object, const QString& name) @@ -1107,7 +1133,7 @@ QStringList PythonQt::introspection(PyObject* module, const QString& objectname, } else { object = lookupObject(module, objectname); if (!object && type == CallOverloads) { - PyObject* dict = lookupObject(module, "__builtins__"); + PythonQtObjectPtr dict = lookupObject(module, "__builtins__"); if (dict) { object = PyDict_GetItemString(dict, QStringToPythonCharPointer(objectname)); } @@ -1159,36 +1185,33 @@ QStringList PythonQt::introspectObject(PyObject* object, ObjectType type) } } } else { - PyObject* keys = NULL; + PythonQtObjectPtr keys; bool isDict = false; if (PyDict_Check(object)) { - keys = PyDict_Keys(object); + keys.setNewRef(PyDict_Keys(object)); isDict = true; } else { #if defined(MEVISLAB) && !defined(PY3K) int oldPy3kWarningFlag = Py_Py3kWarningFlag; Py_Py3kWarningFlag = 0; // temporarily disable Python 3 warnings - keys = PyObject_Dir(object); + keys.setNewRef(PyObject_Dir(object)); Py_Py3kWarningFlag = oldPy3kWarningFlag; #else - keys = PyObject_Dir(object); + keys.setNewRef(PyObject_Dir(object)); #endif } if (keys) { int count = PyList_Size(keys); - PyObject* key; - PyObject* value; - QString keystr; for (int i = 0;i_pythonQtModule = PyModule_Create(&PythonQtModuleDef); + _p->_pythonQtModule.setNewRef(PyModule_Create(&PythonQtModuleDef)); #else _p->_pythonQtModule = Py_InitModule(name.constData(), PythonQtMethods); #endif @@ -1790,7 +1813,11 @@ void PythonQt::initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQ Py_XDECREF(old_module_names); #ifdef PY3K - PyDict_SetItem(PyObject_GetAttrString(sys.object(), "modules"), PyUnicode_FromString(name.constData()), _p->_pythonQtModule.object()); + PyObject *modules = PyObject_GetAttrString(sys.object(), "modules"); + PyObject *nameObj = PyUnicode_FromString(name.constData()); + PyDict_SetItem(modules, nameObj, _p->_pythonQtModule.object()); + Py_XDECREF(modules); + Py_XDECREF(nameObj); #endif } @@ -1810,7 +1837,8 @@ QString PythonQt::getReturnTypeOfWrappedMethod(PyObject* module, const QString& QString PythonQt::getReturnTypeOfWrappedMethod(const QString& typeName, const QString& methodName) { - PythonQtObjectPtr typeObject = getObjectByType(typeName); + PythonQtObjectPtr typeObject; + typeObject.setNewRef(getObjectByType(typeName)); if (typeObject.isNull()) { return ""; } @@ -2105,10 +2133,12 @@ const QMetaObject* PythonQtPrivate::buildDynamicMetaObject(PythonQtClassWrapper* builder.setClassName(((PyTypeObject*)type)->tp_name); PyObject* dict = ((PyTypeObject*)type)->tp_dict; - Py_ssize_t pos = NULL; + Py_ssize_t pos = 0; PyObject* value = NULL; PyObject* key = NULL; - static PyObject* qtSlots = PyString_FromString("_qtSlots"); + static PyObject* qtSlots = NULL; + if (!qtSlots) + qtSlots = PyString_FromString("_qtSlots"); bool needsMetaObject = false; // Iterate over all members and check if they affect the QMetaObject: @@ -2126,7 +2156,7 @@ const QMetaObject* PythonQtPrivate::buildDynamicMetaObject(PythonQtClassWrapper* } } } - pos = NULL; + pos = 0; value = NULL; key = NULL; // 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* } if (PyFunction_Check(value) && PyObject_HasAttr(value, qtSlots)) { // A function which has a "_qtSlots" signature list, add the slots to the meta object - PyObject* signatures = PyObject_GetAttr(value, qtSlots); + PythonQtObjectPtr signatures; + signatures.setNewRef(PyObject_GetAttr(value, qtSlots)); Py_ssize_t count = PyList_Size(signatures); for (Py_ssize_t i = 0; i < count; i++) { - PyObject* signature = PyList_GET_ITEM(signatures, i); + PyObject* signature = PyList_GET_ITEM(signatures.object(), i); QByteArray sig = PyString_AsString(signature); // Split the return type and the rest of the signature, // no spaces should be in the rest of the signature... @@ -2209,9 +2240,11 @@ int PythonQtPrivate::handleMetaCall(QObject* object, PythonQtInstanceWrapper* wr } PythonQtProperty* prop = NULL; // Get directly from the Python class, since we don't want to get the value of the property - PyObject* maybeProp = PyBaseObject_Type.tp_getattro((PyObject*)wrapper, PyString_FromString(metaProp.name())); + PythonQtObjectPtr name, maybeProp; + name.setNewRef(PyString_FromString(metaProp.name())); + maybeProp.setNewRef(PyBaseObject_Type.tp_getattro((PyObject*)wrapper, name)); if (maybeProp && PythonQtProperty_Check(maybeProp)) { - prop = (PythonQtProperty*)maybeProp; + prop = (PythonQtProperty*)maybeProp.object(); } else { return id - methodCount; } @@ -2228,7 +2261,7 @@ int PythonQtPrivate::handleMetaCall(QObject* object, PythonQtInstanceWrapper* wr PyObject* value = prop->data->callGetter((PyObject*)wrapper); if (value) { - void* result = PythonQtConv::ConvertPythonToQt(info, value, false, NULL, args[0]); + void* result = PythonQtConv::ConvertPythonToQt(info, value, false, NULL, args[0]); // FIXME: what happens with result? free? Py_DECREF(value); return (result == NULL ? -1 : 0); } else { @@ -2267,17 +2300,15 @@ QString PythonQtPrivate::getSignature(PyObject* object) PyMethodObject* method = NULL; PyFunctionObject* func = NULL; - bool decrefMethod = false; - if (PythonQtUtils::isPythonClassType(object)) { method = (PyMethodObject*)PyObject_GetAttrString(object, "__init__"); - decrefMethod = true; } else if (object->ob_type == &PyFunction_Type) { func = (PyFunctionObject*)object; } else if (object->ob_type == &PyMethod_Type) { method = (PyMethodObject*)object; + Py_XINCREF(method); } - if (method) { + if (method) { if (PyFunction_Check(method->im_func)) { func = (PyFunctionObject*)method->im_func; } else if (isMethodDescriptor((PyObject*)method)) { @@ -2384,9 +2415,7 @@ QString PythonQtPrivate::getSignature(PyObject* object) signature = funcName + "(" + signature + ")"; } - if (method && decrefMethod) { - Py_DECREF(method); - } + Py_XDECREF(method); } return signature; @@ -2451,7 +2480,7 @@ PythonQtClassInfo* PythonQtPrivate::getClassInfo( const QByteArray& className ) if (_knownLazyClasses.contains(className)) { QByteArray module = _knownLazyClasses.value(className); recursion = true; - PyImport_ImportModule(module); + PyImport_ImportModule(module); // FIXME: reference leaked recursion = false; result = _knownClassInfos.value(className); if (!result) { diff --git a/src/PythonQt.h b/src/PythonQt.h index 93e62c30d..436edf393 100644 --- a/src/PythonQt.h +++ b/src/PythonQt.h @@ -813,7 +813,7 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject { PythonQtInstanceWrapper* findWrapperAndRemoveUnused(void* obj); //! stores pointer to PyObject mapping of wrapped QObjects AND C++ objects - QHash _wrappedObjects; + QHash _wrappedObjects; // FIXME: remove unused entries in cleanup() //! stores the meta info of known Qt classes QHash _knownClassInfos; diff --git a/src/PythonQtImporter.cpp b/src/PythonQtImporter.cpp index 21ec5a43b..04023c30c 100644 --- a/src/PythonQtImporter.cpp +++ b/src/PythonQtImporter.cpp @@ -289,7 +289,9 @@ PythonQtImporter_load_module(PyObject *obj, PyObject *args) // The package attribute is needed to resolve the package name if it is referenced as '.'. For example, // when importing the encodings package, there is an import statement 'from . import aliases'. This import // would fail when reloading the encodings package with importlib. - err = PyDict_SetItemString(dict, "__package__", PyUnicode_FromString(fullname)); + PyObject* fullnameObj = PyUnicode_FromString(fullname); + err = PyDict_SetItemString(dict, "__package__", fullnameObj); + Py_XDECREF(fullnameObj); if (err != 0) { Py_DECREF(code); Py_DECREF(mod); @@ -297,6 +299,7 @@ PythonQtImporter_load_module(PyObject *obj, PyObject *args) } #endif } + Py_DECREF(mod); #ifdef PY3K PyObject* fullnameObj = PyUnicode_FromString(fullname); @@ -912,22 +915,32 @@ void PythonQtImport::init() PythonQtImportError = PyErr_NewException(const_cast("PythonQtImport.PythonQtImportError"), PyExc_ImportError, NULL); - if (PythonQtImportError == NULL) + if (PythonQtImportError == NULL) { + Py_XDECREF(mod); return; + } Py_INCREF(PythonQtImportError); if (PyModule_AddObject(mod, "PythonQtImportError", - PythonQtImportError) < 0) + PythonQtImportError) < 0) { + Py_DECREF(PythonQtImportError); + Py_DECREF(mod); return; + } Py_INCREF(&PythonQtImporter_Type); if (PyModule_AddObject(mod, "PythonQtImporter", - (PyObject *)&PythonQtImporter_Type) < 0) + (PyObject *)&PythonQtImporter_Type) < 0) { + Py_DECREF(&PythonQtImporter_Type); + Py_DECREF(mod); return; + } // set our importer into the path_hooks to handle all path on sys.path PyObject* classobj = PyDict_GetItemString(PyModule_GetDict(mod), "PythonQtImporter"); PyObject* path_hooks = PySys_GetObject(const_cast("path_hooks")); // insert our importer before all other loaders PyList_Insert(path_hooks, 0, classobj); + + Py_DECREF(mod); } diff --git a/src/PythonQtInstanceWrapper.cpp b/src/PythonQtInstanceWrapper.cpp index 7b99e8e9d..67d1ace1f 100644 --- a/src/PythonQtInstanceWrapper.cpp +++ b/src/PythonQtInstanceWrapper.cpp @@ -91,7 +91,7 @@ static void PythonQtInstanceWrapper_deleteObject(PythonQtInstanceWrapper* self, } } else { //mlabDebugConst("Python","qobject wrapper removed " << self->_obj->className() << " " << self->classInfo()->wrappedClassName().latin1()); - if (self->_objPointerCopy) { + if (self->_objPointerCopy && PythonQt::self() && PythonQt::priv()) { PythonQt::priv()->removeWrapperPointer(self->_objPointerCopy); } if (self->_obj) { diff --git a/src/PythonQtSignalReceiver.cpp b/src/PythonQtSignalReceiver.cpp index ceb21b21f..7420b97c5 100644 --- a/src/PythonQtSignalReceiver.cpp +++ b/src/PythonQtSignalReceiver.cpp @@ -54,9 +54,7 @@ int PythonQtSignalReceiver::_destroyedSignal2Id = -2; void PythonQtSignalTarget::call(void **arguments) const { PYTHONQT_GIL_SCOPE PyObject* result = call(_callable, methodInfo(), arguments); - if (result) { - Py_DECREF(result); - } + Py_XDECREF(result); } PyObject* PythonQtSignalTarget::call(PyObject* callable, const PythonQtMethodInfo* methodInfos, void **arguments, bool skipFirstArgumentOfMethodInfo) @@ -100,9 +98,9 @@ PyObject* PythonQtSignalTarget::call(PyObject* callable, const PythonQtMethodInf } } - PyObject* pargs = NULL; + PythonQtObjectPtr pargs; if (count>1) { - pargs = PyTuple_New(count-1); + pargs.setNewRef(PyTuple_New(count-1)); } bool err = false; // transform Qt values to Python @@ -117,7 +115,7 @@ PyObject* PythonQtSignalTarget::call(PyObject* callable, const PythonQtMethodInf } if (arg) { // steals reference, no unref - PyTuple_SetItem(pargs, i-1,arg); + PyTuple_SetItem(pargs, i-1, arg); } else { err = true; break; @@ -134,10 +132,6 @@ PyObject* PythonQtSignalTarget::call(PyObject* callable, const PythonQtMethodInf PythonQt::self()->handleError(); } } - if (pargs) { - // free the arguments again - Py_DECREF(pargs); - } return result; } From 525ea41cb616dada88df51fd3680de2abc2e946d Mon Sep 17 00:00:00 2001 From: Gregor Anich Date: Fri, 9 Oct 2020 18:18:01 +0200 Subject: [PATCH 2/4] More potential leak fixes and build fix --- build/python.prf | 70 +++++++++++++++++++++++++----------------------- src/PythonQt.cpp | 51 ++++++++++++++++++++--------------- 2 files changed, 66 insertions(+), 55 deletions(-) diff --git a/build/python.prf b/build/python.prf index f4be6bd7d..297696263 100644 --- a/build/python.prf +++ b/build/python.prf @@ -8,41 +8,43 @@ isEmpty( PYTHON_VERSION ) { unix:PYTHON_VERSION=2.7 } - -macx { - # for macx you need to have the Python development kit installed as framework - INCLUDEPATH += /System/Library/Frameworks/Python.framework/Headers - LIBS += -F/System/Library/Frameworks -framework Python -} else:win32 { - # for windows install a Python development kit or build Python yourself from the sources - # Make sure that you set the environment variable PYTHON_PATH to point to your - # python installation (or the python sources/header files when building from source). - # Make sure that you set the environment variable PYTHON_LIB to point to - # the directory where the python libs are located. - # - # When using the prebuild Python installer, this will be: - # set PYTHON_PATH = c:\Python26 - # set PYTHON_LIB = c:\Python26\libs - # - # When using the python sources, this will be something like: - # set PYTHON_PATH = c:\yourDir\Python-2.6.1\ - # set PYTHON_LIB = c:\yourDir\Python-2.6.1\PCbuild8\Win32 - - # check if debug or release - CONFIG(debug, debug|release) { - DEBUG_EXT = _d - } else { - DEBUG_EXT = - } - - win32:INCLUDEPATH += $$(PYTHON_PATH)/PC $$(PYTHON_PATH)/include - win32:LIBS += $$(PYTHON_LIB)/python$${PYTHON_VERSION}$${DEBUG_EXT}.lib -} else:unix { - # on linux, python-config is used to autodetect Python. - # make sure that you have installed a matching python-dev package. +message(Using Python version $${PYTHON_VERSION}) + +macx { + # for macx you need to have the Python development kit installed as framework + INCLUDEPATH += /System/Library/Frameworks/Python.framework/Headers + LIBS += -F/System/Library/Frameworks -framework Python +} else:win32 { + # for windows install a Python development kit or build Python yourself from the sources + # Make sure that you set the environment variable PYTHON_PATH to point to your + # python installation (or the python sources/header files when building from source). + # Make sure that you set the environment variable PYTHON_LIB to point to + # the directory where the python libs are located. + # + # When using the prebuild Python installer, this will be: + # set PYTHON_PATH = c:\Python26 + # set PYTHON_LIB = c:\Python26\libs + # + # When using the python sources, this will be something like: + # set PYTHON_PATH = c:\yourDir\Python-2.6.1\ + # set PYTHON_LIB = c:\yourDir\Python-2.6.1\PCbuild8\Win32 + + # check if debug or release + CONFIG(debug, debug|release) { + DEBUG_EXT = _d + } else { + DEBUG_EXT = + } + + win32:INCLUDEPATH += $$(PYTHON_PATH)/PC $$(PYTHON_PATH)/include + win32:LIBS += $$(PYTHON_LIB)/python$${PYTHON_VERSION}$${DEBUG_EXT}.lib +} else:unix { + # on linux, python-config is used to autodetect Python. + # make sure that you have installed a matching python-dev package. system(python$${PYTHON_VERSION}-config --embed --libs) { unix:LIBS += $$system(python$${PYTHON_VERSION}-config --embed --libs) } else: unix:LIBS += $$system(python$${PYTHON_VERSION}-config --libs) - unix:QMAKE_CXXFLAGS += $$system(python$${PYTHON_VERSION}-config --includes) -} + unix:QMAKE_CXXFLAGS += $$system(python$${PYTHON_VERSION}-config --includes) + unix:QMAKE_LFLAGS += $$system(python$${PYTHON_VERSION}-config --ldflags) +} diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index d0f40d06c..6e912b781 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -68,6 +68,15 @@ void PythonQt_init_QtGuiBuiltin(PyObject*); void PythonQt_init_QtCoreBuiltin(PyObject*); +static inline int PyModule_AddObject_DECREF(PyObject *module, const char *name, PyObject *value) +{ + int ret = PyModule_AddObject(module, name, value); + if (ret < 0) + Py_XDECREF(value); + return ret; +} + + void PythonQt::init(int flags, const QByteArray& pythonQtModuleName) { if (!_self) { @@ -244,9 +253,9 @@ void PythonQt::init(int flags, const QByteArray& pythonQtModuleName) for (unsigned int i = 0;i<16; i++) { PyObject* obj = PyObject_GetAttrString(qtNamespace, names[i]); if (obj) { - PyModule_AddObject(pack, names[i], obj); + PyModule_AddObject_DECREF(pack, names[i], obj); Py_INCREF(obj); - PyModule_AddObject(pack2, names[i], obj); + PyModule_AddObject_DECREF(pack2, names[i], obj); } else { std::cerr << "method not found " << names[i] << std::endl; } @@ -268,9 +277,9 @@ void PythonQt::init(int flags, const QByteArray& pythonQtModuleName) for (int i = 0; ipriv()->pythonQtModule().addObject("Debug", _self->priv()->_debugAPI); @@ -278,9 +287,9 @@ void PythonQt::init(int flags, const QByteArray& pythonQtModuleName) Py_INCREF((PyObject*)&PythonQtSlotDecorator_Type); Py_INCREF((PyObject*)&PythonQtSignalFunction_Type); Py_INCREF((PyObject*)&PythonQtProperty_Type); - PyModule_AddObject(pack, "Slot", (PyObject*)&PythonQtSlotDecorator_Type); - PyModule_AddObject(pack, "Signal", (PyObject*)&PythonQtSignalFunction_Type); - PyModule_AddObject(pack, "Property", (PyObject*)&PythonQtProperty_Type); + PyModule_AddObject_DECREF(pack, "Slot", (PyObject*)&PythonQtSlotDecorator_Type); + PyModule_AddObject_DECREF(pack, "Signal", (PyObject*)&PythonQtSignalFunction_Type); + PyModule_AddObject_DECREF(pack, "Property", (PyObject*)&PythonQtProperty_Type); } } @@ -423,11 +432,11 @@ void PythonQt::setRedirectStdInCallback(PythonQtInputChangedCB* callback, void * ((PythonQtStdInRedirect*)in.object())->_cb = callback; ((PythonQtStdInRedirect*)in.object())->_callData = callbackData; // replace the built in file objects with our own objects - PyModule_AddObject(sys.object(), "stdin", in); + PyModule_AddObject_DECREF(sys.object(), "stdin", in); // Backup custom 'stdin' into 'pythonqt_stdin' Py_INCREF(in); // AddObject steals the reference, so increment it - PyModule_AddObject(sys.object(), "pythonqt_stdin", in); + PyModule_AddObject_DECREF(sys.object(), "pythonqt_stdin", in); } void PythonQt::setRedirectStdInCallbackEnabled(bool enabled) @@ -503,7 +512,7 @@ void PythonQtPrivate::registerClass(const QMetaObject* metaobject, const char* p PyObject* classWrapper = info->pythonQtClassWrapper(); // AddObject steals a reference, so we need to INCREF Py_INCREF(classWrapper); - PyModule_AddObject(module, info->className(), classWrapper); + PyModule_AddObject_DECREF(module, info->className(), classWrapper); } if (first) { first = false; @@ -537,13 +546,13 @@ void PythonQtPrivate::createPythonQtClassWrapper(PythonQtClassInfo* info, const outerClassInfo->addNestedClass(info); } else { Py_INCREF(pyobj); - PyModule_AddObject(pack, info->className(), pyobj); + PyModule_AddObject_DECREF(pack, info->className(), pyobj); } if (!module && package && strncmp(package, "Qt", 2) == 0) { // since PyModule_AddObject steals the reference, we need a incref once more... Py_INCREF(pyobj); // put all qt objects into Qt as well - PyModule_AddObject(packageByName("Qt"), info->className(), pyobj); + PyModule_AddObject_DECREF(packageByName("Qt"), info->className(), pyobj); } info->setPythonQtClassWrapper(pyobj); Py_DECREF(pyobj); @@ -1071,7 +1080,7 @@ void PythonQt::addObject(PyObject* object, const QString& name, QObject* qObject PyObject *wrappedObject = _p->wrapQObject(qObject); if (PyModule_Check(object)) { Py_XINCREF(wrappedObject); - PyModule_AddObject(object, QStringToPythonCharPointer(name), wrappedObject); + PyModule_AddObject_DECREF(object, QStringToPythonCharPointer(name), wrappedObject); } else if (PyDict_Check(object)) { PyDict_SetItemString(object, QStringToPythonCharPointer(name), wrappedObject); } else { @@ -1085,7 +1094,7 @@ void PythonQt::addVariable(PyObject* object, const QString& name, const QVariant PyObject *value = PythonQtConv::QVariantToPyObject(v); if (PyModule_Check(object)) { Py_XINCREF(value); - PyModule_AddObject(object, QStringToPythonCharPointer(name), value); + PyModule_AddObject_DECREF(object, QStringToPythonCharPointer(name), value); } else if (PyDict_Check(object)) { PyDict_SetItemString(object, QStringToPythonCharPointer(name), value); } else { @@ -1697,12 +1706,12 @@ void PythonQt::overwriteSysPath(const QStringList& paths) foreach(QString path, paths) { nativePaths << QDir::toNativeSeparators(path); } - PyModule_AddObject(sys, "path", PythonQtConv::QStringListToPyList(nativePaths)); + PyModule_AddObject_DECREF(sys, "path", PythonQtConv::QStringListToPyList(nativePaths)); } void PythonQt::setModuleImportPath(PyObject* module, const QStringList& paths) { - PyModule_AddObject(module, "__path__", PythonQtConv::QStringListToPyList(paths)); + PyModule_AddObject_DECREF(module, "__path__", PythonQtConv::QStringListToPyList(paths)); } void PythonQt::stdOutRedirectCB(const QString& str) @@ -1780,7 +1789,7 @@ void PythonQt::initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQ _p->_pythonQtModuleName = name; Py_INCREF((PyObject*)&PythonQtBoolResult_Type); - PyModule_AddObject(_p->pythonQtModule().object(), "BoolResult", (PyObject*)&PythonQtBoolResult_Type); + PyModule_AddObject_DECREF(_p->pythonQtModule().object(), "BoolResult", (PyObject*)&PythonQtBoolResult_Type); PythonQtObjectPtr sys; sys.setNewRef(PyImport_ImportModule("sys")); @@ -1793,8 +1802,8 @@ void PythonQt::initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQ err = PythonQtStdOutRedirectType.tp_new(&PythonQtStdOutRedirectType,NULL, NULL); ((PythonQtStdOutRedirect*)err.object())->_cb = stdErrRedirectCB; // replace the built in file objects with our own objects - PyModule_AddObject(sys, "stdout", out); - PyModule_AddObject(sys, "stderr", err); + PyModule_AddObject_DECREF(sys, "stdout", out); + PyModule_AddObject_DECREF(sys, "stderr", err); } // add PythonQt to the list of builtin module names @@ -1808,7 +1817,7 @@ void PythonQt::initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQ PyTuple_SetItem(module_names, i, val); } PyTuple_SetItem(module_names, old_size, PyString_FromString(name.constData())); - PyModule_AddObject(sys.object(), "builtin_module_names", module_names); + PyModule_AddObject_DECREF(sys.object(), "builtin_module_names", module_names); } Py_XDECREF(old_module_names); @@ -1991,7 +2000,7 @@ PyObject* PythonQtPrivate::packageByName(const char* name) _packages.insert(name, v); // AddObject steals the reference, so increment it! Py_INCREF(v); - PyModule_AddObject(_pythonQtModule, name, v); + PyModule_AddObject_DECREF(_pythonQtModule, name, v); } return v; } From 295a9ce5992cefd57a36ee037ac6a85d90593f6c Mon Sep 17 00:00:00 2001 From: Gregor Anich Date: Fri, 9 Oct 2020 18:28:41 +0200 Subject: [PATCH 3/4] Stylistic changes and refcount fixes in PythonQtImporter_load_module --- src/PythonQt.cpp | 8 +++++--- src/PythonQtImporter.cpp | 10 ++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index 6e912b781..ab6611956 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -71,8 +71,9 @@ void PythonQt_init_QtCoreBuiltin(PyObject*); static inline int PyModule_AddObject_DECREF(PyObject *module, const char *name, PyObject *value) { int ret = PyModule_AddObject(module, name, value); - if (ret < 0) + if (ret < 0) { Py_XDECREF(value); + } return ret; } @@ -2146,8 +2147,9 @@ const QMetaObject* PythonQtPrivate::buildDynamicMetaObject(PythonQtClassWrapper* PyObject* value = NULL; PyObject* key = NULL; static PyObject* qtSlots = NULL; - if (!qtSlots) - qtSlots = PyString_FromString("_qtSlots"); + if (!qtSlots) { + qtSlots = PyString_FromString("_qtSlots"); + } bool needsMetaObject = false; // Iterate over all members and check if they affect the QMetaObject: diff --git a/src/PythonQtImporter.cpp b/src/PythonQtImporter.cpp index 04023c30c..6e798a9bb 100644 --- a/src/PythonQtImporter.cpp +++ b/src/PythonQtImporter.cpp @@ -224,9 +224,9 @@ PythonQtImporter_load_module(PyObject *obj, PyObject *args) PyObject *code = NULL, *mod = NULL, *dict = NULL; char *fullname; - if (!PyArg_ParseTuple(args, "s:PythonQtImporter.load_module", - &fullname)) + if (!PyArg_ParseTuple(args, "s:PythonQtImporter.load_module", &fullname)) { return NULL; + } PythonQtImport::ModuleInfo info = PythonQtImport::getModuleInfo(self, fullname); if (info.type == PythonQtImport::MI_NOT_FOUND) { @@ -250,7 +250,6 @@ PythonQtImporter_load_module(PyObject *obj, PyObject *args) if (PyDict_SetItemString(dict, "__loader__", (PyObject *)self) != 0) { Py_DECREF(code); - Py_DECREF(mod); return NULL; } @@ -265,7 +264,6 @@ PythonQtImporter_load_module(PyObject *obj, PyObject *args) QStringToPythonConstCharPointer(subname)); if (fullpath == NULL) { Py_DECREF(code); - Py_DECREF(mod); return NULL; } @@ -273,14 +271,12 @@ PythonQtImporter_load_module(PyObject *obj, PyObject *args) Py_DECREF(fullpath); if (pkgpath == NULL) { Py_DECREF(code); - Py_DECREF(mod); return NULL; } err = PyDict_SetItemString(dict, "__path__", pkgpath); Py_DECREF(pkgpath); if (err != 0) { Py_DECREF(code); - Py_DECREF(mod); return NULL; } @@ -294,12 +290,10 @@ PythonQtImporter_load_module(PyObject *obj, PyObject *args) Py_XDECREF(fullnameObj); if (err != 0) { Py_DECREF(code); - Py_DECREF(mod); return NULL; } #endif } - Py_DECREF(mod); #ifdef PY3K PyObject* fullnameObj = PyUnicode_FromString(fullname); From 383fb8e8eff91ae6608440e1c3319d9acccce082 Mon Sep 17 00:00:00 2001 From: Gregor Anich Date: Sat, 10 Oct 2020 08:10:31 +0200 Subject: [PATCH 4/4] Allow setting PYTHON_VERSION and PYTHON_DIR when including python.prf, add rpath --- build/python.prf | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/build/python.prf b/build/python.prf index 297696263..4bfcec32e 100644 --- a/build/python.prf +++ b/build/python.prf @@ -2,12 +2,21 @@ # Change this variable to your python version (2.6, 2.7, 3.3, ...) -PYTHON_VERSION=$$(PYTHON_VERSION) +isEmpty( PYTHON_VERSION ) { + PYTHON_VERSION=$$(PYTHON_VERSION) +} isEmpty( PYTHON_VERSION ) { win32:PYTHON_VERSION=27 unix:PYTHON_VERSION=2.7 } +isEmpty( PYTHON_DIR ) { + PYTHON_DIR=$$(PYTHON_DIR) +} +!isEmpty( PYTHON_DIR ) { + PYTHON_DIR=$${PYTHON_DIR}/ +} + message(Using Python version $${PYTHON_VERSION}) macx { @@ -42,9 +51,16 @@ macx { # on linux, python-config is used to autodetect Python. # make sure that you have installed a matching python-dev package. - system(python$${PYTHON_VERSION}-config --embed --libs) { - unix:LIBS += $$system(python$${PYTHON_VERSION}-config --embed --libs) - } else: unix:LIBS += $$system(python$${PYTHON_VERSION}-config --libs) - unix:QMAKE_CXXFLAGS += $$system(python$${PYTHON_VERSION}-config --includes) - unix:QMAKE_LFLAGS += $$system(python$${PYTHON_VERSION}-config --ldflags) + PYTHON_CONFIG = $${PYTHON_DIR}/bin/python$${PYTHON_VERSION}-config + system($${PYTHON_CONFIG} --embed --libs) { + unix:LIBS += $$system($${PYTHON_CONFIG} --embed --libs) + } else: unix:LIBS += $$system($${PYTHON_CONFIG} --libs) + unix:QMAKE_CXXFLAGS += $$system($${PYTHON_CONFIG} --includes) + PYTHON_LFLAGS = $$system($${PYTHON_CONFIG} --ldflags) + unix:QMAKE_LFLAGS += $${PYTHON_LFLAGS} + # add rpath + PYTHON_LIBDIR = $$find(PYTHON_LFLAGS,-L.*) + RPATH = -Wl,-rpath, + PYTHON_RPATH = $$replace(PYTHON_LIBDIR,-L,$${RPATH}) + unix:QMAKE_LFLAGS += $${PYTHON_RPATH} }