Skip to content

Commit 3b2a186

Browse files
author
Paul McCarthy
committed
Merge branch 'bf/notifier' into 'main'
BF: Fix to `Notifier` class in its handling of garbage-collected callback functions See merge request fsl/fslpy!470
2 parents eae1711 + 3282bbb commit 3b2a186

File tree

3 files changed

+43
-7
lines changed

3 files changed

+43
-7
lines changed

CHANGELOG.rst

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Fixed
1010

1111
* Adjusted the :func:`.featdesign.loadFEATDesignFile` function to handle missing
1212
values (!469).
13+
* Fix to the :class:`.Notifier` class involving handling of callback functions
14+
that have been garbage-collected (!470).
1315

1416

1517
3.22.0 (Monday 17th February 2025)

fsl/tests/test_notifier.py

+28
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,31 @@ def topic_callback(*a):
204204
t.notify(topic='topic')
205205
assert default_called[0] == 14
206206
assert topic_called[ 0] == 6
207+
208+
209+
# Make sure there is no error
210+
# if a callback function is GC'd
211+
# fsl/fslpy!470
212+
def test_gc():
213+
214+
class Thing(notifier.Notifier):
215+
pass
216+
217+
t = Thing()
218+
219+
called = []
220+
221+
def callback(thing, topic, value):
222+
called.append((thing, topic, value))
223+
224+
t.register('callback', callback)
225+
226+
t.notify()
227+
assert called == [(t, None, None)]
228+
229+
called[:] = []
230+
callback = None
231+
del callback
232+
233+
t.notify()
234+
assert called == []

fsl/utils/notifier.py

+13-7
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,12 @@ def expectsArguments(self):
6666
positional arguments - see :meth:`Notifier.register` for details.
6767
"""
6868

69-
func = self.callback
69+
func = self.callback
70+
71+
# the function may have been GC'd
72+
if func is None:
73+
return False
74+
7075
spec = inspect.signature(func)
7176
posargs = 0
7277
varargs = False
@@ -377,22 +382,23 @@ def notify(self, *args, **kwargs):
377382
callback = listener.callback
378383
name = listener.name
379384

380-
if listener.expectsArguments: args = (self, topic, value)
381-
else: args = ()
382-
383385
# The callback, or the owner of the
384386
# callback function may have been
385387
# gc'd - remove it if this is the case.
386388
if callback is None:
387389
log.debug('Listener %s has been gc\'d - '
388390
'removing from list', name)
389391
self.__listeners[listener.topic].pop(name)
392+
continue
390393

391-
elif not listener.enabled:
394+
if not listener.enabled:
392395
continue
393396

394-
elif listener.runOnIdle: idle.idle(callback, *args)
395-
else: callback( *args)
397+
if listener.expectsArguments: args = (self, topic, value)
398+
else: args = ()
399+
400+
if listener.runOnIdle: idle.idle(callback, *args)
401+
else: callback( *args)
396402

397403

398404
def __getListeners(self, topic):

0 commit comments

Comments
 (0)