Added autostore convenience type.
[didex.git] / didex / store.py
CommitLineData
b080a59c
FT
1import threading, pickle
2from . import db, index, cache
3from .db import txnfun
4
5class environment(object):
eca9b3be
FT
6 def __init__(self, *, path=None, getpath=None, recover=False):
7 if path is not None:
8 self.path = path
9 self.getpath = None
10 else:
11 self.path = None
12 self.getpath = getpath
13 self.recover = recover
b080a59c
FT
14 self.lk = threading.Lock()
15 self.bk = None
16
17 def __call__(self):
18 with self.lk:
19 if self.bk is None:
eca9b3be
FT
20 if self.path is None:
21 self.path = self.getpath()
22 self.bk = db.environment(self.path, recover=self.recover)
b080a59c
FT
23 return self.bk
24
25 def close(self):
26 with self.lk:
27 if self.bk is not None:
28 self.bk.close()
29 self.bk = None
30
31class storedesc(object):
32 pass
33
34def storedescs(obj):
35 t = type(obj)
36 ret = getattr(t, "__didex_attr", None)
37 if ret is None:
38 ret = []
39 for nm, val in t.__dict__.items():
40 if isinstance(val, storedesc):
41 ret.append((nm, val))
42 t.__didex_attr = ret
43 return ret
44
45class store(object):
46 def __init__(self, name, *, env=None, path=".", ncache=None):
47 self.name = name
48 self.lk = threading.Lock()
49 if env:
50 self.env = env
51 else:
eca9b3be 52 self.env = environment(path=path)
b080a59c
FT
53 self._db = None
54 if ncache is None:
55 ncache = cache.cache()
56 self.cache = ncache
57 self.cache.load = self._load
58
59 def db(self):
60 with self.lk:
61 if self._db is None:
62 self._db = self.env().db(self.name)
63 return self._db
64
65 def _load(self, id):
66 try:
67 return pickle.loads(self.db().get(id))
68 except:
69 raise KeyError(id, "could not unpickle data")
70
71 def _encode(self, obj):
72 return pickle.dumps(obj)
73
74 def get(self, id, *, load=True):
75 return self.cache.get(id, load=load)
76
77 @txnfun(lambda self: self.db().env.env)
78 def register(self, obj, *, tx):
79 id = self.db().add(self._encode(obj), tx=tx)
80 for nm, attr in storedescs(obj):
81 attr.register(id, obj, tx)
82 self.cache.put(id, obj)
83 return id
84
85 @txnfun(lambda self: self.db().env.env)
ca180faa 86 def unregister(self, id, *, vfy=None, tx):
b080a59c 87 obj = self.get(id)
ca180faa
FT
88 if vfy is not None and obj is not vfy:
89 raise RuntimeError("object identity crisis: " + str(vfy) + " is not cached object " + obj)
b080a59c
FT
90 for nm, attr in storedescs(obj):
91 attr.unregister(id, obj, tx)
92 self.db().remove(id, tx=tx)
93 self.cache.remove(id)
94
95 @txnfun(lambda self: self.db().env.env)
ca180faa 96 def update(self, id, *, vfy=None, tx):
b080a59c 97 obj = self.get(id, load=False)
ca180faa
FT
98 if vfy is not None and obj is not vfy:
99 raise RuntimeError("object identity crisis: " + str(vfy) + " is not cached object " + obj)
b080a59c
FT
100 for nm, attr, in storedescs(obj):
101 attr.update(id, obj, tx)
102 self.db().replace(id, self._encode(obj), tx=tx)
ca180faa
FT
103
104class autotype(type):
105 def __call__(self, *args, **kwargs):
106 new = super().__call__(*args, **kwargs)
107 new.id = self.store.register(new)
108 return new
109
110class autostore(object, metaclass=autotype):
111 def __init__(self):
112 self.id = None
113
114 def save(self):
115 self.store.update(self.id, vfy=self)
116
117 def remove(self):
118 self.store.unregister(self.id, vfy=self)
119 self.id = None