[pygtk] PyGTK object escapes garbage collection

Pierre lists.stackp at online.fr
Wed Dec 22 01:16:16 WST 2010


Hello list,

I run into a surprising behavior regarding garbage collection. In the 
following example, a Notebook page is removed from its parent widget and 
destroyed. However, the page is not garbage-collected unless the "tab" 
widget (a gtk.HBox) is first destroyed.

Only the Page instance refers to the "tab" widget. Besides, "tab" is 
indirectly associated to the page: one of its child widget's signal is 
connected to a Page method. So we have something like this:

   page --> tab --> button --> callback --> page --> tab --> etc.

Once remove_page() returns, I would expect tab and page to be destroyed 
and collected, because both objects become unreachable (unreachable 
through Python variables and GTK calls). But the gc module shows that 
they are not collected.

Is this the expected behavior ?  I'm using pygtk 2.17.0, gtk 2.20.1, and 
Python 2.6.6.

Thank you for your time.

Pierre


# ------------------------------------------------------ #

import gc
import gtk
import gobject

DESTROY_TAB = False

class Page(gtk.VBox):

     def __init__(self):
         gtk.VBox.__init__(self)
         self.pack_start(gtk.TextView(), True, True) # To fill the window
         button = gtk.Button()
         button.connect("clicked", self.hello)
         title = gtk.Label("hello")
         tab = gtk.HBox()
         tab.pack_start(title, True, True)
         tab.pack_end(button, False, False)
         tab.show_all()

         # Keeping a reference here is the culprit. Could it be a
         # circular reference problem ?
         # tab --> button --> hello --> page --> tab --> ...
         self.tab = tab

     def hello(self, widget):
         print "hello"

def add_page(notebook):
     print "Adding a page to the Notebook."
     page = Page()
     page.show_all()
     notebook.append_page(page, tab_label=page.tab)

def remove_page(notebook):
     print "Removing the page."
     page = notebook.get_nth_page(0)
     notebook.remove_page(0)
     page.destroy()
     # Destroying page.tab let the GC collect the page.
     if DESTROY_TAB:
         page.tab.destroy()

def main():
     notebook = gtk.Notebook()
     w = gtk.Window()
     w.add(notebook)
     w.resize(400, 400)
     w.show_all()
     w.connect("destroy", gtk.main_quit)
     gobject.idle_add(add_page, notebook)
     gobject.timeout_add(1000, remove_page, notebook)
     gobject.timeout_add(2000, gtk.main_quit)
     gtk.main()

def seek_page():
     gc.collect()
     oo = gc.get_objects()
     for o in oo:
         if hasattr(o, "__class__") and (o.__class__ is Page
                                         or o.__class__ is gtk.HBox):
             print
             print o, "escaped garbage collection"
             print 'Referrers are :'
             for r in gc.get_referrers(o):
                 print '  *', repr(r)[:65], '...'


main()
seek_page()


# Output:
# ------
#
# Adding a page to the Notebook.
# Removing the page.
#
# <Page object at 0x98a898c (GtkVBox at 0x9960c18)> escaped garbage 
collection
# Referrers are :
#   * [(), {'__setattr__': <slot wrapper '__setattr__' of 'object' obje ...
#   * <bound method Page.hello of <Page object at 0x98a898c (GtkVBox at ...
#   * <frame object at 0x999f68c> ...
#
# <gtk.HBox object at 0x98a8a04 (GtkHBox at 0x9960c70)> escaped garbage 
collection
# Referrers are :
#   * [(), {'__setattr__': <slot wrapper '__setattr__' of 'object' obje ...
#   * <frame object at 0x999f68c> ...
#   * {'tab': <gtk.HBox object at 0x98a8a04 (GtkHBox at 0x9960c70)>} ...


More information about the pygtk mailing list