##// END OF EJS Templates
Use ternary operator for callback registration
Jonathan Frederic -
Show More
@@ -1,322 +1,298 b''
1 """Contains eventful dict and list implementations."""
1 """Contains eventful dict and list implementations."""
2
2
3 # void function used as a callback placeholder.
3 # void function used as a callback placeholder.
4 def _void(*p, **k): return None
4 def _void(*p, **k): return None
5
5
6 class EventfulDict(dict):
6 class EventfulDict(dict):
7 """Eventful dictionary.
7 """Eventful dictionary.
8
8
9 This class inherits from the Python intrinsic dictionary class, dict. It
9 This class inherits from the Python intrinsic dictionary class, dict. It
10 adds events to the get, set, and del actions and optionally allows you to
10 adds events to the get, set, and del actions and optionally allows you to
11 intercept and cancel these actions. The eventfulness isn't recursive. In
11 intercept and cancel these actions. The eventfulness isn't recursive. In
12 other words, if you add a dict as a child, the events of that dict won't be
12 other words, if you add a dict as a child, the events of that dict won't be
13 listened to. If you find you need something recursive, listen to the `add`
13 listened to. If you find you need something recursive, listen to the `add`
14 and `set` methods, and then cancel `dict` values from being set, and instead
14 and `set` methods, and then cancel `dict` values from being set, and instead
15 set `EventfulDict`s that wrap those `dict`s. Then you can wire the events
15 set `EventfulDict`s that wrap those `dict`s. Then you can wire the events
16 to the same handlers if necessary.
16 to the same handlers if necessary.
17
17
18 See the on_events, on_add, on_set, and on_del methods for registering
18 See the on_events, on_add, on_set, and on_del methods for registering
19 event handlers."""
19 event handlers."""
20
20
21 def __init__(self, *args, **kwargs):
21 def __init__(self, *args, **kwargs):
22 """Public constructor"""
22 """Public constructor"""
23 self._add_callback = _void
23 self._add_callback = _void
24 self._del_callback = _void
24 self._del_callback = _void
25 self._set_callback = _void
25 self._set_callback = _void
26 dict.__init__(self, *args, **kwargs)
26 dict.__init__(self, *args, **kwargs)
27
27
28 def on_events(self, *callbacks):
28 def on_events(self, *callbacks):
29 """Register callbacks for add, set, and del actions.
29 """Register callbacks for add, set, and del actions.
30
30
31 See the doctstrings for on_(add/set/del) for details about each
31 See the doctstrings for on_(add/set/del) for details about each
32 callback.
32 callback.
33
33
34 add_callback: callback or None
34 add_callback: callback or None
35 set_callback: callback or None
35 set_callback: callback or None
36 del_callback: callback or None"""
36 del_callback: callback or None"""
37 registers = ['on_add', 'on_set', 'on_del']
37 registers = ['on_add', 'on_set', 'on_del']
38 if len(callbacks) < len(registers):
38 if len(callbacks) < len(registers):
39 raise ValueError('on_events takes {} callbacks'.format(len(registers)))
39 raise ValueError('on_events takes {} callbacks'.format(len(registers)))
40 [getattr(self, n)(callbacks[i]) for i, n in enumerate(registers)]
40 [getattr(self, n)(callbacks[i]) for i, n in enumerate(registers)]
41
41
42 def on_add(self, callback):
42 def on_add(self, callback):
43 """Register a callback for when an item is added to the dict.
43 """Register a callback for when an item is added to the dict.
44
44
45 Allows the listener to detect when items are added to the dictionary and
45 Allows the listener to detect when items are added to the dictionary and
46 optionally cancel the addition.
46 optionally cancel the addition.
47
47
48 callback: callable or None
48 callback: callable or None
49 If you want to ignore the addition event, pass None as the callback.
49 If you want to ignore the addition event, pass None as the callback.
50 The callback should have a signature of callback(key, value). The
50 The callback should have a signature of callback(key, value). The
51 callback should return a boolean True if the additon should be
51 callback should return a boolean True if the additon should be
52 canceled, False or None otherwise."""
52 canceled, False or None otherwise."""
53 if callable(callback):
53 self._add_callback = callback if callable(callback) else _void
54 self._add_callback = callback
55 else:
56 self._add_callback = _void
57
54
58 def on_del(self, callback):
55 def on_del(self, callback):
59 """Register a callback for when an item is deleted from the dict.
56 """Register a callback for when an item is deleted from the dict.
60
57
61 Allows the listener to detect when items are deleted from the dictionary
58 Allows the listener to detect when items are deleted from the dictionary
62 and optionally cancel the deletion.
59 and optionally cancel the deletion.
63
60
64 callback: callable or None
61 callback: callable or None
65 If you want to ignore the deletion event, pass None as the callback.
62 If you want to ignore the deletion event, pass None as the callback.
66 The callback should have a signature of callback(key). The
63 The callback should have a signature of callback(key). The
67 callback should return a boolean True if the deletion should be
64 callback should return a boolean True if the deletion should be
68 canceled, False or None otherwise."""
65 canceled, False or None otherwise."""
69 if callable(callback):
66 self._del_callback = callback if callable(callback) else _void
70 self._del_callback = callback
71 else:
72 self._del_callback = _void
73
67
74 def on_set(self, callback):
68 def on_set(self, callback):
75 """Register a callback for when an item is changed in the dict.
69 """Register a callback for when an item is changed in the dict.
76
70
77 Allows the listener to detect when items are changed in the dictionary
71 Allows the listener to detect when items are changed in the dictionary
78 and optionally cancel the change.
72 and optionally cancel the change.
79
73
80 callback: callable or None
74 callback: callable or None
81 If you want to ignore the change event, pass None as the callback.
75 If you want to ignore the change event, pass None as the callback.
82 The callback should have a signature of callback(key, value). The
76 The callback should have a signature of callback(key, value). The
83 callback should return a boolean True if the change should be
77 callback should return a boolean True if the change should be
84 canceled, False or None otherwise."""
78 canceled, False or None otherwise."""
85 if callable(callback):
79 self._set_callback = callback if callable(callback) else _void
86 self._set_callback = callback
87 else:
88 self._set_callback = _void
89
80
90 def pop(self, key):
81 def pop(self, key):
91 """Returns the value of an item in the dictionary and then deletes the
82 """Returns the value of an item in the dictionary and then deletes the
92 item from the dictionary."""
83 item from the dictionary."""
93 if self._can_del(key):
84 if self._can_del(key):
94 return dict.pop(self, key)
85 return dict.pop(self, key)
95 else:
86 else:
96 raise Exception('Cannot `pop`, deletion of key "{}" failed.'.format(key))
87 raise Exception('Cannot `pop`, deletion of key "{}" failed.'.format(key))
97
88
98 def popitem(self):
89 def popitem(self):
99 """Pop the next key/value pair from the dictionary."""
90 """Pop the next key/value pair from the dictionary."""
100 key = next(iter(self))
91 key = next(iter(self))
101 return key, self.pop(key)
92 return key, self.pop(key)
102
93
103 def update(self, other_dict):
94 def update(self, other_dict):
104 """Copy the key/value pairs from another dictionary into this dictionary,
95 """Copy the key/value pairs from another dictionary into this dictionary,
105 overwriting any conflicting keys in this dictionary."""
96 overwriting any conflicting keys in this dictionary."""
106 for (key, value) in other_dict.items():
97 for (key, value) in other_dict.items():
107 self[key] = value
98 self[key] = value
108
99
109 def clear(self):
100 def clear(self):
110 """Clear the dictionary."""
101 """Clear the dictionary."""
111 for key in list(self.keys()):
102 for key in list(self.keys()):
112 del self[key]
103 del self[key]
113
104
114 def __setitem__(self, key, value):
105 def __setitem__(self, key, value):
115 if (key in self and self._can_set(key, value)) or \
106 if (key in self and self._can_set(key, value)) or \
116 (key not in self and self._can_add(key, value)):
107 (key not in self and self._can_add(key, value)):
117 return dict.__setitem__(self, key, value)
108 return dict.__setitem__(self, key, value)
118
109
119 def __delitem__(self, key):
110 def __delitem__(self, key):
120 if self._can_del(key):
111 if self._can_del(key):
121 return dict.__delitem__(self, key)
112 return dict.__delitem__(self, key)
122
113
123 def _can_add(self, key, value):
114 def _can_add(self, key, value):
124 """Check if the item can be added to the dict."""
115 """Check if the item can be added to the dict."""
125 return not bool(self._add_callback(key, value))
116 return not bool(self._add_callback(key, value))
126
117
127 def _can_del(self, key):
118 def _can_del(self, key):
128 """Check if the item can be deleted from the dict."""
119 """Check if the item can be deleted from the dict."""
129 return not bool(self._del_callback(key))
120 return not bool(self._del_callback(key))
130
121
131 def _can_set(self, key, value):
122 def _can_set(self, key, value):
132 """Check if the item can be changed in the dict."""
123 """Check if the item can be changed in the dict."""
133 return not bool(self._set_callback(key, value))
124 return not bool(self._set_callback(key, value))
134
125
135
126
136 class EventfulList(list):
127 class EventfulList(list):
137 """Eventful list.
128 """Eventful list.
138
129
139 This class inherits from the Python intrinsic `list` class. It adds events
130 This class inherits from the Python intrinsic `list` class. It adds events
140 that allow you to listen for actions that modify the list. You can
131 that allow you to listen for actions that modify the list. You can
141 optionally cancel the actions.
132 optionally cancel the actions.
142
133
143 See the on_del, on_set, on_insert, on_sort, and on_reverse methods for
134 See the on_del, on_set, on_insert, on_sort, and on_reverse methods for
144 registering an event handler.
135 registering an event handler.
145
136
146 Some of the method docstrings were taken from the Python documentation at
137 Some of the method docstrings were taken from the Python documentation at
147 https://docs.python.org/2/tutorial/datastructures.html"""
138 https://docs.python.org/2/tutorial/datastructures.html"""
148
139
149 def __init__(self, *pargs, **kwargs):
140 def __init__(self, *pargs, **kwargs):
150 """Public constructor"""
141 """Public constructor"""
151 self._insert_callback = _void
142 self._insert_callback = _void
152 self._set_callback = _void
143 self._set_callback = _void
153 self._del_callback = _void
144 self._del_callback = _void
154 self._sort_callback = _void
145 self._sort_callback = _void
155 self._reverse_callback = _void
146 self._reverse_callback = _void
156 list.__init__(self, *pargs, **kwargs)
147 list.__init__(self, *pargs, **kwargs)
157
148
158 def on_events(self, *callbacks):
149 def on_events(self, *callbacks):
159 """Register callbacks for add, set, and del actions.
150 """Register callbacks for add, set, and del actions.
160
151
161 See the doctstrings for on_(insert/set/del/reverse/sort) for details
152 See the doctstrings for on_(insert/set/del/reverse/sort) for details
162 about each callback.
153 about each callback.
163
154
164 insert_callback: callback or None
155 insert_callback: callback or None
165 set_callback: callback or None
156 set_callback: callback or None
166 del_callback: callback or None
157 del_callback: callback or None
167 reverse_callback: callback or None
158 reverse_callback: callback or None
168 sort_callback: callback or None"""
159 sort_callback: callback or None"""
169 registers = ['on_insert', 'on_set', 'on_del', 'on_reverse', 'on_sort']
160 registers = ['on_insert', 'on_set', 'on_del', 'on_reverse', 'on_sort']
170 if len(callbacks) < len(registers):
161 if len(callbacks) < len(registers):
171 raise ValueError('on_events takes {} callbacks'.format(len(registers)))
162 raise ValueError('on_events takes {} callbacks'.format(len(registers)))
172 [getattr(self, n)(callbacks[i]) for i, n in enumerate(registers)]
163 [getattr(self, n)(callbacks[i]) for i, n in enumerate(registers)]
173
164
174 def on_insert(self, callback):
165 def on_insert(self, callback):
175 """Register a callback for when an item is inserted into the list.
166 """Register a callback for when an item is inserted into the list.
176
167
177 Allows the listener to detect when items are inserted into the list and
168 Allows the listener to detect when items are inserted into the list and
178 optionally cancel the insertion.
169 optionally cancel the insertion.
179
170
180 callback: callable or None
171 callback: callable or None
181 If you want to ignore the insertion event, pass None as the callback.
172 If you want to ignore the insertion event, pass None as the callback.
182 The callback should have a signature of callback(index, value). The
173 The callback should have a signature of callback(index, value). The
183 callback should return a boolean True if the insertion should be
174 callback should return a boolean True if the insertion should be
184 canceled, False or None otherwise."""
175 canceled, False or None otherwise."""
185 if callable(callback):
176 self._insert_callback = callback if callable(callback) else _void
186 self._insert_callback = callback
187 else:
188 self._insert_callback = _void
189
177
190 def on_del(self, callback):
178 def on_del(self, callback):
191 """Register a callback for item deletion.
179 """Register a callback for item deletion.
192
180
193 Allows the listener to detect when items are deleted from the list and
181 Allows the listener to detect when items are deleted from the list and
194 optionally cancel the deletion.
182 optionally cancel the deletion.
195
183
196 callback: callable or None
184 callback: callable or None
197 If you want to ignore the deletion event, pass None as the callback.
185 If you want to ignore the deletion event, pass None as the callback.
198 The callback should have a signature of callback(index). The
186 The callback should have a signature of callback(index). The
199 callback should return a boolean True if the deletion should be
187 callback should return a boolean True if the deletion should be
200 canceled, False or None otherwise."""
188 canceled, False or None otherwise."""
201 if callable(callback):
189 self._del_callback = callback if callable(callback) else _void
202 self._del_callback = callback
203 else:
204 self._del_callback = _void
205
190
206 def on_set(self, callback):
191 def on_set(self, callback):
207 """Register a callback for items are set.
192 """Register a callback for items are set.
208
193
209 Allows the listener to detect when items are set and optionally cancel
194 Allows the listener to detect when items are set and optionally cancel
210 the setting. Note, `set` is also called when one or more items are
195 the setting. Note, `set` is also called when one or more items are
211 added to the end of the list.
196 added to the end of the list.
212
197
213 callback: callable or None
198 callback: callable or None
214 If you want to ignore the set event, pass None as the callback.
199 If you want to ignore the set event, pass None as the callback.
215 The callback should have a signature of callback(index, value). The
200 The callback should have a signature of callback(index, value). The
216 callback should return a boolean True if the set should be
201 callback should return a boolean True if the set should be
217 canceled, False or None otherwise."""
202 canceled, False or None otherwise."""
218 if callable(callback):
203 self._set_callback = callback if callable(callback) else _void
219 self._set_callback = callback
220 else:
221 self._set_callback = _void
222
204
223 def on_reverse(self, callback):
205 def on_reverse(self, callback):
224 """Register a callback for list reversal.
206 """Register a callback for list reversal.
225
207
226 callback: callable or None
208 callback: callable or None
227 If you want to ignore the reverse event, pass None as the callback.
209 If you want to ignore the reverse event, pass None as the callback.
228 The callback should have a signature of callback(). The
210 The callback should have a signature of callback(). The
229 callback should return a boolean True if the reverse should be
211 callback should return a boolean True if the reverse should be
230 canceled, False or None otherwise."""
212 canceled, False or None otherwise."""
231 if callable(callback):
213 self._reverse_callback = callback if callable(callback) else _void
232 self._reverse_callback = callback
233 else:
234 self._reverse_callback = _void
235
214
236 def on_sort(self, callback):
215 def on_sort(self, callback):
237 """Register a callback for sortting of the list.
216 """Register a callback for sortting of the list.
238
217
239 callback: callable or None
218 callback: callable or None
240 If you want to ignore the sort event, pass None as the callback.
219 If you want to ignore the sort event, pass None as the callback.
241 The callback signature should match that of Python list's `.sort`
220 The callback signature should match that of Python list's `.sort`
242 method or `callback(*pargs, **kwargs)` as a catch all. The callback
221 method or `callback(*pargs, **kwargs)` as a catch all. The callback
243 should return a boolean True if the reverse should be canceled,
222 should return a boolean True if the reverse should be canceled,
244 False or None otherwise."""
223 False or None otherwise."""
245 if callable(callback):
224 self._sort_callback = callback if callable(callback) else _void
246 self._sort_callback = callback
247 else:
248 self._sort_callback = _void
249
225
250 def append(self, x):
226 def append(self, x):
251 """Add an item to the end of the list."""
227 """Add an item to the end of the list."""
252 self[len(self):] = [x]
228 self[len(self):] = [x]
253
229
254 def extend(self, L):
230 def extend(self, L):
255 """Extend the list by appending all the items in the given list."""
231 """Extend the list by appending all the items in the given list."""
256 self[len(self):] = L
232 self[len(self):] = L
257
233
258 def remove(self, x):
234 def remove(self, x):
259 """Remove the first item from the list whose value is x. It is an error
235 """Remove the first item from the list whose value is x. It is an error
260 if there is no such item."""
236 if there is no such item."""
261 del self[self.index(x)]
237 del self[self.index(x)]
262
238
263 def pop(self, i=None):
239 def pop(self, i=None):
264 """Remove the item at the given position in the list, and return it. If
240 """Remove the item at the given position in the list, and return it. If
265 no index is specified, a.pop() removes and returns the last item in the
241 no index is specified, a.pop() removes and returns the last item in the
266 list."""
242 list."""
267 if i is None:
243 if i is None:
268 i = len(self) - 1
244 i = len(self) - 1
269 val = self[i]
245 val = self[i]
270 del self[i]
246 del self[i]
271 return val
247 return val
272
248
273 def reverse(self):
249 def reverse(self):
274 """Reverse the elements of the list, in place."""
250 """Reverse the elements of the list, in place."""
275 if self._can_reverse():
251 if self._can_reverse():
276 list.reverse(self)
252 list.reverse(self)
277
253
278 def insert(self, index, value):
254 def insert(self, index, value):
279 """Insert an item at a given position. The first argument is the index
255 """Insert an item at a given position. The first argument is the index
280 of the element before which to insert, so a.insert(0, x) inserts at the
256 of the element before which to insert, so a.insert(0, x) inserts at the
281 front of the list, and a.insert(len(a), x) is equivalent to
257 front of the list, and a.insert(len(a), x) is equivalent to
282 a.append(x)."""
258 a.append(x)."""
283 if self._can_insert(index, value):
259 if self._can_insert(index, value):
284 list.insert(self, index, value)
260 list.insert(self, index, value)
285
261
286 def sort(self, *pargs, **kwargs):
262 def sort(self, *pargs, **kwargs):
287 """Sort the items of the list in place (the arguments can be used for
263 """Sort the items of the list in place (the arguments can be used for
288 sort customization, see Python's sorted() for their explanation)."""
264 sort customization, see Python's sorted() for their explanation)."""
289 if self._can_sort(*pargs, **kwargs):
265 if self._can_sort(*pargs, **kwargs):
290 list.sort(self, *pargs, **kwargs)
266 list.sort(self, *pargs, **kwargs)
291
267
292 def __delitem__(self, index):
268 def __delitem__(self, index):
293 if self._can_del(index):
269 if self._can_del(index):
294 list.__delitem__(self, index)
270 list.__delitem__(self, index)
295
271
296 def __setitem__(self, index, value):
272 def __setitem__(self, index, value):
297 if self._can_set(index, value):
273 if self._can_set(index, value):
298 list.__setitem__(self, index, value)
274 list.__setitem__(self, index, value)
299
275
300 def __setslice__(self, start, end, value):
276 def __setslice__(self, start, end, value):
301 if self._can_set(slice(start, end), value):
277 if self._can_set(slice(start, end), value):
302 list.__setslice__(self, start, end, value)
278 list.__setslice__(self, start, end, value)
303
279
304 def _can_insert(self, index, value):
280 def _can_insert(self, index, value):
305 """Check if the item can be inserted."""
281 """Check if the item can be inserted."""
306 return not bool(self._insert_callback(index, value))
282 return not bool(self._insert_callback(index, value))
307
283
308 def _can_del(self, index):
284 def _can_del(self, index):
309 """Check if the item can be deleted."""
285 """Check if the item can be deleted."""
310 return not bool(self._del_callback(index))
286 return not bool(self._del_callback(index))
311
287
312 def _can_set(self, index, value):
288 def _can_set(self, index, value):
313 """Check if the item can be set."""
289 """Check if the item can be set."""
314 return not bool(self._set_callback(index, value))
290 return not bool(self._set_callback(index, value))
315
291
316 def _can_reverse(self):
292 def _can_reverse(self):
317 """Check if the list can be reversed."""
293 """Check if the list can be reversed."""
318 return not bool(self._reverse_callback())
294 return not bool(self._reverse_callback())
319
295
320 def _can_sort(self, *pargs, **kwargs):
296 def _can_sort(self, *pargs, **kwargs):
321 """Check if the list can be sorted."""
297 """Check if the list can be sorted."""
322 return not bool(self._sort_callback(*pargs, **kwargs))
298 return not bool(self._sort_callback(*pargs, **kwargs))
General Comments 0
You need to be logged in to leave comments. Login now