Ensure that environment maintanence runs regularly.
[didex.git] / didex / store.py
index a9bf0cd..2b9d65c 100644 (file)
@@ -1,4 +1,4 @@
-import threading, pickle, inspect, atexit
+import threading, pickle, inspect, atexit, weakref
 from . import db, index, cache
 from .db import txnfun
 
@@ -47,8 +47,30 @@ def storedescs(obj):
         t.__didex_attr = ret
     return ret
 
+class icache(object):
+    def __init__(self):
+        self.d = weakref.WeakKeyDictionary()
+
+    def __getitem__(self, key):
+        obj, idx = key
+        return self.d[obj][idx]
+    def __setitem__(self, key, val):
+        obj, idx = key
+        if obj in self.d:
+            self.d[obj][idx] = val
+        else:
+            self.d[obj] = {idx: val}
+    def __delitem__(self, key):
+        obj, idx = key
+        del self.d[obj][idx]
+    def get(self, key, default=None):
+        obj, idx = key
+        if obj not in self.d:
+            return default
+        return self.d[obj].get(idx, default)
+
 class datastore(object):
-    def __init__(self, name, *, env=None, path=".", ncache=None):
+    def __init__(self, name, *, env=None, path=".", ncache=None, codec=None):
         self.name = name
         self.lk = threading.Lock()
         if env:
@@ -58,8 +80,11 @@ class datastore(object):
         self._db = None
         if ncache is None:
             ncache = cache.cache()
+        if codec is not None:
+            self._encode, self._decode = codec
         self.cache = ncache
         self.cache.load = self._load
+        self.icache = icache()
 
     def db(self):
         with self.lk:
@@ -67,19 +92,28 @@ class datastore(object):
                 self._db = self.env().db(self.name)
             return self._db
 
-    def _load(self, id):
+    def _decode(self, data):
         try:
-            return pickle.loads(self.db().get(id))
+            return pickle.loads(data)
         except:
             raise KeyError(id, "could not unpickle data")
 
     def _encode(self, obj):
         return pickle.dumps(obj)
 
+    @txnfun(lambda self: self.db().env)
+    def _load(self, id, *, tx):
+        loaded = self._decode(self.db().get(id, tx=tx))
+        if hasattr(loaded, "__didex_loaded__"):
+            loaded.__didex_loaded__(self, id)
+        for nm, attr in storedescs(loaded):
+            attr.loaded(id, loaded, tx)
+        return loaded
+
     def get(self, id, *, load=True):
         return self.cache.get(id, load=load)
 
-    @txnfun(lambda self: self.db().env.env)
+    @txnfun(lambda self: self.db().env)
     def register(self, obj, *, tx):
         id = self.db().add(self._encode(obj), tx=tx)
         for nm, attr in storedescs(obj):
@@ -87,7 +121,7 @@ class datastore(object):
         self.cache.put(id, obj)
         return id
 
-    @txnfun(lambda self: self.db().env.env)
+    @txnfun(lambda self: self.db().env)
     def unregister(self, id, *, vfy=None, tx):
         obj = self.get(id)
         if vfy is not None and obj is not vfy:
@@ -97,7 +131,7 @@ class datastore(object):
         self.db().remove(id, tx=tx)
         self.cache.remove(id)
 
-    @txnfun(lambda self: self.db().env.env)
+    @txnfun(lambda self: self.db().env)
     def update(self, id, *, vfy=None, tx):
         obj = self.get(id, load=False)
         if vfy is not None and obj is not vfy:
@@ -110,13 +144,17 @@ class autotype(type):
     def __call__(self, *args, **kwargs):
         new = super().__call__(*args, **kwargs)
         new.id = self.store.register(new)
-        self.store.update(new.id, vfy=new) # This doesn't feel too nice.
+        # XXX? ID is not saved now, but relied upon to be __didex_loaded__ later.
         return new
 
 class autostore(object, metaclass=autotype):
     def __init__(self):
         self.id = None
 
+    def __didex_loaded__(self, store, id):
+        assert self.id is None or self.id == id
+        self.id = id
+
     def save(self):
         self.store.update(self.id, vfy=self)