Earlier there was discussion of using a combination of
extend and aside to enable multiple readers and one
writer.
Here is a class that manages it for you: meta_safe.
You access the storage object as an attribute with
meta_safe and have meta_safe manage the extend and
aisde isses.
It also has a simple cross-platform method of 2-level
locking based of of the assumption that mkdir is one
successful once. If you want to manage locks yourself,
you can eaisly turn off locking.
The locking I call gentleman's locking. It is safe to
call unlock even if you do not have the lock(it just
won't do anything). And, you only keep your lock for a
predermined time. If you want to keep your lock
longer, you have to reassert it, before the time runs
out.
I called it meta_safe since with the combination of
locking and extend and aside, it is safer to write to
metakit.
Here is an example of usage
#####
import time,meta_safe
dbfile='main.mk'
#w means write
#open for safe writing and create the view
s=meta_safe.safe(dbfile,'w',create='a[i:S]')
vw = s.db.view('a')
vw.append(i=time.asctime())
s.close()
#open for safe writing
s=meta_safe.safe(dbfile,'w')
vw = s.db.view('a')
vw.append(i=time.asctime())
vw.append(i=time.asctime())
vw.append(i=time.asctime())
s.close()
#open for reading
s=meta_safe.safe(dbfile)
vw=s.db.view('a')
print len(vw)
for item in vw:
print item.i
s.unsafe_close()
#if you do not want locking set use_lock to 0
s=meta_safe.safe(dbfile,'w',create='a[i:S]',use_lock=0)
####meta_safe.py class####
import metakit,os,time
#Version 0.1
#John Nielsen
import metakit,os,time
class lock:
'''cross-platform locking.
Locking will raise exceptions.
Unlocking won't. So, unlock all you want'''
def __init__(self,*args,**kwds):
if not len(args): raise 'need a lock name'
#detemine how long to wait for locks(min)
if 'wait' in kwds:
self.wait=kwds['wait']*60
else:
self.wait=300
name=args[0]
self.d=name+'_lock/'
self.d2=name+'_lock2/'
self.locked={}
lock_successful=0
if self.locked.has_key(self.d2):
try:
#you got a lock, it is yours?
if os.stat(self.d2)[8]==\
self.locked.get(self.d2):
#try to extend lock before loss
#ATOMIC operation, extend may
#fail presence of self.d will
#prevent loss of lock
#just by the act of extending
os.rmdir(self.d2)
os.mkdir(self.d2)
os.rmdir(self.d)
os.mkdir(self.d)
self.locked[self.d2]=\
os.stat(self.d2)[8]
lock_successful=1
if self.locked.has_key(self.d2):
del(self.locked[self.d2])
result='Fail: lost lock'
except:
if self.locked.has_key(self.d2):
del(self.locked[self.d2])
result='Fail: lost lock'
else:
for t in range(0,self.wait): #try 10 times
#not locked yet, try to lock it
try:
os.mkdir(self.d) #ATOMIC
os.mkdir(self.d2) #ATOMIC
self.locked[self.d2]=\
os.stat(self.d2)[8]
lock_successful=1
break
except Exception,error:
result=error
print 'locking??',error
#mkdir probably failed
#lock already there,
#try to delete old lock
m_dir2=0;m_dir=0
if os.path.exists(self.d):
try:
m_dir2=os.stat(self.d2)[8]
except:
pass
try:
m_dir=os.stat(self.d)[8]
except:
pass
cur_tm=int(time.time())
#presence of either directory
#can stop you from taking lock
if cur_tm>m_dir+self.wait and\
cur_tm>m_dir2+self.wait:
#delete old locks
#ATOMIC here
os.rmdir(self.d2)
os.mkdir(self.d2)
os.rmdir(self.d)
os.mkdir(self.d)
self.locked[self.d2]=\
os.stat(self.d2)[8]
lock_successful=1
break
time.sleep(1)
#made it thru the loop, so we got no lock
if not lock_successful:
raise result
def unlock(self):
'''does not raise an exception,
safe to unlock as often as you want
it may just do nothing'''
if self.locked.has_key(self.d2):
#we're the ones that unlocked it,
#if time matched
if self.locked[self.d2]==\
os.stat(self.d2)[8]:
try:
del(self.locked[self.d2])
os.rmdir(self.d2)
os.rmdir(self.d)
return 1
except:
return 0
else:
del(self.locked[self.d2])
return 0
else:
return 0
class safe:
'''manage aside and extend automatically to allow
multiple safe readers and one writer'''
def __init__(self,*args,**kwds):
self.use_lock=1
self.l=None#locking placeholders
if 'lock' in kwds:
self.use_lock=kwds['lock']
if 'store' in kwds:
#must end in / or \
if kwds['store_dir'][-1] in ('/','\\'):
self.store_dir=kwds['store_dir']
else:
raise 'error: need / or \ to end
store'
else:
self.store=''
if len(args)==1:
#read-only
self.flag='r'
self.dbfile=self.store+args[0]
elif len(args)>1:
self.dbfile=args[0]
self.flag=args[1]
if self.flag not in ('r','w'):
raise 'error: use r or w'
self.dbaside=self.dbfile+'a'
if 'create' in kwds:
if self.use_lock:
l=lock(self.dbfile)##########LOCKING
db = metakit.storage(self.dbfile, 1)
vw=db.getas(kwds['create'])
db.commit()
del db
if self.use_lock:
l.unlock() ############UNLOCKING
self.db=self.dba=None
##
if self.flag=='r':
self.db = metakit.storage(self.dbfile, 0)
if os.path.exists(self.dbaside):
self.dba =\
metakit.storage(self.dbaside, 0)
elif self.flag=='w':
if self.use_lock:
self.l=lock(self.dbfile)######LOCKING
self.db = metakit.storage(self.dbfile, 0)
self.dba = metakit.storage(self.dbaside,
2)
self.db.aside(self.dba)
vw=self.db.view('a')
def close(self):
if self.flag=='w':
self.db.commit()
self.dba.commit()
############UNLOCKING
if self.use_lock:
if self.l: self.l.unlock()
del self.db
del self.dba
def unsafe_close(self):
l=lock(self.dbfile)##########LOCKING
#synchronize aside and main
db = metakit.storage(self.dbfile, 1)
dba = metakit.storage(self.dbaside, 1)
db.aside(dba)
db.commit(1)
del db
del dba
l.unlock()
if __name__ == "__main__":
print 'testing'
dbfile='main.mk'
#create is only needed if the view doesn't exist
s=safe(dbfile,'w',create='a[i:S]',lock=1)
vw = s.db.view('a')
vw.append(i=time.asctime())
vw.append(i=time.asctime())
vw.append(i=time.asctime())
s.close()
s=safe(dbfile)
vw=s.db.view('a')
print len(vw)
for item in vw:
print item.i
s.unsafe_close()
__________________________________
Do you Yahoo!?
Free Pop-Up Blocker - Get it now
http://companion.yahoo.com/
_____________________________________________
Metakit mailing list - [EMAIL PROTECTED]
http://www.equi4.com/mailman/listinfo/metakit