Skip to content

Commit b578823

Browse files
committed
Make it possible to use Py_Finalize before calling the PythonQt destructor
1 parent a268782 commit b578823

13 files changed

+239
-141
lines changed

.github/workflows/build.yml

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,13 @@ jobs:
6767
PYTHON_VERSION=$(python3 --version | cut -d " " -f 2 | cut -d "." -f1,2) \
6868
PYTHON_DIR=$(which python3 | xargs dirname | xargs dirname)
6969
make -j $(nproc)
70-
PYTHONDEVMODE=1 PYTHONASYNCIODEBUG=1 PYTHONWARNINGS=error PYTHONMALLOC=malloc_debug \
71-
UBSAN_OPTIONS="halt_on_error=1" ASAN_OPTIONS="detect_leaks=0:detect_stack_use_after_return=1:fast_unwind_on_malloc=0" \
72-
make check TESTARGS="-platform offscreen"
73-
74-
- name: Run memory tests with sanitizers
75-
run: |
76-
QT_VERSION_FULL=$(qmake -query QT_VERSION)
7770
if [[ "$QT_VERSION_FULL" == 5.12* ]]; then
7871
echo "leak:QPlatformIntegrationFactory::create" >> $PWD/lsan.supp
7972
export LSAN_OPTIONS="suppressions=$PWD/lsan.supp"
8073
fi
8174
PYTHONDEVMODE=1 PYTHONASYNCIODEBUG=1 PYTHONWARNINGS=error PYTHONMALLOC=malloc_debug \
82-
UBSAN_OPTIONS="halt_on_error=1" ASAN_OPTIONS="detect_leaks=1:detect_stack_use_after_return=1:fast_unwind_on_malloc=0" \
83-
PYTHONQT_RUN_ONLY_MEMORY_TESTS=1 \
84-
make check TESTARGS="-platform minimal"
75+
UBSAN_OPTIONS="halt_on_error=1" ASAN_OPTIONS="detect_leaks=0:detect_stack_use_after_return=1:fast_unwind_on_malloc=0" \
76+
make check TESTARGS="-platform offscreen"
8577
8678
- name: Generate Wrappers
8779
run: |
@@ -142,7 +134,7 @@ jobs:
142134
"PYTHON_VERSION=${PYTHON_VERSION_SHORT}" "PYTHON_DIR=${PYTHON_DIR}"
143135
make -j $(nproc) && \
144136
PYTHONDEVMODE=1 PYTHONASYNCIODEBUG=1 PYTHONWARNINGS=error PYTHONMALLOC=malloc_debug \
145-
make check TESTARGS="-platform offscreen"
137+
make check
146138
147139
- name: Generate Wrappers
148140
run: |

src/PythonQt.cpp

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -323,11 +323,17 @@ void PythonQt::init(int flags, const QByteArray& pythonQtModuleName)
323323
void PythonQt::cleanup()
324324
{
325325
if (_self) {
326+
_self->removeSignalHandlers();
326327
delete _self;
327328
_self = nullptr;
328329
}
329330
}
330331

332+
void PythonQt::preCleanup()
333+
{
334+
_self->priv()->preCleanup();
335+
}
336+
331337
PythonQt* PythonQt::self() { return _self; }
332338

333339
PythonQt::PythonQt(int flags, const QByteArray& pythonQtModuleName)
@@ -348,6 +354,12 @@ PythonQt::PythonQt(int flags, const QByteArray& pythonQtModuleName)
348354
Py_Initialize();
349355
}
350356

