Memory leak when setting widgets as attributes of object
Hi,
GTK Version: 3.24.33 PyGObject Version: 3.42.2
When setting a widget like a Gtk.Box() as attribute of a object (see example TestWindow class, the attribute self.lb
) then the Window does not get finalized anymore. Comment the assigning out (self.lb = lb
), and the window finalizes. I want to understand why that is. I think its a bug. As destroying the Window should free all references.
run the example, press the button to open the window, close the window and after 2 seconds in the console the finalize check will show that the window has not been finalized
import gi
import sys
import gc
import weakref
gi.require_version("Gtk", "3.0")
from gi.repository import GLib, Gtk, GObject
class Application(Gtk.Application):
def __init__(self, *args, **kwargs):
super().__init__(*args, application_id="org.example.mytestapp", **kwargs)
self.window = None
self.connect('activate', self._activate)
def _activate(self, _application):
if not self.window:
self.window = AppWindow(self, "Main Window")
self.window.present()
class AppWindow(Gtk.ApplicationWindow):
def __init__(self, application, title='test'):
super().__init__(application=application, title=title)
self.application = application
self.set_default_size(300, 300)
button = Gtk.Button(label='press')
button.connect('clicked', self._on_clicked)
box = Gtk.Box()
box.add(button)
self.add(box)
self.connect('destroy', self._on_destroy)
self.show_all()
def _on_clicked(self, button):
TestWindow(self.application)
def _on_destroy(self, widget):
check_finalize(self)
class TestWindow(Gtk.ApplicationWindow):
def __init__(self, app):
Gtk.ApplicationWindow.__init__(self)
self.set_application(app)
self.set_default_size(300, 300)
grid = Gtk.Grid()
grid.set_hexpand(True)
lb = Gtk.ListBox()
grid.attach(lb, 0, 0, 1, 1)
self.lb = lb
self.add(grid)
self.show_all()
self.connect('destroy', self._on_destroy)
def _on_destroy(self, widget):
check_finalize(self)
def check_finalize(obj) -> None:
name = obj.__class__.__name__
finalizer = weakref.finalize(obj, print, f'{name} has been finalized')
g_objects: list[str] = []
if isinstance(obj, GObject.Object):
g_objects.append(name)
def g_object_finalized():
print(f'GObject {name} has been finalized')
g_objects.remove(name)
obj.weak_ref(g_object_finalized)
def is_finalizer_ref(ref):
try:
return isinstance(ref[2][0], str)
except Exception:
return False
def check_finalized():
gc.collect()
gc.collect()
if g_objects:
print('GObject not finalized: %s', name)
tup = finalizer.peek()
if tup is None:
return
print('%s not finalized' % name)
GLib.timeout_add_seconds(2, check_finalized)
if __name__ == "__main__":
app = Application()
app.run(sys.argv)
Edited by lovetox