An improved Leak (Uncollected Objects) Gramplet (#345)
* Improved leak detector (debugging tool) * Fix DummyDb so it garbage collector can reclaim it after close * Fix leak gramplet so pop-ups use correct transient parent
This commit is contained in:
parent
86fd14613e
commit
ebb7111f25
@ -216,8 +216,9 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
|
|||||||
"""
|
"""
|
||||||
Create a new DummyDb instance.
|
Create a new DummyDb instance.
|
||||||
"""
|
"""
|
||||||
DbReadBase.__init__(self)
|
|
||||||
Callback.__init__(self)
|
Callback.__init__(self)
|
||||||
|
self.basedb = None
|
||||||
|
self.__feature = {} # {"feature": VALUE, ...}
|
||||||
self.db_is_open = False
|
self.db_is_open = False
|
||||||
self.readonly = True
|
self.readonly = True
|
||||||
self.name_formats = []
|
self.name_formats = []
|
||||||
|
@ -1271,7 +1271,7 @@ register(GRAMPLET,
|
|||||||
gramps_target_version=MODULE_VERSION,
|
gramps_target_version=MODULE_VERSION,
|
||||||
status = UNSTABLE,
|
status = UNSTABLE,
|
||||||
fname="leak.py",
|
fname="leak.py",
|
||||||
height=200,
|
height=400,
|
||||||
gramplet = 'Leak',
|
gramplet = 'Leak',
|
||||||
gramplet_title="Uncollected Objects",
|
gramplet_title="Uncollected Objects",
|
||||||
)
|
)
|
||||||
|
141
gramps/plugins/gramplet/leak.py
Normal file → Executable file
141
gramps/plugins/gramplet/leak.py
Normal file → Executable file
@ -23,7 +23,13 @@
|
|||||||
"""
|
"""
|
||||||
Show uncollected objects in a window.
|
Show uncollected objects in a window.
|
||||||
"""
|
"""
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Python modules
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
import weakref
|
||||||
|
import sys
|
||||||
#------------------------------------------------------------------------
|
#------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# GNOME/GTK modules
|
# GNOME/GTK modules
|
||||||
@ -40,10 +46,11 @@ import gc
|
|||||||
#------------------------------------------------------------------------
|
#------------------------------------------------------------------------
|
||||||
from gramps.gen.plug import Gramplet
|
from gramps.gen.plug import Gramplet
|
||||||
from gramps.gui.dialog import InfoDialog
|
from gramps.gui.dialog import InfoDialog
|
||||||
from gramps.gui.utils import is_right_click
|
from gramps.gui.utils import is_right_click, ProgressMeter
|
||||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||||
_ = glocale.translation.gettext
|
_ = glocale.translation.gettext
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Leak
|
# Leak
|
||||||
@ -58,7 +65,7 @@ class Leak(Gramplet):
|
|||||||
self.gui.get_container_widget().remove(self.gui.textview)
|
self.gui.get_container_widget().remove(self.gui.textview)
|
||||||
self.gui.get_container_widget().add(self.gui.WIDGET)
|
self.gui.get_container_widget().add(self.gui.WIDGET)
|
||||||
|
|
||||||
flags = gc.DEBUG_UNCOLLECTABLE|gc.DEBUG_SAVEALL
|
flags = gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_SAVEALL
|
||||||
if hasattr(gc, "DEBUG_OBJECTS"):
|
if hasattr(gc, "DEBUG_OBJECTS"):
|
||||||
flags = flags | gc.DEBUG_OBJECTS
|
flags = flags | gc.DEBUG_OBJECTS
|
||||||
gc.set_debug(flags)
|
gc.set_debug(flags)
|
||||||
@ -74,24 +81,27 @@ class Leak(Gramplet):
|
|||||||
self.top.pack_start(self.label, False, False, 6)
|
self.top.pack_start(self.label, False, False, 6)
|
||||||
|
|
||||||
self.scroll = Gtk.ScrolledWindow()
|
self.scroll = Gtk.ScrolledWindow()
|
||||||
#add a listview to the scrollable
|
# add a listview to the scrollable
|
||||||
self.list = Gtk.TreeView()
|
self.list = Gtk.TreeView()
|
||||||
self.list.set_headers_visible(True)
|
self.list.set_headers_visible(True)
|
||||||
self.list.connect('button-press-event', self._button_press)
|
self.list.connect('button-press-event', self._button_press)
|
||||||
self.scroll.add(self.list)
|
self.scroll.add(self.list)
|
||||||
#make a model
|
# make a model
|
||||||
self.modeldata = []
|
self.model = Gtk.ListStore(int, str, str)
|
||||||
self.model = Gtk.ListStore(int, str)
|
|
||||||
self.list.set_model(self.model)
|
self.list.set_model(self.model)
|
||||||
|
|
||||||
#set the columns
|
# set the columns
|
||||||
self.renderer = Gtk.CellRendererText()
|
self.renderer = Gtk.CellRendererText()
|
||||||
column = Gtk.TreeViewColumn(_('Number'), self.renderer, text=0)
|
column = Gtk.TreeViewColumn(_('Number'), self.renderer, text=0)
|
||||||
column.set_resizable(True)
|
column.set_resizable(True)
|
||||||
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||||
self.list.append_column(column)
|
self.list.append_column(column)
|
||||||
|
column = Gtk.TreeViewColumn(_('Referrer'), self.renderer, text=1)
|
||||||
|
column.set_resizable(True)
|
||||||
|
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||||
|
self.list.append_column(column)
|
||||||
column = Gtk.TreeViewColumn(_('Uncollected object'), self.renderer,
|
column = Gtk.TreeViewColumn(_('Uncollected object'), self.renderer,
|
||||||
text=1)
|
text=2)
|
||||||
column.set_resizable(True)
|
column.set_resizable(True)
|
||||||
column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
|
column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
|
||||||
self.list.append_column(column)
|
self.list.append_column(column)
|
||||||
@ -109,7 +119,9 @@ class Leak(Gramplet):
|
|||||||
return self.top
|
return self.top
|
||||||
|
|
||||||
def main(self):
|
def main(self):
|
||||||
self.display()
|
self.label.set_text(_('Press Refresh to see initial results'))
|
||||||
|
self.model.clear()
|
||||||
|
# self.display() # We should only run this on demand
|
||||||
|
|
||||||
def _button_press(self, obj, event):
|
def _button_press(self, obj, event):
|
||||||
if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1:
|
if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1:
|
||||||
@ -120,32 +132,52 @@ class Leak(Gramplet):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def referenced_in(self):
|
def referenced_in(self):
|
||||||
model, iter = self.selection.get_selected()
|
model, _iter = self.selection.get_selected()
|
||||||
if iter is not None:
|
if _iter is not None:
|
||||||
count = model.get_value(iter, 0)
|
count = model.get_value(_iter, 0)
|
||||||
referrers = gc.get_referrers(self.modeldata[count])
|
gc.collect(2)
|
||||||
|
referrers = gc.get_referrers(self.junk[count])
|
||||||
text = ""
|
text = ""
|
||||||
for referrer in referrers:
|
for referrer in referrers:
|
||||||
|
match = ""
|
||||||
try:
|
try:
|
||||||
text += str(referrer) + '\n'
|
if referrer is not self.junk:
|
||||||
|
match = "**** "
|
||||||
|
for indx in range(len(self.junk)):
|
||||||
|
if referrer is self.junk[indx]:
|
||||||
|
match = str(indx) + ": "
|
||||||
|
break
|
||||||
|
match += str(referrer) + '\n'
|
||||||
except ReferenceError:
|
except ReferenceError:
|
||||||
pass
|
match += 'weakly-referenced object no longer exists %s'\
|
||||||
InfoDialog(_('Referrers of %d') % count, text,
|
% type(referrer)
|
||||||
parent=self.uistate.window)
|
except:
|
||||||
|
print(sys.exc_info())
|
||||||
|
text += match
|
||||||
|
InfoDialog(_('Referrers of %d') % count, text, parent=self.parent)
|
||||||
|
|
||||||
def refers_to(self):
|
def refers_to(self):
|
||||||
model, iter = self.selection.get_selected()
|
model, _iter = self.selection.get_selected()
|
||||||
if iter is not None:
|
if _iter is not None:
|
||||||
count = model.get_value(iter, 0)
|
count = model.get_value(_iter, 0)
|
||||||
referents = gc.get_referents(self.modeldata[count])
|
referents = gc.get_referents(self.junk[count])
|
||||||
text = ""
|
text = ""
|
||||||
for referent in referents:
|
for referent in referents:
|
||||||
|
match = ""
|
||||||
try:
|
try:
|
||||||
text += str(referent) + '\n'
|
match = "****: "
|
||||||
|
for indx in range(len(self.junk)):
|
||||||
|
if referent is self.junk[indx]:
|
||||||
|
match = str(indx) + ': '
|
||||||
|
break
|
||||||
|
match += str(referent) + '\n'
|
||||||
except ReferenceError:
|
except ReferenceError:
|
||||||
pass
|
match += '%s weakly-referenced object no longer'\
|
||||||
InfoDialog(_('%d refers to') % count, text,
|
' exists\n' % type(referent)
|
||||||
parent=self.uistate.window)
|
except:
|
||||||
|
print(sys.exc_info())
|
||||||
|
text += match
|
||||||
|
InfoDialog(_('%d refers to') % count, text, parent=self.parent)
|
||||||
|
|
||||||
def display(self):
|
def display(self):
|
||||||
try:
|
try:
|
||||||
@ -155,21 +187,56 @@ class Leak(Gramplet):
|
|||||||
"""
|
"""
|
||||||
Dummy.
|
Dummy.
|
||||||
"""
|
"""
|
||||||
gc.collect(2)
|
self.parent = self.top.get_toplevel()
|
||||||
|
progress = ProgressMeter(
|
||||||
|
_('Updating display...'), '', parent=self.parent)
|
||||||
self.model.clear()
|
self.model.clear()
|
||||||
count = 0
|
self.junk = []
|
||||||
if len(gc.garbage):
|
gc.collect(2)
|
||||||
for each in gc.garbage:
|
self.junk = gc.garbage
|
||||||
|
self.label.set_text(_('Uncollected Objects: %s') %
|
||||||
|
str(len(self.junk)))
|
||||||
|
progress.set_pass(_('Updating display...'), len(self.junk))
|
||||||
|
for count in range(0, len(self.junk)):
|
||||||
|
progress.step()
|
||||||
|
try:
|
||||||
|
refs = []
|
||||||
|
referrers = gc.get_referrers(self.junk[count])
|
||||||
|
for referrer in referrers:
|
||||||
|
try:
|
||||||
|
if referrer is not self.junk:
|
||||||
|
for indx in range(0, len(self.junk)):
|
||||||
|
if referrer is self.junk[indx]:
|
||||||
|
refs.append(str(indx) + ' ')
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
print(sys.exc_info())
|
||||||
|
if len(refs) > 3:
|
||||||
|
ref = ' '.join(refs[0:2]) + "..."
|
||||||
|
else:
|
||||||
|
ref = ' '.join(refs)
|
||||||
try:
|
try:
|
||||||
self.modeldata.append(each)
|
self.model.append((count, ref, str(self.junk[count])))
|
||||||
self.model.append((count, str(each)))
|
|
||||||
except DBError:
|
except DBError:
|
||||||
self.modeldata.append(each)
|
self.model.append((count, ref,
|
||||||
self.model.append((count, 'db.DB instance at %s' % id(each)))
|
'db.DB instance at %s' %
|
||||||
|
id(self.junk[count])))
|
||||||
except ReferenceError:
|
except ReferenceError:
|
||||||
pass
|
self.model.append((
|
||||||
count += 1
|
count, ref,
|
||||||
self.label.set_text(_('Uncollected Objects: %s') % str(len(gc.garbage)))
|
'weakly-referenced object no longer exists %s'
|
||||||
|
% type(self.junk[count])))
|
||||||
|
except TypeError:
|
||||||
|
self.model.append((
|
||||||
|
count, ref,
|
||||||
|
'Object cannot be displayed %s'
|
||||||
|
% type(self.junk[count])))
|
||||||
|
except:
|
||||||
|
print(sys.exc_info())
|
||||||
|
except ReferenceError:
|
||||||
|
InfoDialog(_('Reference Error'), "Refresh to correct",
|
||||||
|
parent=self.parent)
|
||||||
|
progress.close()
|
||||||
|
|
||||||
def apply_clicked(self, obj):
|
def apply_clicked(self, obj):
|
||||||
self.display()
|
self.display()
|
||||||
|
Loading…
Reference in New Issue
Block a user