# Author: Stuart D. Gathman # Copyright 2001 Business Management Systems, Inc. # This code is under the GNU General Public License. See COPYING for details. import os from time import sleep class PLock(object): "A simple /etc/passwd style lock,update,rename protocol for updating files." def __init__(self,basename): self.basename = basename self.fp = None def lock(self,lockname=None,mode=0o660,strict_perms=False): "Start an update transaction. Return FILE to write new version." self.unlock() if not lockname: lockname = self.basename + '.lock' self.lockname = lockname try: st = os.stat(self.basename) mode |= st.st_mode except OSError: pass u = os.umask(0o2) try: fd = os.open(lockname,os.O_WRONLY+os.O_CREAT+os.O_EXCL,mode) finally: os.umask(u) self.fp = os.fdopen(fd,'w') try: os.chown(self.lockname,-1,st.st_gid) except: if strict_perms: self.unlock() raise return self.fp def wlock(self,lockname=None): "Wait until lock is free, then start an update transaction." while True: try: return self.lock(lockname) except OSError: sleep(2) def commit(self,backname=None): "Commit update transaction with optional backup file." if not self.fp: raise IOError("File not locked") self.fp.close() self.fp = None if backname: try: os.remove(backname) except OSError: pass os.link(self.basename,backname) os.rename(self.lockname,self.basename) def unlock(self): "Cancel update transaction." if self.fp: try: self.fp.close() except: pass self.fp = None os.remove(self.lockname)