[PyQt] Conversion to wrong SubClass

Phil Thompson phil at riverbankcomputing.com
Fri Jan 18 13:13:11 GMT 2019


On 18 Jan 2019, at 12:48 pm, Matthias Kuhn <matthias at opengis.ch> wrote:
> 
> 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?

At the moment you would have to call sipConvertFromType() and check the reference count of the retruned object.

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

It should be invalidated when the ref count reaches 0.

Phil


More information about the PyQt mailing list