NgLockedFile.py
Python Class written to enable file writing and reading by multiple processes without conflicting accesses.
es
Some compi
## @package NgLockedFile # @brief NgLockedFile class implementation # @author Nathan # @date 01/05/2008 # @version 0.1 # @bug No known bugs. # @import # @li sys, os, shutil, time, random # @li from datetime import datetime # @li os.path as p import sys, os, shutil, time, random from datetime import datetime import os.path as p ## Class representing a locked file for writing, reading or read appending. # Opens the file in a specified mode and creates a lock file which contains the id of the locker. class NgLockedFile(object): ## Init method for NgLockedFile. # Opens the file in a specified mode and creates a lock file which contains the id of the locker.\n # While the lock file exists any other process using the NgLockedFile to open the file will find \n # a locking conflict for 20 secs, asume it has crashed and so restore the file from an auto backup made with backup() \n # @param _filePath (String) # @param _mode (String) ('w'| 'r'| 'r+') # @return None def __init__(self,_filePath, _mode): ## For storing whether the file is locked. (\b Bool) self.lockStatus = False ## Random 8 digit suffix to keep the self.lockerID unique. (\b Int) self.suffix = random.randrange(10000000,99999999) ## Part of the self.lockerID \b String userName = os.getenv('USERNAME') if not userName: userName = os.getenv('COMPUTERNAME') if not userName: userName ='' ## Used to identify the locker, created from userName+str(self.suffix). (\b String) self.lockerID = userName+str(self.suffix) ## Used to identify whether self owns the lock. (\b Bool) self.myLock =False ## Used when reading the lockerID in a file. (\b String) self.lockedBy='' if not p.exists(_filePath): raise Exception ('Could not locate file: '+_filePath) ## Used to store the file open mode. (\b String) self.filePath = _filePath ## Used to store the current access mode of the open file. (\b String) self.mode = _mode filePathSplit = self.filePath.split('/') ## Used to store the file name the file to be locked. (\b String) self.fileName = filePathSplit[-1] #print "Filename is: "+ str(self.fileName) #LOCKED FILE PATH lockFilePathTmp =filePathSplit[0] if lockFilePathTmp == '': lockFilePathTmp= '/' i=2 else: i=1 while filePathSplit[i] != self.fileName: lockFilePathTmp = lockFilePathTmp+'/'+filePathSplit[i] #print lockFilePathTmp i+=1 lockFilePathTmp = lockFilePathTmp+'/' #print lockFilePathTmp #GET THE FILE EXTENSION fileNameSplit = self.fileName.split('.') ## Used to store the file ext ofthe file to be locked. (\b String) self.fileExt = fileNameSplit[-1] #print "Filename Extension is: "+ str(self.fileExt) i=0 while fileNameSplit[i] != str(self.fileExt): lockFilePathTmp = lockFilePathTmp + fileNameSplit[i]+'.' i+=1 ## Used to store the file ext of the lock. (\b String) self.lockFilePath = (lockFilePathTmp+'locked') #print "File lock path is: "+ str(self.lockFilePath) if p.exists(self.lockFilePath): self.lockStatus = True else: self.lockStatus = False #OPEN AND LOCK #print 'Locking File: '+self.filePath+'...' ## Used to store the current date. (\b Int) self.currentDate = self.getCurrentDate() ## Used to store the currenttime in secs. (\b Float) self.currentTimeSecs = self.currentTimeToSeconds() ## Time in secs before the lock will overide another lock and restore the previous state of the file. (\b Float) self.restoreDuration = 20.0 self.openFile(self.mode, True) ## gets the current date # @return Date (Int) def getCurrentDate(self): timeNow = datetime.now() dateSplit = str(timeNow).split(' ')[0] dateUnits = str(dateSplit).split('-') date = int(dateUnits[2]) return date ## gets the current time in secs. # @return Secs (Float) def currentTimeToSeconds(self): timeNow = datetime.now() timeSplit = str(timeNow).split(' ')[1] dateSplit = str(timeNow).split(' ')[0] dateUnits = str(dateSplit).split('-') timeUnits = str(timeSplit).split(':') date = int(dateUnits[2]) hours = float(timeUnits[0]) mins = float(timeUnits[1]) secs = float(timeUnits[2]) hourSecs = hours *3600.0 minSecs = mins *60.0 secs = hourSecs + minSecs + secs if date != self.currentDate: secs = secs + 86400.0 return secs ## Locks the file\n # Three levels of checking so multiple locks cannot be achieved during read times etc.\n # This lock will retore a locked file and overide its lock if it has to try to lock \n # for longer than the self.restoreDuration/n def lock(self): #self.info('Trying to lock...') locked=0 i=0 while locked ==0 and i < 300: try: j=0 while not self.myLock: if j > 0: self.info('Not My Lock! Locked by:'+self.lockedBy+'...retrying.!') k=0 self.currentDate = self.getCurrentDate() self.currentTimeSecs = self.currentTimeToSeconds() while self.lockStatus == True: if k % 100 == 0: self.info('Locking Conflict...retrying...!') #time.sleep(0.001) tempCurrentTimeToSeconds = self.currentTimeToSeconds() if tempCurrentTimeToSeconds >= self.currentTimeSecs + self.restoreDuration: self.info(self.lockerID+' timed out on file locking!') f= file(self.lockFilePath, 'r') self.lockedBy = f.readline() f.close() self.info(self.lockerID+' restoring file!.....Locked by: '+self.lockedBy) try: os.remove(self.lockFilePath) self.lockStatus = False self.restore() except: self.lockStatus = True #raise Exception("Timed out on file locking!") self.getLockStatus() k+=1 self.lockStatus = True f= file(self.lockFilePath, 'w') f.write(str(self.lockerID)) f.close() self.verifyIsMyLock() j=1 self.backup() #self.info('File Locked!') locked=1 except Exception, why: self.info(self.lockerID+' could not get File lock! Error: '+str(why)) i+=1 if locked ==0: self.info(self.lockerID+' could not get File lock. Error: '+str(why)) raise Exception(self.lockerID+' could not get File lock. Error: '+str(why)) ## Unlocks the file. def unlock(self): if self.lockStatus == False: raise Exception("File is not locked!") if not self.myLock: #self.info("ERROR: This file has not been locked by: "+ str(self.lockerID)+". Files can only be unlocked by the user who locked it!") raise Exception("This file has not been locked by: "+ str(self.lockerID)+". Files can only be unlocked by the user who locked it!") i=0 removed =0 while removed ==0 and i < 10000: try: os.remove(self.lockFilePath) removed =1 except: removed =0 i+=1 self.lockStatus = False #self.info('File Unlocked!') ## Queries self.lockStatus. # @return self.lockStatus def getLockStatus(self): if p.exists(self.lockFilePath): self.lockStatus = True else: self.lockStatus = False return self.lockStatus ## Queries the locked file to check whether the lock belongs to the current instance. def verifyIsMyLock(self): canRead = False i=0 while not canRead and i < 1000000: if not p.exists(self.lockFilePath): canRead = True self.myLock = False return else: try: f= file(self.lockFilePath, 'r') self.lockedBy=f.readline() f.close() if self.lockedBy != str(self.lockerID): if self.lockedBy =='': if not p.exists(self.lockFilePath): canRead = True self.myLock = False return j=0 while self.lockedBy== '': if j==10: self.lockedBy = 'Unknown' break f= file(self.lockFilePath, 'r') self.lockedBy = f.readline() f.close() j+=1 else: #self.info("This file has been locked by: "+ self.lockedBy+". Files cannot be locked twice!") self.myLock = False #raise Exception("This file has been locked by: "+ line+". Files can only be unlocked by the user who locked it!") else: self.myLock = True canRead = True except Exception, why: self.info('Error: '+str(why)) canRead = False i+=1 if i >= 1000000: self.info(self.lockerID+" could not get File lock.") raise Exception("Could not get File lock!") ## Opens the file # @param _mode (String) Can be 'w', 'r', 'r+' # @param _lock (Bool) locks if true def openFile(self, _mode, _lock=True): if _lock: self.lock() self.mode = _mode ## The opened file object \b File Object self.fileInstance = file(self.filePath, self.mode) ## reads the file # @return result (String) def read(self): result = self.fileInstance.read() return result ## reads a line from the file # @return result (String) def readline(self): result = self.fileInstance.readline() return result ## reads a lines from the file # @return result (List of Strings) def readlines(self): result = self.fileInstance.readlines() return result ## Write to the file # @param _toWrite (String) Data to write def write(self,_toWrite): self.fileInstance.write(_toWrite) ## Writes a sequence of data to the file # @param _sequenceToWrite (List of Strings) Datas to write def writelines(self, _sequenceToWrite): self.fileInstance.writelines() ## Closes the file # @param _unlock (Bool) Unlocks if true def closeFile(self,_unlock=True): self.fileInstance.close() if _unlock == True: #print 'Unlocking File: '+self.filePath+'...' self.unlock() ## Backs up the file before locking def backup(self): fileBackUp =(self.filePath+'.NgLockedFile_BackUp') shutil.copyfile( self.filePath, fileBackUp) ## Restores the file from the backup def restore(self): fileBackUp =(self.filePath+'.NgLockedFile_BackUp') shutil.copyfile( fileBackUp, self.filePath) ## Writes to the log in \b Error mode # @param msg (String) def error(self, msg): self.msg = str(msg) self.writeLog('e') ## Writes to the log in \b Info mode # @param msg (String) def info(self, msg): self.msg = str(msg) self.writeLog('i') ## Writes any Exceptions to the log found at: seNger + '/CGI_UK/Resource/Config/logs/NgLockedFile_log.txt # @param _logMode (String) def writeLog(self, _logMode): srcFile = sys._getframe(2).f_code.co_filename srcFile = str(srcFile) srcFile = srcFile.split('\\').pop().strip('.py') srcFile = srcFile.strip('<>').replace(' ', '_') if not len(srcFile): srcFile = 'UNKNOWN_SOURCE_FILE' self.msg = 'Failed to deduct source of error for following error: ' + self.msg self.src = srcFile if os.getenv('NgDEVKIT'): logName = self.src + '_dev' else: logName = self.src if (os.getenv('NgLOCAL')): seNger = 'C:' else: seNger = '//Zeus' if os.getenv('NgLOCATION') == 'london': seNger = '//Jurojin' logPath = seNger + '/CGI_UK/Resource/Config/logs/' + logName + '_log.txt' if not p.exists(logPath): try: f = open( logPath, 'w') f.close() except (Exception), why: raise Exception (why) try: f = open( logPath,'r') oldLog = f.read() f.close() f = open( logPath, 'w') except Exception: return f.write('--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n') if _logMode == 'e': f.write('ERROR:\n') elif _logMode == 'w': f.write('WARNING:\n') elif _logMode == 'i': f.write('INFO:\n') f.write(' - WHO: ' + '"' + self.lockerID + '"' + ' ' + str(datetime.now())+ '\n') f.write(' - WHERE: ' + '"' + self.src + '"' + ' in function ' + '"' + sys._getframe(2).f_code.co_name + '"' + ' on line ' + str(sys._getframe(2).f_lineno) + '\n') f.write(' - WHAT: '+'"'+self.filePath + '\n') f.write(self.msg.rstrip('\n') + '\n') f.write('--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n') f.write(oldLog) f.close()