On 10.10.2009 20:10, Adrian Buehlmann wrote: > On 09.10.2009 09:32, Adrian Buehlmann wrote: >> On 09.10.2009 04:32, Steve Borho wrote: >>> On Thu, Oct 8, 2009 at 9:10 PM, Yuki KODAMA <endflow....@gmail.com> wrote: >>>> On Fri, Oct 9, 2009 at 11:00, TK Soh <teekay...@gmail.com> wrote: >>>>> On Thu, Oct 8, 2009 at 6:57 PM, Steve Borho <st...@borho.org> wrote: >>>>>> FYI >>>>>> >>>>>> I realized today that we need to explicitly 'return False' from all of >>>>>> our >>>>>> idle_add() callback routines. >>>>>> If the return result evaluates as True, the callback will be called >>>>>> endlessly. >>>>> I haven't checked, but it's not documented in PyGTK doc? >>>> Documented: >>>> "If callback returns FALSE it is automatically removed from the >>>> list of event sources and will not be called again." >>>> http://pygstdocs.berlios.de/pygobject-reference/glib-functions.html#function-glib--idle-add >>> I did verify this by taking a callback and adding a print and >>> returning True.. it gets called hundreds of time per second. >>> >>>> After all, for safety, we should return False explicitly. >>> The wrapper class Adrian suggested feels too heavy weight. A lambda >>> wouldn't be too bad, but simply returning False I feel would be the >>> most clear. It's good to know Python is saving us from obvious bugs. >> IMHO, adding 'return False' randomly in functions that happen to be >> used in idle_add is confusing and suboptimal for maintenance. >> >> We could package my approach in a function for example in >> hgtk.gtklib: >> >> def idle_add(f): >> class single_call(object): >> def __init__(self, f): >> self.f = f >> def __call__(self): >> self.f() >> return False >> gobject.idle_add(single_call(f)) >> >> and then use it like this (example): >> >> gtklib.idle_add(self.realize_settings) >> >> This approach is even extensible, as it would allow to add >> parameters (which would be kept in the single_call object >> until f is called, just add an '*args'). >> >> And it can be applied to every function. > > Actually, I just found out that we do need parameters, so we would > need something like: > > diff --git a/tortoisehg/hgtk/gtklib.py b/tortoisehg/hgtk/gtklib.py > --- a/tortoisehg/hgtk/gtklib.py > +++ b/tortoisehg/hgtk/gtklib.py > @@ -604,3 +604,22 @@ def hasspellcheck(): > return True > except ImportError: > return False > + > +def idle_add(f, *args): > + '''wrap function f for gobject.idle_add, so that f is guaranteed to be > + called only once, independent of its return value''' > + > + class single_call(object): > + def __init__(self, f, args): > + self.f = f > + self.args = args > + def __call__(self): > + self.f(*args) # ignore return value of f > + return False # return False to signal: don't call us again > + > + # functions passed to gobject.idle_add must return False, or they > + # will be called repeatedly. The single_call object wraps f and always > + # returns False when called. So the return value of f doesn't matter, > + # it can even return True (which would lead to gobject.idle_add > + # calling the function again, if used without single_call). > + gobject.idle_add(single_call(f, args))
At the risk of adding more confusion, I think we should probably name that function idle_add_single_call: diff --git a/tortoisehg/hgtk/gtklib.py b/tortoisehg/hgtk/gtklib.py --- a/tortoisehg/hgtk/gtklib.py +++ b/tortoisehg/hgtk/gtklib.py @@ -604,3 +604,22 @@ def hasspellcheck(): return True except ImportError: return False + +def idle_add_single_call(f, *args): + '''wrap function f for gobject.idle_add, so that f is guaranteed to be + called only once, independent of its return value''' + + class single_call(object): + def __init__(self, f, args): + self.f = f + self.args = args + def __call__(self): + self.f(*args) # ignore return value of f + return False # return False to signal: don't call us again + + # functions passed to gobject.idle_add must return False, or they + # will be called repeatedly. The single_call object wraps f and always + # returns False when called. So the return value of f doesn't matter, + # it can even return True (which would lead to gobject.idle_add + # calling the function again, if used without single_call). + gobject.idle_add(single_call(f, args)) (To make it clear that idle_add_single_call has slightly different purpose than gobject.idle_add) Then we could use it for example like this: diff --git a/tortoisehg/hgtk/quickop.py b/tortoisehg/hgtk/quickop.py --- a/tortoisehg/hgtk/quickop.py +++ b/tortoisehg/hgtk/quickop.py @@ -35,7 +35,7 @@ class QuickOpDialog(gtk.Dialog): try: repo = hg.repository(ui.ui(), path=paths.find_root()) except hglib.RepoError: - gobject.idle_add(self.destroy) + gtklib.idle_add_single_call(self.destroy) return # Handle rm alias @@ -157,13 +157,13 @@ class QuickOpDialog(gtk.Dialog): if not len(fm): gdialog.Prompt(_('No appropriate files'), _('No files found for this operation'), self).run() - gobject.idle_add(self.destroy) + gtklib.idle_add_single_call(self.destroy) self.hide() return # prepare to show self.gobutton.grab_focus() - gobject.idle_add(self.after_init) + gtklib.idle_add_single_call(self.after_init) def after_init(self): # CmdWidget @@ -244,7 +244,7 @@ class QuickOpDialog(gtk.Dialog): pass if not list: - gobject.idle_add(self.response, gtk.RESPONSE_CLOSE) + gtklib.idle_add_single_call(self.response, gtk.RESPONSE_CLOSE) return cmdline = ['hg', self.command, '--verbose'] + list This emphasizes the fact that here the calling side controls *alone* that the function should be "idle called" only a _single time_. In these cases we don't want to defer that decision (whether it should be called again) to the called function. These functions are not prepared to nor intended to give this answer (namely the answer: "don't call me again, please" -- by returning False). Perhaps, here is the point where I need to send a complete working patch to the list, so we can see what I am talking about. Then we can decide if we drop this attempt or tweak it further. ------------------------------------------------------------------------------ Come build with us! The BlackBerry(R) Developer Conference in SF, CA is the only developer event you need to attend this year. Jumpstart your developing skills, take BlackBerry mobile applications to market and stay ahead of the curve. Join us from November 9 - 12, 2009. Register now! http://p.sf.net/sfu/devconference _______________________________________________ Tortoisehg-develop mailing list Tortoisehg-develop@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/tortoisehg-develop