4333: memory leak in 3.2

svn: r16089
This commit is contained in:
Benny Malengier 2010-10-31 07:33:17 +00:00
parent fd73514004
commit 1fee4971f4
25 changed files with 416 additions and 24 deletions

View File

@ -151,4 +151,7 @@ class LRU(object):
"""
Empties LRU
"""
for obj, node in self.data.iteritems():
node.prev = None
node.next = None
self.data.clear()

View File

@ -129,6 +129,9 @@ class DbBookmarks(object):
def insert(self, pos, item):
self.bookmarks.insert(pos, item)
def close(self):
del self.bookmarks
#-------------------------------------------------------------------------
#
# GrampsDBReadCursor
@ -206,7 +209,6 @@ class DbBsddbRead(DbReadBase, Callback):
"""
Create a new DbBsddbRead instance.
"""
DbReadBase.__init__(self)
Callback.__init__(self)
@ -427,8 +429,26 @@ class DbBsddbRead(DbReadBase, Callback):
The method needs to be overridden in the derived class.
"""
pass
#remove circular dependance
self.basedb = None
#remove links to functions
self.disconnect_all()
for key in self._tables:
for subkey in self._tables[key]:
self._tables[key][subkey] = None
del self._tables[key][subkey]
self._tables[key] = None
del self._tables
## self.bookmarks = None
## self.family_bookmarks = None
## self.event_bookmarks = None
## self.place_bookmarks = None
## self.source_bookmarks = None
## self.repo_bookmarks = None
## self.media_bookmarks = None
## self.note_bookmarks = None
def is_open(self):
"""
Return 1 if the database has been opened.

View File

@ -400,6 +400,10 @@ class DbUndoBSDDB(DbUndo):
Close the undo/redo database
"""
self.undodb.close()
self.undodb = None
self.mapbase = None
self.db = None
try:
os.remove(self.path)
except OSError:

View File

@ -1013,7 +1013,6 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
def close(self):
if not self.db_is_open:
return
self.env.txn_checkpoint()
self.__close_metadata()
@ -1060,6 +1059,26 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.env = None
self.metadata = None
self.db_is_open = False
self.surname_lis = None
DbBsddbRead.close(self)
self.person_map = None
self.family_map = None
self.repository_map = None
self.note_map = None
self.place_map = None
self.source_map = None
self.media_map = None
self.event_map = None
self.tag_map = None
self.reference_map_primary_map = None
self.reference_map_referenced_map = None
self.reference_map = None
self.undo_callback = None
self.redo_callback = None
self.undo_history_callback = None
self.undodb = None
try:
clear_lock_file(self.get_save_path())

View File

@ -40,6 +40,7 @@ import sys
import types
import traceback
import inspect
import copy
log = sys.stderr.write
@ -311,8 +312,16 @@ class Callback(object):
": %s with key: %s\n" % (signal_name,
str(key)))
self.__callback_map[signal_name].remove(cb)
def disconnect_all(self):# Find the key in the callback map.
for signal_name in self.__callback_map:
keymap = copy.copy(self.__callback_map[signal_name])
for key in keymap:
self.__callback_map[signal_name].remove(key)
self.__callback_map[signal_name] = None
self.__callback_map = None
del self.__callback_map
def emit(self, signal_name, args=tuple()):
"""
Emit the signal called signal_name. The args must be a tuple of

View File

@ -38,14 +38,18 @@ import ConfigParser
import errno
import copy
try:
from ast import literal_eval as safe_eval
except:
# PYTHON2.5 COMPATIBILITY: no ast present
# not as safe as literal_eval, but works for python2.5:
def safe_eval(exp):
def safe_eval(exp):
# restrict eval to empty environment
return eval(exp, {})
##try:
## from ast import literal_eval as safe_eval
## # this leaks memory !!
##except:
## # PYTHON2.5 COMPATIBILITY: no ast present
## # not as safe as literal_eval, but works for python2.5:
## def safe_eval(exp):
## # restrict eval to empty environment
## return eval(exp, {})
#---------------------------------------------------------------
#

View File

