Code

Ticket #16679: no_cache_fast_signals.py

File no_cache_fast_signals.py, 9.4 KB (added by Suor, 3 years ago)
Line 
1Index: django/dispatch/dispatcher.py
2===================================================================
3--- django/dispatch/dispatcher.py       (revision 16928)
4+++ django/dispatch/dispatcher.py       (working copy)
5@@ -1,5 +1,6 @@
6 import weakref
7 import threading
8+from collections import defaultdict
9 
10 from django.dispatch import saferef
11 
12@@ -13,21 +14,22 @@
13 class Signal(object):
14     """
15     Base class for all signals
16-   
17+
18     Internal attributes:
19-   
20+
21         receivers
22             { receriverkey (id) : weakref(receiver) }
23     """
24-   
25+
26     def __init__(self, providing_args=None):
27         """
28         Create a new signal.
29-       
30+
31         providing_args
32             A list of the arguments this signal can pass along in a send() call.
33         """
34-        self.receivers = []
35+        self.receivers = defaultdict(list)
36+        self.connect_index = 0
37         if providing_args is None:
38             providing_args = []
39         self.providing_args = set(providing_args)
40@@ -36,9 +38,9 @@
41     def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
42         """
43         Connect receiver to sender for signal.
44-   
45+
46         Arguments:
47-       
48+
49             receiver
50                 A function or an instance method which is to receive signals.
51                 Receivers must be hashable objects.
52@@ -46,7 +48,7 @@
53                 If weak is True, then receiver must be weak-referencable (more
54                 precisely saferef.safeRef() must be able to create a reference
55                 to the receiver).
56-       
57+
58                 Receivers must be able to accept keyword arguments.
59 
60                 If receivers have a dispatch_uid attribute, the receiver will
61@@ -62,19 +64,19 @@
62                 module will attempt to use weak references to the receiver
63                 objects. If this parameter is false, then strong references will
64                 be used.
65-       
66+
67             dispatch_uid
68                 An identifier used to uniquely identify a particular instance of
69                 a receiver. This will usually be a string, though it may be
70                 anything hashable.
71         """
72         from django.conf import settings
73-       
74+
75         # If DEBUG is on, check that we got a good receiver
76         if settings.DEBUG:
77             import inspect
78             assert callable(receiver), "Signal receivers must be callable."
79-           
80+
81             # Check for **kwargs
82             # Not all callables are inspectable with getargspec, so we'll
83             # try a couple different ways but in the end fall back on assuming
84@@ -90,22 +92,19 @@
85             if argspec:
86                 assert argspec[2] is not None, \
87                     "Signal receivers must accept keyword arguments (**kwargs)."
88-       
89-        if dispatch_uid:
90-            lookup_key = (dispatch_uid, _make_id(sender))
91-        else:
92-            lookup_key = (_make_id(receiver), _make_id(sender))
93 
94+        lookup_key = dispatch_uid or _make_id(receiver)
95+
96         if weak:
97             receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver)
98 
99         self.lock.acquire()
100         try:
101-            for r_key, _ in self.receivers:
102-                if r_key == lookup_key:
103-                    break
104-            else:
105-                self.receivers.append((lookup_key, receiver))
106+            # Should really use OrderedDict in python 2.7
107+            registered_keys = (rec[1] for rec in self.receivers[sender])
108+            if lookup_key not in registered_keys:
109+                self.receivers[sender].append((lookup_key, receiver, self.connect_index))
110+                self.connect_index += 1
111         finally:
112             self.lock.release()
113 
114@@ -115,33 +114,29 @@
115 
116         If weak references are used, disconnect need not be called. The receiver
117         will be remove from dispatch automatically.
118-   
119+
120         Arguments:
121-       
122+
123             receiver
124                 The registered receiver to disconnect. May be none if
125                 dispatch_uid is specified.
126-           
127+
128             sender
129                 The registered sender to disconnect
130-           
131+
132             weak
133                 The weakref state to disconnect
134-           
135+
136             dispatch_uid
137                 the unique identifier of the receiver to disconnect
138         """
139-        if dispatch_uid:
140-            lookup_key = (dispatch_uid, _make_id(sender))
141-        else:
142-            lookup_key = (_make_id(receiver), _make_id(sender))
143-       
144+        lookup_key = dispatch_uid or _make_id(receiver)
145+
146         self.lock.acquire()
147         try:
148-            for index in xrange(len(self.receivers)):
149-                (r_key, _) = self.receivers[index]
150-                if r_key == lookup_key:
151-                    del self.receivers[index]
152+            for index, (key, _, _) in enumerate(self.receivers[sender]):
153+                if key == lookup_key:
154+                    del self.receivers[sender][index]
155                     break
156         finally:
157             self.lock.release()
158@@ -155,20 +150,18 @@
159         receivers called if a raises an error.
160 
161         Arguments:
162-       
163+
164             sender
165                 The sender of the signal Either a specific object or None.
166-   
167+
168             named
169                 Named arguments which will be passed to receivers.
170 
171         Returns a list of tuple pairs [(receiver, response), ... ].
172         """
173         responses = []
174-        if not self.receivers:
175-            return responses
176 
177-        for receiver in self._live_receivers(_make_id(sender)):
178+        for receiver in self._live_receivers(sender):
179             response = receiver(signal=self, sender=sender, **named)
180             responses.append((receiver, response))
181         return responses
182@@ -178,7 +171,7 @@
183         Send signal from sender to all connected receivers catching errors.
184 
185         Arguments:
186-       
187+
188             sender
189                 The sender of the signal. Can be any python object (normally one
190                 registered with a connect if you actually want something to
191@@ -197,12 +190,10 @@
192         receiver.
193         """
194         responses = []
195-        if not self.receivers:
196-            return responses
197 
198         # Call each receiver with whatever arguments it can accept.
199         # Return a list of tuple pairs [(receiver, response), ... ].
200-        for receiver in self._live_receivers(_make_id(sender)):
201+        for receiver in self._live_receivers(sender):
202             try:
203                 response = receiver(signal=self, sender=sender, **named)
204             except Exception, err:
205@@ -211,45 +202,57 @@
206                 responses.append((receiver, response))
207         return responses
208 
209-    def _live_receivers(self, senderkey):
210+    def _live_receivers(self, sender):
211         """
212-        Filter sequence of receivers to get resolved, live receivers.
213+        Construct sequence of receivers to get resolved, live receivers.
214 
215         This checks for weak references and resolves them, then returning only
216         live receivers.
217         """
218-        none_senderkey = _make_id(None)
219-        receivers = []
220+        def combine(a, b):
221+            if not a:
222+                return b
223+            elif not b:
224+                return a
225 
226-        for (receiverkey, r_senderkey), receiver in self.receivers:
227-            if r_senderkey == none_senderkey or r_senderkey == senderkey:
228+            a_index = 0
229+            b_index = 0
230+            while a_index < len(a) and b_index < len(b):
231+                if a[a_index][2] < b[b_index][2]:
232+                    yield a[a_index][1]
233+                    a_index += 1
234+                else:
235+                    yield b[b_index][1]
236+                    b_index += 1
237+            for i in range(a_index, len(a)):
238+                yield a[i][1]
239+            for i in range(b_index, len(b)):
240+                yield b[i][1]
241+
242+        def deref(receivers):
243+            for receiver in receivers:
244                 if isinstance(receiver, WEAKREF_TYPES):
245                     # Dereference the weak reference.
246                     receiver = receiver()
247                     if receiver is not None:
248-                        receivers.append(receiver)
249+                        yield receiver
250                 else:
251-                    receivers.append(receiver)
252-        return receivers
253+                    yield receiver
254 
255-    def _remove_receiver(self, receiver):
256+        receivers = combine(self.receivers[None], self.receivers[sender])
257+        return list(deref(receivers))
258+
259+    def _remove_receiver(self, dead_receiver):
260         """
261         Remove dead receivers from connections.
262         """
263 
264         self.lock.acquire()
265         try:
266-            to_remove = []
267-            for key, connected_receiver in self.receivers:
268-                if connected_receiver == receiver:
269-                    to_remove.append(key)
270-            for key in to_remove:
271-                last_idx = len(self.receivers) - 1
272-                # enumerate in reverse order so that indexes are valid even
273-                # after we delete some items
274-                for idx, (r_key, _) in enumerate(reversed(self.receivers)):
275-                    if r_key == key:
276-                        del self.receivers[last_idx-idx]
277+            for receivers in self.receivers.values():
278+                for index, (key, receiver, _) in enumerate(receivers):
279+                    if receiver == dead_receiver:
280+                        del receivers[index]
281         finally:
282             self.lock.release()
283