I ran into a very similar issue myself this week, except that I was using Celery without Django. I found that the sender parameter only worked when the instance of the task was passed to it, rather than simply a reference to the sender class itself.
I looked into the problem a bit, and found that the sender parameter is used by celery to compare id's of a specific task, and a signal's registered sender (which is set to filter for a specific sender.)
In the celery/utils/dispatch/signals.py module, the following performs this evaluation:
def _make_id(target): # pragma: no cover
if hasattr(target, 'im_func'):
return (id(target.im_self), id(target.im_func))
return id(target)
First an id of some target object is fetched. When a sender is specified in the decorator, it is saved in tuple similar to this:
lookup_key = (_make_id(receiver), _make_id(sender))
Later, when a task fires, the _live_receivers
method is called, which essentially performs an evaluation to see whether the target sender specified in the lookup_key matches the id of the current sender (the firing task):
def _live_receivers(self, senderkey):
"""Filter sequence of receivers to get resolved, live receivers.
This checks for weak references and resolves them, then returning only
live receivers.
"""
none_senderkey = _make_id(None)
receivers = []
for (receiverkey, r_senderkey), receiver in self.receivers:
if r_senderkey == none_senderkey or r_senderkey == senderkey:
if isinstance(receiver, WEAKREF_TYPES):
# Dereference the weak reference.
receiver = receiver()
if receiver is not None:
receivers.append(receiver)
else:
receivers.append(receiver)
return receivers
Now the problem that I was experiencing was that even though I specified the task which I wanted to receive a signal on, when it was fired, the sender keys never matched.
The only way I could get this to work correctly was to register the signal from within the __init__
method of an abstract task class I had built for the specific task itself. By doing this, I was able to pass Celery the exact instance (self
), that it had registered for the task.
I have no idea if this problem was with my logic or understanding of how signals work, or if it was a bug in Celery, but I do know that passing the task instance fixed the problem and everything worked fine thereafter.
Some things to note:
- I wasn't using Django, and therefore was not using the Django-Celery extension
- I am not confident that the problem was with Celery (it is more likely that I made a mistake in logic or misunderstood something, somewhere)
- I don't know if this case even applies to you, because I am not sure how Django-Celery changes the way in which the Celery backend works.
Nonetheless, I hope that this is helpful.
Good Luck!