diff src/memory/persistent.py @ 2182:087eec4c6c07

memory (persistent, sqlite): better private values handling + new LazyPersistentBinaryDict: - merged private values method handling in sqlite, and added a keys arguments to get only some keys - LazyPersistentBinaryDict allow to get values in database only when they are needed, saving memory for big data
author Goffi <goffi@goffi.org>
date Sun, 12 Mar 2017 19:33:17 +0100
parents 2daf7b4c6756
children ea41cf1e6d29
line wrap: on
line diff
--- a/src/memory/persistent.py	Sun Mar 12 19:32:59 2017 +0100
+++ b/src/memory/persistent.py	Sun Mar 12 19:33:17 2017 +0100
@@ -28,30 +28,38 @@
 
 class PersistentDict(object):
     r"""A dictionary which save persistently each value assigned
+
     /!\ be careful, each assignment means a database write
     /!\ Memory must be initialised before loading/setting value with instances of this class"""
     storage = None
+    binary = False
 
     def __init__(self, namespace, profile=None):
-        """@param namespace: unique namespace for this dictionary
-        @param profile: profile which *MUST* exists, or None for general values"""
+        """
+
+        @param namespace: unique namespace for this dictionary
+        @param profile(unicode, None): profile which *MUST* exists, or None for general values
+        """
         if not self.storage:
             log.error(_("PersistentDict can't be used before memory initialisation"))
             raise MemoryNotInitializedError
-        self._cache = {}
+        self._cache = None
         self.namespace = namespace
         self.profile = profile
 
+    def _setCache(self, data):
+        self._cache = data
+
     def load(self):
         """Load persistent data from storage.
 
+        need to be called before any other operation
         @return: defers the PersistentDict instance itself
         """
-        if not self.profile:
-            d = self.storage.loadGenPrivates(self._cache, self.namespace)
-        else:
-            d = self.storage.loadIndPrivates(self._cache, self.namespace, self.profile)
-        return d.addCallback(lambda dummy: self)
+        d = self.storage.getPrivates(self.namespace, binary=self.binary, profile=self.profile)
+        d.addCallback(self._setCache)
+        d.addCallback(lambda dummy: self)
+        return d
 
     def iteritems(self):
         return self._cache.iteritems()
@@ -102,17 +110,11 @@
         return self._cache.__getitem__(key)
 
     def __setitem__(self, key, value):
-        if not self.profile:
-            self.storage.setGenPrivate(self.namespace, key, value)
-        else:
-            self.storage.setIndPrivate(self.namespace, key, value, self.profile)
+        self.storage.setPrivateValue(self.namespace, key, value, self.binary, self.profile)
         return self._cache.__setitem__(key, value)
 
     def __delitem__(self, key):
-        if not self.profile:
-            self.storage.delGenPrivate(self.namespace, key)
-        else:
-            self.storage.delIndPrivate(self.namespace, key, self.profile)
+        self.storage.detPrivateValue(self.namepace, key, self.binary, self.profile)
         return self._cache.__delitem__(key)
 
     def get(self, key, default=None):
@@ -127,40 +129,103 @@
 
     def force(self, name):
         """Force saving of an attribute to storage
-        @return: deferred fired when data is actually saved"""
-        if not self.profile:
-            return self.storage.setGenPrivate(self.namespace, name, self._cache[name])
-        return self.storage.setIndPrivate(self.namespace, name, self._cache[name], self.profile)
+
+        @return: deferred fired when data is actually saved
+        """
+        return self.storage.setPrivateValue(self.namespace, name, self._cache[name], self.binary, self.profile)
 
 
 class PersistentBinaryDict(PersistentDict):
     """Persistent dict where value can be any python data (instead of string only)"""
+    binary = True
+
+
+class LazyPersistentBinaryDict(PersistentBinaryDict):
+    ur"""PersistentBinaryDict which get key/value when needed
+
+    This Persistent need more database access, it is suitable for largest data,
+    to save memory.
+    /!\ most of methods return a Deferred
+    """
+    # TODO: missing methods should be implemented using database access
+    # TODO: a cache would be useful (which is deleted after a timeout)
 
     def load(self):
-        """load persistent data from storage
-        """
-        if not self.profile:
-            return self.storage.loadGenPrivatesBinary(self._cache, self.namespace)
-        else:
-            return self.storage.loadIndPrivatesBinary(self._cache, self.namespace, self.profile)
+        # we show a warning as calling load on LazyPersistentBinaryDict sounds like a code mistake
+        log.warning(_(u"Calling load on LazyPersistentBinaryDict while it's not needed"))
+
+    def iteritems(self):
+        raise NotImplementedError
+
+    def items(self):
+        return self.storage.getPrivates(self.namespace, binary=self.binary, profile=self.profile)
+
+    def __repr__(self):
+        raise NotImplementedError
+
+    def __str__(self):
+        return "LazyPersistentBinaryDict (namespace: {})".format(self.namespace)
+
+    def __lt__(self, other):
+        raise NotImplementedError
+
+    def __le__(self, other):
+        raise NotImplementedError
+
+    def __eq__(self, other):
+        raise NotImplementedError
+
+    def __ne__(self, other):
+        raise NotImplementedError
+
+    def __gt__(self, other):
+        raise NotImplementedError
+
+    def __ge__(self, other):
+        raise NotImplementedError
+
+    def __cmp__(self, other):
+        raise NotImplementedError
+
+    def __hash__(self):
+        return hash(unicode(self.__class__) + self.namespace + (self.profile or u''))
+
+    def __nonzero__(self):
+        raise NotImplementedError
+
+    def __contains__(self, key):
+        raise NotImplementedError
+
+    def __iter__(self):
+        raise NotImplementedError
+
+    def __getitem__(self, key):
+        """get the value as a Deferred"""
+        return self.storage.getPrivates(self.namespace, keys=[key], binary=self.binary, profile=self.profile)
 
     def __setitem__(self, key, value):
-        if not self.profile:
-            self.storage.setGenPrivateBinary(self.namespace, key, value)
-        else:
-            self.storage.setIndPrivateBinary(self.namespace, key, value, self.profile)
-        return self._cache.__setitem__(key, value)
+        self.storage.setPrivateValue(self.namespace, key, value, self.binary, self.profile)
 
     def __delitem__(self, key):
-        if not self.profile:
-            self.storage.delGenPrivateBinary(self.namespace, key)
-        else:
-            self.storage.delIndPrivateBinary(self.namespace, key, self.profile)
-        return self._cache.__delitem__(key)
+        self.storage.detPrivateValue(self.namepace, key, self.binary, self.profile)
+
+    def _valueOrDefault(self, value, default):
+        if value is None:
+            return default
+        return value
 
-    def force(self, name):
+    def get(self, key, default=None):
+        d = self.__getitem__(key)
+        d.addCallback(self._valueOrDefault)
+        return d
+
+    def setdefault(self, key, default):
+        raise NotImplementedError
+
+    def force(self, name, value):
         """Force saving of an attribute to storage
-        @return: deferred fired when data is actually saved"""
-        if not self.profile:
-            return self.storage.setGenPrivateBinary(self.namespace, name, self._cache[name])
-        return self.storage.setIndPrivateBinary(self.namespace, name, self._cache[name], self.profile)
+
+        @param value(object): value is needed for LazyPersistentBinaryDict
+        @return: deferred fired when data is actually saved
+        """
+        return self.storage.setPrivateValue(self.namespace, name, value, self.binary, self.profile)