Ticket #16679: no_cache_fast_signals.py

File no_cache_fast_signals.py, 9.4 KB (added by Alexander Schepanovski, 13 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
Back to Top