2010-11-21 22:54:23 +08:00
|
|
|
import threading
|
2014-01-07 08:11:13 +08:00
|
|
|
import weakref
|
2006-05-02 09:31:56 +08:00
|
|
|
|
2015-06-11 05:24:04 +08:00
|
|
|
from django.utils.inspect import func_accepts_kwargs
|
2006-05-02 09:31:56 +08:00
|
|
|
|
2013-08-20 11:14:21 +08:00
|
|
|
|
2008-08-06 23:32:46 +08:00
|
|
|
def _make_id(target):
|
2012-05-13 04:32:43 +08:00
|
|
|
if hasattr(target, '__func__'):
|
|
|
|
return (id(target.__self__), id(target.__func__))
|
2008-08-06 23:32:46 +08:00
|
|
|
return id(target)
|
2016-11-13 01:11:23 +08:00
|
|
|
|
|
|
|
|
2012-12-16 09:53:25 +08:00
|
|
|
NONE_ID = _make_id(None)
|
|
|
|
|
|
|
|
# A marker for caching
|
|
|
|
NO_RECEIVERS = object()
|
2006-05-02 09:31:56 +08:00
|
|
|
|
2013-08-20 11:14:21 +08:00
|
|
|
|
2017-01-19 15:39:46 +08:00
|
|
|
class Signal:
|
2009-05-21 00:13:55 +08:00
|
|
|
"""
|
|
|
|
Base class for all signals
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2008-08-06 23:32:46 +08:00
|
|
|
Internal attributes:
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
receivers
|
2014-03-02 22:25:53 +08:00
|
|
|
{ receiverkey (id) : weakref(receiver) }
|
2008-08-06 23:32:46 +08:00
|
|
|
"""
|
2012-12-16 09:53:25 +08:00
|
|
|
def __init__(self, providing_args=None, use_caching=False):
|
2009-05-21 00:13:55 +08:00
|
|
|
"""
|
|
|
|
Create a new signal.
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
providing_args
|
|
|
|
A list of the arguments this signal can pass along in a send() call.
|
2008-08-06 23:32:46 +08:00
|
|
|
"""
|
|
|
|
self.receivers = []
|
|
|
|
if providing_args is None:
|
|
|
|
providing_args = []
|
|
|
|
self.providing_args = set(providing_args)
|
2010-11-21 22:54:23 +08:00
|
|
|
self.lock = threading.Lock()
|
2012-12-16 09:53:25 +08:00
|
|
|
self.use_caching = use_caching
|
|
|
|
# For convenience we create empty caches even if they are not used.
|
|
|
|
# A note about caching: if use_caching is defined, then for each
|
|
|
|
# distinct sender we cache the receivers that sender has in
|
|
|
|
# 'sender_receivers_cache'. The cache is cleaned when .connect() or
|
|
|
|
# .disconnect() is called and populated on send().
|
2013-08-20 11:14:21 +08:00
|
|
|
self.sender_receivers_cache = weakref.WeakKeyDictionary() if use_caching else {}
|
2014-02-05 02:19:14 +08:00
|
|
|
self._dead_receivers = False
|
2008-08-06 23:32:46 +08:00
|
|
|
|
|
|
|
def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
|
2009-05-21 00:13:55 +08:00
|
|
|
"""
|
|
|
|
Connect receiver to sender for signal.
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
Arguments:
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
receiver
|
|
|
|
A function or an instance method which is to receive signals.
|
|
|
|
Receivers must be hashable objects.
|
2008-08-06 23:32:46 +08:00
|
|
|
|
2014-12-02 10:53:15 +08:00
|
|
|
If weak is True, then receiver must be weak referenceable.
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
Receivers must be able to accept keyword arguments.
|
2008-08-06 23:32:46 +08:00
|
|
|
|
2015-01-24 01:58:57 +08:00
|
|
|
If a receiver is connected with a dispatch_uid argument, it
|
|
|
|
will not be added if another receiver was already connected
|
|
|
|
with that dispatch_uid.
|
2006-07-22 04:48:17 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
sender
|
2010-11-03 02:30:00 +08:00
|
|
|
The sender to which the receiver should respond. Must either be
|
2016-05-30 20:17:42 +08:00
|
|
|
a Python object, or None to receive events from any sender.
|
2008-08-06 23:32:46 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
weak
|
2010-11-03 02:30:00 +08:00
|
|
|
Whether to use weak references to the receiver. By default, the
|
2009-05-21 00:13:55 +08:00
|
|
|
module will attempt to use weak references to the receiver
|
|
|
|
objects. If this parameter is false, then strong references will
|
|
|
|
be used.
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
dispatch_uid
|
|
|
|
An identifier used to uniquely identify a particular instance of
|
|
|
|
a receiver. This will usually be a string, though it may be
|
|
|
|
anything hashable.
|
2008-08-06 23:32:46 +08:00
|
|
|
"""
|
|
|
|
from django.conf import settings
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2008-08-26 02:24:05 +08:00
|
|
|
# If DEBUG is on, check that we got a good receiver
|
2013-11-24 01:18:42 +08:00
|
|
|
if settings.configured and settings.DEBUG:
|
2008-08-26 02:24:05 +08:00
|
|
|
assert callable(receiver), "Signal receivers must be callable."
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2008-08-26 02:24:05 +08:00
|
|
|
# Check for **kwargs
|
2015-06-11 05:24:04 +08:00
|
|
|
if not func_accepts_kwargs(receiver):
|
|
|
|
raise ValueError("Signal receivers must accept keyword arguments (**kwargs).")
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2008-08-06 23:32:46 +08:00
|
|
|
if dispatch_uid:
|
|
|
|
lookup_key = (dispatch_uid, _make_id(sender))
|
2006-07-22 04:48:17 +08:00
|
|
|
else:
|
2014-02-06 03:45:57 +08:00
|
|
|
lookup_key = (_make_id(receiver), _make_id(sender))
|
2006-05-02 09:31:56 +08:00
|
|
|
|
2008-08-06 23:32:46 +08:00
|
|
|
if weak:
|
2014-01-07 08:11:13 +08:00
|
|
|
ref = weakref.ref
|
2014-02-06 03:45:57 +08:00
|
|
|
receiver_object = receiver
|
2014-01-07 08:11:13 +08:00
|
|
|
# Check for bound methods
|
|
|
|
if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'):
|
2016-12-01 18:38:01 +08:00
|
|
|
ref = weakref.WeakMethod
|
2014-02-06 03:45:57 +08:00
|
|
|
receiver_object = receiver.__self__
|
2016-12-01 18:38:01 +08:00
|
|
|
receiver = ref(receiver)
|
|
|
|
weakref.finalize(receiver_object, self._remove_receiver)
|
2006-05-02 09:31:56 +08:00
|
|
|
|
2012-06-23 23:11:15 +08:00
|
|
|
with self.lock:
|
2014-02-05 02:19:14 +08:00
|
|
|
self._clear_dead_receivers()
|
2017-12-27 02:44:12 +08:00
|
|
|
if not any(r_key == lookup_key for r_key, _ in self.receivers):
|
2014-02-06 03:45:57 +08:00
|
|
|
self.receivers.append((lookup_key, receiver))
|
2013-08-20 11:14:21 +08:00
|
|
|
self.sender_receivers_cache.clear()
|
2006-07-22 04:48:17 +08:00
|
|
|
|
2016-11-07 22:46:42 +08:00
|
|
|
def disconnect(self, receiver=None, sender=None, dispatch_uid=None):
|
2008-08-06 23:32:46 +08:00
|
|
|
"""
|
2009-05-21 00:13:55 +08:00
|
|
|
Disconnect receiver from sender for signal.
|
2006-07-22 04:48:17 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
If weak references are used, disconnect need not be called. The receiver
|
2017-01-25 05:23:56 +08:00
|
|
|
will be removed from dispatch automatically.
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
Arguments:
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
receiver
|
|
|
|
The registered receiver to disconnect. May be none if
|
|
|
|
dispatch_uid is specified.
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
sender
|
|
|
|
The registered sender to disconnect
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
dispatch_uid
|
|
|
|
the unique identifier of the receiver to disconnect
|
|
|
|
"""
|
2008-08-06 23:32:46 +08:00
|
|
|
if dispatch_uid:
|
|
|
|
lookup_key = (dispatch_uid, _make_id(sender))
|
|
|
|
else:
|
|
|
|
lookup_key = (_make_id(receiver), _make_id(sender))
|
2012-06-23 23:11:15 +08:00
|
|
|
|
2015-01-02 22:25:33 +08:00
|
|
|
disconnected = False
|
2012-06-23 23:11:15 +08:00
|
|
|
with self.lock:
|
2014-02-05 02:19:14 +08:00
|
|
|
self._clear_dead_receivers()
|
2014-12-13 21:04:36 +08:00
|
|
|
for index in range(len(self.receivers)):
|
2014-02-06 03:45:57 +08:00
|
|
|
(r_key, _) = self.receivers[index]
|
2010-11-21 22:54:23 +08:00
|
|
|
if r_key == lookup_key:
|
2015-01-02 22:25:33 +08:00
|
|
|
disconnected = True
|
2010-11-21 22:54:23 +08:00
|
|
|
del self.receivers[index]
|
|
|
|
break
|
2013-08-20 11:14:21 +08:00
|
|
|
self.sender_receivers_cache.clear()
|
2015-01-02 22:25:33 +08:00
|
|
|
return disconnected
|
2006-07-22 04:48:17 +08:00
|
|
|
|
2012-09-20 08:18:19 +08:00
|
|
|
def has_listeners(self, sender=None):
|
2012-12-16 09:53:25 +08:00
|
|
|
return bool(self._live_receivers(sender))
|
2012-09-20 08:18:19 +08:00
|
|
|
|
2008-08-06 23:32:46 +08:00
|
|
|
def send(self, sender, **named):
|
2009-05-21 00:13:55 +08:00
|
|
|
"""
|
|
|
|
Send signal from sender to all connected receivers.
|
2006-07-22 04:48:17 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
If any receiver raises an error, the error propagates back through send,
|
2016-01-12 09:46:07 +08:00
|
|
|
terminating the dispatch loop. So it's possible that all receivers
|
|
|
|
won't be called if an error is raised.
|
2009-05-21 00:13:55 +08:00
|
|
|
|
|
|
|
Arguments:
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
sender
|
2016-01-12 09:46:07 +08:00
|
|
|
The sender of the signal. Either a specific object or None.
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
named
|
|
|
|
Named arguments which will be passed to receivers.
|
2006-05-02 09:31:56 +08:00
|
|
|
|
2017-01-25 05:23:56 +08:00
|
|
|
Return a list of tuple pairs [(receiver, response), ... ].
|
2008-08-06 23:32:46 +08:00
|
|
|
"""
|
2012-12-16 09:53:25 +08:00
|
|
|
if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
|
2016-11-22 22:30:14 +08:00
|
|
|
return []
|
2006-05-02 09:31:56 +08:00
|
|
|
|
2016-11-22 22:30:14 +08:00
|
|
|
return [
|
|
|
|
(receiver, receiver(signal=self, sender=sender, **named))
|
|
|
|
for receiver in self._live_receivers(sender)
|
|
|
|
]
|
2007-02-26 11:17:04 +08:00
|
|
|
|
2008-08-06 23:32:46 +08:00
|
|
|
def send_robust(self, sender, **named):
|
|
|
|
"""
|
2009-05-21 00:13:55 +08:00
|
|
|
Send signal from sender to all connected receivers catching errors.
|
2006-07-22 04:48:17 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
Arguments:
|
2012-07-21 00:53:11 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
sender
|
2018-10-09 21:26:07 +08:00
|
|
|
The sender of the signal. Can be any Python object (normally one
|
2009-05-21 00:13:55 +08:00
|
|
|
registered with a connect if you actually want something to
|
|
|
|
occur).
|
|
|
|
|
|
|
|
named
|
|
|
|
Named arguments which will be passed to receivers. These
|
|
|
|
arguments must be a subset of the argument names defined in
|
|
|
|
providing_args.
|
|
|
|
|
2017-01-25 05:23:56 +08:00
|
|
|
Return a list of tuple pairs [(receiver, response), ... ].
|
2009-05-21 00:13:55 +08:00
|
|
|
|
2010-11-03 02:30:00 +08:00
|
|
|
If any receiver raises an error (specifically any subclass of
|
2017-01-25 05:23:56 +08:00
|
|
|
Exception), return the error instance as the result for that receiver.
|
2009-05-21 00:13:55 +08:00
|
|
|
"""
|
2012-12-16 09:53:25 +08:00
|
|
|
if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
|
2016-11-22 22:30:14 +08:00
|
|
|
return []
|
2006-07-22 04:48:17 +08:00
|
|
|
|
2008-08-06 23:32:46 +08:00
|
|
|
# Call each receiver with whatever arguments it can accept.
|
|
|
|
# Return a list of tuple pairs [(receiver, response), ... ].
|
2016-11-22 22:30:14 +08:00
|
|
|
responses = []
|
2012-12-16 09:53:25 +08:00
|
|
|
for receiver in self._live_receivers(sender):
|
2008-08-06 23:32:46 +08:00
|
|
|
try:
|
|
|
|
response = receiver(signal=self, sender=sender, **named)
|
2012-04-29 00:09:37 +08:00
|
|
|
except Exception as err:
|
2008-08-06 23:32:46 +08:00
|
|
|
responses.append((receiver, err))
|
|
|
|
else:
|
|
|
|
responses.append((receiver, response))
|
|
|
|
return responses
|
|
|
|
|
2014-02-05 02:19:14 +08:00
|
|
|
def _clear_dead_receivers(self):
|
|
|
|
# Note: caller is assumed to hold self.lock.
|
|
|
|
if self._dead_receivers:
|
|
|
|
self._dead_receivers = False
|
2018-01-04 02:24:02 +08:00
|
|
|
self.receivers = [
|
|
|
|
r for r in self.receivers
|
|
|
|
if not(isinstance(r[1], weakref.ReferenceType) and r[1]() is None)
|
|
|
|
]
|
2014-02-05 02:19:14 +08:00
|
|
|
|
2012-12-16 09:53:25 +08:00
|
|
|
def _live_receivers(self, sender):
|
2009-05-21 00:13:55 +08:00
|
|
|
"""
|
|
|
|
Filter sequence of receivers to get resolved, live receivers.
|
2008-08-06 23:32:46 +08:00
|
|
|
|
2009-05-21 00:13:55 +08:00
|
|
|
This checks for weak references and resolves them, then returning only
|
|
|
|
live receivers.
|
2008-08-06 23:32:46 +08:00
|
|
|
"""
|
2012-12-16 09:53:25 +08:00
|
|
|
receivers = None
|
2014-02-05 02:19:14 +08:00
|
|
|
if self.use_caching and not self._dead_receivers:
|
2012-12-16 09:53:25 +08:00
|
|
|
receivers = self.sender_receivers_cache.get(sender)
|
|
|
|
# We could end up here with NO_RECEIVERS even if we do check this case in
|
|
|
|
# .send() prior to calling _live_receivers() due to concurrent .send() call.
|
|
|
|
if receivers is NO_RECEIVERS:
|
|
|
|
return []
|
|
|
|
if receivers is None:
|
|
|
|
with self.lock:
|
2014-02-05 02:19:14 +08:00
|
|
|
self._clear_dead_receivers()
|
2012-12-16 09:53:25 +08:00
|
|
|
senderkey = _make_id(sender)
|
|
|
|
receivers = []
|
2014-02-06 03:45:57 +08:00
|
|
|
for (receiverkey, r_senderkey), receiver in self.receivers:
|
2012-12-16 09:53:25 +08:00
|
|
|
if r_senderkey == NONE_ID or r_senderkey == senderkey:
|
2009-05-21 00:13:55 +08:00
|
|
|
receivers.append(receiver)
|
2012-12-16 09:53:25 +08:00
|
|
|
if self.use_caching:
|
|
|
|
if not receivers:
|
|
|
|
self.sender_receivers_cache[sender] = NO_RECEIVERS
|
|
|
|
else:
|
|
|
|
# Note, we must cache the weakref versions.
|
|
|
|
self.sender_receivers_cache[sender] = receivers
|
|
|
|
non_weak_receivers = []
|
|
|
|
for receiver in receivers:
|
2014-01-07 08:11:13 +08:00
|
|
|
if isinstance(receiver, weakref.ReferenceType):
|
2012-12-16 09:53:25 +08:00
|
|
|
# Dereference the weak reference.
|
|
|
|
receiver = receiver()
|
|
|
|
if receiver is not None:
|
|
|
|
non_weak_receivers.append(receiver)
|
|
|
|
else:
|
|
|
|
non_weak_receivers.append(receiver)
|
|
|
|
return non_weak_receivers
|
2008-08-06 23:32:46 +08:00
|
|
|
|
2014-02-06 03:45:57 +08:00
|
|
|
def _remove_receiver(self, receiver=None):
|
2014-02-05 02:19:14 +08:00
|
|
|
# Mark that the self.receivers list has dead weakrefs. If so, we will
|
|
|
|
# clean those up in connect, disconnect and _live_receivers while
|
|
|
|
# holding self.lock. Note that doing the cleanup here isn't a good
|
|
|
|
# idea, _remove_receiver() will be called as side effect of garbage
|
|
|
|
# collection, and so the call can happen while we are already holding
|
|
|
|
# self.lock.
|
|
|
|
self._dead_receivers = True
|
2013-08-20 11:14:21 +08:00
|
|
|
|
2010-09-13 03:58:05 +08:00
|
|
|
|
|
|
|
def receiver(signal, **kwargs):
|
|
|
|
"""
|
|
|
|
A decorator for connecting receivers to signals. Used by passing in the
|
2012-06-08 18:00:51 +08:00
|
|
|
signal (or list of signals) and keyword arguments to connect::
|
2010-09-13 03:58:05 +08:00
|
|
|
|
|
|
|
@receiver(post_save, sender=MyModel)
|
|
|
|
def signal_receiver(sender, **kwargs):
|
|
|
|
...
|
|
|
|
|
2012-06-08 18:00:51 +08:00
|
|
|
@receiver([post_save, post_delete], sender=MyModel)
|
|
|
|
def signals_receiver(sender, **kwargs):
|
|
|
|
...
|
2010-09-13 03:58:05 +08:00
|
|
|
"""
|
|
|
|
def _decorator(func):
|
2012-06-08 18:00:51 +08:00
|
|
|
if isinstance(signal, (list, tuple)):
|
2012-06-23 22:54:45 +08:00
|
|
|
for s in signal:
|
|
|
|
s.connect(func, **kwargs)
|
2012-06-08 18:00:51 +08:00
|
|
|
else:
|
|
|
|
signal.connect(func, **kwargs)
|
2010-09-13 03:58:05 +08:00
|
|
|
return func
|
|
|
|
return _decorator
|