Ticket #20943: 0001-Fixed-20943-Weakly-reference-senders-when-caching-th.patch

File 0001-Fixed-20943-Weakly-reference-senders-when-caching-th.patch, 4.9 KB (added by Simon Charette, 11 years ago)
  • django/db/models/signals.py

    From c4707d0304f9064e48fbf7d066cc3b3a8b4dba3f Mon Sep 17 00:00:00 2001
    From: Simon Charette <charette.s@gmail.com>
    Date: Mon, 19 Aug 2013 23:14:21 -0400
    Subject: [PATCH] Fixed #20943 -- Weakly reference senders when caching their
     associated receivers
    
    ---
     django/db/models/signals.py             |  2 +-
     django/dispatch/dispatcher.py           | 12 ++++++++----
     tests/dispatch/tests/test_dispatcher.py | 21 +++++++++++++++++++++
     3 files changed, 30 insertions(+), 5 deletions(-)
    
    diff --git a/django/db/models/signals.py b/django/db/models/signals.py
    index 3e32189..0782442 100644
    a b pre_delete = Signal(providing_args=["instance", "using"], use_caching=True)  
    1313post_delete = Signal(providing_args=["instance", "using"], use_caching=True)
    1414
    1515pre_syncdb = Signal(providing_args=["app", "create_models", "verbosity", "interactive", "db"])
    16 post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive", "db"], use_caching=True)
     16post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive", "db"])
    1717
    1818m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"], use_caching=True)
  • django/dispatch/dispatcher.py

    diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py
    index 65c5c40..a8cdc93 100644
    a b import threading  
    44from django.dispatch import saferef
    55from django.utils.six.moves import xrange
    66
     7
    78WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
    89
     10
    911def _make_id(target):
    1012    if hasattr(target, '__func__'):
    1113        return (id(target.__self__), id(target.__func__))
    NONE_ID = _make_id(None)  
    1517# A marker for caching
    1618NO_RECEIVERS = object()
    1719
     20
    1821class Signal(object):
    1922    """
    2023    Base class for all signals
    class Signal(object):  
    4245        # distinct sender we cache the receivers that sender has in
    4346        # 'sender_receivers_cache'. The cache is cleaned when .connect() or
    4447        # .disconnect() is called and populated on send().
    45         self.sender_receivers_cache = {}
     48        self.sender_receivers_cache = weakref.WeakKeyDictionary() if use_caching else {}
    4649
    4750    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
    4851        """
    class Signal(object):  
    116119                    break
    117120            else:
    118121                self.receivers.append((lookup_key, receiver))
    119             self.sender_receivers_cache = {}
     122            self.sender_receivers_cache.clear()
    120123
    121124    def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None):
    122125        """
    class Signal(object):  
    151154                if r_key == lookup_key:
    152155                    del self.receivers[index]
    153156                    break
    154             self.sender_receivers_cache = {}
     157            self.sender_receivers_cache.clear()
    155158
    156159    def has_listeners(self, sender=None):
    157160        return bool(self._live_receivers(sender))
    class Signal(object):  
    276279                for idx, (r_key, _) in enumerate(reversed(self.receivers)):
    277280                    if r_key == key:
    278281                        del self.receivers[last_idx - idx]
    279             self.sender_receivers_cache = {}
     282            self.sender_receivers_cache.clear()
     283
    280284
    281285def receiver(signal, **kwargs):
    282286    """
  • tests/dispatch/tests/test_dispatcher.py

    diff --git a/tests/dispatch/tests/test_dispatcher.py b/tests/dispatch/tests/test_dispatcher.py
    index 5f7dca8..e25f60b 100644
    a b import gc  
    22import sys
    33import time
    44import unittest
     5import weakref
    56
    67from django.dispatch import Signal, receiver
    78
    class Callable(object):  
    3536a_signal = Signal(providing_args=["val"])
    3637b_signal = Signal(providing_args=["val"])
    3738c_signal = Signal(providing_args=["val"])
     39d_signal = Signal(providing_args=["val"], use_caching=True)
     40
    3841
    3942class DispatcherTests(unittest.TestCase):
    4043    """Test suite for dispatcher (barely started)"""
    class DispatcherTests(unittest.TestCase):  
    7275        self.assertEqual(result, expected)
    7376        self._testIsClean(a_signal)
    7477
     78    def testCachedGarbagedCollected(self):
     79        """
     80        Make sure signal caching sender receivers don't prevent garbage
     81        collection of senders.
     82        """
     83        class sender:
     84            pass
     85        wref = weakref.ref(sender)
     86        d_signal.connect(receiver_1_arg)
     87        d_signal.send(sender, val='garbage')
     88        del sender
     89        garbage_collect()
     90        try:
     91            self.assertIsNone(wref())
     92        finally:
     93            # Disconnect after reference check since it flushes the tested cache.
     94            d_signal.disconnect(receiver_1_arg)
     95
    7596    def testMultipleRegistration(self):
    7697        a = Callable()
    7798        a_signal.connect(a)
Back to Top