On 5/14/2007 11:01 AM, Phil Thompson wrote:

=========================================
from qt import *
import weakref

app = QApplication([])

ql = QListView(None)
viewport = ql.viewport()

o = QObject(viewport)
o.xxx = viewport  # bug-trigger!

destroyed = []
def cb(wr):
     destroyed.append(1)
wr = weakref.ref(o, cb)

del o
del viewport
assert not destroyed, "object destroyed too early #1!"

import gc
gc.collect()
assert not destroyed, "object destroyed too early #2!"

del ql
import gc
gc.collect()
assert destroyed, "object never destroyed!"
=========================================
Traceback (most recent call last):
   File "pyqtbug19.py", line 25, in ?
     assert not destroyed, "object destroyed too early #2!"
AssertionError: object destroyed too early #2!


This happens with latest PyQt and SIP official releases (3.17.1 and
4.6). The line that seems to trigger the bug is the one marked with a
comment.
This behaves as I would expect - ie. it's a missing feature rather than
a bug.

Although ql is the parent of viewport, Python doesn't know that and
there is no hidden extra reference to viewport to keep it alive when
collect() is run.
But the problem here is that "o" is collected. o is a QObject whose
lifetime should be transferred to C++ (given that it has a non-NULL
parent, right?).
But that parent is owned by Python. When viewpoint (Python) goes,
viewpoint (C++) goes, which takes o (C++), which takes o (Python).
Are you saying that "viewport" is owned by Python?

I was but I was just in the process of replying to myself to say I was talking rubbish.

Ownership these days covers a couple of things that are actually applied separately. One thing is the responsibility to call the C++ dtor, the other thing is the associations maintained for the cyclic garbage collector.

When the result of viewport() is wrapped (assuming this is the first call) then C++ has the responsibility for calling the dtor. However as far as Python is concerned there is no gc association with any other Python object.

"Association" is a parent/child relationship and the parent has an (internally maintained) Python reference to the child.

When o is wrapped C++ is responsible for the dtor. A gc association is also made with viewport, ie. the viewport Python object has a reference to the o Python object. Obviously o is given a reference to viewport via the xxx attribute, so creating the cycle.

When o and viewport are del'ed and collect() is run the cycle is broken and the o and viewport Python objects garbage collected. The o and viewport C++ instances are left untouched.

OK, this clears up everything but why both objects manage to survive even to a gc.collect() call if the "xxx" attribute does not exist.

If the new /Transfer/ function annotation were to be applied to viewport() then a gc association would be established between viewport and ql with the ql Python object having a reference to the viewport Python object. This would prevent viewport (and also o) from being garbage collected until ql was.

What about queryList()/findChildren()? In my original code, the C++-allocated instance was accessed through a queryList() call. It's basically the same (getting a reference to a C++ instance never seen before by Python), but I guess the annotation won't be enough...

I wonder: can't you just apply it automatically to all cases where you create a wrapper for an already existing C++ object that was never wrapped before? How can't that be wrong?

(PS: what about a debugging function in sip which tells who owns who?
sip.owner(foo) which returns a sip.voidptr() to the owner if it's C++, or
None if it's owned by Python. It would make debugging of situations like
this a little easier... using weakref/SIGNAL(destroyed) can bring other
bugs into the table and makes things more confusing)

Would you want to use the value? Or would something like sip.debug(foo) that displayed lots of stuff to stdout be better?

I was thinking it just for debugging purposes, so sip.debug(foo) should be fine.
--
Giovanni Bajo

_______________________________________________
PyQt mailing list    [email protected]
http://www.riverbankcomputing.com/mailman/listinfo/pyqt

Reply via email to