[PyQt] Conversion to wrong SubClass

Matthias Kuhn matthias at opengis.ch
Fri Jan 18 12:48:30 GMT 2019

On 1/18/19 12:38 PM, Phil Thompson wrote:
> On 18 Jan 2019, at 11:22 am, Matthias Kuhn <matthias at opengis.ch> wrote:
>> Hi
>> In QGIS we are facing an issue, where in some cases a wrong subclass
>> mapping is used in the python bindings.
>> The code looks like this:
>>  * There is a parent class QgsAbstractGeometry
>>  * There are various child classes, among them QgsPoint and QgsMultiPolygon
>> After heavy operation it can happen, that the python bindings indicate,
>> that an object is of type QgsPoint, whereas the underlying object is
>> actually a QgsMultiPolygon (verified that when an overwritten method is
>> called on the object, the one from QgsMultiPolygon is executed).
>> It looks like objects which are created subsequently have exactly the
>> same memory address (see below) and I suspect sip caches type
>> information with memory addresses somewhere (based on "When SIP needs to
>> wrap a C++ class instance it first checks to make sure it hasn’t already
>> done so. " /
>> http://pyqt.sourceforge.net/Docs/sip4/directives.html?highlight=subclass#directive-%ConvertToSubClassCode)
>> Does sip invalidate this cache information somehow (when the python
>> wrapper is garbage collected or something else)? Is it possible to
>> proactively trigger cache invalidation from a destructor? Are there
>> other ideas what could go wrong and how to deal with that?
>> ** Insights from debugging:
>> Unconditional debug print statements have been added to
>>  * QgsAbstractGeometry::QgsAbstractGeometry() (Constructor)
>>  * QgsAbstractGeometry::~QgsAbstractGeometry() (Destructor)
>>  * %SipConvertToSubClassCode
>> grepping the log for the memory address of an affected geometry reveals,
>> that the constructor is called twice with the object on the same
>> address, but the SipConvertToSubClassCode is only called once
>> ----------
>> Constructor: 0x8b1cbd0
>> SipConvertToSubClassCode - Type QgsPoint: 0x8b1cbd0
>> Destructor: 0x8b1cbd0
>> Constructor: 0x8b1cbd0
>> Destructor: 0x8b1cbd0
>> ---------
>> Any hints would be much appreciated
> The cache entry will be invalidated when sip knows the C++ instance has been destroyed. This is easy enough when the instance has been created from Python and has virtual dtors - but you can have C++ APIs which make this all but impossible.. You could look at sip.setdeleted() and similar.
Yes, in this case it is impossible to automatically detect deletion by
sip because the object is created in C++ code.

>From what I can see, sep.setdeleted() works on a Python object. The only
thing we have available in the destructor is a pointer to the C++
object. Is there a way to get the matching python object like done in
the code that calls SipConvertToSubClassCode?

Or does this mean that if the refcount for this Python object dropped to
0 the cache will be invalidated anyway (and the real issue here is, that
the python object is still alive somewhere)?


More information about the PyQt mailing list