Changed persistent module to use shelve dictionaries

master
Stefan Krulj 14 years ago
parent efa16f5f64
commit de2ef57c41
  1. 105
      sys/persistent.py

@ -1,49 +1,90 @@
from ConfigParser import ConfigParser import shelve
import cPickle as pickle
import base64
from threading import Lock from threading import Lock
#TODO replace ConfigParser for better performance
class PersistentContainer(object): class PersistentContainer(object):
cfgparsers = dict() """A wrapper for shelve dictionaries, allowing basic transactions and namespaces
locks = dict() Be carefull as this module returns copies but no references. This means it needs
careful programming:
>>> pc = PersistentContainer("ns","foobar")
>>> pc["num"] = 3 # direct access using = operators works
>>> pc["list"] = [1,2,3]
>>> pc["num"] += 3 # this will work...
>>> print pc["num"]
6
>>> pc["list"].append(4) # ...but this wont.
>>> print pc["list"]
[1, 2, 3]
>>> tmp = pc["list"] # instead store a copy
>>> tmp.append(4) # alter the copy
>>> pc["list"]=tmp # and write it back
>>> print pc["list"]
[1, 2, 3, 4]
"""
refs = dict()
def __init__(self,namespace,path): def __init__(self,namespace,path):
"""Create a PersistentContainer object
namespace -- the namespace in which the variables will be saved
path -- path to the (n)dbm-file used to permanently store the values
"""
self.namespace = namespace self.namespace = namespace
self.path = path self.path = path
self.trans_cache = dict() self.trans_cache = dict()
self.trans_del = [] self.trans_del = []
if not path in PersistentContainer.locks: if not path in PersistentContainer.refs:
PersistentContainer.locks[path] = Lock() ref_cnt = 0
self.transaction = PersistentContainer.locks[path] lock = Lock()
dic = shelve.open(path,writeback=False)
if not path in PersistentContainer.cfgparsers: PersistentContainer.refs[path] = [ref_cnt,lock,dic]
PersistentContainer.cfgparsers[path] = ConfigParser() ref = PersistentContainer.refs[path]
PersistentContainer.cfgparsers[path].read([path]) ref[0]+=1
self.cp = PersistentContainer.cfgparsers[path] self.transaction = ref[1]
self.dic = ref[2]
if not self.cp.has_section(namespace):
self.cp.add_section(namespace) def __del__(self):
"""delete the object"""
assert self.path in PersistentContainer.refs
ref = PersistentContainer.refs[self.path]
ref[0] -= 1
if ref[0]==0:
self.dic.close()
del PersistentContainer.refs[self.path]
def start_transaction(self,blocking=True): def start_transaction(self,blocking=True):
"""Start an atomic transaction.
blocking -- If True the start of the transaction will wait until the running
is finished
"""
return self.transaction.acquire(blocking) return self.transaction.acquire(blocking)
def commit_transaction(self): def commit_transaction(self):
"""Commit the values that changed during the transaction"""
self._write() self._write()
self.transaction.release() self.transaction.release()
def rollback(self): def rollback(self):
for k in self.trans_cache: """Discard the values that changed during the transaction"""
for k in self.trans_cache.keys():
del self.trans_cache[k] del self.trans_cache[k]
self.transaction.release() self.transaction.release()
def keys(self):
"""Return the valid keys in the namespace"""
ret = []
for k in self.dict.keys():
if k.endswith("."+self.namespace):
ret.append(k[:-len("."+self.namespace)])
return ret
def is_owned(self): def is_owned(self):
"""Return True if there is an transaction running"""
return self.transaction.locked() return self.transaction.locked()
def __getitem__(self, name): def __getitem__(self, name):
if self.cp.has_option(self.namespace,name): key = name+"."+self.namespace
b64 = self.cp.get(self.namespace,name) if key in self.dic:
return pickle.loads(base64.b64decode(b64)) return self.dic[key]
return None return None
def __setitem__(self, name,val): def __setitem__(self, name,val):
@ -61,18 +102,18 @@ class PersistentContainer(object):
def __contains__(self,name): def __contains__(self,name):
return self.cp.has_option(self.namespace,name) key = name+"."+self.namespace
return key in self.dic
def _write(self): def _write(self):
for k in self.trans_cache: for name in self.trans_cache.keys():
val = self.trans_cache[k] key = name+"."+self.namespace
ser = pickle.dumps(val) val = self.trans_cache[name]
self.cp.set(self.namespace,k,base64.b64encode(ser)) self.dic[key]=val
del self.trans_cache[name]
while len(self.trans_del): while len(self.trans_del):
n = self.trans_del.pop() name = self.trans_del.pop()
self.cp.remove_option(self.namespace,n) key = name+"."+self.namespace
del self.dic[key]
f = open(self.path,'w') self.dic.sync()
self.cp.write(f)
f.close()

Loading…
Cancel
Save