1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """
23 Flumotion Twisted-like flavors
24
25 Inspired by L{twisted.spread.flavors}
26 """
27
28 from twisted.internet import defer
29 from twisted.python import components
30 from twisted.spread import pb
31
32
33 from flumotion.twisted import compat
34 compat.filterWarnings(components, 'ComponentsDeprecationWarning')
35
36 from flumotion.twisted.compat import Interface
37
38
40 """
41 I am an interface for objects that want to listen to changes on
42 cached states.
43 """
45 """
46 @type object: L{StateRemoteCache}
47 @param object: the state object having changed
48 @type key: string
49 @param key: the key being set
50 @param value: the value the key is being set to
51
52 The given key on the given object has been set to the given value.
53 """
54
56 """
57 @type object: L{StateRemoteCache}
58 @param object: the state object having changed
59 @type key: string
60 @param key: the key being appended to
61 @param value: the value being appended to the list given by key
62
63 The given value has been added to the list given by the key.
64 """
65
67 """
68 @type object: L{StateRemoteCache}
69 @param object: the state object having changed
70 @type key: string
71 @param key: the key being removed from
72 @param value: the value being removed from the list given by key
73
74 The given value has been removed from the list given by the key.
75 """
76
78 """
79 I am a cacheable state object.
80
81 I cache key-value pairs, where values can be either single objects
82 or list of objects.
83 """
85 self._observers = []
86 self._dict = {}
87
88
89 - def addKey(self, key, value=None):
90 """
91 Add a key to the state cache so it can be used with set.
92 """
93 self._dict[key] = value
94
95
96
98 """
99 Add a key for a list of objects to the state cache.
100 """
101 if value is None:
102 value = []
103 self._dict[key] = value
104
105
106
108 """
109 Add a key for a dict value to the state cache.
110 """
111 if value is None:
112 value = {}
113 self._dict[key] = value
114
116 return key in self._dict.keys()
117
119 return self._dict.keys()
120
121 - def get(self, key, otherwise=None):
122 """
123 Get the state cache value for the given key.
124
125 Return otherwise in case where key is present but value None.
126 """
127 if not key in self._dict.keys():
128 raise KeyError('%s in %r' % (key, self))
129
130 v = self._dict[key]
131
132 if v == None:
133 return otherwise
134
135 return v
136
137 - def set(self, key, value):
138 """
139 Set a given state key to the given value.
140 Notifies observers of this Cacheable through observe_set.
141 """
142 if not key in self._dict.keys():
143 raise KeyError('%s in %r' % (key, self))
144
145 self._dict[key] = value
146 list = [o.callRemote('set', key, value) for o in self._observers]
147 return defer.DeferredList(list)
148
149 - def append(self, key, value):
150 """
151 Append the given object to the given list.
152 Notifies observers of this Cacheable through observe_append.
153 """
154 if not key in self._dict.keys():
155 raise KeyError('%s in %r' % (key, self))
156
157 self._dict[key].append(value)
158 list = [o.callRemote('append', key, value) for o in self._observers]
159 return defer.DeferredList(list)
160
161 - def remove(self, key, value):
162 """
163 Remove the given object from the given list.
164 Notifies observers of this Cacheable through observe_remove.
165 """
166 if not key in self._dict.keys():
167 raise KeyError('%s in %r' % (key, self))
168
169 try:
170 self._dict[key].remove(value)
171 except ValueError:
172 raise ValueError('value %r not in list %r for key %r' % (
173 value, self._dict[key], key))
174 list = [o.callRemote('remove', key, value) for o in self._observers]
175 dl = defer.DeferredList(list)
176 return dl
177
178 - def setitem(self, key, subkey, value):
179 """
180 Set a value in the given dict.
181 Notifies observers of this Cacheable through observe_setitem.
182 """
183 if not key in self._dict.keys():
184 raise KeyError('%s in %r' % (key, self))
185
186 self._dict[key][subkey] = value
187 list = [o.callRemote('setitem', key, subkey, value)
188 for o in self._observers]
189 return defer.DeferredList(list)
190
192 """
193 Removes an element from the given dict. Note that the key refers
194 to the dict; it is the subkey (and its value) that will be removed.
195 Notifies observers of this Cacheable through observe_delitem.
196 """
197 if not key in self._dict.keys():
198 raise KeyError('%s in %r' % (key, self))
199
200 try:
201 value = self._dict[key].pop(subkey)
202 except KeyError:
203 raise KeyError('key %r not in dict %r for key %r' % (
204 subkey, self._dict[key], key))
205 list = [o.callRemote('delitem', key, subkey, value) for o in
206 self._observers]
207 dl = defer.DeferredList(list)
208 return dl
209
210
212 self._observers.append(observer)
213 return self._dict
214
216 self._observers.remove(observer)
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
233 """
234 I am a remote cache of a state object.
235 """
238
239
240
241
243 return key in self._dict.keys()
244
246 return self._dict.keys()
247
248 - def get(self, key, otherwise=None):
249 """
250 Get the state cache value for the given key.
251
252 Return otherwise in case where key is present but value None.
253 """
254 if not key in self._dict.keys():
255 raise KeyError('%s in %r' % (key, self))
256
257 v = self._dict[key]
258
259 if v == None:
260 return otherwise
261
262 return v
263
265
266
267 if not hasattr(self, '_listeners'):
268
269
270 self._listeners = {}
271
272 - def addListener(self, listener, set=None, append=None, remove=None,
273 setitem=None, delitem=None, invalidate=None):
274 """
275 Adds a listener to the remote cache.
276
277 The caller will be notified of state events via the functions
278 given as the 'set', 'append', and 'remove', 'setitem', and
279 'delitem' keyword arguments.
280
281 Setting one of the event handlers to None will ignore that
282 event. It is an error for all event handlers to be None.
283
284 @param listener: A new listener object that wants to receive
285 cache state change notifications.
286 @type listener: object implementing
287 L{flumotion.twisted.flavors.IStateListener}
288 @param set: A procedure to call when a value is set
289 @type set: procedure(object, key, value) -> None
290 @param append: A procedure to call when a value is appended to a
291 list
292 @type append: procedure(object, key, value) -> None
293 @param remove: A procedure to call when a value is removed from
294 a list
295 @type remove: procedure(object, key, value) -> None
296 @param setitem: A procedure to call when a value is set in a
297 dict.
298 @type setitem: procedure(object, key, subkey, value) -> None
299 @param delitem: A procedure to call when a value is removed
300 from a dict.
301 @type delitem: procedure(object, key, subkey, value) -> None
302 @param invalidate: A procedure to call when this cache has been
303 invalidated.
304 @type invalidate: procedure(object) -> None
305 """
306 if not (set or append or remove or setitem or delitem or invalidate):
307 print ("Warning: Use of deprecated %r.addListener(%r) without "
308 "explicit event handlers" % (self, listener))
309 set = listener.stateSet
310 append = listener.stateAppend
311 remove = listener.stateRemove
312 self._ensureListeners()
313 if listener in self._listeners:
314 raise KeyError, listener
315 self._listeners[listener] = [set, append, remove, setitem,
316 delitem, invalidate]
317 if invalidate and hasattr(self, '_cache_invalid'):
318 invalidate(self)
319
321 self._ensureListeners()
322 if listener not in self._listeners:
323 raise KeyError, listener
324 del self._listeners[listener]
325
326
329
342
356
374
388
406
408 """Invalidate this StateRemoteCache.
409
410 Calling this method will result in the invalidate callback being
411 called for all listeners that passed an invalidate handler to
412 addListener. This method is not called automatically; it is
413 provided as a convenience to applications.
414 """
415 assert not hasattr(self, '_cache_invalid'), \
416 'object has already been invalidated'
417
418
419
420
421 setattr(self, '_cache_invalid', True)
422
423 self._ensureListeners()
424
425 for l in self._listeners.keys():
426 invalidate = self._listeners[l][5]
427 if invalidate:
428 invalidate(self)
429