@ -78,6 +78,8 @@ class NameEmbedList(GroupEmbeddedList):
]
def __init__(self, dbstate, uistate, track, data, person, callback):
"""callback is the function to call when preferred name changes
on the namelist """
self.data = data
self.person = person
self.callback = callback
@ -86,6 +88,14 @@ class NameEmbedList(GroupEmbeddedList):
NameModel, move_buttons=True)
self.tree.expand_all()
def _cleanup_on_exit(self):
"""Unset all things that can block garbage collection.
Finalize rest
"""
self.person = None
self.callback = None
self.data = None
def get_data(self):
return ([self.person.get_primary_name()],
self.data)

View File

@ -448,6 +448,15 @@ class EditFamily(EditPrimary):
else:
self.add_parent = False
def _cleanup_on_exit(self):
"""Unset all things that can block garbage collection.
Finalize rest
"""
#FIXME, we rebind show_all below, this prevents garbage collection of
# the dialog, fix the rebind
self.window.show_all = None
EditPrimary._cleanup_on_exit(self)
def empty_object(self):
return gen.lib.Family()

View File

@ -267,16 +267,17 @@ class EditName(EditSecondary):
notebook = self.top.get_object("notebook")
self._add_tab(notebook, self.gennam)
self.track_ref_for_deletion("gennam")
self.srcref_list = self._add_tab(
notebook,
SourceEmbedList(self.dbstate,self.uistate,self.track,self.obj))
self.srcref_list = SourceEmbedList(self.dbstate,self.uistate,self.track,self.obj)
self._add_tab(notebook, self.srcref_list)
self.track_ref_for_deletion("srcref_list")
self.note_tab = self._add_tab(
notebook,
NoteTab(self.dbstate, self.uistate, self.track,
self.note_tab = NoteTab(self.dbstate, self.uistate, self.track,
self.obj.get_note_list(),
notetype=NoteType.PERSONNAME))
notetype=NoteType.PERSONNAME)
self._add_tab(notebook, self.note_tab)
self.track_ref_for_deletion("note_tab")
self._setup_notebook_tabs( notebook)
@ -442,6 +443,7 @@ class EditName(EditSecondary):
if closeit:
if self.callback:
self.callback(self.obj)
self.callback = None
self.close()
def _cleanup_on_exit(self):

View File