357+
#ifdef PYTHONQT_FULL_THREAD_SUPPORT
358+
if (!PyEval_ThreadsInitialized()) {
359+
PyEval_InitThreads();
360+
}
361+
#endif
362+
351363
// add our own python object types for qt object slots
352364
if (PyType_Ready(&PythonQtSlotFunction_Type) < 0) {
353365
std::cerr << "could not initialize PythonQtSlotFunction_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
@@ -414,9 +426,9 @@ PythonQtPrivate::~PythonQtPrivate() {
414426
delete _defaultImporter;
415427
_defaultImporter = nullptr;
416428

417-
{
418-
qDeleteAll(_knownClassInfos);
419-
}
429+
qDeleteAll(_knownClassInfos);
430+
_knownClassInfos.clear();
431+
PythonQtClassInfo::clearInteralStaticData();
420432

421433
PythonQtMethodInfo::cleanupCachedMethodInfos();
422434
PythonQtArgumentFrame::cleanupFreeList();
@@ -1558,6 +1570,16 @@ PythonQtClassInfo* PythonQtPrivate::currentClassInfoForClassWrapperCreation()
15581570
return info;
15591571
}
15601572

1573+
void PythonQtPrivate::preCleanup()
1574+
{
1575+
_pySourceFileLoader = nullptr;
1576+
_pySourcelessFileLoader = nullptr;
1577+
_pyEnsureFuture = nullptr;
1578+
_pyFutureClass = nullptr;
1579+
_pyTaskDoneCallback = nullptr;
1580+
_pythonQtModule = nullptr;
1581+
}
1582+
15611583
void PythonQtPrivate::addDecorators(QObject* o, int decoTypes)
15621584
{
15631585
o->setParent(this);
@@ -2353,7 +2375,7 @@ const QMetaObject* PythonQtPrivate::buildDynamicMetaObject(PythonQtClassWrapper*
23532375
}
23542376
if (needsMetaObject) {
23552377
type->_dynamicClassInfo->_dynamicMetaObject = builder.toMetaObject();
2356-
type->_dynamicClassInfo->_classInfo = new PythonQtClassInfo();
2378+
type->_dynamicClassInfo->_classInfo.reset(new PythonQtClassInfo());
23572379
type->_dynamicClassInfo->_classInfo->setupQObject(type->_dynamicClassInfo->_dynamicMetaObject);
23582380
} else {
23592381
// we don't need an own meta object, just use the one from our base class
@@ -2635,6 +2657,23 @@ PythonQtClassInfo* PythonQtPrivate::getClassInfo( const QByteArray& className )
26352657
}
26362658
}
26372659
}
2660+
2661+
if (!result) {
2662+
bool ambiguity = false;
2663+
for(auto &&key: _knownClassInfos.keys()) {
2664+
if (key.indexOf(QByteArray("::") + className) >= 0) {
2665+
if (!result) {
2666+
result = _knownClassInfos.value(key);
2667+
} else {
2668+
ambiguity = true;
2669+
std::cerr << "Multiple candidates found for" << '\n';
2670+
}
2671+
}
2672+
}
2673+
if (ambiguity) {
2674+
return nullptr;
2675+
}
2676+
}
26382677
return result;
26392678
}
26402679

