Skip to content

Commit b1d9219

Browse files
jonashaagOmer Katz
authored andcommitted
* Maybe fix celery#4699 * Add unit test for celery#4699 * Fix test on PyPy * Factor WeakMethod logic and add comment
1 parent ea5a5bf commit b1d9219

File tree

2 files changed

+34
-2
lines changed

2 files changed

+34
-2
lines changed

celery/utils/dispatch/signal.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,28 @@ def _make_id(target): # pragma: no cover
3737
return id(target)
3838

3939

40+
def _boundmethod_safe_weakref(obj):
41+
"""Get weakref constructor appropriate for `obj`. `obj` may be a bound method.
42+
43+
Bound method objects must be special-cased because they're usually garbage
44+
collected immediately, even if the instance they're bound to persists.
45+
46+
Returns:
47+
a (weakref constructor, main object) tuple. `weakref constructor` is
48+
either :class:`weakref.ref` or :class:`weakref.WeakMethod`. `main
49+
object` is the instance that `obj` is bound to if it is a bound method;
50+
otherwise `main object` is simply `obj.
51+
"""
52+
try:
53+
obj.__func__
54+
obj.__self__
55+
# Bound method
56+
return WeakMethod, obj.__self__
57+
except AttributeError:
58+
# Not a bound method
59+
return weakref.ref, obj
60+
61+
4062
def _make_lookup_key(receiver, sender, dispatch_uid):
4163
if dispatch_uid:
4264
return (dispatch_uid, _make_id(sender))
@@ -183,8 +205,7 @@ def _connect_signal(self, receiver, sender, weak, dispatch_uid):
183205
lookup_key = _make_lookup_key(receiver, sender, dispatch_uid)
184206

185207
if weak:
186-
ref = weakref.ref
187-
receiver_object = receiver
208+
ref, receiver_object = _boundmethod_safe_weakref(receiver)
188209
if PY3:
189210
receiver = ref(receiver)
190211
weakref.finalize(receiver_object, self._remove_receiver)

t/unit/utils/test_dispatcher.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,14 @@ def test_retry_with_dispatch_uid(self):
173173
assert a_signal.receivers[0][0][0] == uid
174174
a_signal.disconnect(receiver_1_arg, sender=self, dispatch_uid=uid)
175175
self._testIsClean(a_signal)
176+
177+
def test_boundmethod(self):
178+
a = Callable()
179+
a_signal.connect(a.a, sender=self)
180+
expected = [(a.a, 'test')]
181+
garbage_collect()
182+
result = a_signal.send(sender=self, val='test')
183+
assert result == expected
184+
del a, result, expected
185+
garbage_collect()
186+
self._testIsClean(a_signal)

0 commit comments

Comments
 (0)