1 | Index: 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 |
|
---|