#!/usr/bin/env python

import gtk, gobject, gc, sys

gc.set_threshold (0, 0, 0)

if len(sys.argv) != 2:
    print 'usage: %s bigimage' % sys.argv[0]
    sys.exit(1)

old_objects = None

def get_garbage_collectable_objects ():
    if True:
        return gc.get_objects()
    else:
        return [object for object in gc.get_objects ()
                if (type    (object).__module__.startswith ('gtk')
                    or type (object).__module__.startswith ('gobject'))]

def create_window():
    global old_objects
    window = gtk.Window()
    box = gtk.HBox()
    button = gtk.Button('eat more memory')
    box.pixbuf = gtk.gdk.pixbuf_new_from_file(sys.argv[1])
    button.connect('clicked', close)
    box.add(button)
    window.add(box)
    window.show_all()
    ## the following line triggers the leak
    button.linkto = box

    num_passes  = 1
    num_objects = len (get_garbage_collectable_objects ())
    while num_passes < 20:
        gc.collect ()
        num_objects_new = len (get_garbage_collectable_objects ())

        if num_objects_new < num_objects:
            num_objects  = num_objects_new
            num_passes  += 1
        else:
            break

    print '\nobjects: %d (in %d passes)' % (num_objects, num_passes)

    if old_objects is not None:
        print 'New objects:'
        for object in get_garbage_collectable_objects ():
            if id (object) not in old_objects and object is not old_objects:
                print '  %s  [%s]' % (object, type (object).__module__)

    old_objects = set ([id (object) for object in get_garbage_collectable_objects ()])
            

def close(button):
    window = button.get_toplevel()
    ## we remove ourselves here explicitly as winow.destroy() disconnects all
    ## signal handlers and with that breaks the cycle
    window.remove(window.child)
    window.destroy()
    del window
    create_window()

create_window()
gtk.main()