@ -854,6 +854,7 @@ class EditPerson(EditPrimary):
self.close()
if self.callback:
self.callback(self.obj)
self.callback = None
def _edit_name_clicked(self, obj):
"""
@ -1017,7 +1018,23 @@ class EditPerson(EditPrimary):
return child_ref_list
def _cleanup_on_exit(self):
pass
"""Unset all things that can block garbage collection.
Finalize rest
"""
## self.private.destroy()
## self.gender.destroy()
## self.ntype_field.destroy()
## self.given.destroy()
## self.call.destroy()
## self.title.destroy()
## self.suffix.destroy()
## self.nick.destroy()
## self.surname_field.destroy()
## self.prefix.destroy()
## self.ortype_field.destroy()
## self.tags.destroy()
## self.gid.destroy()
EditPrimary._cleanup_on_exit(self)
#config.save()

View File

@ -151,7 +151,13 @@ class EditPrimary(ManagedWindow.ManagedWindow, DbGUIElement):
return page
def _cleanup_on_exit(self):
pass
"""Unset all things that can block garbage collection.
Finalize rest
"""
for tab in self.__tabs:
if hasattr(tab, '_cleanup_on_exit'):
tab._cleanup_on_exit()
self.__tabs = None
def object_is_empty(self):
return cmp(self.obj.serialize()[1:],
@ -173,7 +179,12 @@ class EditPrimary(ManagedWindow.ManagedWindow, DbGUIElement):
self._cleanup_db_connects()
self.dbstate.disconnect(self.dbstate_connect_key)
self._cleanup_on_exit()
self.get_from_handle = None
self.get_from_gramps_id = None
ManagedWindow.ManagedWindow.close(self)
self.dbstate = None
self.uistate = None
self.db = None
def _cleanup_db_connects(self):
"""
@ -236,7 +247,9 @@ class EditPrimary(ManagedWindow.ManagedWindow, DbGUIElement):
self.obj.serialize()[1:]) != 0
def save(self, *obj):
pass
""" Save changes and close. Inheriting classes must implement this
"""
self.close()
def set_contexteventbox(self, eventbox):
"""Set the contextbox that grabs button presses if not grabbed

View File

@ -107,7 +107,19 @@ class EditSecondary(ManagedWindow.ManagedWindow, DbGUIElement):
return page
def _cleanup_on_exit(self):
pass
"""Unset all things that can block garbage collection.
Finalize rest
"""
for tab in self.__tabs:
if hasattr(tab, '_cleanup_on_exit'):
tab._cleanup_on_exit()
self.__tabs = None
self.dbstate = None
self.uistate = None
self.obj = obj
self.db = None
self.callman.database = None
self.callman = None
def define_ok_button(self,button,function):
button.connect('clicked',function)

View File

@ -262,6 +262,9 @@ class ListView(NavigationView):
filter_info = (False, value, value[0] in self.exact_search())
if self.dirty or not self.model:
if self.model:
self.list.set_model(None)
self.model.destroy()
self.model = self.make_model(self.dbstate.db, self.sort_col,
search=filter_info,
sort_map=self.column_order())

View File

@ -99,6 +99,17 @@ class EventModel(FlatBaseModel):
FlatBaseModel.__init__(self, db, scol, order, tooltip_column=8,
search=search, skip=skip, sort_map=sort_map)
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
self.db = None
self.gen_cursor = None
self.map = None
self.fmap = None
self.smap = None
FlatBaseModel.destroy(self)
def on_get_n_columns(self):
return len(self.fmap)+1

View File

@ -89,6 +89,17 @@ class FamilyModel(FlatBaseModel):
FlatBaseModel.__init__(self, db, scol, order, tooltip_column=9,
search=search, skip=skip, sort_map=sort_map)
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
self.db = None
self.gen_cursor = None
self.map = None
self.fmap = None
self.smap = None
FlatBaseModel.destroy(self)
def color_column(self):
"""
Return the color column.

View File

@ -122,6 +122,14 @@ class FlatNodeMap(object):
self._reverse = False
self.__corr = (0, 1)
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
self._index2hndl = None
self._fullhndl = None
self._hndl2index = None
def set_path_map(self, index2hndllist, fullhndllist, identical=True,
reverse=False):
"""
@ -408,6 +416,18 @@ class FlatBaseModel(gtk.GenericTreeModel):
_LOG.debug(self.__class__.__name__ + ' __init__ ' +
str(time.clock() - cput) + ' sec')
def destroy(self):
"""
Unset all elements that prevent garbage collection
"""
self.db = None
self.sort_func = None
if self.node_map:
self.node_map.destroy()
self.node_map = None
self.rebuild_data = None
self.search = None
def set_search(self, search):
"""
Change the search function that filters the data in the model.

View File

@ -89,6 +89,17 @@ class MediaModel(FlatBaseModel):
FlatBaseModel.__init__(self, db, scol, order, tooltip_column=9,
search=search, skip=skip, sort_map=sort_map)
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
self.db = None
self.gen_cursor = None
self.map = None
self.fmap = None
self.smap = None
FlatBaseModel.destroy(self)
def color_column(self):
"""
Return the color column.

View File

@ -79,6 +79,17 @@ class NoteModel(FlatBaseModel):
FlatBaseModel.__init__(self, db, scol, order, search=search,
skip=skip, sort_map=sort_map)
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
self.db = None
self.gen_cursor = None
self.map = None
self.fmap = None
self.smap = None
FlatBaseModel.destroy(self)
def color_column(self):
"""
Return the color column.

View File

