1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 __maintainer__ = 'Florian Boucault <florian@fluendo.com>'
19 __maintainer2__ = 'Philippe Normand <philippe@fluendo.com>'
20
21
23 """ Exception raised by Signal when the developer tries to emit
24 wrong types values with the Signal.
25
26 @ivar arg: the value of the wrongly typed object
27 @type arg: object
28 @ivar expected_type: the expcected type for the argument, given at
29 Signal creation
30 @type expected_args: type
31 @ivar position: position of the argument in the arguments list, if it's
32 not a keyword argument
33 @type position: int or None
34 """
35
36 - def __init__(self, arg, expected_type, position):
41
43 """ Textual representation of the Exception
44
45 @rtype: string
46 """
47 if self.position is not None:
48 position = "at position %s" % self.position
49 else:
50 position = "as keyword argument"
51 msg = "Expected an %r %s, got %r (%r) instead."
52 msg = msg % (self.expected_type.__name__, position,
53 self.arg, type(self.arg))
54 return msg
55
57 """
58 A Signal is a 1-to-many communication system.
59 It provides a way for two objects to communicate without knowing
60 about each other. They only have to know about the signal object. Receivers
61 objects connect to the signal and emitters can trigger the emission of the
62 signal so that it is sent to all the receivers. Signal emission is
63 synchronous, that is, the receivers are immediatly called upon emission,
64 one after the other.
65
66 WARNING:
67 - a Signal is *not* thread-safe
68 - the receivers are called in the emitting thread
69 - receivers calling is not ordered
70
71 TODO:
72 - might be useful to send a reference of the sender
73 - maybe add static extra data at connect time
74 - think about implementing return values support
75 - think about weak references for receivers
76 http://docs.python.org/lib/module-weakref.html
77 - think about integrating it with Twisted and making signals
78 deferred in threads
79
80 """
81
82
83 - def __init__(self, name, *args_types, **kw_types):
84 """
85 Initialize a signal with the signature of receivers that can connect
86 to it.
87
88 @param name: name of the signal. Not unique, but developer
89 friendly
90 @type name: string
91 @param args_types: arguments types of the receivers that can connect
92 to the signal
93 @type args_types: tuple of types
94 @param kw_types: arguments types of the receivers that can connect
95 to the signal
96 @type kw_types: dictionary of types
97 """
98 self._name = name
99 self._args_types = args_types
100 self._kw_types = kw_types
101 self._receivers = []
102
104 """ Textual representation of the Signal
105
106 @returns: information about the signal and its receivers
107 @rtype: string
108 """
109 r = '<Signal %s with %d receivers>' % (self._name, len(self._receivers))
110 return r
111
113 """
114 Connect a receiver to the signal. It will be called when the signal
115 is emitted.
116
117 @param receiver: object to be called when the signal is emitted
118 @type receiver: callable
119
120 @exception TypeError: raised if the receiver is not callable
121 """
122 if callable(receiver):
123 if receiver not in self._receivers:
124 self._receivers.append(receiver)
125 else:
126 raise TypeError("Receiver is not a callable")
127
129 """
130 Disconnect a receiver from the signal.
131
132 @param receiver: object to be disconnected
133 @type receiver: callable
134
135 @exception Exception: raised if the receiver is not connected
136 """
137 try:
138 self._receivers.remove(receiver)
139 except:
140 raise Exception("%s not connected to the signal" % receiver)
141
142 - def emit(self, *args, **kw):
143 """
144 Call all the connected receivers. It avoids calling the
145 neutralized ones. Since receivers calling is not ordered, one should be
146 careful with the potential side effects in the receivers.
147
148 @param args: arguments passed to the receivers
149 @type args: tuple
150 @param kw: arguments passed to the receivers
151 @type kw: dictionary
152
153 @exception WrongArgument: Raised if one of the arguments is of the
154 wrong type
155 """
156 self._check_args_types(args, kw)
157
158 for receiver in self._receivers:
159 receiver(*args, **kw)
160
162 """ Check the types of arguments and keywords comply with
163 types specifications given at Signal creation.
164
165 @exception WrongArgument: Raised if one of the arguments is of the
166 wrong type
167 """
168 position = 0
169 for arg in args:
170 expected_type = self._args_types[position]
171 if not isinstance(arg, expected_type):
172 raise WrongArgument(arg, expected_type, position)
173 position += 1
174
175 for key, value in self._kw_types.iteritems():
176 if key in kw and not isinstance(kw[key], value):
177 raise WrongArgument(kw[key], value, None)
178
179
180
181
182
183
184
185
186