|
0
|
1 |
import weakref |
|
|
2 |
try: |
|
|
3 |
set |
|
|
4 |
except NameError: |
|
|
5 |
from sets import Set as set # Python 2.3 fallback |
|
|
6 |
|
|
|
7 |
from django.dispatch import saferef |
|
|
8 |
|
|
|
9 |
WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) |
|
|
10 |
|
|
|
11 |
def _make_id(target): |
|
|
12 |
if hasattr(target, 'im_func'): |
|
|
13 |
return (id(target.im_self), id(target.im_func)) |
|
|
14 |
return id(target) |
|
|
15 |
|
|
|
16 |
class Signal(object): |
|
|
17 |
""" |
|
|
18 |
Base class for all signals |
|
|
19 |
|
|
|
20 |
Internal attributes: |
|
|
21 |
|
|
|
22 |
receivers |
|
|
23 |
{ receriverkey (id) : weakref(receiver) } |
|
|
24 |
""" |
|
|
25 |
|
|
|
26 |
def __init__(self, providing_args=None): |
|
|
27 |
""" |
|
|
28 |
Create a new signal. |
|
|
29 |
|
|
|
30 |
providing_args |
|
|
31 |
A list of the arguments this signal can pass along in a send() call. |
|
|
32 |
""" |
|
|
33 |
self.receivers = [] |
|
|
34 |
if providing_args is None: |
|
|
35 |
providing_args = [] |
|
|
36 |
self.providing_args = set(providing_args) |
|
|
37 |
|
|
|
38 |
def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): |
|
|
39 |
""" |
|
|
40 |
Connect receiver to sender for signal. |
|
|
41 |
|
|
|
42 |
Arguments: |
|
|
43 |
|
|
|
44 |
receiver |
|
|
45 |
A function or an instance method which is to receive signals. |
|
|
46 |
Receivers must be hashable objects. |
|
|
47 |
|
|
|
48 |
if weak is True, then receiver must be weak-referencable (more |
|
|
49 |
precisely saferef.safeRef() must be able to create a reference |
|
|
50 |
to the receiver). |
|
|
51 |
|
|
|
52 |
Receivers must be able to accept keyword arguments. |
|
|
53 |
|
|
|
54 |
If receivers have a dispatch_uid attribute, the receiver will |
|
|
55 |
not be added if another receiver already exists with that |
|
|
56 |
dispatch_uid. |
|
|
57 |
|
|
|
58 |
sender |
|
|
59 |
The sender to which the receiver should respond Must either be |
|
|
60 |
of type Signal, or None to receive events from any sender. |
|
|
61 |
|
|
|
62 |
weak |
|
|
63 |
Whether to use weak references to the receiver By default, the |
|
|
64 |
module will attempt to use weak references to the receiver |
|
|
65 |
objects. If this parameter is false, then strong references will |
|
|
66 |
be used. |
|
|
67 |
|
|
|
68 |
dispatch_uid |
|
|
69 |
An identifier used to uniquely identify a particular instance of |
|
|
70 |
a receiver. This will usually be a string, though it may be |
|
|
71 |
anything hashable. |
|
|
72 |
""" |
|
|
73 |
from django.conf import settings |
|
|
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 |
# Check for **kwargs |
|
|
81 |
# Not all callables are inspectable with getargspec, so we'll |
|
|
82 |
# try a couple different ways but in the end fall back on assuming |
|
|
83 |
# it is -- we don't want to prevent registration of valid but weird |
|
|
84 |
# callables. |
|
|
85 |
try: |
|
|
86 |
argspec = inspect.getargspec(receiver) |
|
|
87 |
except TypeError: |
|
|
88 |
try: |
|
|
89 |
argspec = inspect.getargspec(receiver.__call__) |
|
|
90 |
except (TypeError, AttributeError): |
|
|
91 |
argspec = None |
|
|
92 |
if argspec: |
|
|
93 |
assert argspec[2] is not None, \ |
|
|
94 |
"Signal receivers must accept keyword arguments (**kwargs)." |
|
|
95 |
|
|
|
96 |
if dispatch_uid: |
|
|
97 |
lookup_key = (dispatch_uid, _make_id(sender)) |
|
|
98 |
else: |
|
|
99 |
lookup_key = (_make_id(receiver), _make_id(sender)) |
|
|
100 |
|
|
|
101 |
if weak: |
|
|
102 |
receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver) |
|
|
103 |
|
|
|
104 |
for r_key, _ in self.receivers: |
|
|
105 |
if r_key == lookup_key: |
|
|
106 |
break |
|
|
107 |
else: |
|
|
108 |
self.receivers.append((lookup_key, receiver)) |
|
|
109 |
|
|
|
110 |
def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None): |
|
|
111 |
""" |
|
|
112 |
Disconnect receiver from sender for signal. |
|
|
113 |
|
|
|
114 |
If weak references are used, disconnect need not be called. The receiver |
|
|
115 |
will be remove from dispatch automatically. |
|
|
116 |
|
|
|
117 |
Arguments: |
|
|
118 |
|
|
|
119 |
receiver |
|
|
120 |
The registered receiver to disconnect. May be none if |
|
|
121 |
dispatch_uid is specified. |
|
|
122 |
|
|
|
123 |
sender |
|
|
124 |
The registered sender to disconnect |
|
|
125 |
|
|
|
126 |
weak |
|
|
127 |
The weakref state to disconnect |
|
|
128 |
|
|
|
129 |
dispatch_uid |
|
|
130 |
the unique identifier of the receiver to disconnect |
|
|
131 |
""" |
|
|
132 |
if dispatch_uid: |
|
|
133 |
lookup_key = (dispatch_uid, _make_id(sender)) |
|
|
134 |
else: |
|
|
135 |
lookup_key = (_make_id(receiver), _make_id(sender)) |
|
|
136 |
|
|
|
137 |
for index in xrange(len(self.receivers)): |
|
|
138 |
(r_key, _) = self.receivers[index] |
|
|
139 |
if r_key == lookup_key: |
|
|
140 |
del self.receivers[index] |
|
|
141 |
break |
|
|
142 |
|
|
|
143 |
def send(self, sender, **named): |
|
|
144 |
""" |
|
|
145 |
Send signal from sender to all connected receivers. |
|
|
146 |
|
|
|
147 |
If any receiver raises an error, the error propagates back through send, |
|
|
148 |
terminating the dispatch loop, so it is quite possible to not have all |
|
|
149 |
receivers called if a raises an error. |
|
|
150 |
|
|
|
151 |
Arguments: |
|
|
152 |
|
|
|
153 |
sender |
|
|
154 |
The sender of the signal Either a specific object or None. |
|
|
155 |
|
|
|
156 |
named |
|
|
157 |
Named arguments which will be passed to receivers. |
|
|
158 |
|
|
|
159 |
Returns a list of tuple pairs [(receiver, response), ... ]. |
|
|
160 |
""" |
|
|
161 |
responses = [] |
|
|
162 |
if not self.receivers: |
|
|
163 |
return responses |
|
|
164 |
|
|
|
165 |
for receiver in self._live_receivers(_make_id(sender)): |
|
|
166 |
response = receiver(signal=self, sender=sender, **named) |
|
|
167 |
responses.append((receiver, response)) |
|
|
168 |
return responses |
|
|
169 |
|
|
|
170 |
def send_robust(self, sender, **named): |
|
|
171 |
""" |
|
|
172 |
Send signal from sender to all connected receivers catching errors. |
|
|
173 |
|
|
|
174 |
Arguments: |
|
|
175 |
|
|
|
176 |
sender |
|
|
177 |
The sender of the signal Can be any python object (normally one |
|
|
178 |
registered with a connect if you actually want something to |
|
|
179 |
occur). |
|
|
180 |
|
|
|
181 |
named |
|
|
182 |
Named arguments which will be passed to receivers. These |
|
|
183 |
arguments must be a subset of the argument names defined in |
|
|
184 |
providing_args. |
|
|
185 |
|
|
|
186 |
Return a list of tuple pairs [(receiver, response), ... ]. May raise |
|
|
187 |
DispatcherKeyError. |
|
|
188 |
|
|
|
189 |
if any receiver raises an error (specifically any subclass of |
|
|
190 |
Exception), the error instance is returned as the result for that |
|
|
191 |
receiver. |
|
|
192 |
""" |
|
|
193 |
responses = [] |
|
|
194 |
if not self.receivers: |
|
|
195 |
return responses |
|
|
196 |
|
|
|
197 |
# Call each receiver with whatever arguments it can accept. |
|
|
198 |
# Return a list of tuple pairs [(receiver, response), ... ]. |
|
|
199 |
for receiver in self._live_receivers(_make_id(sender)): |
|
|
200 |
try: |
|
|
201 |
response = receiver(signal=self, sender=sender, **named) |
|
|
202 |
except Exception, err: |
|
|
203 |
responses.append((receiver, err)) |
|
|
204 |
else: |
|
|
205 |
responses.append((receiver, response)) |
|
|
206 |
return responses |
|
|
207 |
|
|
|
208 |
def _live_receivers(self, senderkey): |
|
|
209 |
""" |
|
|
210 |
Filter sequence of receivers to get resolved, live receivers. |
|
|
211 |
|
|
|
212 |
This checks for weak references and resolves them, then returning only |
|
|
213 |
live receivers. |
|
|
214 |
""" |
|
|
215 |
none_senderkey = _make_id(None) |
|
|
216 |
receivers = [] |
|
|
217 |
|
|
|
218 |
for (receiverkey, r_senderkey), receiver in self.receivers: |
|
|
219 |
if r_senderkey == none_senderkey or r_senderkey == senderkey: |
|
|
220 |
if isinstance(receiver, WEAKREF_TYPES): |
|
|
221 |
# Dereference the weak reference. |
|
|
222 |
receiver = receiver() |
|
|
223 |
if receiver is not None: |
|
|
224 |
receivers.append(receiver) |
|
|
225 |
else: |
|
|
226 |
receivers.append(receiver) |
|
|
227 |
return receivers |
|
|
228 |
|
|
|
229 |
def _remove_receiver(self, receiver): |
|
|
230 |
""" |
|
|
231 |
Remove dead receivers from connections. |
|
|
232 |
""" |
|
|
233 |
|
|
|
234 |
to_remove = [] |
|
|
235 |
for key, connected_receiver in self.receivers: |
|
|
236 |
if connected_receiver == receiver: |
|
|
237 |
to_remove.append(key) |
|
|
238 |
for key in to_remove: |
|
|
239 |
for idx, (r_key, _) in enumerate(self.receivers): |
|
|
240 |
if r_key == key: |
|
|
241 |
del self.receivers[idx] |