@ -147,6 +147,17 @@ class PeopleBaseModel(object):
self.lru_bdate = LRU(PeopleBaseModel._CACHE_SIZE)
self.lru_ddate = LRU(PeopleBaseModel._CACHE_SIZE)
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
self.db = None
self.gen_cursor = None
self.map = None
self.fmap = None
self.smap = None
self.clear_local_cache()
def color_column(self):
"""
Return the color column.
@ -461,7 +472,6 @@ class PersonListModel(PeopleBaseModel, FlatBaseModel):
"""
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None,
skip=set(), sort_map=None):
PeopleBaseModel.__init__(self, db)
FlatBaseModel.__init__(self, db, search=search, skip=skip,
tooltip_column=13,
@ -471,6 +481,13 @@ class PersonListModel(PeopleBaseModel, FlatBaseModel):
""" Clear the LRU cache """
PeopleBaseModel.clear_local_cache(self, handle)
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
PeopleBaseModel.destroy(self)
FlatBaseModel.destroy(self)
class PersonTreeModel(PeopleBaseModel, TreeBaseModel):
"""
Hierarchical people model.
@ -482,6 +499,15 @@ class PersonTreeModel(PeopleBaseModel, TreeBaseModel):
TreeBaseModel.__init__(self, db, 13, search=search, skip=skip,
scol=scol, order=order, sort_map=sort_map)
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
PeopleBaseModel.destroy(self)
self.hmap = None
self.number_items = None
TreeBaseModel.destroy(self)
def _set_base_data(self):
"""See TreeBaseModel, we also set some extra lru caches
"""

View File

@ -118,6 +118,16 @@ class PlaceBaseModel(object):
self.column_handle,
]
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
self.db = None
self.gen_cursor = None
self.map = None
self.fmap = None
self.smap = None
def on_get_n_columns(self):
return len(self.fmap)+1
@ -224,6 +234,13 @@ class PlaceListModel(PlaceBaseModel, FlatBaseModel):
FlatBaseModel.__init__(self, db, scol, order, tooltip_column=15,
search=search, skip=skip, sort_map=sort_map)
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
PlaceBaseModel.destroy(self)
FlatBaseModel.destroy(self)
def column_name(self, data):
return unicode(data[2])
@ -246,6 +263,15 @@ class PlaceTreeModel(PlaceBaseModel, TreeBaseModel):
nrgroups = 3,
group_can_have_handle = True)
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
PlaceBaseModel.destroy(self)
self.hmap = None
self.number_items = None
TreeBaseModel.destroy(self)
def _set_base_data(self):
"""See TreeBaseModel, for place, most have been set in init of
PlaceBaseModel

View File

@ -95,6 +95,18 @@ class RepositoryModel(FlatBaseModel):
FlatBaseModel.__init__(self, db, scol, order, tooltip_column=14,
search=search, skip=skip, sort_map=sort_map)
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
self.db = None
self.gen_cursor = None
self.get_handles = None
self.map = None
self.fmap = None
self.smap = None
FlatBaseModel.destroy(self)
def on_get_n_columns(self):
return len(self.fmap)+1

View File

@ -76,6 +76,17 @@ class SourceModel(FlatBaseModel):
FlatBaseModel.__init__(self,db,scol, order,tooltip_column=7,search=search,
skip=skip, sort_map=sort_map)
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
self.db = None
self.gen_cursor = None
self.map = None
self.fmap = None
self.smap = None
FlatBaseModel.destroy(self)
def on_get_n_columns(self):
return len(self.fmap)+1

View File

@ -173,6 +173,12 @@ class NodeMap(object):
"""
def __init__(self):
self.id2node = {}
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
self.id2node.clear()
def add_node(self, node):
"""
@ -310,6 +316,20 @@ class TreeBaseModel(gtk.GenericTreeModel):
_LOG.debug(self.__class__.__name__ + ' __init__ ' +
str(time.clock() - cput) + ' sec')
def destroy(self):
"""
Unset all elements that prevent garbage collection
"""
self.db = None
self.sort_func = None
if self.nodemap:
self.nodemap.destroy()
self.nodemap = None
self.rebuild_data = None
self._build_data = None
self.search = None
self.clear_cache()
def _set_base_data(self):
"""
This method must be overwritten in the inheriting class, setting

View File

@ -72,6 +72,12 @@ class IconButton(gtk.Button):
self.connect('button-press-event', func, handle)
self.connect('key-press-event', func, handle)
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## gtk.Button.destroy(self)
#-------------------------------------------------------------------------
#
# WarnButton class
@ -91,6 +97,13 @@ class WarnButton(gtk.Button):
self.func = None
self.hide()
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## self.func = None
## gtk.Button.destroy(self)
def on_clicked(self, func):
self.connect('button-press-event', self._button_press)
self.func = func
@ -112,6 +125,12 @@ class SimpleButton(gtk.Button):
self.add(gtk.image_new_from_stock(image, gtk.ICON_SIZE_BUTTON))
self.connect('clicked', func)
self.show()
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## gtk.Button.destroy(self)
#-------------------------------------------------------------------------
#
@ -127,6 +146,12 @@ class PrivacyButton(object):
self.set_active(obj.get_privacy())
self.button.set_sensitive(not readonly)
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## self.obj = None
def set_sensitive(self, val):
self.button.set_sensitive(val)

View File

@ -76,6 +76,14 @@ class MonitoredCheckbox(object):
self.set_val(obj.get_active())
if self.on_toggle:
self.on_toggle(self.get_val())
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## self.set_val = None
## self.get_val = None
## self.obj = None
#-------------------------------------------------------------------------
#
@ -99,6 +107,14 @@ class MonitoredEntry(object):
if autolist:
AutoComp.fill_entry(obj, autolist)
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## self.set_val = None
## self.get_val = None
## self.obj = None
def reinit(self, set_val, get_val):
self.set_val = set_val
self.get_val = get_val
@ -166,6 +182,14 @@ class MonitoredSpinButton(object):
if autolist:
AutoComp.fill_entry(obj,autolist)
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## self.set_val = None
## self.get_val = None
## self.obj = None
def reinit(self, set_val, get_val):
"""
Reinitialize class with the specified callback functions.
@ -270,6 +294,14 @@ class MonitoredText(object):
self.buf.connect('changed', self.on_change)
obj.set_editable(not read_only)
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## self.set_val = None
## self.get_val = None
## self.buf = None
def on_change(self, obj):
s, e = self.buf.get_bounds()
self.set_val(unicode(self.buf.get_text(s, e, False)))
@ -301,6 +333,14 @@ class MonitoredType(object):
self.obj.set_sensitive(not readonly)
self.obj.connect('changed', self.on_change)
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## self.set_val = None
## self.get_val = None
## self.obj = None
def reinit(self, set_val, get_val):
self.set_val = set_val
self.get_val = get_val
@ -369,6 +409,14 @@ class MonitoredDataType(object):
self.obj.set_sensitive(not readonly)
self.obj.connect('changed', self.on_change)
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## self.set_val = None
## self.get_val = None
## self.obj = None
def reinit(self, set_val, get_val):
self.set_val = set_val
self.get_val = get_val
@ -410,6 +458,14 @@ class MonitoredMenu(object):
self.obj.connect('changed', self.on_change)
self.obj.set_sensitive(not readonly)
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## self.set_val = None
## self.get_val = None
## self.obj = None
def force(self, value):
self.obj.set_active(value)
@ -465,6 +521,15 @@ class MonitoredStrMenu(object):
self.obj.connect('changed', self.on_change)
self.obj.set_sensitive(not readonly)
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## self.set_val = None
## self.get_val = None
## self.obj = None
## self.model = None
def on_change(self, obj):
self.set_val(self.data[obj.get_active()])
@ -531,6 +596,15 @@ class MonitoredComboSelectedEntry(object):
#set correct editable
self.enable(not read_only)
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## self.set_val_list = None
## self.get_val_list = None
## self.objcombo = None
## self.objentry = None
def __fill(self):
"""
Fill combo with data
@ -643,6 +717,15 @@ class MonitoredTagList(object):
self._display()
## def destroy(self):
## """
## Unset all elements that can prevent garbage collection
## """
## self.uistate = None
## self.track = None
## self.db = None
## self.set_list = None
def _display(self):
"""
Display the tag list.