[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)?

Matthias



More information about the PyQt mailing list