1
|
|
|
""" |
2
|
|
|
Enarksh |
3
|
|
|
|
4
|
|
|
Copyright 2013-2016 Set Based IT Consultancy |
5
|
|
|
|
6
|
|
|
Licence MIT |
7
|
|
|
""" |
8
|
|
|
import sys |
9
|
|
|
import traceback |
10
|
|
|
|
11
|
|
|
from enarksh.event.Actor import Actor |
12
|
|
|
from enarksh.event.Event import Event |
13
|
|
|
|
14
|
|
|
|
15
|
|
|
class EventController(Actor): |
16
|
|
|
""" |
17
|
|
|
A single threaded and a run-to-completion event controller. That is, each event is processed completely before any |
18
|
|
|
other event is processed. Hence, an event listener will run entirely before any other code runs (which can |
19
|
|
|
potentially modify the data the event listener invokes). |
20
|
|
|
""" |
21
|
|
|
|
22
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
23
|
|
|
def __init__(self): |
24
|
|
|
""" |
25
|
|
|
Object constructor. |
26
|
|
|
""" |
27
|
|
|
Actor.__init__(self) |
28
|
|
|
|
29
|
|
|
Event.event_controller = self |
30
|
|
|
|
31
|
|
|
self._events = dict() |
32
|
|
|
""" |
33
|
|
|
All events in the current program. |
34
|
|
|
|
35
|
|
|
:type: dict[enarksh.event.Event.Event,dict[*,list[*]]] |
36
|
|
|
""" |
37
|
|
|
|
38
|
|
|
self._listener_objects = dict() |
39
|
|
|
""" |
40
|
|
|
|
41
|
|
|
:type: dict[*, set[enarksh.event.Event.Event]] |
42
|
|
|
""" |
43
|
|
|
|
44
|
|
|
self._event_loop_start = Event(self) |
45
|
|
|
""" |
46
|
|
|
Event that will be fired at the start of the event loop. |
47
|
|
|
|
48
|
|
|
:type: enarksh.event.Event.Event |
49
|
|
|
""" |
50
|
|
|
|
51
|
|
|
self._event_loop_end = Event(self) |
52
|
|
|
""" |
53
|
|
|
Event that will be fired at the end of the event loop. |
54
|
|
|
|
55
|
|
|
:type: enarksh.event.Event.Event |
56
|
|
|
""" |
57
|
|
|
|
58
|
|
|
self._event_queue_empty = Event(self) |
59
|
|
|
""" |
60
|
|
|
Event that will be fired when the event queue is empty. |
61
|
|
|
|
62
|
|
|
:type: enarksh.event.Event.Event |
63
|
|
|
""" |
64
|
|
|
|
65
|
|
|
self._exit = False |
66
|
|
|
""" |
67
|
|
|
If True the event loop terminates as soon as the event queue is emtpy. No event_queue_empty event will be fired. |
68
|
|
|
|
69
|
|
|
:type: bool |
70
|
|
|
""" |
71
|
|
|
|
72
|
|
|
self._queue = [] |
73
|
|
|
""" |
74
|
|
|
The queue with events that have fired but have not been processed yet. |
75
|
|
|
|
76
|
|
|
:type: list[(enarksh.event.Event.Event,*)] |
77
|
|
|
""" |
78
|
|
|
|
79
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
80
|
|
|
@property |
81
|
|
|
def event_loop_end(self): |
82
|
|
|
""" |
83
|
|
|
Returns the event that will be fired at the end of the event loop. |
84
|
|
|
|
85
|
|
|
:rtype: enarksh.event.Event.Event |
86
|
|
|
""" |
87
|
|
|
return self._event_loop_end |
88
|
|
|
|
89
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
90
|
|
|
@property |
91
|
|
|
def event_loop_start(self): |
92
|
|
|
""" |
93
|
|
|
Returns the event that will be fired at the start of the event loop. |
94
|
|
|
|
95
|
|
|
:rtype: enarksh.event.Event.Event |
96
|
|
|
""" |
97
|
|
|
return self._event_loop_start |
98
|
|
|
|
99
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
100
|
|
|
@property |
101
|
|
|
def event_queue_empty(self): |
102
|
|
|
""" |
103
|
|
|
Returns the event that will be fired when the event queue is empty. |
104
|
|
|
|
105
|
|
|
:rtype: enarksh.event.Event.Event |
106
|
|
|
""" |
107
|
|
|
return self._event_queue_empty |
108
|
|
|
|
109
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
110
|
|
|
def loop(self): |
111
|
|
|
""" |
112
|
|
|
Start the event handler loop. |
113
|
|
|
""" |
114
|
|
|
self._dispatch_event(self._event_loop_start, None) |
115
|
|
|
|
116
|
|
|
while not self._exit: |
117
|
|
|
event, event_data = self._queue.pop(0) |
118
|
|
|
|
119
|
|
|
self._dispatch_event(event, event_data) |
120
|
|
|
|
121
|
|
|
if not self._queue and not self._exit: |
122
|
|
|
self._dispatch_event(self._event_queue_empty, None) |
123
|
|
|
if not self._queue: |
124
|
|
|
self._exit = True |
125
|
|
|
|
126
|
|
|
self._dispatch_event(self._event_loop_end, None) |
127
|
|
|
|
128
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
129
|
|
|
def _dispatch_event(self, event, event_data): |
130
|
|
|
""" |
131
|
|
|
Dispatches an event. |
132
|
|
|
|
133
|
|
|
:param enarksh.event.Event.Event event: The event to be dispatch. |
134
|
|
|
:param * event_data: Additional data supplied by the event source. |
135
|
|
|
""" |
136
|
|
|
try: |
137
|
|
|
for listeners in self._events[event].values(): |
138
|
|
|
for listener, listener_data in listeners: |
139
|
|
|
listener(event, event_data, listener_data) |
140
|
|
|
except Exception as exception: |
|
|
|
|
141
|
|
|
print(exception, file=sys.stderr) |
142
|
|
|
traceback.print_exc(file=sys.stderr) |
143
|
|
|
|
144
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
145
|
|
|
def queue_event(self, event, event_data): |
146
|
|
|
""" |
147
|
|
|
Puts a event that has fired on the event queue. |
148
|
|
|
|
149
|
|
|
Note: Do not use this method directly. Use enarksh.event.Event.Event.fire() instead. |
150
|
|
|
|
151
|
|
|
:param enarksh.event.Event.Event event: The event that has fired. |
152
|
|
|
:param * event_data: Additional data supplied by the event source. |
153
|
|
|
""" |
154
|
|
|
self._queue.append((event, event_data)) |
155
|
|
|
|
156
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
157
|
|
|
def unregister_event(self, event): |
158
|
|
|
""" |
159
|
|
|
Removes an event as an event in the current program. |
160
|
|
|
|
161
|
|
|
Note: Do not use this method directly. Use enarksh.event.Actor.Actor.destroy() instead. |
162
|
|
|
|
163
|
|
|
:param enarksh.event.Event.Event event: The event that must be removed. |
164
|
|
|
""" |
165
|
|
|
if event in self._events: |
166
|
|
|
for listener_object in self._events[event].keys(): |
167
|
|
|
events = self._listener_objects[listener_object] |
168
|
|
|
events.remove(event) |
169
|
|
|
|
170
|
|
|
if not events: |
171
|
|
|
del self._listener_objects[listener_object] |
172
|
|
|
|
173
|
|
|
del self._events[event] |
174
|
|
|
|
175
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
176
|
|
|
def unregister_listener_object(self, listener_object): |
177
|
|
|
""" |
178
|
|
|
Removes an object as a listener object (i.e. an object with one or more methods registered as event listeners). |
179
|
|
|
|
180
|
|
|
Note: Do not use this method directly. Use enarksh.event.Actor.Actor.destroy() instead. |
181
|
|
|
|
182
|
|
|
:param enarksh.event.Listener.Listener listener_object: The listener object. |
183
|
|
|
""" |
184
|
|
|
if listener_object in self._listener_objects: |
185
|
|
|
# Remove the object from all events. |
186
|
|
|
for event in self._listener_objects[listener_object]: |
187
|
|
|
del self._events[event][listener_object] |
188
|
|
|
|
189
|
|
|
# Remove the object as listener object. |
190
|
|
|
del self._listener_objects[listener_object] |
191
|
|
|
|
192
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
193
|
|
|
def register_event(self, event): |
194
|
|
|
""" |
195
|
|
|
Registers an event as, well, an event in the current program. |
196
|
|
|
|
197
|
|
|
Note: Do not use this method directly. This method is automatically called by |
198
|
|
|
enarksh.event.Event.Event#__init__() |
199
|
|
|
|
200
|
|
|
:param enarksh.event.Event.Event event: The event that must be registered. |
201
|
|
|
""" |
202
|
|
|
self._events[event] = dict() |
203
|
|
|
|
204
|
|
|
event.source.registered_events.add(event) |
205
|
|
|
|
206
|
|
|
# ------------------------------------------------------------------------------------------------------------------ |
207
|
|
|
def register_listener(self, event, listener, listener_data=None): |
208
|
|
|
""" |
209
|
|
|
Registers an object as a listener for this event. |
210
|
|
|
|
211
|
|
|
Note: Do not use this method directly. Use enarksh.event.Event.Event.register_listener() instead. |
212
|
|
|
|
213
|
|
|
:param enarksh.event.Event.Event event: The event for which the listener needs to notified. |
214
|
|
|
:param callable listener: The method that must be called when the event has fired. |
215
|
|
|
:param listener_data: Additional data supplied by the listener. |
216
|
|
|
""" |
217
|
|
|
if hasattr(listener, '__self__'): |
218
|
|
|
if not isinstance(listener.__self__, Actor): |
219
|
|
|
raise ValueError('Only an actor can be a listener, got {0}'.format(listener.__class__)) |
220
|
|
|
listener_object = listener.__self__ |
221
|
|
|
else: |
222
|
|
|
listener_object = None |
223
|
|
|
|
224
|
|
|
if listener_object not in self._events[event]: |
225
|
|
|
self._events[event][listener_object] = list() |
226
|
|
|
self._events[event][listener_object].append((listener, listener_data)) |
227
|
|
|
|
228
|
|
|
if listener_object not in self._listener_objects: |
229
|
|
|
self._listener_objects[listener_object] = set() |
230
|
|
|
self._listener_objects[listener_object].add(event) |
231
|
|
|
|
232
|
|
|
# ---------------------------------------------------------------------------------------------------------------------- |
233
|
|
|
|
Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.
So, unless you specifically plan to handle any error, consider adding a more specific exception.