Loading django/db/models/signals.py +9 −8 Original line number Diff line number Diff line Loading @@ -2,15 +2,16 @@ from django.dispatch import Signal class_prepared = Signal(providing_args=["class"]) pre_init = Signal(providing_args=["instance", "args", "kwargs"]) post_init = Signal(providing_args=["instance"]) pre_init = Signal(providing_args=["instance", "args", "kwargs"], use_caching=True) post_init = Signal(providing_args=["instance"], use_caching=True) pre_save = Signal(providing_args=["instance", "raw", "using", "update_fields"]) post_save = Signal(providing_args=["instance", "raw", "created", "using", "update_fields"]) pre_save = Signal(providing_args=["instance", "raw", "using", "update_fields"], use_caching=True) post_save = Signal(providing_args=["instance", "raw", "created", "using", "update_fields"], use_caching=True) pre_delete = Signal(providing_args=["instance", "using"]) post_delete = Signal(providing_args=["instance", "using"]) pre_delete = Signal(providing_args=["instance", "using"], use_caching=True) post_delete = Signal(providing_args=["instance", "using"], use_caching=True) post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive"]) post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive"], use_caching=True) m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"]) m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"], use_caching=True) django/dispatch/dispatcher.py +51 −22 Original line number Diff line number Diff line Loading @@ -10,6 +10,10 @@ def _make_id(target): if hasattr(target, '__func__'): return (id(target.__self__), id(target.__func__)) return id(target) NONE_ID = _make_id(None) # A marker for caching NO_RECEIVERS = object() class Signal(object): """ Loading @@ -20,8 +24,7 @@ class Signal(object): receivers { receriverkey (id) : weakref(receiver) } """ def __init__(self, providing_args=None): def __init__(self, providing_args=None, use_caching=False): """ Create a new signal. Loading @@ -33,6 +36,13 @@ class Signal(object): providing_args = [] self.providing_args = set(providing_args) self.lock = threading.Lock() 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(). self.sender_receivers_cache = {} def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): """ Loading Loading @@ -106,6 +116,7 @@ class Signal(object): break else: self.receivers.append((lookup_key, receiver)) self.sender_receivers_cache = {} def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None): """ Loading Loading @@ -140,9 +151,10 @@ class Signal(object): if r_key == lookup_key: del self.receivers[index] break self.sender_receivers_cache = {} def has_listeners(self, sender=None): return bool(self._live_receivers(_make_id(sender))) return bool(self._live_receivers(sender)) def send(self, sender, **named): """ Loading @@ -163,10 +175,10 @@ class Signal(object): Returns a list of tuple pairs [(receiver, response), ... ]. """ responses = [] if not self.receivers: if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS: return responses for receiver in self._live_receivers(_make_id(sender)): for receiver in self._live_receivers(sender): response = receiver(signal=self, sender=sender, **named) responses.append((receiver, response)) return responses Loading Loading @@ -195,12 +207,12 @@ class Signal(object): receiver. """ responses = [] if not self.receivers: if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS: return responses # Call each receiver with whatever arguments it can accept. # Return a list of tuple pairs [(receiver, response), ... ]. for receiver in self._live_receivers(_make_id(sender)): for receiver in self._live_receivers(sender): try: response = receiver(signal=self, sender=sender, **named) except Exception as err: Loading @@ -209,26 +221,43 @@ class Signal(object): responses.append((receiver, response)) return responses def _live_receivers(self, senderkey): def _live_receivers(self, sender): """ 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 = None if self.use_caching: 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: senderkey = _make_id(sender) receivers = [] for (receiverkey, r_senderkey), receiver in self.receivers: if r_senderkey == none_senderkey or r_senderkey == senderkey: if r_senderkey == NONE_ID or r_senderkey == senderkey: receivers.append(receiver) 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: if isinstance(receiver, WEAKREF_TYPES): # Dereference the weak reference. receiver = receiver() if receiver is not None: receivers.append(receiver) non_weak_receivers.append(receiver) else: receivers.append(receiver) return receivers non_weak_receivers.append(receiver) return non_weak_receivers def _remove_receiver(self, receiver): """ Loading @@ -247,7 +276,7 @@ class Signal(object): for idx, (r_key, _) in enumerate(reversed(self.receivers)): if r_key == key: del self.receivers[last_idx - idx] self.sender_receivers_cache = {} def receiver(signal, **kwargs): """ Loading Loading
django/db/models/signals.py +9 −8 Original line number Diff line number Diff line Loading @@ -2,15 +2,16 @@ from django.dispatch import Signal class_prepared = Signal(providing_args=["class"]) pre_init = Signal(providing_args=["instance", "args", "kwargs"]) post_init = Signal(providing_args=["instance"]) pre_init = Signal(providing_args=["instance", "args", "kwargs"], use_caching=True) post_init = Signal(providing_args=["instance"], use_caching=True) pre_save = Signal(providing_args=["instance", "raw", "using", "update_fields"]) post_save = Signal(providing_args=["instance", "raw", "created", "using", "update_fields"]) pre_save = Signal(providing_args=["instance", "raw", "using", "update_fields"], use_caching=True) post_save = Signal(providing_args=["instance", "raw", "created", "using", "update_fields"], use_caching=True) pre_delete = Signal(providing_args=["instance", "using"]) post_delete = Signal(providing_args=["instance", "using"]) pre_delete = Signal(providing_args=["instance", "using"], use_caching=True) post_delete = Signal(providing_args=["instance", "using"], use_caching=True) post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive"]) post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive"], use_caching=True) m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"]) m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"], use_caching=True)
django/dispatch/dispatcher.py +51 −22 Original line number Diff line number Diff line Loading @@ -10,6 +10,10 @@ def _make_id(target): if hasattr(target, '__func__'): return (id(target.__self__), id(target.__func__)) return id(target) NONE_ID = _make_id(None) # A marker for caching NO_RECEIVERS = object() class Signal(object): """ Loading @@ -20,8 +24,7 @@ class Signal(object): receivers { receriverkey (id) : weakref(receiver) } """ def __init__(self, providing_args=None): def __init__(self, providing_args=None, use_caching=False): """ Create a new signal. Loading @@ -33,6 +36,13 @@ class Signal(object): providing_args = [] self.providing_args = set(providing_args) self.lock = threading.Lock() 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(). self.sender_receivers_cache = {} def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): """ Loading Loading @@ -106,6 +116,7 @@ class Signal(object): break else: self.receivers.append((lookup_key, receiver)) self.sender_receivers_cache = {} def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None): """ Loading Loading @@ -140,9 +151,10 @@ class Signal(object): if r_key == lookup_key: del self.receivers[index] break self.sender_receivers_cache = {} def has_listeners(self, sender=None): return bool(self._live_receivers(_make_id(sender))) return bool(self._live_receivers(sender)) def send(self, sender, **named): """ Loading @@ -163,10 +175,10 @@ class Signal(object): Returns a list of tuple pairs [(receiver, response), ... ]. """ responses = [] if not self.receivers: if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS: return responses for receiver in self._live_receivers(_make_id(sender)): for receiver in self._live_receivers(sender): response = receiver(signal=self, sender=sender, **named) responses.append((receiver, response)) return responses Loading Loading @@ -195,12 +207,12 @@ class Signal(object): receiver. """ responses = [] if not self.receivers: if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS: return responses # Call each receiver with whatever arguments it can accept. # Return a list of tuple pairs [(receiver, response), ... ]. for receiver in self._live_receivers(_make_id(sender)): for receiver in self._live_receivers(sender): try: response = receiver(signal=self, sender=sender, **named) except Exception as err: Loading @@ -209,26 +221,43 @@ class Signal(object): responses.append((receiver, response)) return responses def _live_receivers(self, senderkey): def _live_receivers(self, sender): """ 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 = None if self.use_caching: 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: senderkey = _make_id(sender) receivers = [] for (receiverkey, r_senderkey), receiver in self.receivers: if r_senderkey == none_senderkey or r_senderkey == senderkey: if r_senderkey == NONE_ID or r_senderkey == senderkey: receivers.append(receiver) 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: if isinstance(receiver, WEAKREF_TYPES): # Dereference the weak reference. receiver = receiver() if receiver is not None: receivers.append(receiver) non_weak_receivers.append(receiver) else: receivers.append(receiver) return receivers non_weak_receivers.append(receiver) return non_weak_receivers def _remove_receiver(self, receiver): """ Loading @@ -247,7 +276,7 @@ class Signal(object): for idx, (r_key, _) in enumerate(reversed(self.receivers)): if r_key == key: del self.receivers[last_idx - idx] self.sender_receivers_cache = {} def receiver(signal, **kwargs): """ Loading