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