Mercurial > libervia-backend
comparison libervia/backend/memory/persistent.py @ 4071:4b842c1fb686
refactoring: renamed `sat` package to `libervia.backend`
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 02 Jun 2023 11:49:51 +0200 |
parents | sat/memory/persistent.py@524856bd7b19 |
children | 0d7bb4df2343 |
comparison
equal
deleted
inserted
replaced
4070:d10748475025 | 4071:4b842c1fb686 |
---|---|
1 #!/usr/bin/env python3 | |
2 | |
3 | |
4 # SAT: a jabber client | |
5 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org) | |
6 | |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
17 # You should have received a copy of the GNU Affero General Public License | |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
20 from twisted.internet import defer | |
21 from twisted.python import failure | |
22 from libervia.backend.core.i18n import _ | |
23 from libervia.backend.core.log import getLogger | |
24 | |
25 | |
26 log = getLogger(__name__) | |
27 | |
28 | |
29 class MemoryNotInitializedError(Exception): | |
30 pass | |
31 | |
32 | |
33 class PersistentDict: | |
34 r"""A dictionary which save persistently each value assigned | |
35 | |
36 /!\ be careful, each assignment means a database write | |
37 /!\ Memory must be initialised before loading/setting value with instances of this class""" | |
38 storage = None | |
39 binary = False | |
40 | |
41 def __init__(self, namespace, profile=None): | |
42 """ | |
43 | |
44 @param namespace: unique namespace for this dictionary | |
45 @param profile(unicode, None): profile which *MUST* exists, or None for general values | |
46 """ | |
47 if not self.storage: | |
48 log.error(_("PersistentDict can't be used before memory initialisation")) | |
49 raise MemoryNotInitializedError | |
50 self._cache = {} | |
51 self.namespace = namespace | |
52 self.profile = profile | |
53 | |
54 def _set_cache(self, data): | |
55 self._cache = data | |
56 | |
57 def load(self): | |
58 """Load persistent data from storage. | |
59 | |
60 need to be called before any other operation | |
61 @return: defers the PersistentDict instance itself | |
62 """ | |
63 d = defer.ensureDeferred(self.storage.get_privates( | |
64 self.namespace, binary=self.binary, profile=self.profile | |
65 )) | |
66 d.addCallback(self._set_cache) | |
67 d.addCallback(lambda __: self) | |
68 return d | |
69 | |
70 def iteritems(self): | |
71 return iter(self._cache.items()) | |
72 | |
73 def items(self): | |
74 return self._cache.items() | |
75 | |
76 def __repr__(self): | |
77 return self._cache.__repr__() | |
78 | |
79 def __str__(self): | |
80 return self._cache.__str__() | |
81 | |
82 def __lt__(self, other): | |
83 return self._cache.__lt__(other) | |
84 | |
85 def __le__(self, other): | |
86 return self._cache.__le__(other) | |
87 | |
88 def __eq__(self, other): | |
89 return self._cache.__eq__(other) | |
90 | |
91 def __ne__(self, other): | |
92 return self._cache.__ne__(other) | |
93 | |
94 def __gt__(self, other): | |
95 return self._cache.__gt__(other) | |
96 | |
97 def __ge__(self, other): | |
98 return self._cache.__ge__(other) | |
99 | |
100 def __cmp__(self, other): | |
101 return self._cache.__cmp__(other) | |
102 | |
103 def __hash__(self): | |
104 return self._cache.__hash__() | |
105 | |
106 def __bool__(self): | |
107 return self._cache.__len__() != 0 | |
108 | |
109 def __contains__(self, key): | |
110 return self._cache.__contains__(key) | |
111 | |
112 def __iter__(self): | |
113 return self._cache.__iter__() | |
114 | |
115 def __getitem__(self, key): | |
116 return self._cache.__getitem__(key) | |
117 | |
118 def __setitem__(self, key, value): | |
119 defer.ensureDeferred( | |
120 self.storage.set_private_value( | |
121 self.namespace, key, value, self.binary, self.profile | |
122 ) | |
123 ) | |
124 return self._cache.__setitem__(key, value) | |
125 | |
126 def __delitem__(self, key): | |
127 self.storage.del_private_value(self.namespace, key, self.binary, self.profile) | |
128 return self._cache.__delitem__(key) | |
129 | |
130 def clear(self): | |
131 """Delete all values from this namespace""" | |
132 self._cache.clear() | |
133 return self.storage.del_private_namespace(self.namespace, self.binary, self.profile) | |
134 | |
135 def get(self, key, default=None): | |
136 return self._cache.get(key, default) | |
137 | |
138 def aset(self, key, value): | |
139 """Async set, return a Deferred fired when value is actually stored""" | |
140 self._cache.__setitem__(key, value) | |
141 return defer.ensureDeferred( | |
142 self.storage.set_private_value( | |
143 self.namespace, key, value, self.binary, self.profile | |
144 ) | |
145 ) | |
146 | |
147 def adel(self, key): | |
148 """Async del, return a Deferred fired when value is actually deleted""" | |
149 self._cache.__delitem__(key) | |
150 return self.storage.del_private_value( | |
151 self.namespace, key, self.binary, self.profile) | |
152 | |
153 def setdefault(self, key, default): | |
154 try: | |
155 return self._cache[key] | |
156 except: | |
157 self.__setitem__(key, default) | |
158 return default | |
159 | |
160 def force(self, name): | |
161 """Force saving of an attribute to storage | |
162 | |
163 @return: deferred fired when data is actually saved | |
164 """ | |
165 return defer.ensureDeferred( | |
166 self.storage.set_private_value( | |
167 self.namespace, name, self._cache[name], self.binary, self.profile | |
168 ) | |
169 ) | |
170 | |
171 | |
172 class PersistentBinaryDict(PersistentDict): | |
173 """Persistent dict where value can be any python data (instead of string only)""" | |
174 binary = True | |
175 | |
176 | |
177 class LazyPersistentBinaryDict(PersistentBinaryDict): | |
178 r"""PersistentBinaryDict which get key/value when needed | |
179 | |
180 This Persistent need more database access, it is suitable for largest data, | |
181 to save memory. | |
182 /!\ most of methods return a Deferred | |
183 """ | |
184 # TODO: missing methods should be implemented using database access | |
185 # TODO: a cache would be useful (which is deleted after a timeout) | |
186 | |
187 def load(self): | |
188 # we show a warning as calling load on LazyPersistentBinaryDict sounds like a code mistake | |
189 log.warning(_("Calling load on LazyPersistentBinaryDict while it's not needed")) | |
190 | |
191 def iteritems(self): | |
192 raise NotImplementedError | |
193 | |
194 def items(self): | |
195 d = defer.ensureDeferred(self.storage.get_privates( | |
196 self.namespace, binary=self.binary, profile=self.profile | |
197 )) | |
198 d.addCallback(lambda data_dict: data_dict.items()) | |
199 return d | |
200 | |
201 def all(self): | |
202 return defer.ensureDeferred(self.storage.get_privates( | |
203 self.namespace, binary=self.binary, profile=self.profile | |
204 )) | |
205 | |
206 def __repr__(self): | |
207 return self.__str__() | |
208 | |
209 def __str__(self): | |
210 return "LazyPersistentBinaryDict (namespace: {})".format(self.namespace) | |
211 | |
212 def __lt__(self, other): | |
213 raise NotImplementedError | |
214 | |
215 def __le__(self, other): | |
216 raise NotImplementedError | |
217 | |
218 def __eq__(self, other): | |
219 raise NotImplementedError | |
220 | |
221 def __ne__(self, other): | |
222 raise NotImplementedError | |
223 | |
224 def __gt__(self, other): | |
225 raise NotImplementedError | |
226 | |
227 def __ge__(self, other): | |
228 raise NotImplementedError | |
229 | |
230 def __cmp__(self, other): | |
231 raise NotImplementedError | |
232 | |
233 def __hash__(self): | |
234 return hash(str(self.__class__) + self.namespace + (self.profile or '')) | |
235 | |
236 def __bool__(self): | |
237 raise NotImplementedError | |
238 | |
239 def __contains__(self, key): | |
240 raise NotImplementedError | |
241 | |
242 def __iter__(self): | |
243 raise NotImplementedError | |
244 | |
245 def _data2value(self, data, key): | |
246 try: | |
247 return data[key] | |
248 except KeyError as e: | |
249 # we return a Failure here to avoid the jump | |
250 # into debugger in debug mode. | |
251 raise failure.Failure(e) | |
252 | |
253 def __getitem__(self, key): | |
254 """get the value as a Deferred""" | |
255 d = defer.ensureDeferred(self.storage.get_privates( | |
256 self.namespace, keys=[key], binary=self.binary, profile=self.profile | |
257 )) | |
258 d.addCallback(self._data2value, key) | |
259 return d | |
260 | |
261 def __setitem__(self, key, value): | |
262 defer.ensureDeferred( | |
263 self.storage.set_private_value( | |
264 self.namespace, key, value, self.binary, self.profile | |
265 ) | |
266 ) | |
267 | |
268 def __delitem__(self, key): | |
269 self.storage.del_private_value(self.namespace, key, self.binary, self.profile) | |
270 | |
271 def _default_or_exception(self, failure_, default): | |
272 failure_.trap(KeyError) | |
273 return default | |
274 | |
275 def get(self, key, default=None): | |
276 d = self.__getitem__(key) | |
277 d.addErrback(self._default_or_exception, default=default) | |
278 return d | |
279 | |
280 def aset(self, key, value): | |
281 """Async set, return a Deferred fired when value is actually stored""" | |
282 # FIXME: redundant with force, force must be removed | |
283 # XXX: similar as PersistentDict.aset, but doesn't use cache | |
284 return defer.ensureDeferred( | |
285 self.storage.set_private_value( | |
286 self.namespace, key, value, self.binary, self.profile | |
287 ) | |
288 ) | |
289 | |
290 def adel(self, key): | |
291 """Async del, return a Deferred fired when value is actually deleted""" | |
292 # XXX: similar as PersistentDict.adel, but doesn't use cache | |
293 return self.storage.del_private_value( | |
294 self.namespace, key, self.binary, self.profile) | |
295 | |
296 def setdefault(self, key, default): | |
297 raise NotImplementedError | |
298 | |
299 def force(self, name, value): | |
300 """Force saving of an attribute to storage | |
301 | |
302 @param value(object): value is needed for LazyPersistentBinaryDict | |
303 @return: deferred fired when data is actually saved | |
304 """ | |
305 return defer.ensureDeferred( | |
306 self.storage.set_private_value( | |
307 self.namespace, name, value, self.binary, self.profile | |
308 ) | |
309 ) | |
310 | |
311 def remove(self, key): | |
312 """Delete a key from sotrage, and return a deferred called when it's done | |
313 | |
314 @param key(unicode): key to delete | |
315 @return (D): A deferred fired when delete is done | |
316 """ | |
317 return self.storage.del_private_value(self.namespace, key, self.binary, self.profile) |