src/PythonQt.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ class PYTHONQT_EXPORT PythonQt : public QObject {
241241
//! get the singleton instance
242242
static PythonQt* self();
243243

244+
static void preCleanup();
245+
244246
//@}
245247

246248
//! defines the object types for introspection
@@ -662,6 +664,8 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject {
662664
PythonQtPrivate();
663665
~PythonQtPrivate() override;
664666

667+
void preCleanup();
668+
665669
enum DecoratorTypes {
666670
StaticDecorator = 1,
667671
ConstructorDecorator = 2,

src/PythonQtClassInfo.cpp

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ PythonQtClassInfo::~PythonQtClassInfo()
8686
if (_destructor) {
8787
_destructor->deleteOverloadsAndThis();
8888
}
89-
Q_FOREACH(PythonQtSlotInfo* info, _decoratorSlots) {
90-
info->deleteOverloadsAndThis();
89+
for(auto &&info: _decoratorSlots) {
90+
info->deleteOverloadsAndThis();
9191
}
9292
}
9393

@@ -290,9 +290,7 @@ bool PythonQtClassInfo::lookForEnumAndCache(const QMetaObject* meta, const char*
290290
if (escapeReservedNames(e.key(j)) == memberName) {
291291
PyObject* enumType = findEnumWrapper(e.name());
292292
if (enumType) {
293-
PythonQtObjectPtr enumValuePtr;
294-
enumValuePtr.setNewRef(PythonQtPrivate::createEnumValueInstance(enumType, e.value(j)));
295-
PythonQtMemberInfo newInfo(enumValuePtr);
293+
PythonQtMemberInfo newInfo(PythonQtPrivate::createEnumValueInstance(enumType, e.value(j)));
296294
_cachedMembers.insert(memberName, newInfo);
297295
#ifdef PYTHONQT_DEBUG
298296
std::cout << "caching enum " << memberName << " on " << meta->className() << std::endl;
@@ -875,7 +873,7 @@ void PythonQtClassInfo::createEnumWrappers(const QMetaObject* meta)
875873
for (int i = meta->enumeratorOffset();i<meta->enumeratorCount();i++) {
876874
QMetaEnum e = meta->enumerator(i);
877875
PythonQtObjectPtr p;
878-
p.setNewRef(PythonQtPrivate::createNewPythonQtEnumWrapper(e.name(), _pythonQtClassWrapper));
876+
p.setNewRef(PythonQtPrivate::createNewPythonQtEnumWrapper(e.name(), _pythonQtClassWrapper.object()));
879877
// add enum values to the enum type itself, in case enum value names are so generic
880878
// that they are not unique
881879
for (int j = 0; j < e.keyCount(); j++) {
@@ -1043,6 +1041,11 @@ void PythonQtClassInfo::addGlobalNamespaceWrapper(PythonQtClassInfo* namespaceWr
10431041
_globalNamespaceWrappers.insert(0, namespaceWrapper);
10441042
}
10451043

1044+
void PythonQtClassInfo::clearInteralStaticData()
1045+
{
1046+
_globalNamespaceWrappers.clear();
1047+
}
1048+
10461049
void PythonQtClassInfo::updateRefCountingCBs()
10471050
{
10481051
if (!_refCallback) {
@@ -1116,38 +1119,3 @@ bool PythonQtClassInfo::supportsRichCompare()
11161119
}
11171120
return (_typeSlots & PythonQt::Type_RichCompare);
11181121
}
1119-
1120-
//-------------------------------------------------------------------------
1121-
1122-
PythonQtMemberInfo::PythonQtMemberInfo( PythonQtSlotInfo* info ) : _slot(info)
1123-
{
1124-
if (info->metaMethod()->methodType() == QMetaMethod::Signal) {
1125-
_type = Signal;
1126-
} else {
1127-
_type = Slot;
1128-
}
1129-
_enumValue = nullptr;
1130-
_pythonType = nullptr;
1131-
}
1132-
1133-
PythonQtMemberInfo::PythonQtMemberInfo( const PythonQtObjectPtr& enumValue )
1134-
{
1135-
_type = EnumValue;
1136-
_slot = nullptr;
1137-
_enumValue = enumValue;
1138-
_pythonType = nullptr;
1139-
}
1140-
1141-
PythonQtMemberInfo::PythonQtMemberInfo( const QMetaProperty& prop )
1142-
{
1143-
_type = Property;
1144-
_slot = nullptr;
1145-
_property = prop;
1146-
_enumValue = nullptr;
1147-
_pythonType = nullptr;
1148-
}
1149-
1150-
PythonQtDynamicClassInfo::~PythonQtDynamicClassInfo()
1151-
{
1152-
delete _classInfo;
1153-
}

src/PythonQtClassInfo.h

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,37 +41,40 @@
4141
#include <QByteArray>
4242
#include <QList>
4343

44-
class PythonQtSlotInfo;
45-
class PythonQtClassInfo;
44+
#include <PythonQtMethodInfo.h>
4645

4746
struct PythonQtDynamicClassInfo
4847
{
49-
PythonQtDynamicClassInfo() { _dynamicMetaObject = nullptr; _classInfo = nullptr; }
50-
~PythonQtDynamicClassInfo();
51-
52-
const QMetaObject* _dynamicMetaObject;
53-
PythonQtClassInfo* _classInfo;
48+
const QMetaObject* _dynamicMetaObject {};
49+
QScopedPointer<PythonQtClassInfo> _classInfo;
5450
};
5551

5652
struct PythonQtMemberInfo {
5753
enum Type {
5854
Invalid, Slot, Signal, EnumValue, EnumWrapper, Property, NestedClass, NotFound
5955
};
6056

61-
PythonQtMemberInfo():_type(Invalid),_slot(nullptr),_pythonType(nullptr),_enumValue(nullptr) { }
57+
PythonQtMemberInfo() = default;
6258

63-
PythonQtMemberInfo(PythonQtSlotInfo* info);
59+
explicit PythonQtMemberInfo(PythonQtSlotInfo* info)
60+
: _type(info->metaMethod()->methodType() == QMetaMethod::Signal? Signal : Slot)
61+
, _slot(info)
62+
{}
6463

65-
PythonQtMemberInfo(const PythonQtObjectPtr& enumValue);
64+
explicit PythonQtMemberInfo(PyObject* enumValue)
65+
: _type (EnumValue), _enumValue(enumValue)
66+
{}
6667

67-
PythonQtMemberInfo(const QMetaProperty& prop);
68+
explicit PythonQtMemberInfo(const QMetaProperty& prop)
69+
: _type (Property), _property(prop)
70+
{}
6871

69-
Type _type;
72+
Type _type { Invalid };
7073

7174
// TODO: this could be a union...
72-
PythonQtSlotInfo* _slot;
73-
PyObject* _pythonType;
74-
PythonQtObjectPtr _enumValue;
75+
PythonQtSlotInfo* _slot {};
76+
PyObject* _pythonType {};
77+
PyObject* _enumValue {};
7578
QMetaProperty _property;
7679
};
7780

@@ -177,10 +180,10 @@ class PYTHONQT_EXPORT PythonQtClassInfo {
177180
void addParentClass(const ParentClassInfo& info) { _parentClasses.append(info); }
178181

179182
//! set the associated PythonQtClassWrapper (which handles instance creation of this type)
180-
void setPythonQtClassWrapper(PyObject* obj) { _pythonQtClassWrapper = obj; }
183+
void setPythonQtClassWrapper(PyObject* obj) { _pythonQtClassWrapper.setNewRef(obj); }
181184

182185
//! get the associated PythonQtClassWrapper (which handles instance creation of this type)
183-
PyObject* pythonQtClassWrapper() { return _pythonQtClassWrapper; }
186+
PyObject* pythonQtClassWrapper() { return _pythonQtClassWrapper.object(); }
184187

185188
//! set the shell set instance wrapper cb
186189
void setShellSetInstanceWrapperCB(PythonQtShellSetInstanceWrapperCB* cb) {
@@ -244,6 +247,9 @@ class PYTHONQT_EXPORT PythonQtClassInfo {
244247
//! Add a wrapper that contains global enums
245248
static void addGlobalNamespaceWrapper(PythonQtClassInfo* namespaceWrapper);
246249

250+
//! Clear all statically allocated caches and wrappers
251+
static void clearInteralStaticData();
252+
247253
private:
248254
void updateRefCountingCBs();
249255

@@ -294,7 +300,7 @@ class PYTHONQT_EXPORT PythonQtClassInfo {
294300
QObject* _decoratorProvider;
295301
PythonQtQObjectCreatorFunctionCB* _decoratorProviderCB;
296302

297-
PyObject* _pythonQtClassWrapper;
303+
PythonQtObjectPtr _pythonQtClassWrapper;
298304

299305
PythonQtShellSetInstanceWrapperCB* _shellSetInstanceWrapperCB;
300306

src/PythonQtInstanceWrapper.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ static PyObject *PythonQtInstanceWrapper_getattro(PyObject *obj,PyObject *name)
458458
PythonQtClassInfo* classInfo = nullptr;
459459
PythonQtClassWrapper* classType = (PythonQtClassWrapper*)Py_TYPE(wrapper);
460460
while (classType->_dynamicClassInfo) {
461-
classInfo = classType->_dynamicClassInfo->_classInfo;
461+
classInfo = classType->_dynamicClassInfo->_classInfo.data();
462462
if (classInfo) {
463463
PythonQtMemberInfo member = classInfo->member(attributeName);
464464
if (member._type == PythonQtMemberInfo::Signal) {

src/PythonQtMethodInfo.cpp

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
#include "PythonQtClassInfo.h"
4444
#include <iostream>
4545

46-
QHash<QByteArray, PythonQtMethodInfo*> PythonQtMethodInfo::_cachedSignatures;
46+
QHash<QByteArray, QSharedPointer<PythonQtMethodInfo>> PythonQtMethodInfo::_cachedSignatures;
4747
QHash<int, PythonQtMethodInfo::ParameterInfo> PythonQtMethodInfo::_cachedParameterInfos;
4848
QHash<QByteArray, QByteArray> PythonQtMethodInfo::_parameterNameAliases;
4949

@@ -100,12 +100,11 @@ const PythonQtMethodInfo* PythonQtMethodInfo::getCachedMethodInfo(const QMetaMet
100100
QByteArray sig(PythonQtUtils::signature(signal));
101101
sig = sig.mid(sig.indexOf('('));
102102
QByteArray fullSig = QByteArray(signal.typeName()) + " " + sig;
103-
PythonQtMethodInfo* result = _cachedSignatures.value(fullSig);
103+
auto &result = _cachedSignatures[fullSig];
104104
if (!result) {
105-
result = new PythonQtMethodInfo(signal, classInfo);
106-
_cachedSignatures.insert(fullSig, result);
105+
result.reset(new PythonQtMethodInfo(signal, classInfo));
107106
}
108-
return result;
107+
return result.data();
109108
}
110109

111110
const PythonQtMethodInfo* PythonQtMethodInfo::getCachedMethodInfoFromArgumentList(int numArgs, const char** args)
@@ -123,12 +122,11 @@ const PythonQtMethodInfo* PythonQtMethodInfo::getCachedMethodInfoFromArgumentLis
123122
arguments << arg;
124123
}
125124
fullSig += ")";
126-
PythonQtMethodInfo* result = _cachedSignatures.value(fullSig);
125+
auto &result = _cachedSignatures[fullSig];
127126
if (!result) {
128-
result = new PythonQtMethodInfo(typeName, arguments);
129-
_cachedSignatures.insert(fullSig, result);
127+
result.reset(new PythonQtMethodInfo(typeName, arguments));
130128
}
131-
return result;
129+
return result.data();
132130
}
133131

134132
void PythonQtMethodInfo::fillParameterInfo(ParameterInfo& type, const QByteArray& orgName, PythonQtClassInfo* classInfo)
@@ -403,7 +401,7 @@ int PythonQtMethodInfo::nameToType(const char* name)
403401
_parameterTypeDict.insert("QVariant", PythonQtMethodInfo::Variant);
404402
// own special types... (none so far, could be e.g. ObjectList
405403
}
406-
QHash<QByteArray, int>::const_iterator it = _parameterTypeDict.find(name);
404+
auto it = _parameterTypeDict.find(name);
407405
if (it!=_parameterTypeDict.end()) {
408406
return it.value();
409407
} else {
@@ -413,10 +411,6 @@ int PythonQtMethodInfo::nameToType(const char* name)
413411

414412
void PythonQtMethodInfo::cleanupCachedMethodInfos()
415413
{
416-
QHashIterator<QByteArray, PythonQtMethodInfo *> i(_cachedSignatures);
417-
while (i.hasNext()) {
418-
delete i.next().value();
419-
}
420414
_cachedSignatures.clear();
421415
_cachedParameterInfos.clear();
422416
}

src/PythonQtMethodInfo.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ class PYTHONQT_EXPORT PythonQtMethodInfo
139139
static QHash<QByteArray, QByteArray> _parameterNameAliases;
140140

141141
//! stores the cached signatures of methods to speedup mapping from Qt to Python types
142-
static QHash<QByteArray, PythonQtMethodInfo*> _cachedSignatures;
142+
static QHash<QByteArray, QSharedPointer<PythonQtMethodInfo>> _cachedSignatures;
143143

144144
static QHash<int, ParameterInfo> _cachedParameterInfos;
145145

0 commit comments

Comments
 (0)